| 1 | //
 | 
|---|
| 2 | // mpqc_extract.cc
 | 
|---|
| 3 | //
 | 
|---|
| 4 | // Copyright (C) 1996 Limit Point Systems, Inc.
 | 
|---|
| 5 | //
 | 
|---|
| 6 | // Author: Edward Seidl <seidl@janed.com>
 | 
|---|
| 7 | // Maintainer: LPS
 | 
|---|
| 8 | //
 | 
|---|
| 9 | // This file is part of MPQC.
 | 
|---|
| 10 | //
 | 
|---|
| 11 | // MPQC is free software; you can redistribute it and/or modify
 | 
|---|
| 12 | // it under the terms of the GNU General Public License as published by
 | 
|---|
| 13 | // the Free Software Foundation; either version 2, or (at your option)
 | 
|---|
| 14 | // any later version.
 | 
|---|
| 15 | //
 | 
|---|
| 16 | // MPQC is distributed in the hope that it will be useful,
 | 
|---|
| 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 19 | // GNU General Public License for more details.
 | 
|---|
| 20 | //
 | 
|---|
| 21 | // You should have received a copy of the GNU General Public License
 | 
|---|
| 22 | // along with the MPQC; see the file COPYING.  If not, write to
 | 
|---|
| 23 | // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
|---|
| 24 | //
 | 
|---|
| 25 | // The U.S. Government is granted a limited license as per AL 91-7.
 | 
|---|
| 26 | //
 | 
|---|
| 27 | // \note This was extracted from \file mpqc.cc for refactoring into a library.
 | 
|---|
| 28 | 
 | 
|---|
| 29 | #ifdef HAVE_CONFIG_H
 | 
|---|
| 30 | #include <scconfig.h>
 | 
|---|
| 31 | #endif
 | 
|---|
| 32 | 
 | 
|---|
| 33 | #ifdef HAVE_JOBMARKET
 | 
|---|
| 34 | // include headers that implement a archive in simple text format
 | 
|---|
| 35 | // otherwise BOOST_CLASS_EXPORT_IMPLEMENT has no effect
 | 
|---|
| 36 | #include <boost/archive/text_oarchive.hpp>
 | 
|---|
| 37 | #include <boost/archive/text_iarchive.hpp>
 | 
|---|
| 38 | 
 | 
|---|
| 39 | #include "JobMarket/Results/FragmentResult.hpp"
 | 
|---|
| 40 | #include "JobMarket/poolworker_main.hpp"
 | 
|---|
| 41 | 
 | 
|---|
| 42 | #include "chemistry/qc/scf/scfops.h"
 | 
|---|
| 43 | 
 | 
|---|
| 44 | #ifdef HAVE_MPQCDATA
 | 
|---|
| 45 | #include "Jobs/MPQCJob.hpp"
 | 
|---|
| 46 | #include "Fragmentation/Summation/Containers/MPQCData.hpp"
 | 
|---|
| 47 | 
 | 
|---|
| 48 | #include <chemistry/qc/basis/obint.h>
 | 
|---|
| 49 | #include <chemistry/qc/basis/symmint.h>
 | 
|---|
| 50 | #endif
 | 
|---|
| 51 | 
 | 
|---|
| 52 | #include <algorithm>
 | 
|---|
| 53 | #include <stdlib.h>
 | 
|---|
| 54 | #endif
 | 
|---|
| 55 | 
 | 
|---|
| 56 | #include <chemistry/qc/scf/linkage.h>
 | 
|---|
| 57 | #include <chemistry/qc/dft/linkage.h>
 | 
|---|
| 58 | #include <chemistry/qc/mbpt/linkage.h>
 | 
|---|
| 59 | #ifdef HAVE_SC_SRC_LIB_CHEMISTRY_QC_MBPTR12
 | 
|---|
| 60 | #  include <chemistry/qc/mbptr12/linkage.h>
 | 
|---|
| 61 | #endif
 | 
|---|
| 62 | #ifdef HAVE_SC_SRC_LIB_CHEMISTRY_QC_CINTS
 | 
|---|
| 63 | #  include <chemistry/qc/cints/linkage.h>
 | 
|---|
| 64 | #endif
 | 
|---|
| 65 | //#include <chemistry/qc/psi/linkage.h>
 | 
|---|
| 66 | #include <util/state/linkage.h>
 | 
|---|
| 67 | #ifdef HAVE_SC_SRC_LIB_CHEMISTRY_QC_CC
 | 
|---|
| 68 | #  include <chemistry/qc/cc/linkage.h>
 | 
|---|
| 69 | #endif
 | 
|---|
| 70 | #ifdef HAVE_SC_SRC_LIB_CHEMISTRY_QC_PSI
 | 
|---|
| 71 | #  include <chemistry/qc/psi/linkage.h>
 | 
|---|
| 72 | #endif
 | 
|---|
| 73 | #ifdef HAVE_SC_SRC_LIB_CHEMISTRY_QC_INTCCA
 | 
|---|
| 74 | #  include <chemistry/qc/intcca/linkage.h>
 | 
|---|
| 75 | #endif
 | 
|---|
| 76 | 
 | 
|---|
| 77 | #include "mpqc_extract.h"
 | 
|---|
| 78 | 
 | 
|---|
| 79 | using namespace std;
 | 
|---|
| 80 | using namespace sc;
 | 
|---|
| 81 | 
 | 
|---|
| 82 | static int getCoreElectrons(const int z)
 | 
