50
53
#define CHECK_SYMMETRIZED_INTEGRALS 0
55
/////////////////////////////////////////////////////////////////////////
57
// This maps a TwoBodyThreeCenterInt into a OneBodyInt
59
class ChargeDistInt: public OneBodyInt {
60
Ref<TwoBodyThreeCenterInt> tbint_;
62
Ref<GaussianBasisSet> ebasis0_;
63
Ref<GaussianBasisSet> ebasis1_;
64
Ref<GaussianBasisSet> atom_basis_;
65
std::vector<int> i_cd_;
68
// The electronic basis are bs1 and bs2 in tbint and the
69
// nuclear basis is bs3.
70
ChargeDistInt(const Ref<TwoBodyThreeCenterInt>& tbint,
73
void compute_shell(int,int);
78
ChargeDistInt::ChargeDistInt(const Ref<TwoBodyThreeCenterInt>& tbint,
80
OneBodyInt(tbint->integral(),tbint->basis1(),tbint->basis2()),
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()];
91
for (int i=0; i<atom_basis_->ncenter(); i++) {
92
if (atom_basis_->nshell_on_center(i) > 0) i_cd_.push_back(i);
97
ChargeDistInt::compute_shell(int ish,int jsh)
99
// std::cout << "starting " << ish << " " << jsh << std::endl;
101
= ebasis0_->shell(ish).nfunction()
102
* ebasis1_->shell(jsh).nfunction();
103
memset(buffer_,0,nijbf*sizeof(buffer_[0]));
104
const double *tbbuffer = tbint_->buffer();
107
for (int ii=0; ii<i_cd_.size(); 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++) {
117
-= coef_[coef_offset+kbf] * tbbuffer[ijbf*nbfk + kbf];
118
// std::cout << " adding "
119
// << coef_[coef_offset+kbf] * tbbuffer[ijbf*nbfk + kbf]
121
// << coef_[coef_offset+kbf]
123
// << tbbuffer[ijbf*nbfk + kbf]
124
// << " at location "
130
coef_offset += nshell;
132
// std::cout << "done with " << ish << " " << jsh << std::endl;
136
ChargeDistInt::cloneable()
138
// not cloneable because tbint is not cloneable
144
/////////////////////////////////////////////////////////////////////////
52
146
static ClassDesc Wavefunction_cd(
53
typeid(Wavefunction),"Wavefunction",5,"public MolecularEnergy",
147
typeid(Wavefunction),"Wavefunction",7,"public MolecularEnergy",
56
150
Wavefunction::Wavefunction(const Ref<KeyVal>&keyval):
385
519
hao.element_op(hc);
388
Ref<OneBodyInt> nuc = integral_->nuclear();
390
hc = new OneBodyIntOp(new SymmOneBodyIntIter(nuc, pl));
522
if (atom_basis_.null()) {
523
Ref<OneBodyInt> nuc = integral_->nuclear();
525
hc = new OneBodyIntOp(new SymmOneBodyIntIter(nuc, pl));
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
534
// find out which atoms are point charges and which ones are charge
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);
540
int n_pc = i_pc.size();
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);
552
Ref<PointChargeData> pc_data
553
= new PointChargeData(n_pc, pc_xyz.get(), pc_charges.get());
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));
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));
570
integral()->set_basis(gbs_);
394
576
// now symmetrize Hso
395
577
RefSymmSCMatrix h(so_dimension(), basis_matrixkit());
459
642
return hcore_.result_noupdate();
645
// Compute lists of centers that are point charges and lists that
646
// are charge distributions.
648
Wavefunction::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)
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);
666
if (is_Q) q_pc.push_back(i);
667
else n_pc.push_back(i);
673
Wavefunction::nuclear_repulsion_energy()
675
if (atom_basis_.null()) return molecule()->nuclear_repulsion_energy();
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);
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 */);
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);
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 */);
708
Wavefunction::nuc_rep_pc_pc(const std::vector<int>&c1,
709
const std::vector<int>&c2,
714
if (c1.size() == 0 || c2.size() == 0) return e;
716
for (int ii=0; ii<c1.size(); 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++) {
723
SCVector3 aj(molecule()->r(j));
724
e += Zi * molecule()->charge(j) / ai.dist(aj);
732
Wavefunction::nuc_rep_pc_cd(const std::vector<int>&pc,
733
const std::vector<int>&cd)
737
if (pc.size() == 0 || cd.size() == 0) return e;
739
integral()->set_basis(atom_basis());
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];
748
for (int ii=0; ii<pc.size(); ii++) {
750
charges.get()[ii] = molecule()->charge(i);
751
for (int j=0; j<3; j++) positions.get()[ii][j] = molecule()->r(i,j);
754
Ref<PointChargeData> pcdata = new PointChargeData(pc.size(),
757
Ref<OneBodyOneCenterInt> ob = integral()->point_charge1(pcdata);
759
const double *buffer = ob->buffer();
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++) {
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];
774
integral()->set_basis(basis());
780
Wavefunction::nuc_rep_cd_cd(const std::vector<int>&c1,
781
const std::vector<int>&c2,
786
if (c1.size() == 0 || c2.size() == 0) return e;
788
integral()->set_basis(atom_basis());
790
Ref<TwoBodyTwoCenterInt> tb = integral()->electron_repulsion2();
792
const double *buffer = tb->buffer();
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]
822
integral()->set_basis(basis());
828
Wavefunction::nuclear_repulsion_energy_gradient(double *g)
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];
835
nuclear_repulsion_energy_gradient(gp.get());
839
Wavefunction::nuclear_repulsion_energy_gradient(double **g)
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]);
850
for (int i=0; i<molecule()->natom(); i++) {
851
for (int j=0; j<3; j++) g[i][j] = 0.0;
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);
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 */);
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);
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 */);
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");
887
Wavefunction::nuc_rep_grad_pc_pc(double **grad,
888
const std::vector<int>&c1,
889
const std::vector<int>&c2,
892
if (c1.size() == 0 || c2.size() == 0) return;
894
for (int ii=0; ii<c1.size(); 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++) {
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];
915
Wavefunction::nuc_rep_grad_pc_cd(double **grad,
916
const std::vector<int>&pc,
917
const std::vector<int>&cd)
919
if (pc.size() == 0 || cd.size() == 0) return;
921
throw std::runtime_error("Wavefunction::nuclear_repulsion_energy_gradient: not done");
925
Wavefunction::nuc_rep_grad_cd_cd(double **grad,
926
const std::vector<int>&c1,
927
const std::vector<int>&c2,
930
if (c1.size() == 0 || c2.size() == 0) return;
932
throw std::runtime_error("Wavefunction::nuclear_repulsion_energy_gradient: not done");
462
935
// returns the orthogonalization matrix
464
937
Wavefunction::so_to_orthog_so()
614
1114
return orthog_method_;
1118
Wavefunction::set_orthog_method(const OverlapOrthog::OrthogMethod& omethod)
1120
if (orthog_method_ != omethod) {
1121
orthog_method_ = omethod;
618
1128
Wavefunction::lindep_tol() const
620
1130
return lindep_tol_;
1134
Wavefunction::set_lindep_tol(double lindep_tol)
1136
if (lindep_tol_ != lindep_tol) {
1137
lindep_tol_ = lindep_tol;
1144
Wavefunction::scale_atom_basis_coef()
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);
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");
1157
int coef_offset = 0;
1159
for (int ii=0; ii<i_cd.size(); ii++) {
1161
int nshell = atom_basis_->nshell_on_center(i);
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
1184
nfunc += shell.ncontraction();
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];
1192
coef_offset += nshell;
623
1196
/////////////////////////////////////////////////////////////////////////////
625
1198
// Local Variables: