Program Listing for File api.cpp¶
↰ Return to documentation for file (/Users/robertshaw/devfiles/libecpint/src/lib/api.cpp
)
/*
* Copyright (c) 2020 Robert Shaw
* This file is a part of Libecpint.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "api.hpp"
#include <iostream>
#include <cmath>
#include <cassert>
#include "mathutil.hpp"
namespace libecpint {
void ECPIntegrator::set_gaussian_basis (
const int nshells, const double* coords, const double* exponents, const double* coefs, const int* ams, const int* shell_lengths) {
int ctr = 0;
min_alpha = 100.0;
for (int i = 0; i < nshells; i++) {
ncart += (ams[i]+1)*(ams[i]+2)/2;
std::array<double, 3> cvec = {coords[3*i], coords[3*i+1], coords[3*i+2]};
GaussianShell newShell(cvec, ams[i]);
if (ams[i] > maxLB) maxLB = ams[i];
for (int n = 0; n < shell_lengths[i]; n++) {
newShell.addPrim(exponents[ctr], coefs[ctr]);
ctr++;
}
min_alpha = newShell.min_exp < min_alpha ? newShell.min_exp : min_alpha;
shells.push_back(newShell);
}
basis_is_set = true;
}
void ECPIntegrator::set_ecp_basis(
const int necps, const double* coords, const double* exponents, const double* coefs, const int* ams, const int* ns, const int* shell_lengths) {
int ctr = 0;
for (int i = 0; i < necps; i++) {
ECP newU(&coords[3*i]);
for (int n = 0; n < shell_lengths[i]; n++) {
newU.addPrimitive(ns[ctr], ams[ctr], exponents[ctr], coefs[ctr]);
ctr++;
}
newU.sort();
ecps.addECP(newU, 0);
}
ecp_is_set = true;
}
void ECPIntegrator::set_ecp_basis_from_library(
const int necps, const double* coords, const int* charges, const std::vector<std::string> & names, const std::string & share_dir) {
for (int i = 0; i < necps; i++) {
std::array<double, 3> center = {coords[3*i], coords[3*i+1], coords[3*i+2]};
int q = charges[i];
std::string filename = share_dir + "/xml/" + names[i] + ".xml";
ecps.addECP_from_file(q, center, filename);
}
ecp_is_set = true;
}
void ECPIntegrator::update_gaussian_basis_coords(const int nshells, const double* coords) {
assert(nshells == shells.size());
for (int i = 0; i < nshells; i++){
shells[i].localCenter[0] = coords[3*i];
shells[i].localCenter[1] = coords[3*i+1];
shells[i].localCenter[2] = coords[3*i+2];
}
}
void ECPIntegrator::update_ecp_basis_coords(const int necps, const double* coords) {
assert(necps == ecps.getN());
for (int i = 0; i < necps; i++)
ecps.getECP(i).setPos(coords[3*i], coords[3*i+1], coords[3*i+2]);
}
void ECPIntegrator::init(const int deriv_) {
assert(ecp_is_set);
assert(basis_is_set);
deriv = std::max(0, std::min(2, deriv_));
ecpint = std::make_shared<ECPIntegral>(maxLB, ecps.getMaxL(), deriv);
// Determine the internal atom ids
natoms = 0;
std::vector<std::array<double, 3>> centers;
for (auto& s : shells) {
int i = 0;
bool found = false;
while ( !found && (i < centers.size()) ) {
double diff = std::abs(centers[i][0] - s.centerVec[0]);
diff += std::abs(centers[i][1] - s.centerVec[1]);
diff += std::abs(centers[i][2] - s.centerVec[2]);
if (diff < 1e-4) {
s.atom_id = i;
found = true;
}
i++;
}
if (!found) {
s.atom_id = natoms;
natoms++;
centers.push_back({s.centerVec[0], s.centerVec[1], s.centerVec[2]});
}
}
for (int n = 0; n < ecps.getN(); n++) {
ECP& U = ecps.getECP(n);
int i = 0;
bool found = false;
while ( !found && (i < centers.size()) ) {
double diff = std::abs(centers[i][0] - U.center_[0]);
diff += std::abs(centers[i][1] - U.center_[1]);
diff += std::abs(centers[i][2] - U.center_[2]);
if (diff < 1e-4) {
U.atom_id = i;
found = true;
}
i++;
}
if (!found) {
U.atom_id = natoms;
natoms++;
centers.push_back({U.center_[0], U.center_[1], U.center_[2]});
}
}
}
double shell_bound(const int la, const double alpha, const double A2, const double eta) {
double sigma;
if (A2 < 1e-6) {
sigma = 0.5 * (1.0 + eta/alpha);
} else {
sigma = 1.0/(2.0*alpha*(eta*eta*A2 + la*(alpha + eta)));
sigma = sigma * la * (alpha + eta) * (alpha + eta);
}
double atilde = (1.0 - sigma) * alpha;
double Na = la / (2*M_EULER*alpha*sigma);
Na = FAST_POW[la](std::sqrt(Na));
double result = atilde * eta * A2 / (atilde + eta);
result = std::exp(-result) * Na;
return result;
}
void ECPIntegrator::compute_integrals() {
// initialise all to zero
integrals.assign(ncart, ncart, 0.0);
// loop over shells
TwoIndex<double> tempValues;
int nshells = shells.size();
double thresh = FAST_POW[maxLB+3]((maxLB+3.0)/min_alpha)*FAST_POW[3](M_PI/(2*maxLB+3.0));
thresh /= FAST_POW[maxLB](2.0*M_EULER);
thresh = TWO_C_TOLERANCE / std::sqrt(thresh);
int n1 = 0;
double acx, acy, acz, A2, sb;
for(auto s1=0; s1<nshells; ++s1) {
GaussianShell& shellA = shells[s1];
int ncartA = shellA.ncartesian();
std::vector<int> ns;
for (int i = 0; i < ecps.getN(); i++) {
ECP& U = ecps.getECP(i);
acx = shellA.center()[0] - U.center_[0];
acy = shellA.center()[1] - U.center_[1];
acz = shellA.center()[2] - U.center_[2];
A2 = acx*acx + acy*acy + acz*acz;
sb = shell_bound(shellA.l, shellA.min_exp, A2, U.min_exp);
if (sb > thresh) ns.push_back(i);
}
if (ns.size() > 0) {
int n2 = 0;
for(auto s2=0; s2<=s1; ++s2) {
GaussianShell& shellB = shells[s2];
int ncartB = shellB.ncartesian();
TwoIndex<double> shellPairInts(ncartA, ncartB, 0.0);
for (auto i : ns) {
ECP& U = ecps.getECP(i);
ecpint->compute_shell_pair(U, shellA, shellB, tempValues);
shellPairInts.add(tempValues);
}
for (int i = n1; i < n1 + ncartA; i++) {
for (int j = n2; j < n2 + ncartB; j++) {
integrals(i, j) = shellPairInts(i-n1, j-n2);
integrals(j, i) = integrals(i, j);
}
}
n2 += ncartB;
}
}
n1 += ncartA;
}
//std::cout << "Total: " << ecpint->skipped + ecpint->zero + ecpint->nonzero << std::endl;
//std::cout << "Skipped: " << ecpint->skipped << std::endl;
//std::cout << "Zero: " << ecpint->zero << std::endl;
//std::cout << "Non-zero: " << ecpint->nonzero << std::endl;
}
void ECPIntegrator::compute_first_derivs() {
assert(deriv > 0);
for (int n = 0; n < 3*natoms; n++)
first_derivs.push_back(TwoIndex<double>(ncart, ncart, 0.0));
// loop over shells
std::array<TwoIndex<double>, 9> tempValues;
int nshells = shells.size();
int n1 = 0;
int Aix, Bix, Cix;
for(auto s1=0; s1<nshells; ++s1) {
GaussianShell& shellA = shells[s1];
int ncartA = shellA.ncartesian();
Aix = shellA.atom_id;
int n2 = 0;
for(auto s2=0; s2<=s1; ++s2) {
GaussianShell& shellB = shells[s2];
int ncartB = shellB.ncartesian();
Bix = shellB.atom_id;
for (int i = 0; i < ecps.getN(); i++) {
ECP& U = ecps.getECP(i);
Cix = U.atom_id;
ecpint->compute_shell_pair_derivative(U, shellA, shellB, tempValues);
// work out where to put them
for (int n = 0; n < 3; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
first_derivs[3*Aix+n](k, l) += tempValues[n](k-n1, l-n2);
first_derivs[3*Bix+n](k, l) += tempValues[n+3](k-n1, l-n2);
first_derivs[3*Cix+n](k, l) += tempValues[n+6](k-n1, l-n2);
if (s2 < s1) {
first_derivs[3*Aix+n](l, k) = first_derivs[3*Aix+n](k, l);
first_derivs[3*Bix+n](l, k) = first_derivs[3*Bix+n](k, l);
first_derivs[3*Cix+n](l, k) = first_derivs[3*Cix+n](k, l);
}
}
}
}
}
n2 += ncartB;
}
n1 += ncartA;
}
}
void ECPIntegrator::compute_second_derivs() {
assert(deriv > 1);
int nhess = (3*natoms*(3*natoms+1))/2;
for (int n = 0; n < nhess; n++)
second_derivs.push_back(TwoIndex<double>(ncart, ncart, 0.0));
// loop over shells
std::array<TwoIndex<double>, 45> tempValues;
int nshells = shells.size();
int n1 = 0;
int Aix, Bix, Cix;
int saa, sab, sac, sbb, sbc, scc;
int ixes[6] = {0, 1, 2, 4, 5, 8};
int back_ixes[6] = {0, 3, 6, 4, 7, 8};
int jxes[9] = {0, 3, 6, 1, 4, 7, 2, 5, 8};
for(auto s1=0; s1<nshells; ++s1) {
GaussianShell& shellA = shells[s1];
int ncartA = shellA.ncartesian();
Aix = shellA.atom_id;
int n2 = 0;
for(auto s2=0; s2<=s1; ++s2) {
GaussianShell& shellB = shells[s2];
int ncartB = shellB.ncartesian();
Bix = shellB.atom_id;
saa = H_START(Aix, Aix, natoms) + 3;
sbb = H_START(Bix, Bix, natoms) + 3;
sab = H_START(std::min(Aix, Bix), std::max(Aix, Bix), natoms);
sab = Aix == Bix ? sab + 3 : sab;
for (int i = 0; i < ecps.getN(); i++) {
ECP& U = ecps.getECP(i);
Cix = U.atom_id;
ecpint->compute_shell_pair_second_derivative(U, shellA, shellB, tempValues);
// work out where to put them
scc = H_START(Cix, Cix, natoms) + 3;
sac = H_START(std::min(Aix, Cix), std::max(Aix, Cix), natoms);
sac = Aix == Cix ? sac + 3 : sac;
sbc = H_START(std::min(Bix, Cix), std::max(Bix, Cix), natoms);
sbc = Bix == Cix ? sbc + 3 : sbc;
if ((Aix == Cix) || (Bix == Cix)) {
if (Bix != Aix) {
// two distinct atoms
// only need to worry about AA, AB, and BB blocks
for (int n = 0; n < 6; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
second_derivs[saa+n](k, l) += tempValues[n](k-n1, l-n2);
second_derivs[sbb+n](k, l) += tempValues[n+24](k-n1, l-n2);
if (s1 != s2) {
second_derivs[saa+n](l, k) = second_derivs[saa+n](k, l);
second_derivs[sbb+n](l, k) = second_derivs[sbb+n](k, l);
}
}
}
}
for (int n = 0; n < 9; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
if (Aix > Bix) {
second_derivs[sab+n](k, l) += tempValues[jxes[n]+6](k-n1, l-n2);
if (s1 != s2) second_derivs[sab+n](l, k) = second_derivs[sab+n](k, l);
} else {
second_derivs[sab+n](k, l) += tempValues[n+6](k-n1, l-n2);
if (s1 != s2) second_derivs[sab+n](l, k) = second_derivs[sab+n](k, l);
}
}
}
}
} // else everything is zero
} else if (Aix == Bix) {
// two distinct atoms, need to worry about everything
for (int n = 0; n < 6; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
second_derivs[saa+n](k, l) += tempValues[n](k-n1, l-n2); // aa
second_derivs[saa+n](k, l) += tempValues[n+24](k-n1, l-n2); // bb = aa
second_derivs[scc+n](k, l) += tempValues[n+39](k-n1, l-n2); // cc
second_derivs[saa+n](k, l) += tempValues[ixes[n]+6](k-n1, l-n2); // ab = aa
second_derivs[saa+n](k, l) += tempValues[back_ixes[n]+6](k-n1, l-n2); // ba = aa
if (s1 != s2) {
second_derivs[saa+n](l, k) = second_derivs[saa+n](k, l);
second_derivs[scc+n](l, k) = second_derivs[scc+n](k, l);
}
}
}
}
for (int n = 0; n < 9; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
if (Aix > Cix) {
second_derivs[sac+n](k, l) += tempValues[jxes[n]+15](k-n1, l-n2);
second_derivs[sac+n](k, l) += tempValues[jxes[n]+30](k-n1, l-n2); // bc = ac
if (s1 != s2) second_derivs[sac+n](l, k) = second_derivs[sac+n](k, l);
} else {
second_derivs[sac+n](k, l) += tempValues[n+15](k-n1, l-n2);
second_derivs[sac+n](k, l) += tempValues[n+30](k-n1, l-n2); // bc = ac
if (s1 != s2) second_derivs[sbc+n](l, k) = second_derivs[sbc+n](k, l);
}
}
}
}
} else {
for (int n = 0; n < 6; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
second_derivs[saa+n](k, l) += tempValues[n](k-n1, l-n2);
second_derivs[sbb+n](k, l) += tempValues[n+24](k-n1, l-n2);
second_derivs[scc+n](k, l) += tempValues[n+39](k-n1, l-n2);
if (s1 != s2) {
second_derivs[saa+n](l, k) = second_derivs[saa+n](k, l);
second_derivs[sbb+n](l, k) = second_derivs[sbb+n](k, l);
second_derivs[scc+n](l, k) = second_derivs[scc+n](k, l);
}
}
}
}
for (int n = 0; n < 9; n++) {
for (int k = n1; k < n1 + ncartA; k++) {
for (int l = n2; l < n2 + ncartB; l++) {
if (Aix > Bix) {
second_derivs[sab+n](k, l) += tempValues[jxes[n]+6](k-n1, l-n2);
if (s1 != s2) second_derivs[sab+n](l, k) = second_derivs[sab+n](k, l);
} else {
second_derivs[sab+n](k, l) += tempValues[n+6](k-n1, l-n2);
if (s1 != s2) second_derivs[sab+n](l, k) = second_derivs[sab+n](k, l);
}
if (Aix > Cix) {
second_derivs[sac+n](k, l) += tempValues[jxes[n]+15](k-n1, l-n2);
if (s1 != s2) second_derivs[sac+n](l, k) = second_derivs[sac+n](k, l);
} else {
second_derivs[sac+n](k, l) += tempValues[n+15](k-n1, l-n2);
if (s1 != s2) second_derivs[sac+n](l, k) = second_derivs[sac+n](k, l);
}
if (Bix > Cix) {
second_derivs[sbc+n](k, l) += tempValues[jxes[n]+30](k-n1, l-n2);
if (s1 != s2) second_derivs[sbc+n](l, k) = second_derivs[sbc+n](k, l);
} else {
second_derivs[sbc+n](k, l) += tempValues[n+30](k-n1, l-n2);
if (s1 != s2) second_derivs[sbc+n](l, k) = second_derivs[sbc+n](k, l);
}
}
}
}
}
}
n2 += ncartB;
}
n1 += ncartA;
}
}
}