|---|
| 83 | {
 | 
|---|
| 84 |   int n=0;
 | 
|---|
| 85 |   if (z > 2) n += 2;
 | 
|---|
| 86 |   if (z > 10) n += 8;
 | 
|---|
| 87 |   if (z > 18) n += 8;
 | 
|---|
| 88 |   if (z > 30) n += 10;
 | 
|---|
| 89 |   if (z > 36) n += 8;
 | 
|---|
| 90 |   if (z > 48) n += 10;
 | 
|---|
| 91 |   if (z > 54) n += 8;
 | 
|---|
| 92 |   return n;
 | 
|---|
| 93 | }
 | 
|---|
| 94 | 
 | 
|---|
| 95 | /** Finds the region index to a given timer region name.
 | 
|---|
| 96 |  *
 | 
|---|
| 97 |  * @param nregion number of regions
 | 
|---|
| 98 |  * @param region_names array with name of each region
 | 
|---|
| 99 |  * @param name name of desired region
 | 
|---|
| 100 |  * @return index of desired region in array
 | 
|---|
| 101 |  */
 | 
|---|
| 102 | int findTimerRegion(const int &nregion, const char **®ion_names, const char *name)
 | 
|---|
| 103 | {
 | 
|---|
| 104 |   int region=0;
 | 
|---|
| 105 |   for (;region<nregion;++region) {
 | 
|---|
| 106 |     //std::cout << "Comparing " << region_names[region] << " and " << name << "." << std::endl;
 | 
|---|
| 107 |     if (strcmp(region_names[region], name) == 0)
 | 
|---|
| 108 |       break;
 | 
|---|
| 109 |   }
 | 
|---|
| 110 |   if (region == nregion)
 | 
|---|
| 111 |     region = 0;
 | 
|---|
| 112 |   return region;
 | 
|---|
| 113 | }
 | 
|---|
| 114 | 
 | 
|---|
| 115 | /** Extractor function that is called after all calculations have been made.
 | 
|---|
| 116 |  *
 | 
|---|
| 117 |  * \param data result structure to fill
 | 
|---|
| 118 |  */
 | 
|---|
| 119 | void extractResults(
 | 
|---|
| 120 |          Ref<MolecularEnergy> &mole,
 | 
|---|
| 121 |          void *_data
 | 
|---|
| 122 |          )
 | 
