Program Listing for File generate.cpp¶
↰ Return to documentation for file (/Users/robertshaw/devfiles/libecpint/src/generate.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 "generate.hpp"
void generate_lists(int LA, int LB, int lam, libecpint::AngularIntegral& angInts) {
using namespace libecpint;
// Create the code file
std::string ofname = "generated/Q" + std::to_string(LA) + std::to_string(LB) + std::to_string(lam) + ".cpp";
std::ofstream outfile(ofname);
if (!outfile.is_open())
std::cerr << "Problems writing to file!" << std::endl;
else {
std::cout << "Generating Q(" << LA << ", " << LB << ", " << lam << ")... " << std::flush;
// Top matter
outfile << "// Generated as part of Libecpint, Copyright 2017 Robert A Shaw" << std::endl;
outfile << "#include \"qgen.hpp\"" << std::endl;
outfile << "namespace libecpint {" << std::endl << "namespace qgen {" << std::endl;
outfile << "void Q" << LA << "_" << LB << "_" << lam << "(const ECP& U, const GaussianShell& shellA, const GaussianShell& shellB, "
<< "const FiveIndex<double> &CA, const FiveIndex<double> &CB, const TwoIndex<double> &SA, const TwoIndex<double> &SB, const double Am, const double Bm, "
<< "const RadialIntegral &radint, const AngularIntegral& angint, const RadialIntegral::Parameters& parameters, ThreeIndex<double> &values) {" << std::endl << std::endl;
double prefac = 16.0 * M_PI * M_PI;
int na = 0;
int z1, z2;
double ang_alpha, ang_beta, ang;
// Do we need to unrol the angular integrals too?
bool unrolling = LA <= maxUnrol && LB <= maxUnrol && (LA + LB + lam) <= 3*maxUnrol;
// Store the terms and radials if unrolling, just radial indices if not
std::vector<SumTerm> terms;
std::vector<Triple> radial_triples;
// Loop over cartesian functions in alpha order
for (int x1 = LA; x1 >= 0; x1--) {
for (int r1 = LA-x1; r1 >= 0; r1--) {
z1 = LA - x1 - r1;
int nb = 0;
for (int x2 = LB; x2 >= 0; x2--) {
for (int y2 = LB - x2; y2 >= 0; y2--) {
z2 = LB - x2 - y2;
for (int alpha_x = 0; alpha_x <= x1; alpha_x++) {
for (int alpha_y = 0; alpha_y <= r1; alpha_y++) {
for (int alpha_z = 0; alpha_z <= z1; alpha_z++) {
int alpha = alpha_x + alpha_y + alpha_z;
for (int beta_x = 0; beta_x <= x2; beta_x++) {
for (int beta_y = 0; beta_y <= y2; beta_y++) {
for (int beta_z = 0; beta_z <= z2; beta_z++) {
int beta = beta_x + beta_y + beta_z;
int N = alpha + beta;
for (int lam1 = 0; lam1 <= lam + alpha; lam1++) {
int lam2start = (lam1 + N) % 2;
for (int lam2 = lam2start; lam2 <= lam + beta; lam2+=2) {
for (int mu1 = -lam1; mu1 <= lam1; mu1++) {
for (int mu2 = -lam2; mu2 <= lam2; mu2++) {
for (int mu = -lam; mu <= lam; mu++) {
ang_alpha = angInts.getIntegral(alpha_x, alpha_y, alpha_z, lam, mu, lam1, mu1);
ang_beta = angInts.getIntegral(beta_x, beta_y, beta_z, lam, mu, lam2, mu2);
ang = ang_alpha * ang_beta;
// Screen based on the angular integrals
if (fabs(ang) > 1e-15) {
if (unrolling) {
SumTerm newTerm;
newTerm.SA = Pair(lam1, lam1+mu1);
newTerm.SB = Pair(lam2, lam2+mu2);
newTerm.radial = Triple(N, lam1, lam2);
newTerm.CA = Quintuple(0, na, alpha_x, alpha_y, alpha_z);
newTerm.CB = Quintuple(0, nb, beta_x, beta_y, beta_z);
newTerm.ang = prefac * ang;
newTerm.mu = lam+mu;
newTerm.na = na;
newTerm.nb = nb;
terms.push_back(newTerm);
}
radial_triples.push_back({N, lam1, lam2});
}
}
}
}
}
}
}
}
}
}
}
}
nb++;
}
}
na++;
}
}
// Sort the radial triples and eliminate repeats
std::sort(radial_triples.begin(), radial_triples.end());
radial_triples.erase(std::unique(radial_triples.begin(), radial_triples.end()), radial_triples.end());
// Determine the maximum number of base integrals needed across the set of all radial integrals
int nbase = 0;
if (radial_triples.size() > 0) {
Triple& tmax = radial_triples[radial_triples.size()-1];
nbase = std::get<0>(tmax) + std::get<1>(tmax) - 1;
nbase = nbase < 0 ? 0 : nbase;
}
// Sort the radials into two lists, depending on whether l1 <= l2 (radial_A), or l2 > l1 (radial_B)
// swapping the order of l1/l2 in the latter case
std::vector<Triple> radial_A, radial_B;
for (Triple& t : radial_triples) {
if (std::get<1>(t) <= std::get<2>(t)) radial_A.push_back(t);
else radial_B.push_back({std::get<0>(t), std::get<2>(t), std::get<1>(t)});
}
// Compute the correctly ordered radials first
outfile << "\tstd::vector<Triple> radial_triples_A = {" << std::endl;
bool first = true;
for (Triple& t : radial_A) {
if (!first) outfile << "," << std::endl;
else first = false;
outfile << "\t\t{" + std::to_string(std::get<0>(t)) + ", "
+ std::to_string(std::get<1>(t)) + ", "
+ std::to_string(std::get<2>(t)) + "}";
}
outfile << "\t};" << std::endl << std::endl;
outfile << "\tThreeIndex<double> radials(" << lam+LA+LB+1 << ", " << lam+LA+1 << ", " << lam+LB+1 << ");" << std::endl;
outfile << "\tradint.type2(radial_triples_A, " << nbase << ", " << lam << ", U, shellA, shellB, Am, Bm, radials);" << std::endl << std::endl;
// Now compute the reverse-ordered radials
outfile << "\tstd::vector<Triple> radial_triples_B = {" << std::endl;
first = true;
for (Triple& t : radial_B) {
if (!first) outfile << "," << std::endl;
else first = false;
outfile << "\t\t{" + std::to_string(std::get<0>(t)) + ", "
+ std::to_string(std::get<1>(t)) + ", "
+ std::to_string(std::get<2>(t)) + "}";
}
outfile << "\t};" << std::endl << std::endl;
outfile << "\tThreeIndex<double> radials_B(" << lam+LA+LB+1 << ", " << lam+LB+1 << ", " << lam+LA+1 << ");" << std::endl;
outfile << "\tradint.type2(radial_triples_B, " << nbase << ", " << lam << ", U, shellB, shellA, Bm, Am, radials_B);" << std::endl;
// These need to be compressed into the radials array, with l1/l2 reversed back
outfile << "\tfor (Triple& t : radial_triples_B) radials(std::get<0>(t), std::get<2>(t), std::get<1>(t)) = radials_B(std::get<0>(t), std::get<1>(t), std::get<2>(t));" << std::endl << std::endl;
if (unrolling) {
// Print out the unrolled angular integral code if needed
std::cout << "unrolling... " << std::flush;
for (auto& term : terms) outfile << "\t" << term << std::endl;
} else {
// Just use the generic rolled-up angular integral code
outfile << "\trolled_up(" << lam << ", " << LA << ", " << LB << ", radials, CA, CB, SA, SB, angint, values);" << std::endl;
}
outfile << "}" << std::endl << "}" << std::endl << "}" << std::endl;
std::cout << "done." << std::endl;
outfile.close();
}
}
int main(int argc, char* argv[]) {
// Factorial singletons will not have been initialised
libecpint::initFactorials();
int maxL = libecpint::maxL;
libecpint::AngularIntegral angInts(maxL, maxL);
angInts.compute();
// Generate the qgen.hpp header file
std::string header_name;
if (argc > 1) {
header_name = argv[1];
header_name += "qgen.hpp";
} else {
header_name = "generated/qgen";
}
std::ifstream qgen_part("generated/qgen.part");
std::ofstream qgen_head(header_name);
if (!qgen_part.is_open() || !qgen_head.is_open())
std::cerr << "Problem creating qgen header file!" << std::endl;
else {
std::string line;
while(!qgen_part.eof()) {
std::getline(qgen_part, line);
qgen_head << line << std::endl;
}
qgen_part.close();
// Loop over all possible (l1, l2, lam) integrals up to l1 = l2 = lam = maxL
// with l1 <= l2, generating the code and adding the function to the header file.
for (int j = 0; j <= maxL; j++) {
for (int i = 0; i <= j; i++) {
for (int k = 0; k <= maxL; k++) {
generate_lists(i, j, k, angInts);
qgen_head << "\tvoid Q" << i << "_" << j << "_" << k << "("
<< "const ECP&, const GaussianShell&, const GaussianShell&, const FiveIndex<double>&, const FiveIndex<double>&, "
<< "const TwoIndex<double>&, const TwoIndex<double>&, double, double, const RadialIntegral&, "
<< "const AngularIntegral&, const RadialIntegral::Parameters&, ThreeIndex<double>&);" << std::endl;
}
}
}
qgen_head << std::endl << "}" << std::endl << "}" << std::endl;
qgen_head << "#endif" << std::endl;
qgen_head.close();
// Now generate the function pointer array in ecpint_gen.cpp
std::ifstream ecpgen_part("generated/ecpint_gen.part");
std::ofstream ecpgen_head("generated/ecpint_gen.cpp");
if (!ecpgen_part.is_open() || !ecpgen_head.is_open())
std::cerr << "Problem reading/writing ecpgen file!" << std::endl;
else {
while(!ecpgen_part.eof()) {
std::getline(ecpgen_part, line);
ecpgen_head << line << std::endl;
}
ecpgen_part.close();
for (int i =0; i <= maxL; i++) {
ecpgen_head << "\t\t{ ";
for (int j = 0; j<= maxL; j++) {
ecpgen_head << "\t\t\t{";
int I = std::min(i, j);
int J = std::max(i, j);
for (int k = 0; k< maxL; k++)
ecpgen_head << "qgen::Q" << I << "_" << J << "_" << k << ", ";
ecpgen_head << "qgen::Q" << I << "_" << J << "_" << maxL << "}";
if (j != maxL) ecpgen_head << ",";
ecpgen_head << std::endl;
}
ecpgen_head << "\t\t}";
if (i != maxL) ecpgen_head << ",";
ecpgen_head << std::endl;
}
ecpgen_head << "\t};" << std::endl << "}" << std::endl;
ecpgen_head.close();
}
}
return 0;
}