source: ThirdParty/mpqc_open/src/lib/chemistry/qc/wfn/wfn.cc

Candidate_v1.6.1
Last change on this file was 860145, checked in by Frederik Heber <heber@…>, 8 years ago

Merge commit '0b990dfaa8c6007a996d030163a25f7f5fc8a7e7' as 'ThirdParty/mpqc_open'

  • Property mode set to 100644
File size: 31.7 KB
Line 
1//
2// wfn.cc
3//
4// Copyright (C) 1996 Limit Point Systems, Inc.
5//
6// Author: Curtis Janssen <cljanss@limitpt.com>
7// Maintainer: LPS
8//
9// This file is part of the SC Toolkit.
10//
11// The SC Toolkit is free software; you can redistribute it and/or modify
12// it under the terms of the GNU Library General Public License as published by
13// the Free Software Foundation; either version 2, or (at your option)
14// any later version.
15//
16// The SC Toolkit 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 Library General Public License for more details.
20//
21// You should have received a copy of the GNU Library General Public License
22// along with the SC Toolkit; see the file COPYING.LIB. 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
28#ifdef __GNUC__
29#pragma implementation
30#endif
31
32#include <stdlib.h>
33#include <math.h>
34
35#include <stdexcept>
36#include <iostream>
37#include <iterator>
38
39#include <util/keyval/keyval.h>
40#include <util/misc/timer.h>
41#include <util/misc/formio.h>
42#include <util/misc/autovec.h>
43#include <util/state/stateio.h>
44#include <chemistry/qc/basis/obint.h>
45#include <chemistry/qc/basis/symmint.h>
46#include <chemistry/qc/intv3/intv3.h>
47
48#include <chemistry/qc/wfn/wfn.h>
49
50using namespace std;
51using namespace sc;
52
53#define CHECK_SYMMETRIZED_INTEGRALS 0
54
55/////////////////////////////////////////////////////////////////////////
56
57// This maps a TwoBodyThreeCenterInt into a OneBodyInt
58namespace sc {
59class ChargeDistInt: public OneBodyInt {
60 Ref<TwoBodyThreeCenterInt> tbint_;
61 Ref<Molecule> mol_;
62 Ref<GaussianBasisSet> ebasis0_;
63 Ref<GaussianBasisSet> ebasis1_;
64 Ref<GaussianBasisSet> atom_basis_;
65 std::vector<int> i_cd_;
66 const double *coef_;
67 public:
68 // The electronic basis are bs1 and bs2 in tbint and the
69 // nuclear basis is bs3.
70 ChargeDistInt(const Ref<TwoBodyThreeCenterInt>& tbint,
71 const double *coef);
72
73 void compute_shell(int,int);
74
75 bool cloneable();
76};
77
78ChargeDistInt::ChargeDistInt(const Ref<TwoBodyThreeCenterInt>& tbint,
79 const double *coef):
80 OneBodyInt(tbint->integral(),tbint->basis1(),tbint->basis2()),
81 tbint_(tbint),
82 coef_(coef)
83{
84 ebasis0_ = tbint->basis1();
85 ebasis1_ = tbint->basis2();
86 atom_basis_ = tbint->basis3();
87 mol_ = atom_basis_->molecule();
88 buffer_ = new double[tbint->basis1()->max_nfunction_in_shell()
89 *tbint->basis2()->max_nfunction_in_shell()];
90
91 for (int i=0; i<atom_basis_->ncenter(); i++) {
92 if (atom_basis_->nshell_on_center(i) > 0) i_cd_.push_back(i);
93 }
94}
95
96void
97ChargeDistInt::compute_shell(int ish,int jsh)
98{
99// std::cout << "starting " << ish << " " << jsh << std::endl;
100 int nijbf
101 = ebasis0_->shell(ish).nfunction()
102 * ebasis1_->shell(jsh).nfunction();
103 memset(buffer_,0,nijbf*sizeof(buffer_[0]));
104 const double *tbbuffer = tbint_->buffer();
105 int ksh = 0;
106 int coef_offset = 0;
107 for (int ii=0; ii<i_cd_.size(); ii++) {
108 int i = i_cd_[ii];
109 int nshell = atom_basis_->nshell_on_center(i);
110 for (int j=0; j<nshell; j++, ksh++) {
111 std::cout << "computing " << ish << " " << jsh << " " << ksh << std::endl;
112 tbint_->compute_shell(ish,jsh,ksh);
113 int nbfk = atom_basis_->shell(i).nfunction();
114 for (int ijbf=0; ijbf<nijbf; ijbf++) {
115 for (int kbf=0; kbf<nbfk; kbf++) {
116 buffer_[ijbf]
117 -= coef_[coef_offset+kbf] * tbbuffer[ijbf*nbfk + kbf];
118// std::cout << " adding "
119// << coef_[coef_offset+kbf] * tbbuffer[ijbf*nbfk + kbf]
120// << " = "
121// << coef_[coef_offset+kbf]
122// << " * "
123// << tbbuffer[ijbf*nbfk + kbf]
124// << " at location "
125// << ijbf
126// << std::endl;
127 }
128 }
129 }
130 coef_offset += nshell;
131 }
132// std::cout << "done with " << ish << " " << jsh << std::endl;
133}
134
135bool
136ChargeDistInt::cloneable()
137{
138 // not cloneable because tbint is not cloneable
139 return false;
140}
141
142}
143
144/////////////////////////////////////////////////////////////////////////
145
146static ClassDesc Wavefunction_cd(
147 typeid(Wavefunction),"Wavefunction",7,"public MolecularEnergy",
148 0, 0, 0);
149
150Wavefunction::Wavefunction(const Ref<KeyVal>&keyval):
151 // this will let molecule be retrieved from basis
152 // MolecularEnergy(new AggregateKeyVal(keyval,
153 // new PrefixKeyVal("basis", keyval))),
154 MolecularEnergy(keyval),
155 overlap_(this),
156 hcore_(this),
157 natural_orbitals_(this),
158 natural_density_(this),
159 bs_values(0),
160 bsg_values(0)
161{
162 overlap_.compute() = 0;
163 hcore_.compute() = 0;
164 natural_orbitals_.compute() = 0;
165 natural_density_.compute() = 0;
166
167 overlap_.computed() = 0;
168 hcore_.computed() = 0;
169 natural_orbitals_.computed() = 0;
170 natural_density_.computed() = 0;
171
172 print_nao_ = keyval->booleanvalue("print_nao");
173 print_npa_ = keyval->booleanvalue("print_npa");
174 KeyValValuedouble lindep_tol_def(1.e-8);
175 lindep_tol_ = keyval->doublevalue("lindep_tol", lindep_tol_def);
176 if (keyval->exists("symm_orthog")) {
177 ExEnv::out0() << indent
178 << "WARNING: using obsolete \"symm_orthog\" keyword"
179 << endl;
180 if (keyval->booleanvalue("symm_orthog")) {
181 orthog_method_ = OverlapOrthog::Symmetric;
182 }
183 else {
184 orthog_method_ = OverlapOrthog::Canonical;
185 }
186 }
187 else {
188 char *orthog_name = keyval->pcharvalue("orthog_method");
189 if (!orthog_name) {
190 orthog_method_ = OverlapOrthog::Symmetric;
191 }
192 else if (::strcmp(orthog_name, "canonical") == 0) {
193 orthog_method_ = OverlapOrthog::Canonical;
194 }
195 else if (::strcmp(orthog_name, "symmetric") == 0) {
196 orthog_method_ = OverlapOrthog::Symmetric;
197 }
198 else if (::strcmp(orthog_name, "gramschmidt") == 0) {
199 orthog_method_ = OverlapOrthog::GramSchmidt;
200 }
201 else {
202 ExEnv::errn() << "ERROR: bad orthog_method: \""
203 << orthog_name << "\"" << endl;
204 abort();
205 }
206 delete[] orthog_name;
207 }
208
209 debug_ = keyval->intvalue("debug");
210
211 gbs_ = require_dynamic_cast<GaussianBasisSet*>(
212 keyval->describedclassvalue("basis").pointer(),
213 "Wavefunction::Wavefunction\n"
214 );
215
216 atom_basis_ << keyval->describedclassvalue("atom_basis");
217 if (atom_basis_.nonnull()) {
218 atom_basis_coef_ = new double[atom_basis_->nbasis()];
219 for (int i=0; i<atom_basis_->nbasis(); i++) {
220 atom_basis_coef_[i] = keyval->doublevalue("atom_basis_coef",i);
221 }
222 scale_atom_basis_coef();
223
224 }
225 else {
226 atom_basis_coef_ = 0;
227 }
228
229 integral_ << keyval->describedclassvalue("integrals");
230 if (integral_.null()) {
231 Integral* default_intf = Integral::get_default_integral();
232 integral_ = default_intf->clone();
233 }
234
235 integral_->set_basis(gbs_);
236 Ref<PetiteList> pl = integral_->petite_list();
237
238 sodim_ = pl->SO_basisdim();
239 aodim_ = pl->AO_basisdim();
240 basiskit_ = gbs_->so_matrixkit();
241}
242
243Wavefunction::Wavefunction(StateIn&s):
244 SavableState(s),
245 MolecularEnergy(s),
246 overlap_(this),
247 hcore_(this),
248 natural_orbitals_(this),
249 natural_density_(this),
250 bs_values(0),
251 bsg_values(0)
252{
253 debug_ = 0;
254
255 overlap_.compute() = 0;
256 hcore_.compute() = 0;
257 natural_orbitals_.compute() = 0;
258 natural_density_.compute() = 0;
259
260 overlap_.computed() = 0;
261 hcore_.computed() = 0;
262 natural_orbitals_.computed() = 0;
263 natural_density_.computed() = 0;
264
265 if (s.version(::class_desc<Wavefunction>()) >= 2) {
266 s.get(print_nao_);
267 s.get(print_npa_);
268 }
269 else {
270 print_nao_ = 0;
271 print_npa_ = 0;
272 }
273
274 if (s.version(::class_desc<Wavefunction>()) >= 5) {
275 int orthog_enum;
276 s.get(orthog_enum);
277 orthog_method_ = (OverlapOrthog::OrthogMethod) orthog_enum;
278 }
279 else if (s.version(::class_desc<Wavefunction>()) >= 3) {
280 int symm_orthog;
281 s.get(symm_orthog);
282 if (symm_orthog) orthog_method_ = OverlapOrthog::Symmetric;
283 else orthog_method_ = OverlapOrthog::Canonical;
284 }
285 else {
286 orthog_method_ = OverlapOrthog::Symmetric;
287 }
288
289 if (s.version(::class_desc<Wavefunction>()) >= 4) {
290 s.get(lindep_tol_);
291 }
292 else {
293 lindep_tol_ = 1.e-6;
294 }
295
296 gbs_ << SavableState::restore_state(s);
297 integral_ << SavableState::restore_state(s);
298
299 if (s.version(::class_desc<Wavefunction>()) >= 6) {
300 Ref<KeyVal> original_override = s.override();
301 Ref<AssignedKeyVal> matrixkit_override = new AssignedKeyVal;
302 matrixkit_override->assign("matrixkit", gbs_->so_matrixkit().pointer());
303 Ref<KeyVal> new_override
304 = new AggregateKeyVal(matrixkit_override.pointer(),
305 original_override.pointer());
306 s.set_override(new_override);
307 orthog_ << SavableState::restore_state(s);
308 s.set_override(original_override);
309 }
310
311 if (s.version(::class_desc<Wavefunction>()) >= 7) {
312 atom_basis_ << SavableState::restore_state(s);
313 if (atom_basis_.nonnull()) {
314 s.get_array_double(atom_basis_coef_, atom_basis_->nbasis());
315 }
316 }
317 else {
318 atom_basis_coef_ = 0;
319 }
320
321 integral_->set_basis(gbs_);
322 Ref<PetiteList> pl = integral_->petite_list();
323
324 sodim_ = pl->SO_basisdim();
325 aodim_ = pl->AO_basisdim();
326 basiskit_ = gbs_->so_matrixkit();
327}
328
329void
330Wavefunction::symmetry_changed()
331{
332 MolecularEnergy::symmetry_changed();
333
334 Ref<PetiteList> pl = integral_->petite_list();
335 sodim_ = pl->SO_basisdim();
336 aodim_ = pl->AO_basisdim();
337 overlap_.result_noupdate() = 0;
338 basiskit_ = gbs_->so_matrixkit();
339
340 orthog_ = 0;
341}
342
343Wavefunction::~Wavefunction()
344{
345 if (bs_values) {
346 delete[] bs_values;
347 bs_values=0;
348 }
349 if (bsg_values) {
350 delete[] bsg_values;
351 bsg_values=0;
352 }
353}
354
355void
356Wavefunction::save_data_state(StateOut&s)
357{
358 MolecularEnergy::save_data_state(s);
359
360 // overlap and hcore integrals are cheap so don't store them.
361 // same goes for natural orbitals
362
363 s.put(print_nao_);
364 s.put(print_npa_);
365 s.put((int)orthog_method_);
366 s.put(lindep_tol_);
367
368 SavableState::save_state(gbs_.pointer(),s);
369 SavableState::save_state(integral_.pointer(),s);
370 SavableState::save_state(orthog_.pointer(),s);
371 SavableState::save_state(atom_basis_.pointer(), s);
372 if (atom_basis_.nonnull()) {
373 s.put_array_double(atom_basis_coef_,atom_basis_->nbasis());
374 }
375}
376
377double
378Wavefunction::charge()
379{
380 return molecule()->nuclear_charge() - nelectron();
381}
382
383RefSymmSCMatrix
384Wavefunction::ao_density()
385{
386 return integral()->petite_list()->to_AO_basis(density());
387}
388
389RefSCMatrix
390Wavefunction::natural_orbitals()
391{
392 if (!natural_orbitals_.computed()) {
393 RefSymmSCMatrix dens = density();
394
395 // transform the density into an orthogonal basis
396 RefSCMatrix ortho = so_to_orthog_so();
397
398 RefSymmSCMatrix densortho(oso_dimension(), basis_matrixkit());
399 densortho.assign(0.0);
400 densortho.accumulate_transform(so_to_orthog_so_inverse().t(),dens);
401
402 RefSCMatrix natorb(oso_dimension(), oso_dimension(),
403 basis_matrixkit());
404 RefDiagSCMatrix natden(oso_dimension(), basis_matrixkit());
405
406 densortho.diagonalize(natden, natorb);
407
408 // natorb is the ortho SO to NO basis transform
409 // make natural_orbitals_ the SO to the NO basis transform
410 natural_orbitals_ = so_to_orthog_so().t() * natorb;
411 natural_density_ = natden;
412
413 natural_orbitals_.computed() = 1;
414 natural_density_.computed() = 1;
415 }
416
417 return natural_orbitals_.result_noupdate();
418}
419
420RefDiagSCMatrix
421Wavefunction::natural_density()
422{
423 if (!natural_density_.computed()) {
424 natural_orbitals();
425 }
426
427 return natural_density_.result_noupdate();
428}
429
430RefSymmSCMatrix
431Wavefunction::overlap()
432{
433 if (!overlap_.computed()) {
434 integral()->set_basis(gbs_);
435 Ref<PetiteList> pl = integral()->petite_list();
436
437#if ! CHECK_SYMMETRIZED_INTEGRALS
438 // first form skeleton s matrix
439 RefSymmSCMatrix s(basis()->basisdim(), basis()->matrixkit());
440 Ref<SCElementOp> ov =
441 new OneBodyIntOp(new SymmOneBodyIntIter(integral()->overlap(), pl));
442
443 s.assign(0.0);
444 s.element_op(ov);
445 ov=0;
446
447 if (debug_ > 1) s.print("AO skeleton overlap");
448
449 // then symmetrize it
450 RefSymmSCMatrix sb(so_dimension(), basis_matrixkit());
451 pl->symmetrize(s,sb);
452
453 overlap_ = sb;
454#else
455 ExEnv::out0() << "Checking symmetrized overlap" << endl;
456
457 RefSymmSCMatrix s(basis()->basisdim(), basis()->matrixkit());
458 Ref<SCElementOp> ov =
459 new OneBodyIntOp(new OneBodyIntIter(integral()->overlap()));
460
461 s.assign(0.0);
462 s.element_op(ov);
463 ov=0;
464
465 overlap_ = pl->to_SO_basis(s);
466
467 //// use petite list to form saopl
468
469 // form skeleton Hcore in AO basis
470 RefSymmSCMatrix saopl(basis()->basisdim(), basis()->matrixkit());
471 saopl.assign(0.0);
472
473 ov = new OneBodyIntOp(new SymmOneBodyIntIter(integral_->overlap(), pl));
474 saopl.element_op(ov);
475 ov=0;
476
477 // also symmetrize using pl->symmetrize():
478 RefSymmSCMatrix spl(so_dimension(), basis_matrixkit());
479 pl->symmetrize(saopl,spl);
480
481 //// compare the answers
482
483 int n = overlap_.result_noupdate().dim().n();
484 int me = MessageGrp::get_default_messagegrp()->me();
485 for (int i=0; i<n; i++) {
486 for (int j=0; j<=i; j++) {
487 double val1 = overlap_.result_noupdate().get_element(i,j);
488 double val2 = spl.get_element(i,j);
489 if (me == 0) {
490 if (fabs(val1-val2) > 1.0e-6) {
491 ExEnv::out0() << "bad overlap vals for " << i << " " << j
492 << ": " << val1 << " " << val2 << endl;
493 }
494 }
495 }
496 }
497#endif
498
499 overlap_.computed() = 1;
500 }
501
502 return overlap_.result_noupdate();
503}
504
505RefSymmSCMatrix
506Wavefunction::core_hamiltonian()
507{
508 if (!hcore_.computed()) {
509 integral()->set_basis(gbs_);
510 Ref<PetiteList> pl = integral()->petite_list();
511
512#if ! CHECK_SYMMETRIZED_INTEGRALS
513 // form skeleton Hcore in AO basis
514 RefSymmSCMatrix hao(basis()->basisdim(), basis()->matrixkit());
515 hao.assign(0.0);
516
517 Ref<SCElementOp> hc =
518 new OneBodyIntOp(new SymmOneBodyIntIter(integral_->kinetic(), pl));
519 hao.element_op(hc);
520 hc=0;
521
522 if (atom_basis_.null()) {
523 Ref<OneBodyInt> nuc = integral_->nuclear();
524 nuc->reinitialize();
525 hc = new OneBodyIntOp(new SymmOneBodyIntIter(nuc, pl));
526 hao.element_op(hc);
527 hc=0;
528 }
529 else {
530 // we have an atom_basis, so some nuclear charges will be computed
531 // with the two electron integral code and some will be computed
532 // with the point charge code
533
534 // find out which atoms are point charges and which ones are charge
535 // distributions
536 std::vector<int> i_pc; // point charge centers
537 for (int i=0; i<atom_basis_->ncenter(); i++) {
538 if (atom_basis_->nshell_on_center(i) == 0) i_pc.push_back(i);
539 }
540 int n_pc = i_pc.size();
541
542 // initialize the point charge data
543 auto_vec<double> pc_charges(new double[n_pc]);
544 auto_vec<double*> pc_xyz(new double*[n_pc]);
545 auto_vec<double> pc_xyz0(new double[n_pc*3]);
546 pc_xyz[0] = pc_xyz0.get();
547 for (int i=1; i<n_pc; i++) pc_xyz[i] = pc_xyz[i-1] + 3;
548 for (int i=0; i<n_pc; i++) {
549 pc_charges[i] = molecule()->charge(i_pc[i]);
550 for (int j=0; j<3; j++) pc_xyz[i][j] = molecule()->r(i_pc[i],j);
551 }
552 Ref<PointChargeData> pc_data
553 = new PointChargeData(n_pc, pc_xyz.get(), pc_charges.get());
554
555 // compute the point charge contributions
556 Ref<OneBodyInt> pc_int = integral_->point_charge(pc_data);
557 hc = new OneBodyIntOp(new SymmOneBodyIntIter(pc_int,pl));
558 hao.element_op(hc);
559 hc=0;
560 pc_int=0;
561
562 // compute the charge distribution contributions
563 // H_ij += sum_A -q_A sum_k N_A_k (ij|A_k)
564 integral()->set_basis(gbs_,gbs_,atom_basis_);
565 Ref<TwoBodyThreeCenterInt> cd_tbint
566 = integral_->electron_repulsion3();
567 Ref<OneBodyInt> cd_int = new ChargeDistInt(cd_tbint, atom_basis_coef_);
568 hc = new OneBodyIntOp(new SymmOneBodyIntIter(cd_int,pl));
569 hao.element_op(hc);
570 integral()->set_basis(gbs_);
571 hc=0;
572 cd_int=0;
573 cd_tbint=0;
574 }
575
576 // now symmetrize Hso
577 RefSymmSCMatrix h(so_dimension(), basis_matrixkit());
578 pl->symmetrize(hao,h);
579
580 hcore_ = h;
581#else
582 ExEnv::out0() << "Checking symmetrized hcore" << endl;
583
584 RefSymmSCMatrix hao(basis()->basisdim(), basis()->matrixkit());
585 hao.assign(0.0);
586
587 Ref<SCElementOp> hc =
588 new OneBodyIntOp(new OneBodyIntIter(integral_->kinetic()));
589 hao.element_op(hc);
590 hc=0;
591
592// std::cout << "null atom_basis" << std::endl;
593 Ref<OneBodyInt> nuc = integral_->nuclear();
594 nuc->reinitialize();
595 hc = new OneBodyIntOp(new OneBodyIntIter(nuc));
596 hao.element_op(hc);
597 hc=0;
598
599 hcore_ = pl->to_SO_basis(hao);
600
601 //// use petite list to form haopl
602
603 // form skeleton Hcore in AO basis
604 RefSymmSCMatrix haopl(basis()->basisdim(), basis()->matrixkit());
605 haopl.assign(0.0);
606
607 hc = new OneBodyIntOp(new SymmOneBodyIntIter(integral_->kinetic(), pl));
608 haopl.element_op(hc);
609 hc=0;
610
611 nuc = integral_->nuclear();
612 nuc->reinitialize();
613 hc = new OneBodyIntOp(new SymmOneBodyIntIter(nuc, pl));
614 haopl.element_op(hc);
615 hc=0;
616
617 // also symmetrize using pl->symmetrize():
618 RefSymmSCMatrix h(so_dimension(), basis_matrixkit());
619 pl->symmetrize(haopl,h);
620
621 //// compare the answers
622
623 int n = hcore_.result_noupdate().dim().n();
624 int me = MessageGrp::get_default_messagegrp()->me();
625 for (int i=0; i<n; i++) {
626 for (int j=0; j<=i; j++) {
627 double val1 = hcore_.result_noupdate().get_element(i,j);
628 double val2 = h.get_element(i,j);
629 if (me == 0) {
630 if (fabs(val1-val2) > 1.0e-6) {
631 ExEnv::outn() << "bad hcore vals for " << i << " " << j
632 << ": " << val1 << " " << val2 << endl;
633 }
634 }
635 }
636 }
637#endif
638
639 hcore_.computed() = 1;
640 }
641
642 return hcore_.result_noupdate();
643}
644
645// Compute lists of centers that are point charges and lists that
646// are charge distributions.
647void
648Wavefunction::set_up_charge_types(
649 std::vector<int> &q_pc,
650 std::vector<int> &q_cd,
651 std::vector<int> &n_pc,
652 std::vector<int> &n_cd)
653{
654 q_pc.clear();
655 q_cd.clear();
656 n_pc.clear();
657 n_cd.clear();
658
659 for (int i=0; i<atom_basis_->ncenter(); i++) {
660 bool is_Q = (molecule()->atom_symbol(i) == "Q");
661 if (atom_basis_->nshell_on_center(i) > 0) {
662 if (is_Q) q_cd.push_back(i);
663 else n_cd.push_back(i);
664 }
665 else {
666 if (is_Q) q_pc.push_back(i);
667 else n_pc.push_back(i);
668 }
669 }
670}
671
672double
673Wavefunction::nuclear_repulsion_energy()
674{
675 if (atom_basis_.null()) return molecule()->nuclear_repulsion_energy();
676
677 double nucrep = 0.0;
678
679 std::vector<int> q_pc, q_cd, n_pc, n_cd;
680 set_up_charge_types(q_pc,q_cd,n_pc,n_cd);
681
682 // compute point charge - point charge contribution
683 nucrep += nuc_rep_pc_pc(n_pc, n_pc, true /* i > j */);
684 nucrep += nuc_rep_pc_pc(q_pc, n_pc, false /* all i j */);
685 if (molecule()->include_qq()) {
686 nucrep += nuc_rep_pc_pc(q_pc, q_pc, true /* i > j */);
687 }
688
689 // compute point charge - charge distribution contribution
690 nucrep += nuc_rep_pc_cd(n_pc, n_cd);
691 nucrep += nuc_rep_pc_cd(q_pc, n_cd);
692 nucrep += nuc_rep_pc_cd(n_pc, q_cd);
693 if (molecule()->include_qq()) {
694 nucrep += nuc_rep_pc_cd(q_pc, q_cd);
695 }
696
697 // compute the charge distribution - charge distribution contribution
698 nucrep += nuc_rep_cd_cd(n_cd, n_cd, true /* i > j */);
699 nucrep += nuc_rep_cd_cd(q_cd, n_cd, false /* all i j */);
700 if (molecule()->include_qq()) {
701 nucrep += nuc_rep_cd_cd(q_cd, q_cd, true /* i > j */);
702 }
703
704 return nucrep;
705}
706
707double
708Wavefunction::nuc_rep_pc_pc(const std::vector<int>&c1,
709 const std::vector<int>&c2,
710 bool uniq)
711{
712 double e = 0.0;
713
714 if (c1.size() == 0 || c2.size() == 0) return e;
715
716 for (int ii=0; ii<c1.size(); ii++) {
717 int i = c1[ii];
718 SCVector3 ai(molecule()->r(i));
719 double Zi = molecule()->charge(i);
720 int jfence = (uniq?ii:c2.size());
721 for (int jj=0; jj<jfence; jj++) {
722 int j = c2[jj];
723 SCVector3 aj(molecule()->r(j));
724 e += Zi * molecule()->charge(j) / ai.dist(aj);
725 }
726 }
727
728 return e;
729}
730
731double
732Wavefunction::nuc_rep_pc_cd(const std::vector<int>&pc,
733 const std::vector<int>&cd)
734{
735 double e = 0.0;
736
737 if (pc.size() == 0 || cd.size() == 0) return e;
738
739 integral()->set_basis(atom_basis());
740
741 sc::auto_vec<double> charges(new double[pc.size()]);
742 sc::auto_vec<double*> positions(new double*[pc.size()]);
743 sc::auto_vec<double> xyz(new double[pc.size()*3]);
744 for (int i=0; i<pc.size(); i++) {
745 positions.get()[i] = &xyz.get()[i*3];
746 }
747
748 for (int ii=0; ii<pc.size(); ii++) {
749 int i=pc[ii];
750 charges.get()[ii] = molecule()->charge(i);
751 for (int j=0; j<3; j++) positions.get()[ii][j] = molecule()->r(i,j);
752 }
753
754 Ref<PointChargeData> pcdata = new PointChargeData(pc.size(),
755 positions.get(),
756 charges.get());
757 Ref<OneBodyOneCenterInt> ob = integral()->point_charge1(pcdata);
758
759 const double *buffer = ob->buffer();
760
761 for (int ii=0,icoef=0; ii<cd.size(); ii++) {
762 int icenter = cd[ii];
763 int joff = atom_basis()->shell_on_center(icenter,0);
764 for (int j=0; j<atom_basis()->nshell_on_center(icenter); j++) {
765 int jsh = j + joff;
766 ob->compute_shell(jsh);
767 int nfunc = atom_basis()->shell(jsh).nfunction();
768 for (int k=0; k<nfunc; k++,icoef++) {
769 e -= atom_basis_coef_[icoef] * buffer[k];
770 }
771 }
772 }
773
774 integral()->set_basis(basis());
775
776 return e;
777}
778
779double
780Wavefunction::nuc_rep_cd_cd(const std::vector<int>&c1,
781 const std::vector<int>&c2,
782 bool uniq)
783{
784 double e = 0.0;
785
786 if (c1.size() == 0 || c2.size() == 0) return e;
787
788 integral()->set_basis(atom_basis());
789
790 Ref<TwoBodyTwoCenterInt> tb = integral()->electron_repulsion2();
791
792 const double *buffer = tb->buffer();
793
794 for (int ii=0; ii<c1.size(); ii++) {
795 int icenter = c1[ii];
796 int inshell = atom_basis()->nshell_on_center(icenter);
797 int ish = atom_basis()->shell_on_center(icenter,0);
798 for (int iish=0; iish<inshell; iish++,ish++) {
799 int infunc = atom_basis()->shell(ish).nfunction();
800 int ifuncoff = atom_basis()->shell_to_function(ish);
801 int jjfence = (uniq?ii:c2.size());
802 for (int jj=0; jj<jjfence; jj++) {
803 int jcenter = c2[jj];
804 int jnshell = atom_basis()->nshell_on_center(jcenter);
805 int jsh = atom_basis()->shell_on_center(jcenter,0);
806 for (int jjsh=0; jjsh<jnshell; jjsh++,jsh++) {
807 int jnfunc = atom_basis()->shell(jsh).nfunction();
808 int jfuncoff = atom_basis()->shell_to_function(jsh);
809 tb->compute_shell(ish,jsh);
810 for (int ifunc=0, ijfunc=0; ifunc<infunc; ifunc++) {
811 for (int jfunc=0; jfunc<jnfunc; jfunc++, ijfunc++) {
812 e += atom_basis_coef_[ifuncoff+ifunc]
813 * atom_basis_coef_[jfuncoff+jfunc]
814 * buffer[ijfunc];
815 }
816 }
817 }
818 }
819 }
820 }
821
822 integral()->set_basis(basis());
823
824 return e;
825}
826
827void
828Wavefunction::nuclear_repulsion_energy_gradient(double *g)
829{
830 int natom = molecule()->natom();
831 sc::auto_vec<double*> gp(new double*[natom]);
832 for (int i=0; i<natom; i++) {
833 gp.get()[i] = &g[i*3];
834 }
835 nuclear_repulsion_energy_gradient(gp.get());
836}
837
838void
839Wavefunction::nuclear_repulsion_energy_gradient(double **g)
840{
841 if (atom_basis_.null()) {
842 int natom = molecule()->natom();
843 for (int i=0; i<natom; i++) {
844 molecule()->nuclear_repulsion_1der(i,g[i]);
845 }
846 return;
847 }
848
849 // zero the gradient
850 for (int i=0; i<molecule()->natom(); i++) {
851 for (int j=0; j<3; j++) g[i][j] = 0.0;
852 }
853
854 // compute charge types
855 std::vector<int> q_pc, q_cd, n_pc, n_cd;
856 set_up_charge_types(q_pc,q_cd,n_pc,n_cd);
857
858 // compute point charge - point charge contribution
859 nuc_rep_grad_pc_pc(g, n_pc, n_pc, true /* i > j */);
860 nuc_rep_grad_pc_pc(g, q_pc, n_pc, false /* all i j */);
861 if (molecule()->include_qq()) {
862 nuc_rep_grad_pc_pc(g, q_pc, q_pc, true /* i > j */);
863 }
864
865 // compute point charge - charge distribution contribution
866 nuc_rep_grad_pc_cd(g, n_pc, n_cd);
867 nuc_rep_grad_pc_cd(g, q_pc, n_cd);
868 nuc_rep_grad_pc_cd(g, n_pc, q_cd);
869 if (molecule()->include_qq()) {
870 nuc_rep_grad_pc_cd(g, q_pc, q_cd);
871 }
872
873 // compute the charge distribution - charge distribution contribution
874 nuc_rep_grad_cd_cd(g, n_cd, n_cd, true /* i > j */);
875 nuc_rep_grad_cd_cd(g, q_cd, n_cd, false /* all i j */);
876 if (molecule()->include_qq()) {
877 nuc_rep_grad_cd_cd(g, q_cd, q_cd, true /* i > j */);
878 }
879
880 // note: the electronic terms still need to be done in
881 // a new hcore_deriv implemented in Wavefunction.
882 throw std::runtime_error("Wavefunction::nuclear_repulsion_energy_gradient: not done");
883
884}
885
886void
887Wavefunction::nuc_rep_grad_pc_pc(double **grad,
888 const std::vector<int>&c1,
889 const std::vector<int>&c2,
890 bool uniq)
891{
892 if (c1.size() == 0 || c2.size() == 0) return;
893
894 for (int ii=0; ii<c1.size(); ii++) {
895 int i = c1[ii];
896 SCVector3 ai(molecule()->r(i));
897 double Zi = molecule()->charge(i);
898 int jfence = (uniq?ii:c2.size());
899 for (int jj=0; jj<jfence; jj++) {
900 int j = c2[jj];
901 SCVector3 aj(molecule()->r(j));
902 double Zj = molecule()->charge(j);
903 SCVector3 rdiff = ai - aj;
904 double r2 = rdiff.dot(rdiff);
905 double factor = - Zi * Zj / (r2*sqrt(r2));
906 for (int k=0; k<3; k++) {
907 grad[i][k] += factor * rdiff[k];
908 grad[j][k] -= factor * rdiff[k];
909 }
910 }
911 }
912}
913
914void
915Wavefunction::nuc_rep_grad_pc_cd(double **grad,
916 const std::vector<int>&pc,
917 const std::vector<int>&cd)
918{
919 if (pc.size() == 0 || cd.size() == 0) return;
920
921 throw std::runtime_error("Wavefunction::nuclear_repulsion_energy_gradient: not done");
922}
923
924void
925Wavefunction::nuc_rep_grad_cd_cd(double **grad,
926 const std::vector<int>&c1,
927 const std::vector<int>&c2,
928 bool uniq)
929{
930 if (c1.size() == 0 || c2.size() == 0) return;
931
932 throw std::runtime_error("Wavefunction::nuclear_repulsion_energy_gradient: not done");
933}
934
935// returns the orthogonalization matrix
936RefSCMatrix
937Wavefunction::so_to_orthog_so()
938{
939 if (orthog_.null()) init_orthog();
940 return orthog_->basis_to_orthog_basis();
941}
942
943RefSCMatrix
944Wavefunction::so_to_orthog_so_inverse()
945{
946 if (orthog_.null()) init_orthog();
947 return orthog_->basis_to_orthog_basis_inverse();
948}
949
950Ref<GaussianBasisSet>
951Wavefunction::basis() const
952{
953 return gbs_;
954}
955
956Ref<Molecule>
957Wavefunction::molecule() const
958{
959 return basis()->molecule();
960}
961
962Ref<GaussianBasisSet>
963Wavefunction::atom_basis() const
964{
965 return atom_basis_;
966}
967
968const double *
969Wavefunction::atom_basis_coef() const
970{
971 return atom_basis_coef_;
972}
973
974Ref<Integral>
975Wavefunction::integral()
976{
977 return integral_;
978}
979
980RefSCDimension
981Wavefunction::so_dimension()
982{
983 return sodim_;
984}
985
986RefSCDimension
987Wavefunction::ao_dimension()
988{
989 return aodim_;
990}
991
992RefSCDimension
993Wavefunction::oso_dimension()
994{
995 if (orthog_.null()) init_orthog();
996 return orthog_->orthog_dim();
997}
998
999Ref<SCMatrixKit>
1000Wavefunction::basis_matrixkit()
1001{
1002 return basiskit_;
1003}
1004
1005void
1006Wavefunction::print(ostream&o) const
1007{
1008 MolecularEnergy::print(o);
1009 ExEnv::out0() << indent << "Electronic basis:" << std::endl;
1010 ExEnv::out0() << incindent;
1011 basis()->print_brief(o);
1012 ExEnv::out0() << decindent;
1013 if (atom_basis_.nonnull()) {
1014 ExEnv::out0() << indent << "Nuclear basis:" << std::endl;
1015 ExEnv::out0() << incindent;
1016 atom_basis_->print_brief(o);
1017 ExEnv::out0() << decindent;
1018 }
1019 // the other stuff is a wee bit too big to print
1020 if (print_nao_ || print_npa_) {
1021 tim_enter("NAO");
1022 RefSCMatrix naos = ((Wavefunction*)this)->nao();
1023 tim_exit("NAO");
1024 if (print_nao_) naos.print("NAO");
1025 }
1026}
1027
1028RefSymmSCMatrix
1029Wavefunction::alpha_density()
1030{
1031 if (!spin_polarized()) {
1032 RefSymmSCMatrix result = density().copy();
1033 result.scale(0.5);
1034 return result;
1035 }
1036 ExEnv::errn() << class_name() << "::alpha_density not implemented" << endl;
1037 abort();
1038 return 0;
1039}
1040
1041RefSymmSCMatrix
1042Wavefunction::beta_density()
1043{
1044 if (!spin_polarized()) {
1045 RefSymmSCMatrix result = density().copy();
1046 result.scale(0.5);
1047 return result;
1048 }
1049 ExEnv::errn() << class_name() << "::beta_density not implemented" << endl;
1050 abort();
1051 return 0;
1052}
1053
1054RefSymmSCMatrix
1055Wavefunction::alpha_ao_density()
1056{
1057 return integral()->petite_list()->to_AO_basis(alpha_density());
1058}
1059
1060RefSymmSCMatrix
1061Wavefunction::beta_ao_density()
1062{
1063 return integral()->petite_list()->to_AO_basis(beta_density());
1064}
1065
1066void
1067Wavefunction::obsolete()
1068{
1069 orthog_ = 0;
1070
1071 MolecularEnergy::obsolete();
1072}
1073
1074void
1075Wavefunction::copy_orthog_info(const Ref<Wavefunction>&wfn)
1076{
1077 if (orthog_.nonnull()) {
1078 ExEnv::errn() << "WARNING: Wavefunction: orthogonalization info changing"
1079 << endl;
1080 }
1081 if (wfn->orthog_.null())
1082 wfn->init_orthog();
1083 orthog_ = wfn->orthog_->copy();
1084}
1085
1086void
1087Wavefunction::init_orthog()
1088{
1089 orthog_ = new OverlapOrthog(
1090 orthog_method_,
1091 overlap(),
1092 basis_matrixkit(),
1093 lindep_tol_,
1094 debug_
1095 );
1096}
1097
1098double
1099Wavefunction::min_orthog_res()
1100{
1101 return orthog_->min_orthog_res();
1102}
1103
1104double
1105Wavefunction::max_orthog_res()
1106{
1107 if (orthog_.null()) init_orthog();
1108 return orthog_->max_orthog_res();
1109}
1110
1111OverlapOrthog::OrthogMethod
1112Wavefunction::orthog_method() const
1113{
1114 return orthog_method_;
1115}
1116
1117void
1118Wavefunction::set_orthog_method(const OverlapOrthog::OrthogMethod& omethod)
1119{
1120 if (orthog_method_ != omethod) {
1121 orthog_method_ = omethod;
1122 init_orthog();
1123 obsolete();
1124 }
1125}
1126
1127double
1128Wavefunction::lindep_tol() const
1129{
1130 return lindep_tol_;
1131}
1132
1133void
1134Wavefunction::set_lindep_tol(double lindep_tol)
1135{
1136 if (lindep_tol_ != lindep_tol) {
1137 lindep_tol_ = lindep_tol;
1138 init_orthog();
1139 obsolete();
1140 }
1141}
1142
1143void
1144Wavefunction::scale_atom_basis_coef()
1145{
1146 std::vector<int> i_cd;
1147 for (int i=0; i<atom_basis_->ncenter(); i++) {
1148 if (atom_basis_->nshell_on_center(i) > 0) i_cd.push_back(i);
1149 }
1150
1151 if (atom_basis_->max_angular_momentum() > 0) {
1152 // Only s functions will preserve the full symmetry.
1153 // Can only normalize functions that don't integrate to zero.
1154 throw std::runtime_error("ChargeDistInt: max am larger than 0");
1155 }
1156
1157 int coef_offset = 0;
1158 int icoef = 0;
1159 for (int ii=0; ii<i_cd.size(); ii++) {
1160 int i = i_cd[ii];
1161 int nshell = atom_basis_->nshell_on_center(i);
1162 int nfunc = 0;
1163 if (nshell > 0) {
1164 double raw_charge = 0.0;
1165 for (int jj=0, icoef=coef_offset; jj<nshell; jj++) {
1166 int j = atom_basis_->shell_on_center(i,jj);
1167 const GaussianShell &shell = atom_basis_->shell(j);
1168 // loop over the contractions
1169 // the number of functions in each contraction is 1
1170 // since only s functions are allowed
1171 for (int k=0; k<shell.ncontraction(); k++, icoef++) {
1172 for (int l=0; l<shell.nprimitive(); l++) {
1173 double alpha = shell.exponent(l);
1174 double con_coef = shell.coefficient_unnorm(k,l);
1175 // The exponent is halved because the normalization
1176 // formula is for the s function squared.
1177 double integral = ::pow(M_PI/alpha,1.5);
1178 raw_charge += atom_basis_coef_[icoef] * con_coef * integral;
1179// std::cout << "con_coef = " << con_coef
1180// << " integral = " << integral
1181// << std::endl;
1182 }
1183 }
1184 nfunc += shell.ncontraction();
1185 }
1186 double charge = atom_basis_->molecule()->charge(i);
1187 double correction = charge/raw_charge;
1188 for (int icoef=coef_offset; icoef<coef_offset+nfunc; icoef++) {
1189 atom_basis_coef_[icoef] = correction * atom_basis_coef_[icoef];
1190 }
1191 }
1192 coef_offset += nshell;
1193 }
1194}
1195
1196/////////////////////////////////////////////////////////////////////////////
1197
1198// Local Variables:
1199// mode: c++
1200// c-file-style: "ETS"
1201// End:
Note: See TracBrowser for help on using the repository browser.