|---|
| 123 | {
 | 
|---|
| 124 |   MPQCData &data = *static_cast<MPQCData *>(_data);
 | 
|---|
| 125 |         Ref<Wavefunction> wfn;
 | 
|---|
| 126 |         wfn << mole;
 | 
|---|
| 127 | //     ExEnv::out0() << "The number of atomic orbitals: " << wfn->ao_dimension()->n() << endl;
 | 
|---|
| 128 | //     ExEnv::out0() << "The AO density matrix is ";
 | 
|---|
| 129 | //     wfn->ao_density()->print(ExEnv::out0());
 | 
|---|
| 130 | //     ExEnv::out0() << "The natural density matrix is ";
 | 
|---|
| 131 | //     wfn->natural_density()->print(ExEnv::out0());
 | 
|---|
| 132 | //     ExEnv::out0() << "The Gaussian basis is " << wfn->basis()->name() << endl;
 | 
|---|
| 133 | //     ExEnv::out0() << "The Gaussians sit at the following centers: " << endl;
 | 
|---|
| 134 | //     for (int nr = 0; nr< wfn->basis()->ncenter(); ++nr) {
 | 
|---|
| 135 | //       ExEnv::out0() << nr << " basis function has its center at ";
 | 
|---|
| 136 | //       for (int i=0; i < 3; ++i)
 | 
|---|
| 137 | //           ExEnv::out0() << wfn->basis()->r(nr,i) << "\t";
 | 
|---|
| 138 | //       ExEnv::out0() << endl;
 | 
|---|
| 139 | //     }
 | 
|---|
| 140 |         // store accuracies
 | 
|---|
| 141 |         data.accuracy = mole->value_result().actual_accuracy();
 | 
|---|
| 142 |         data.desired_accuracy = mole->value_result().desired_accuracy();
 | 
|---|
| 143 |         // print the energy
 | 
|---|
| 144 |         data.energies.total = wfn->energy();
 | 
|---|
| 145 |         data.energies.nuclear_repulsion = wfn->nuclear_repulsion_energy();
 | 
|---|
| 146 |         {
 | 
|---|
| 147 |           CLHF *clhf = dynamic_cast<CLHF*>(wfn.pointer());
 | 
|---|
| 148 |           if (clhf != NULL) {
 | 
|---|
| 149 |             double ex, ec;
 | 
|---|
| 150 |             clhf->two_body_energy(ec, ex);
 | 
|---|
| 151 |             data.energies.electron_coulomb = ec;
 | 
|---|
| 152 |             data.energies.electron_exchange = ex;
 | 
|---|
| 153 |             clhf = NULL;
 | 
|---|
| 154 |           } else {
 | 
|---|
| 155 |             ExEnv::out0() << "INFO: There is no direct CLHF information available." << endl;
 | 
|---|
| 156 |             data.energies.electron_coulomb = 0.;
 | 
|---|
| 157 |             data.energies.electron_exchange = 0.;
 | 
|---|
| 158 |           }
 | 
|---|
| 159 |         }
 | 
|---|
| 160 |         SCF *scf = NULL;
 | 
|---|
| 161 |         {
 | 
|---|
| 162 |           MBPT2 *mbpt2 = dynamic_cast<MBPT2*>(wfn.pointer());
 | 
|---|
| 163 |           if (mbpt2 != NULL) {
 | 
|---|
| 164 |             data.energies.correlation = mbpt2->corr_energy();
 | 
|---|
| 165 |             scf = mbpt2->ref().pointer();
 | 
|---|
| 166 |             CLHF *clhf = dynamic_cast<CLHF*>(scf);
 | 
|---|
| 167 |             if (clhf != NULL) {
 | 
|---|
| 168 |               double ex, ec;
 | 
|---|
| 169 |               clhf->two_body_energy(ec, ex);
 | 
|---|
| 170 |               data.energies.electron_coulomb = ec;
 | 
|---|
| 171 |               data.energies.electron_exchange = ex;
 | 
|---|
| 172 |               clhf = NULL;
 | 
|---|
| 173 |             } else {
 | 
|---|
| 174 |               ExEnv::out0() << "INFO: There is no reference CLHF information available either." << endl;
 | 
|---|
| 175 |               data.energies.electron_coulomb = 0.;
 | 
|---|
| 176 |               data.energies.electron_exchange = 0.;
 | 
|---|
| 177 |             }
 | 
|---|
| 178 |             mbpt2 = 0;
 | 
|---|
| 179 |           } else {
 | 
|---|
| 180 |             ExEnv::out0() << "INFO: There is no MBPT2 information available." << endl;
 | 
|---|
| 181 |             data.energies.correlation = 0.;
 | 
|---|
| 182 |             scf = dynamic_cast<SCF*>(wfn.pointer());
 | 
|---|
| 183 |             if (scf == NULL)
 | 
|---|
| 184 |               abort();
 | 
|---|
| 185 |           }
 | 
|---|
| 186 |         }
 | 
|---|
| 187 |         {
 | 
|---|
| 188 |           // taken from clscf.cc: CLSCF::scf_energy() (but see also Szabo/Ostlund)
 | 
|---|
| 189 | 
 | 
|---|
| 190 |           RefSymmSCMatrix t = scf->overlap();
 | 
|---|
| 191 |           RefSymmSCMatrix cl_dens_ = scf->ao_density();
 | 
|---|
| 192 | 
 | 
|---|
| 193 |           SCFEnergy *eop = new SCFEnergy;
 | 
|---|
| 194 |           eop->reference();
 | 
|---|
| 195 |           if (t.dim()->equiv(cl_dens_.dim())) {
 | 
|---|
| 196 |             Ref<SCElementOp2> op = eop;
 | 
|---|
| 197 |             t.element_op(op,cl_dens_);
 | 
|---|
| 198 |             op=0;
 | 
|---|
| 199 |           }
 | 
|---|
| 200 |           eop->dereference();
 | 
|---|
| 201 | 
 | 
|---|
| 202 |           data.energies.overlap = eop->result();
 | 
|---|
| 203 | 
 | 
|---|
| 204 |           delete eop;
 | 
|---|
| 205 |           t = 0;
 | 
|---|
| 206 |           cl_dens_ = 0;
 | 
|---|
| 207 |         }
 | 
|---|
| 208 |         {
 | 
|---|
| 209 |           // taken from Wavefunction::core_hamiltonian()
 | 
|---|
| 210 |           RefSymmSCMatrix hao(scf->basis()->basisdim(), scf->basis()->matrixkit());
 | 
|---|
| 211 |           hao.assign(0.0);
 | 
|---|
| 212 |           Ref<PetiteList> pl = scf->integral()->petite_list();
 | 
|---|
| 213 |           Ref<SCElementOp> hc =
 | 
|---|
| 214 |             new OneBodyIntOp(new SymmOneBodyIntIter(scf->integral()->kinetic(), pl));
 | 
|---|
| 215 |           hao.element_op(hc);
 | 
|---|
| 216 |           hc=0;
 | 
|---|
| 217 | 
 | 
|---|
| 218 |           RefSymmSCMatrix h(scf->so_dimension(), scf->basis_matrixkit());
 | 
|---|
| 219 |           pl->symmetrize(hao,h);
 | 
|---|
| 220 | 
 | 
|---|
| 221 |           // taken from clscf.cc: CLSCF::scf_energy() (but see also Szabo/Ostlund)
 | 
|---|
| 222 |           RefSymmSCMatrix cl_dens_ = scf->ao_density();
 | 
|---|
| 223 | 
 | 
|---|
| 224 |           SCFEnergy *eop = new SCFEnergy;
 | 
|---|
| 225 |           eop->reference();
 | 
|---|
| 226 |           if (h.dim()->equiv(cl_dens_.dim())) {
 | 
|---|
| 227 |             Ref<SCElementOp2> op = eop;
 | 
|---|
| 228 |             h.element_op(op,cl_dens_);
 | 
|---|
| 229 |             op=0;
 | 
|---|
| 230 |           }
 | 
|---|
| 231 |           eop->dereference();
 | 
|---|
| 232 | 
 | 
|---|
| 233 |           data.energies.kinetic = 2.*eop->result();
 | 
|---|
| 234 | 
 | 
|---|
| 235 |           delete eop;
 | 
|---|
| 236 |           hao = 0;
 | 
|---|
| 237 |           h = 0;
 | 
|---|
| 238 |           cl_dens_ = 0;
 | 
|---|
| 239 |         }
 | 
|---|
| 240 |         {
 | 
|---|
| 241 |           // set to potential energy between nuclei and electron charge distribution
 | 
|---|
| 242 |           RefSymmSCMatrix hao(scf->basis()->basisdim(), scf->basis()->matrixkit());
 | 
|---|
| 243 |           hao.assign(0.0);
 | 
|---|
| 244 |           Ref<PetiteList> pl = scf->integral()->petite_list();
 | 
|---|
| 245 |           Ref<SCElementOp> hc =
 | 
|---|
| 246 |             new OneBodyIntOp(new SymmOneBodyIntIter(scf->integral()->nuclear(), pl));
 | 
|---|
| 247 |           hao.element_op(hc);
 | 
|---|
| 248 |           hc=0;
 | 
|---|
| 249 | 
 | 
|---|
| 250 |           RefSymmSCMatrix h(scf->so_dimension(), scf->basis_matrixkit());
 | 
|---|
| 251 |           pl->symmetrize(hao,h);
 | 
|---|
| 252 | 
 | 
|---|
| 253 |           // taken from clscf.cc: CLSCF::scf_energy() (but see also Szabo/Ostlund)
 | 
|---|
| 254 |           RefSymmSCMatrix cl_dens_ = scf->ao_density();
 | 
|---|
| 255 | 
 | 
|---|
| 256 |           SCFEnergy *eop = new SCFEnergy;
 | 
|---|
| 257 |           eop->reference();
 | 
|---|
| 258 |           if (h.dim()->equiv(cl_dens_.dim())) {
 | 
|---|
| 259 |             Ref<SCElementOp2> op = eop;
 | 
|---|
| 260 |             h.element_op(op,cl_dens_);
 | 
|---|
| 261 |             op=0;
 | 
|---|
| 262 |           }
 | 
|---|
| 263 |           eop->dereference();
 | 
|---|
| 264 | 
 | 
|---|
| 265 |           data.energies.hcore = 2.*eop->result();
 | 
|---|
| 266 | 
 | 
|---|
| 267 |           delete eop;
 | 
|---|
| 268 |           hao = 0;
 | 
|---|
| 269 |           h = 0;
 | 
|---|
| 270 |           cl_dens_ = 0;
 | 
|---|
| 271 |         }
 | 
|---|
| 272 |         ExEnv::out0() << "total is " << data.energies.total << endl;
 | 
|---|
| 273 |         ExEnv::out0() << "nuclear_repulsion is " << data.energies.nuclear_repulsion << endl;
 | 
|---|
| 274 |         ExEnv::out0() << "electron_coulomb is " << data.energies.electron_coulomb << endl;
 | 
|---|
| 275 |         ExEnv::out0() << "electron_exchange is " << data.energies.electron_exchange << endl;
 | 
|---|
| 276 |         ExEnv::out0() << "correlation is " << data.energies.correlation << endl;
 | 
|---|
| 277 |         ExEnv::out0() << "overlap is " << data.energies.overlap << endl;
 | 
|---|
| 278 |         ExEnv::out0() << "kinetic is " << data.energies.kinetic << endl;
 | 
|---|
| 279 |         ExEnv::out0() << "hcore is " << data.energies.hcore << endl;
 | 
|---|
| 280 |         ExEnv::out0() << "sum is " <<
 | 
|---|
| 281 |             data.energies.nuclear_repulsion
 | 
|---|
| 282 |             + data.energies.electron_coulomb
 | 
|---|
| 283 |             + data.energies.electron_exchange
 | 
|---|
| 284 |             + data.energies.correlation
 | 
|---|
| 285 |             + data.energies.kinetic
 | 
|---|
| 286 |             + data.energies.hcore
 | 
|---|
| 287 |             << endl;
 | 
|---|
| 288 | 
 | 
|---|
| 289 |         ExEnv::out0() << endl << indent
 | 
|---|
| 290 |              << scprintf("Value of the MolecularEnergy: %15.10f",
 | 
|---|
| 291 |                          mole->energy())
 | 
|---|
| 292 |              << endl;
 | 
|---|
| 293 |         // print the gradient
 | 
|---|
| 294 |         RefSCVector grad;
 | 
|---|
| 295 |         if (mole->gradient_result().computed()) {
 | 
|---|
| 296 |           grad = mole->gradient_result().result_noupdate();
 | 
|---|
| 297 |         }
 | 
|---|
| 298 |         // gradient calculation needs to be activated in the configuration
 | 
|---|
| 299 |         // some methods such as open shell MBPT2 do not allow for gradient calc.
 | 
|---|
| 300 | //     else {
 | 
|---|
| 301 | //       grad = mole->gradient();
 | 
|---|
| 302 | //     }
 | 
|---|
| 303 |         if (grad.nonnull()) {
 | 
|---|
| 304 |           data.forces.resize(grad.dim()/3);
 | 
|---|
| 305 |           for (int j=0;j<grad.dim()/3; ++j) {
 | 
|---|
| 306 |             data.forces[j].resize(3, 0.);
 | 
|---|
| 307 |           }
 | 
|---|
| 308 |           ExEnv::out0() << "Gradient of the MolecularEnergy:" << std::endl;
 | 
|---|
| 309 |           for (int j=0;j<grad.dim()/3; ++j) {
 | 
|---|
| 310 |             ExEnv::out0() << "\t";
 | 
|---|
| 311 |             for (int i=0; i< 3; ++i) {
 | 
|---|
| 312 |               data.forces[j][i] = grad[3*j+i];
 | 
|---|
| 313 |               ExEnv::out0() << grad[3*j+i] << "\t";
 | 
|---|
| 314 |             }
 | 
|---|
| 315 |             ExEnv::out0() << endl;
 | 
|---|
| 316 |           }
 | 
|---|
| 317 |         } else {
 | 
|---|
| 318 |           ExEnv::out0() << "INFO: There is no gradient information available." << endl;
 | 
|---|
| 319 |         }
 | 
|---|
| 320 |         grad = NULL;
 | 
|---|
| 321 | 
 | 
|---|
| 322 |         {
 | 
|---|
| 323 |           // eigenvalues (this only works if we have a OneBodyWavefunction, i.e. SCF procedure)
 | 
|---|
| 324 |           // eigenvalues seem to be invalid for unrestricted SCF calculation
 | 
|---|
| 325 |           // (see UnrestrictedSCF::eigenvalues() implementation)
 | 
|---|
| 326 |           UnrestrictedSCF *uscf = dynamic_cast<UnrestrictedSCF*>(wfn.pointer());
 | 
|---|
| 327 |           if ((scf != NULL) && (uscf == NULL)) {
 | 
|---|
| 328 | //          const double scfernergy = scf->energy();
 | 
|---|
| 329 |             RefDiagSCMatrix evals = scf->eigenvalues();
 | 
|---|
| 330 | 
 | 
|---|
| 331 |             ExEnv::out0() << "Eigenvalues:" << endl;
 | 
|---|
| 332 |             for(int i=0;i<wfn->oso_dimension(); ++i) {
 | 
|---|
| 333 |               data.energies.eigenvalues.push_back(evals(i));
 | 
|---|
| 334 |               ExEnv::out0() << i << "th eigenvalue is " << evals(i) << endl;
 | 
|---|
| 335 |             }
 | 
|---|
| 336 |           } else {
 | 
|---|
| 337 |               ExEnv::out0() << "INFO: There is no eigenvalue information available." << endl;
 | 
|---|
| 338 |           }
 | 
|---|
| 339 |         }
 | 
|---|
| 340 |         // we do sample the density only on request
 | 
|---|
| 341 |           {
 | 
|---|
| 342 |             // fill positions and charges (NO LONGER converting from bohr radii to angstroem)
 | 
|---|
| 343 |             const double AtomicLengthToAngstroem = 1.;//0.52917721;
 | 
|---|
| 344 |             data.positions.reserve(wfn->molecule()->natom());
 | 
|---|
| 345 |             data.atomicnumbers.reserve(wfn->molecule()->natom());
 | 
|---|
| 346 |             data.charges.reserve(wfn->molecule()->natom());
 | 
|---|
| 347 |             for (int iatom=0;iatom < wfn->molecule()->natom(); ++iatom) {
 | 
|---|
| 348 |               data.atomicnumbers.push_back(wfn->molecule()->Z(iatom));
 | 
|---|
| 349 |               double charge = wfn->molecule()->Z(iatom);
 | 
|---|
| 350 |               if (data.DoValenceOnly == MPQCData::DoSampleValenceOnly)
 | 
|---|
| 351 |                 charge -= getCoreElectrons((int)charge);
 | 
|---|
| 352 |               data.charges.push_back(charge);
 | 
|---|
| 353 |               std::vector<double> pos(3, 0.);
 | 
|---|
| 354 |               for (int j=0;j<3;++j)
 | 
|---|
| 355 |                 pos[j] = wfn->molecule()->r(iatom, j)*AtomicLengthToAngstroem;
 | 
|---|
| 356 |               data.positions.push_back(pos);
 | 
|---|
| 357 |             }
 | 
|---|
| 358 |             ExEnv::out0() << "We have "
 | 
|---|
| 359 |                 << data.positions.size() << " positions and "
 | 
|---|
| 360 |                 << data.charges.size() << " charges." << endl;
 | 
|---|
| 361 |           }
 | 
|---|
| 362 |           if (data.DoLongrange) {
 | 
|---|
| 363 |           if (data.sampled_grid.level != 0)
 | 
|---|
| 364 |           {
 | 
|---|
| 365 |             // we now need to sample the density on the grid
 | 
|---|
| 366 |             // 1. get max and min over all basis function positions
 | 
|---|
| 367 |             assert( scf->basis()->ncenter() > 0 );
 | 
|---|
| 368 |             SCVector3 bmin( scf->basis()->r(0,0), scf->basis()->r(0,1), scf->basis()->r(0,2) );
 | 
|---|
| 369 |             SCVector3 bmax( scf->basis()->r(0,0), scf->basis()->r(0,1), scf->basis()->r(0,2) );
 | 
|---|
| 370 |             for (int nr = 1; nr< scf->basis()->ncenter(); ++nr) {
 | 
|---|
| 371 |               for (int i=0; i < 3; ++i) {
 | 
|---|
| 372 |                 if (scf->basis()->r(nr,i) < bmin(i))
 | 
|---|
| 373 |                   bmin(i) = scf->basis()->r(nr,i);
 | 
|---|
| 374 |                 if (scf->basis()->r(nr,i) > bmax(i))
 | 
|---|
| 375 |                   bmax(i) = scf->basis()->r(nr,i);
 | 
|---|
| 376 |               }
 | 
|---|
| 377 |             }
 | 
|---|
| 378 |             ExEnv::out0() << "Basis min is at " << bmin << " and max is at " << bmax << endl;
 | 
|---|
| 379 | 
 | 
|---|
| 380 |             // 2. choose an appropriately large grid
 | 
|---|
| 381 |             // we have to pay attention to capture the right amount of the exponential decay
 | 
|---|
| 382 |             // and also to have a power of two size of the grid at best
 | 
|---|
| 383 |             SCVector3 boundaryV(5.);  // boundary extent around compact domain containing basis functions
 | 
|---|
| 384 |             bmin -= boundaryV;
 | 
|---|
| 385 |             bmax += boundaryV;
 | 
|---|
| 386 |             for (size_t i=0;i<3;++i) {
 | 
|---|
| 387 |               if (bmin(i) < data.sampled_grid.begin[i])
 | 
|---|
| 388 |                 bmin(i) = data.sampled_grid.begin[i];
 | 
|---|
| 389 |               if (bmax(i) > data.sampled_grid.end[i])
 | 
|---|
| 390 |                 bmax(i) = data.sampled_grid.end[i];
 | 
|---|
| 391 |             }
 | 
|---|
| 392 |             // set the non-zero window of the sampled_grid
 | 
|---|
| 393 |             data.sampled_grid.setWindow(bmin.data(), bmax.data());
 | 
|---|
| 394 | 
 | 
|---|
| 395 |             // for the moment we always generate a grid of full size
 | 
|---|
| 396 |             // (NO LONGER converting grid dimensions from angstroem to bohr radii)
 | 
|---|
| 397 |             const double AtomicLengthToAngstroem = 1.;//0.52917721;
 | 
|---|
| 398 |             SCVector3 min;
 | 
|---|
| 399 |             SCVector3 max;
 | 
|---|
| 400 |             SCVector3 delta;
 | 
|---|
| 401 |             size_t samplepoints[3];
 | 
|---|
| 402 |             // due to periodic boundary conditions, we don't need gridpoints-1 here
 | 
|---|
| 403 |             // TODO: in case of open boundary conditions, we need data on the right
 | 
|---|
| 404 |             // hand side boundary as well
 | 
|---|
| 405 | //          const int gridpoints = data.sampled_grid.getGridPointsPerAxis();
 | 
|---|
| 406 |             for (size_t i=0;i<3;++i) {
 | 
|---|
| 407 |               min(i) = data.sampled_grid.begin_window[i]/AtomicLengthToAngstroem;
 | 
|---|
| 408 |               max(i) = data.sampled_grid.end_window[i]/AtomicLengthToAngstroem;
 | 
|---|
| 409 |               delta(i) = data.sampled_grid.getDeltaPerAxis(i)/AtomicLengthToAngstroem;
 | 
|---|
| 410 |               samplepoints[i] = data.sampled_grid.getWindowGridPointsPerAxis(i);
 | 
|---|
| 411 |             }
 | 
|---|
| 412 |             ExEnv::out0() << "Grid starts at " << min
 | 
|---|
| 413 |                 << " and ends at " << max
 | 
|---|
| 414 |                 << " with a delta of " << delta
 | 
|---|
| 415 |                 << " to get "
 | 
|---|
| 416 |                 << samplepoints[0] << ","
 | 
|---|
| 417 |                 << samplepoints[1] << ","
 | 
|---|
| 418 |                 << samplepoints[2] << " samplepoints."
 | 
|---|
| 419 |                 << endl;
 | 
|---|
| 420 |             assert( data.sampled_grid.sampled_grid.size() == samplepoints[0]*samplepoints[1]*samplepoints[2]);
 | 
|---|
| 421 | 
 | 
|---|
| 422 |             // 3. sample the atomic density
 | 
|---|
| 423 |             const double element_volume_conversion =
 | 
|---|
| 424 |                 1./AtomicLengthToAngstroem/AtomicLengthToAngstroem/AtomicLengthToAngstroem;
 | 
|---|
| 425 |             SCVector3 r = min;
 | 
|---|
| 426 | 
 | 
|---|
| 427 |             std::set<int> valence_indices;
 | 
|---|
| 428 |             RefDiagSCMatrix evals = scf->eigenvalues();
 | 
|---|
| 429 |             if (data.DoValenceOnly == MPQCData::DoSampleValenceOnly) {
 | 
|---|
| 430 |               // find valence orbitals
 | 
|---|
| 431 | //           std::cout << "All Eigenvalues:" << std::endl;
 | 
|---|
| 432 | //           for(int i=0;i<wfn->oso_dimension(); ++i)
 | 
|---|
| 433 | //             std::cout << i << "th eigenvalue is " << evals(i) << std::endl;
 | 
|---|
| 434 | //            int n_electrons = scf->nelectron();
 | 
|---|
| 435 |               int n_core_electrons =  wfn->molecule()->n_core_electrons();
 | 
|---|
| 436 |               std::set<double> evals_sorted;
 | 
|---|
| 437 |               {
 | 
|---|
| 438 |                 int i=0;
 | 
|---|
| 439 |                 double first_positive_ev = std::numeric_limits<double>::max();
 | 
|---|
| 440 |                 for(i=0;i<wfn->oso_dimension(); ++i) {
 | 
|---|
| 441 |                   if (evals(i) < 0.)
 | 
|---|
| 442 |                     evals_sorted.insert(evals(i));
 | 
|---|
| 443 |                   else
 | 
|---|
| 444 |                     first_positive_ev = std::min(first_positive_ev, (double)evals(i));
 | 
|---|
| 445 |                 }
 | 
|---|
| 446 |                 // add the first positive for the distance
 | 
|---|
| 447 |                 evals_sorted.insert(first_positive_ev);
 | 
|---|
| 448 |               }
 | 
|---|
| 449 |               std::set<double> evals_distances;
 | 
|---|
| 450 |               std::set<double>::const_iterator advancer = evals_sorted.begin();
 | 
|---|
| 451 |               std::set<double>::const_iterator iter = advancer++;
 | 
|---|
| 452 |               for(;advancer != evals_sorted.end(); ++advancer,++iter)
 | 
|---|
| 453 |                 evals_distances.insert((*advancer)-(*iter));
 | 
|---|
| 454 |               const double largest_distance = *(evals_distances.rbegin());
 | 
|---|
| 455 |               ExEnv::out0()  << "Largest distance between EV is " << largest_distance << std::endl;
 | 
|---|
| 456 |               advancer = evals_sorted.begin();
 | 
|---|
| 457 |               iter = advancer++;
 | 
|---|
| 458 |               for(;advancer != evals_sorted.begin(); ++advancer,++iter)
 | 
|---|
| 459 |                 if (fabs(fabs((*advancer)-(*iter)) - largest_distance) < 1e-10)
 | 
|---|
| 460 |                   break;
 | 
|---|
| 461 |               assert( advancer != evals_sorted.begin() );
 | 
|---|
| 462 |               const double last_core_ev = (*iter);
 | 
|---|
| 463 |               ExEnv::out0()  << "Last core EV might be " << last_core_ev << std::endl;
 | 
|---|
| 464 |               ExEnv::out0()  << "First valence index is " << n_core_electrons/2 << std::endl;
 | 
|---|
| 465 |               for(int i=n_core_electrons/2;i<wfn->oso_dimension(); ++i)
 | 
|---|
| 466 |                 if (evals(i) > last_core_ev)
 | 
|---|
| 467 |                   valence_indices.insert(i);
 | 
|---|
| 468 | //           {
 | 
|---|
| 469 | //             int i=0;
 | 
|---|
| 470 | //             std::cout << "Valence eigenvalues:" << std::endl;
 | 
|---|
| 471 | //             for (std::set<int>::const_iterator iter = valence_indices.begin();
 | 
|---|
| 472 | //                 iter != valence_indices.end(); ++iter)
 | 
|---|
| 473 | //               std::cout << i++ << "th eigenvalue is " << (*iter) << std::endl;
 | 
|---|
| 474 | //           }
 | 
|---|
| 475 |             } else {
 | 
|---|
| 476 |               // just insert all indices
 | 
|---|
| 477 |               for(int i=0;i<wfn->oso_dimension(); ++i)
 | 
|---|
| 478 |                 valence_indices.insert(i);
 | 
|---|
| 479 |             }
 | 
|---|
| 480 | 
 | 
|---|
| 481 |             // testing alternative routine from SCF::so_density()
 | 
|---|
| 482 |             RefSCMatrix oso_vector = scf->oso_eigenvectors();
 | 
|---|
| 483 |             RefSCMatrix vector = scf->so_to_orthog_so().t() * oso_vector;
 | 
|---|
| 484 |             oso_vector = 0;
 | 
|---|
| 485 |             RefSymmSCMatrix occ(scf->oso_dimension(), scf->basis_matrixkit());
 | 
|---|
| 486 |             occ.assign(0.0);
 | 
|---|
| 487 |             for (std::set<int>::const_iterator iter = valence_indices.begin();
 | 
|---|
| 488 |                 iter != valence_indices.end(); ++iter) {
 | 
|---|
| 489 |               const int i = *iter;
 | 
|---|
| 490 |               occ(i,i) = scf->occupation(i);
 | 
|---|
| 491 |               ExEnv::out0() << "# " << i << " has ev of " << evals(i) << ", occupied by " << scf->occupation(i) << std::endl;
 | 
|---|
| 492 |             }
 | 
|---|
| 493 |             RefSymmSCMatrix d2(scf->so_dimension(), scf->basis_matrixkit());
 | 
|---|
| 494 |             d2.assign(0.0);
 | 
|---|
| 495 |             d2.accumulate_transform(vector, occ);
 | 
|---|
| 496 | 
 | 
|---|
| 497 |             // taken from scf::density()
 | 
|---|
| 498 |             RefSCMatrix nos
 | 
|---|
| 499 |                 = scf->integral()->petite_list()->evecs_to_AO_basis(scf->natural_orbitals());
 | 
|---|
| 500 |             RefDiagSCMatrix nd = scf->natural_density();
 | 
|---|
| 501 |             GaussianBasisSet::ValueData *valdat
 | 
|---|
| 502 |               = new GaussianBasisSet::ValueData(scf->basis(), scf->integral());
 | 
|---|
| 503 |             std::vector<double>::iterator griditer = data.sampled_grid.sampled_grid.begin();
 | 
|---|
| 504 |             const int nbasis = scf->basis()->nbasis();
 | 
|---|
| 505 |             double *bs_values = new double[nbasis];
 | 
|---|
| 506 | 
 | 
|---|
| 507 |             // TODO: need to take care when we have periodic boundary conditions.
 | 
|---|
| 508 |             for (size_t x = 0; x < samplepoints[0]; ++x, r.x() += delta(0)) {
 | 
|---|
| 509 |               std::cout << "Sampling now for x=" << r.x() << std::endl;
 | 
|---|
| 510 |               for (size_t y = 0; y < samplepoints[1]; ++y, r.y() += delta(1)) {
 | 
|---|
| 511 |                 for (size_t z = 0; z < samplepoints[2]; ++z, r.z() += delta(2)) {
 | 
|---|
| 512 |                   scf->basis()->values(r,valdat,bs_values);
 | 
|---|
| 513 | 
 | 
|---|
| 514 |                   // loop over natural orbitals adding contributions to elec_density
 | 
|---|
| 515 |                   double elec_density=0.0;
 | 
|---|
| 516 |                   for (int i=0; i<nbasis; ++i) {
 | 
|---|
| 517 |                       double tmp = 0.0;
 | 
|---|
| 518 |                       for (int j=0; j<nbasis; ++j) {
 | 
|---|
| 519 |                           tmp += d2(j,i)*bs_values[j]*bs_values[i];
 | 
|---|
| 520 |                         }
 | 
|---|
| 521 |                       elec_density += tmp;
 | 
|---|
| 522 |                     }
 | 
|---|
| 523 |                   const double dens_at_r = elec_density * element_volume_conversion;
 | 
|---|
| 524 | //             const double dens_at_r = scf->density(r) * element_volume_conversion;
 | 
|---|
| 525 | 
 | 
|---|
| 526 | //             if (fabs(dens_at_r) > 1e-4)
 | 
|---|
| 527 | //               std::cout << "Electron density at " << r << " is " << dens_at_r << std::endl;
 | 
|---|
| 528 |                   if (griditer != data.sampled_grid.sampled_grid.end())
 | 
|---|
| 529 |                     *griditer++ = dens_at_r;
 | 
|---|
| 530 |                   else
 | 
|---|
| 531 |                     std::cerr << "PAST RANGE!" << std::endl;
 | 
|---|
| 532 |                 }
 | 
|---|
| 533 |                 r.z() = min.z();
 | 
|---|
| 534 |               }
 | 
|---|
| 535 |               r.y() = min.y();
 | 
|---|
| 536 |             }
 | 
|---|
| 537 |             delete[] bs_values;
 | 
|---|
| 538 |             delete valdat;
 | 
|---|
| 539 |             assert( griditer == data.sampled_grid.sampled_grid.end());
 | 
|---|
| 540 |             // normalization of electron charge to equal electron number
 | 
|---|
| 541 |             {
 | 
|---|
| 542 |               double integral_value = 0.;
 | 
|---|
| 543 |               const double volume_element = pow(AtomicLengthToAngstroem,3)*delta(0)*delta(1)*delta(2);
 | 
|---|
| 544 |               for (std::vector<double>::const_iterator diter = data.sampled_grid.sampled_grid.begin();
 | 
|---|
| 545 |                   diter != data.sampled_grid.sampled_grid.end(); ++diter)
 | 
|---|
| 546 |                   integral_value += *diter;
 | 
|---|
| 547 |               integral_value *= volume_element;
 | 
|---|
| 548 |               int n_electrons = scf->nelectron();
 | 
|---|
| 549 |               if (data.DoValenceOnly == MPQCData::DoSampleValenceOnly)
 | 
|---|
| 550 |                 n_electrons -= wfn->molecule()->n_core_electrons();
 | 
|---|
| 551 |               const double normalization =
 | 
|---|
| 552 |                   ((integral_value == 0) || (n_electrons == 0)) ?
 | 
|---|
| 553 |                       1. : n_electrons/integral_value;
 | 
|---|
| 554 |               std::cout << "Created " << data.sampled_grid.sampled_grid.size() << " grid points"
 | 
|---|
| 555 |                   << " with integral value of " << integral_value
 | 
|---|
| 556 |                   << " against " << ((data.DoValenceOnly == MPQCData::DoSampleValenceOnly) ? "n_valence_electrons" : "n_electrons")
 | 
|---|
| 557 |                   << " of " << n_electrons << "." << std::endl;
 | 
|---|
| 558 |               // with normalization we also get the charge right : -1.
 | 
|---|
| 559 |               for (std::vector<double>::iterator diter = data.sampled_grid.sampled_grid.begin();
 | 
|---|
| 560 |                   diter != data.sampled_grid.sampled_grid.end(); ++diter)
 | 
|---|
| 561 |                   *diter *= -1.*normalization;
 | 
|---|
| 562 |             }
 | 
|---|
| 563 |           }
 | 
|---|
| 564 |         }
 | 
|---|
| 565 |         scf = 0;
 | 
|---|
| 566 | }
 | 
|---|
| 567 | 
 | 
|---|
| 568 | void extractTimings(
 | 
|---|
| 569 |          Ref<RegionTimer> &tim,
 | 
|---|
| 570 |          void *_data)
 | 
|---|
| 571 | {
 | 
|---|
| 572 |   MPQCData &data = *static_cast<MPQCData *>(_data);
 | 
|---|
| 573 |   // times obtain from key "mpqc" which should be the first
 | 
|---|
| 574 |   const int nregion = tim->nregion();
 | 
|---|
| 575 |   //std::cout << "There are " << nregion << " timed regions." << std::endl;
 | 
|---|
| 576 |   const char **region_names = new const char*[nregion];
 | 
|---|
| 577 |   tim->get_region_names(region_names);
 | 
|---|
| 578 |   // find "gather"
 | 
|---|
| 579 |   size_t gather_region = findTimerRegion(nregion, region_names, "gather");
 | 
|---|
| 580 |   size_t mpqc_region = findTimerRegion(nregion, region_names, "mpqc");
 | 
|---|
| 581 |   delete[] region_names;
 | 
|---|
| 582 | 
 | 
|---|
| 583 |   // get timings
 | 
|---|
| 584 |   double *cpu_time = new double[nregion];
 | 
|---|
| 585 |   double *wall_time = new double[nregion];
 | 
|---|
| 586 |   double *flops = new double[nregion];
 | 
|---|
| 587 |   tim->get_cpu_times(cpu_time);
 | 
|---|
| 588 |   tim->get_wall_times(wall_time);
 | 
|---|
| 589 |   tim->get_flops(flops);
 | 
|---|
| 590 |   if (cpu_time != NULL) {
 | 
|---|
| 591 |          data.times.total_cputime = cpu_time[mpqc_region];
 | 
|---|
| 592 |          data.times.gather_cputime = cpu_time[gather_region];
 | 
|---|
| 593 |   }
 | 
|---|
| 594 |   if (wall_time != NULL) {
 | 
|---|
| 595 |          data.times.total_walltime = wall_time[mpqc_region];
 | 
|---|
| 596 |          data.times.gather_walltime = wall_time[gather_region];
 | 
|---|
| 597 |   }
 | 
|---|
| 598 |   if (flops != NULL) {
 | 
|---|
| 599 |          data.times.total_flops = flops[mpqc_region];
 | 
|---|
| 600 |          data.times.gather_flops = flops[gather_region];
 | 
|---|
| 601 |   }
 | 
|---|
| 602 |   delete[] cpu_time;
 | 
|---|
| 603 |   delete[] wall_time;
 | 
|---|
| 604 |   delete[] flops;
 | 
|---|
| 605 | }
 | 
|---|
| 606 | 
 | 
|---|