diff --git a/src/apps/cc2/cc2.cc b/src/apps/cc2/cc2.cc index 50b03add557..79f7d3864b7 100644 --- a/src/apps/cc2/cc2.cc +++ b/src/apps/cc2/cc2.cc @@ -93,13 +93,10 @@ int main(int argc, char **argv) { calc->param.print("dft","end"); print("\n"); cc2.tdhf->get_parameters().print("response","end"); + print("\n"); + nemo->molecule().print(); } double hf_energy = nemo->value(); - if (world.rank() == 0) - std::cout << "\n\n\n\n\n\n Reference Calculation Ended\n SCF Energy is: " << hf_energy - << "\n current wall-time: " << wall_time() - << "\n current cpu-time: " << cpu_time() << "\n\n\n"; - cc2.solve(); if (world.rank() == 0) printf("\nfinished at time %.1fs\n\n", wall_time()); diff --git a/src/apps/mp2/mp2.cc b/src/apps/mp2/mp2.cc index 8f7753bf0a8..111f5d7c2b4 100644 --- a/src/apps/mp2/mp2.cc +++ b/src/apps/mp2/mp2.cc @@ -80,11 +80,19 @@ int main(int argc, char** argv) { if (world.rank() == 0) printf("\nstarting at time %.1fs\n", wall_time()); - const double hf_energy = mp2.get_hf().value(); - const double mp2_energy = mp2.value(); - if (world.rank() == 0) { - printf("final hf/mp2/total energy %12.8f %12.8f %12.8f\n", - hf_energy, mp2_energy, hf_energy + mp2_energy); + const double hf_energy=mp2.get_hf().value(); + const double mp2_energy=mp2.value(); +// const double mp2_energy=0.0; + if(world.rank() == 0) { + printf("final hf/mp2/total energy %12.8f %12.8f %12.8f\n", + hf_energy,mp2_energy,hf_energy+mp2_energy); + } + double mp3_correction=mp2.mp3(); + + double mp3_energy=mp3_correction+mp2_energy; + if(world.rank() == 0) { + printf("final hf/mp2/mp3/total energy %12.8f %12.8f %12.8f %12.8f\n", + hf_energy,mp2_energy,mp3_correction,hf_energy+mp3_energy); } } catch (std::exception& e) { diff --git a/src/examples/heat.cc b/src/examples/heat.cc index 8e154b1570e..a9e59fa3de2 100644 --- a/src/examples/heat.cc +++ b/src/examples/heat.cc @@ -130,7 +130,9 @@ int main(int argc, char** argv) { expnt[0] = 1.0/(4.0*c*tstep); coeff[0] = pow(4.0*constants::pi*c*tstep,-1.5); - operatorT G(world, coeff, expnt); + double lo_dummy=1.e-4; + double thresh_dummy=1.e-6; + operatorT G(world, coeff, expnt, lo_dummy, thresh_dummy); functionT ut = G(u0); diff --git a/src/examples/heat2.cc b/src/examples/heat2.cc index d7e9f1d1040..6d2cfdfec04 100644 --- a/src/examples/heat2.cc +++ b/src/examples/heat2.cc @@ -147,7 +147,7 @@ int main(int argc, char** argv) { real_tensor expnt(1), coeff(1); expnt[0] = 1.0/(4.0*c*tstep*0.5); coeff[0] = pow(4.0*constants::pi*c*tstep*0.5,-1.5); - real_convolution_3d G(world, coeff, expnt); + real_convolution_3d G(world, coeff, expnt, 1.e-4, 1.e-4); // Propagate forward 50 time steps real_function_3d u = u0; diff --git a/src/examples/smooth.h b/src/examples/smooth.h index 74342bce25d..7a5ec6c90c7 100644 --- a/src/examples/smooth.h +++ b/src/examples/smooth.h @@ -607,7 +607,7 @@ class smooth { Tensor coeffs(1), exponents(1); exponents(0L) = 1.0 / (2.0 * 0.04); coeffs(0L) = pow((1.0 / (2.0 * 0.04)) / M_PI, 0.5); - SeparatedConvolution op(world, coeffs, exponents); + SeparatedConvolution op(world, coeffs, exponents,1.e-5,1.e-5); real_function_1d sf = apply(op, f); double diff = (f - sf).norm2(); output("||f - smoothed_f||_1D=" + stringify(diff)); diff --git a/src/examples/tdse_example.cc b/src/examples/tdse_example.cc index 49a71c56373..fbc08fdc099 100644 --- a/src/examples/tdse_example.cc +++ b/src/examples/tdse_example.cc @@ -175,7 +175,7 @@ void converge(World& world, functionT& potn, functionT& psi, double& eps) { Tensor coeff(1); coeff[0] = 1.0/pow(constants::pi*tmax,1.5); Tensor expnt(1); expnt[0] = 1.0/tmax; - operatorT* op = new operatorT(world,coeff,expnt); + operatorT* op = new operatorT(world,coeff,expnt,0.0,0.0); for (int iter=0; iter<20; iter++) { if (world.rank() == 0) print("ITER",iter); @@ -197,7 +197,7 @@ void converge(World& world, functionT& potn, functionT& psi, double& eps) { delete op; coeff[0] = 1.0/sqrt(constants::pi*tmax); expnt[0] = 1.0/tmax; - op = new operatorT(world,coeff,expnt); + op = new operatorT(world,coeff,expnt,0.0,0.0); } } delete op; diff --git a/src/madness/chem/CC2.cc b/src/madness/chem/CC2.cc index 0ffa7e5e10a..d54f9b73708 100644 --- a/src/madness/chem/CC2.cc +++ b/src/madness/chem/CC2.cc @@ -10,6 +10,7 @@ #include #include "MolecularOrbitals.h" #include "localizer.h" +#include namespace madness { @@ -36,10 +37,10 @@ CC2::solve() { // singles for ground state CC_vecfunction cc2singles(PARTICLE); - double mp2_energy, cc2_energy; + double mp2_energy=0.0, cc2_energy=0.0, mp3_energy=0.0; bool need_tdhf=parameters.response(); - bool need_mp2=(ctype==CT_MP2 or ctype==CT_CISPD or ctype==CT_ADC2); + bool need_mp2=(ctype==CT_MP2 or ctype==CT_CISPD or ctype==CT_ADC2 or ctype==CT_MP3); bool need_cc2=(ctype==CT_LRCC2 or ctype==CT_CC2); // check for restart data for CC2, otherwise use MP2 as guess @@ -57,14 +58,18 @@ CC2::solve() { } if (need_mp2) { - initialize_pairs(mp2pairs, GROUND_STATE, CT_MP2, CC_vecfunction(PARTICLE), CC_vecfunction(RESPONSE), 0); - mp2_energy = solve_mp2_coupled(mp2pairs); + bool restarted=initialize_pairs(mp2pairs, GROUND_STATE, CT_MP2, CC_vecfunction(PARTICLE), CC_vecfunction(RESPONSE), 0); + if (restarted and parameters.no_compute_mp2()) { +// for (auto& pair : mp2pairs.allpairs) mp2_energy+=CCOPS.compute_pair_correlation_energy(pair.second); + } else { + mp2_energy = solve_mp2_coupled(mp2pairs); + } output_calc_info_schema("mp2",mp2_energy); output.section(assign_name(CT_MP2) + " Calculation Ended !"); if (world.rank() == 0) { printf_msg_energy_time("MP2 correlation energy",mp2_energy,wall_time()); // std::cout << std::fixed << std::setprecision(10) << " MP2 Correlation Energy =" << mp2_energy << "\n"; - } + } } if (need_cc2) { @@ -96,6 +101,8 @@ CC2::solve() { ; // we're good } else if (ctype == CT_MP2) { ; // we're good + } else if (ctype == CT_MP3) { + mp3_energy=compute_mp3(mp2pairs); } else if (ctype == CT_CC2) { ; // we're good } else if (ctype == CT_CISPD) { @@ -296,12 +303,14 @@ CC2::solve() { } void CC2::output_calc_info_schema(const std::string model, const double& energy) const { - nlohmann::json j; - j["model"]=model; - j["driver"]="energy"; - j["return_energy"]=energy; - j["mp2_correlation_energy"]=energy; - update_schema(nemo->get_param().prefix()+".calc_info", j); + if (world.rank()==0) { + nlohmann::json j; + j["model"]=model; + j["driver"]="energy"; + j["return_energy"]=energy; + j["mp2_correlation_energy"]=energy; + update_schema(nemo->get_param().prefix()+".calc_info", j); + } } @@ -352,7 +361,7 @@ Tensor CC2::enforce_core_valence_separation(const Tensor& fmat) } MADNESS_CHECK(Localizer::check_core_valence_separation(fock2,lmo.get_localize_sets())); - if (world.rank()==0) lmo.pretty_print("localized MOs"); + // if (world.rank()==0) lmo.pretty_print("localized MOs"); return fock2; }; @@ -391,114 +400,89 @@ double CC2::solve_mp2_coupled(Pairs& doubles) { // make vector holding CCPairs for partitioner of MacroTask std::vector pair_vec=Pairs::pairs2vector(doubles,triangular_map); - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting constant part at time " << wall_time() << std::endl; - // calc constant part via taskq - auto taskq = std::shared_ptr(new MacroTaskQ(world, world.size())); - taskq->set_printlevel(3); - MacroTaskMp2ConstantPart t; - MacroTask task(world, t, taskq); - std::vector result_vec = task(pair_vec, CCOPS.mo_ket().get_vecfunction(), - CCOPS.mo_bra().get_vecfunction(), parameters, - nemo->R_square, nemo->ncf->U1vec(),std::vector({"Ue","KffK"})); -// std::vector Gfij_vec = task(pair_vec, CCOPS.mo_ket().get_vecfunction(), -// CCOPS.mo_bra().get_vecfunction(), parameters, -// nemo->R_square, nemo->ncf->U1vec(),std::vector({"f12phi"})); - taskq->print_taskq(); - taskq->run_all(); - - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nFinished constant part at time " << wall_time() << std::endl; - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting saving pairs and energy calculation at time " << wall_time() << std::endl; - - // compute coupling for the constant term -// Pairs Gfij_pair=Pairs::vector2pairs(Gfij_vec, PairVectorMap::triangular_map(nfreeze,nocc)); -// Pairs coupling_constant_term=compute_local_coupling(Gfij_pair); -// std::vector coupling_constant_term_vec=Pairs::pairs2vector(coupling_constant_term,triangular_map); - - // transform vector back to Pairs structure - for (size_t i = 0; i < pair_vec.size(); i++) { - pair_vec[i].constant_part = result_vec[i];// - coupling_constant_term_vec[i]; - //save(pair_vec[i].constant_part, pair_vec[i].name() + "_const"); - pair_vec[i].constant_part.truncate().reduce_rank(); - pair_vec[i].function().truncate().reduce_rank(); - if (pair_vec[i].type == GROUND_STATE) total_energy += CCOPS.compute_pair_correlation_energy(pair_vec[i]); + // read constant part from file + if (parameters.no_compute_mp2_constantpart()) { + if (world.rank()==0) print("Skipping MP2 constant part calculation"); + for (auto& c : pair_vec) { + MADNESS_CHECK_THROW(c.constant_part.is_initialized(), "could not find constant part"); + // constant part is zero-order guess for pair.function + if (not c.function().is_initialized()) c.update_u(c.constant_part); + } + + } else { + if (world.rank()==0) print_header3("Starting MP2 constant part calculation"); + // calc constant part via taskq + auto taskq = std::shared_ptr(new MacroTaskQ(world, world.size())); + taskq->set_printlevel(3); + MacroTaskMp2ConstantPart t; + MacroTask task(world, t, taskq); + std::vector result_vec = task(pair_vec, CCOPS.mo_ket().get_vecfunction(), + CCOPS.mo_bra().get_vecfunction(), parameters, + nemo->R_square, nemo->ncf->U1vec(),std::vector({"Ue","KffK"})); + taskq->print_taskq(); + taskq->run_all(); + + if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nFinished constant part at time " << wall_time() << std::endl; + if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting saving pairs and energy calculation at time " << wall_time() << std::endl; + + // transform vector back to Pairs structure + for (size_t i = 0; i < pair_vec.size(); i++) { + pair_vec[i].constant_part = result_vec[i]; + pair_vec[i].functions[0] = CCPairFunction(result_vec[i]); + pair_vec[i].constant_part.truncate().reduce_rank(); + pair_vec[i].constant_part.print_size("constant_part"); + pair_vec[i].function().truncate().reduce_rank(); + save(pair_vec[i].constant_part, pair_vec[i].name() + "_const"); + // save(pair_vec[i].function(), pair_vec[i].name()); + if (pair_vec[i].type == GROUND_STATE) { + double energy = CCOPS.compute_pair_correlation_energy(pair_vec[i]); + if (world.rank()==0) printf("pair energy for pair %zu %zu: %12.8f\n", pair_vec[i].i, pair_vec[i].j, energy); + total_energy += energy; + } + } + if (world.rank()==0) { + printf("current decoupled mp2 energy %12.8f\n", total_energy); + std::cout << std::fixed << std::setprecision(1) << "\nFinished saving pairs and energy calculation at time " << wall_time() << std::endl; + } } - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nFinished saving pairs and energy calculation at time " << wall_time() << std::endl; + + print_header3("Starting updating MP2 pairs"); // create new pairs structure Pairs updated_pairs; - for (auto& tmp_pair : pair_vec) { - updated_pairs.insert(tmp_pair.i, tmp_pair.j, tmp_pair); - } -// typedef allocator allocT; -// allocT alloc(world, pair_vec.size()); -// XNonlinearSolver, double, allocT> solver(alloc); + for (auto& tmp_pair : pair_vec) updated_pairs.insert(tmp_pair.i, tmp_pair.j, tmp_pair); + auto solver= nonlinear_vector_solver(world,pair_vec.size()); solver.set_maxsub(parameters.kain_subspace()); solver.do_print = (world.rank() == 0); + for (size_t iter = 0; iter < parameters.iter_max_6D(); iter++) { -// if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting coupling at time " << wall_time() << std::endl; // compute the coupling between the pair functions Pairs coupling=compute_local_coupling(updated_pairs); auto coupling_vec=Pairs::pairs2vector(coupling,triangular_map); + if (parameters.debug()) print_size(world, coupling_vec, "couplingvector"); -// if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nFinished coupling at time " << wall_time() << std::endl; - - -// // make coupling vector that can be stored in cloud -// // pair -> position -// // (i,j) -> j(j+1)+i -// // Pairs struc does not provide access to last element -// int i_max = 0; -// int j_max = 0; -// for (auto& tmp_coupling: coupling.allpairs){ -// if (std::get<0>(tmp_coupling.first) > i_max) i_max = std::get<0>(tmp_coupling.first); -// if (std::get<1>(tmp_coupling.first) > j_max) j_max = std::get<1>(tmp_coupling.first); -// } -// int last_position = j_max*(j_max+1) + i_max; -// std::vector coupling_vec = zero_functions_compressed(world, (last_position+1)); -// -// for (auto& tmp_coupling : coupling.allpairs) { -// int i = std::get<0>(tmp_coupling.first); -// int j = std::get<1>(tmp_coupling.first); -// int position = j*(j+1) + i; -// //if (world.rank() == 0) std::cout << i << " ," << j << " -> "<< position << std::endl; -// coupling_vec[position] = tmp_coupling.second; -// } - //print coupling vec - if (world.rank()==0) std::cout << "aaaaa coupling vector"; - print_size(world, coupling_vec, "couplingvector"); - - double total_norm = 0.0; double old_energy = total_energy; total_energy = 0.0; - //NonlinearSolverND<6> solver(parameters.kain_subspace()); - //solver.do_print = (world.rank() == 0); - - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting pairs update at time " << wall_time() << std::endl; // calc update for pairs via macrotask auto taskq = std::shared_ptr(new MacroTaskQ(world, world.size())); taskq->set_printlevel(3); //taskq->cloud.set_debug(true); MacroTaskMp2UpdatePair t; - MacroTask task(world, t, taskq); - std::vector u_update = task(pair_vec, coupling_vec, parameters, nemo->get_calc()->molecule.get_all_coords_vec(), + MacroTask task1(world, t, taskq); + std::vector u_update = task1(pair_vec, coupling_vec, parameters, nemo->get_calc()->molecule.get_all_coords_vec(), CCOPS.mo_ket().get_vecfunction(), CCOPS.mo_bra().get_vecfunction(), nemo->ncf->U1vec(), nemo->ncf->U2()); taskq->print_taskq(); taskq->run_all(); - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nFinished pairs update at time " << wall_time() << std::endl; - - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting saving pairs and energy calculation at time " << wall_time() << std::endl; if (parameters.kain()) { if (world.rank()==0) std::cout << "Update with KAIN" << std::endl; - StrongOrthogonalityProjector Q(world); - Q.set_spaces(CCOPS.mo_bra().get_vecfunction(), CCOPS.mo_ket().get_vecfunction(), CCOPS.mo_bra().get_vecfunction(), CCOPS.mo_ket().get_vecfunction()); std::vector u; for (auto p : pair_vec) u.push_back(p.function()); @@ -516,52 +500,40 @@ double CC2::solve_mp2_coupled(Pairs& doubles) { } // calculate energy and error and update pairs + double total_rnorm = 0.0, maxrnorm=0.0; for (size_t i = 0; i < pair_vec.size(); i++) { - //const real_function_6d residue = pair_vec[i].function() - u_update[i]; const double error = u_update[i].norm2(); if (world.rank()==0) std::cout << "residual " << pair_vec[i].i << " " << pair_vec[i].j << " " << error << std::endl; - total_norm = std::max(total_norm, error); - - // if (parameters.kain()) { - // if (world.rank()==0) std::cout << "Update with KAIN" << std::endl; - // StrongOrthogonalityProjector Q(world); - // Q.set_spaces(CCOPS.mo_bra().get_vecfunction(), CCOPS.mo_ket().get_vecfunction(), CCOPS.mo_bra().get_vecfunction(), CCOPS.mo_ket().get_vecfunction()); - // std::vector kain_update = copy(solver.update(pair_vec[i].function(), u_update[i])); - // kain_update = Q(kain_update); - // kain_update.truncate().reduce_rank(); - // kain_update.print_size("Kain-Update-Function"); - // pair_vec[i].update_u(copy(kain_update)); - // } else { - // if (world.rank()==0) std::cout << "Update without KAIN" << std::endl; - // pair_vec[i].update_u(pair_vec[i].function() - u_update[i]); - // } - - //save(pair_vec[i].function(), pair_vec[i].name()); + maxrnorm = std::max(maxrnorm, error); + total_rnorm+=error; + + save(pair_vec[i].function(), pair_vec[i].name()); double energy = 0.0; - if (pair_vec[i].type == GROUND_STATE) energy = CCOPS.compute_pair_correlation_energy(pair_vec[i]); + if (pair_vec[i].type == GROUND_STATE) { + double energy = CCOPS.compute_pair_correlation_energy(pair_vec[i]); + if (world.rank()==0) printf("pair energy for pair %zu %zu: %12.8f\n", pair_vec[i].i, pair_vec[i].j, energy); + total_energy += energy; + } total_energy += energy; } - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nFinished saving pairs and energy calculation at time " << wall_time() << std::endl; - - // create new Pairs struc for MP2 pairs, needed for coupling - // only temporary! will be removed when compute_local_coupling is changed - //Pairs updated_pairs; for (auto& tmp_pair : pair_vec) { updated_pairs(tmp_pair.i, tmp_pair.j).update_u(tmp_pair.function()); } - output("\n--Iteration " + stringify(iter) + " ended--"); - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "at time " << wall_time() << std::endl; - // std::cout << "old_energy = " << old_energy << std::endl; - // std::cout << "total_energy = " << total_energy << std::endl; - // std::cout << "total_norm = " << total_norm << std::endl; - // std::cout << "econv = " << parameters.econv() << std::endl; - // std::cout << "dconv_6D = " << parameters.dconv_6D() << std::endl; + if (world.rank()==0) { + std::cout << "convergence: total/max residual, energy/norm change " + << std::scientific << std::setprecision(1) + << maxrnorm << " " << total_rnorm << " " + << std::abs(old_energy - total_energy) << std::endl; + // << std::abs(old_norm - total_norm); + printf("finished iteration %2d at time %8.1fs with energy %12.8f\n", + int(iter), wall_time(), total_energy); + } + bool converged = ((std::abs(old_energy - total_energy) < parameters.econv()) - and (total_norm < parameters.dconv_6D())); + and (maxrnorm < parameters.dconv_6D())); - if (world.rank()==0) std::cout << std::fixed << std::setprecision(1) << "\nStarting final energy calculation at time " << wall_time() << std::endl; //print pair energies if converged if (converged) { if (world.rank() == 0) std::cout << "\nPairs converged!\n"; @@ -575,16 +547,6 @@ double CC2::solve_mp2_coupled(Pairs& doubles) { } if (world.rank() == 0) std::cout << "sum =" << total_energy << "\n"; break; - } else { - if (world.rank() == 0) std::cout << "\nCurrent pair energies:\n"; - for (auto& pair : updated_pairs.allpairs) { - const double pair_energy = CCOPS.compute_pair_correlation_energy(pair.second); - if (world.rank() == 0) { - std::cout << std::fixed << std::setprecision(10) << "omega_" - << pair.second.i << pair.second.j << "=" << pair_energy << "\n"; - } - } - if (world.rank() == 0) std::cout << "sum =" << total_energy << "\n"; } } if (world.rank()==0) { @@ -592,38 +554,10 @@ double CC2::solve_mp2_coupled(Pairs& doubles) { print_header2("end computing the MP1 wave function"); } + doubles=Pairs::vector2pairs(pair_vec,triangular_map); return total_energy; } -double -CC2::solve_mp2(Pairs& doubles) { -// output.section("Solve MP2"); - double omega = 0.0; - Pairs pair_energies; - for (auto& tmp_pair : doubles.allpairs) { - MADNESS_ASSERT(tmp_pair.second.type == GROUND_STATE); - MADNESS_ASSERT(tmp_pair.second.ctype == CT_MP2); - - if (parameters.no_compute_mp2()) output("Found no_compute_mp2 keyword"); - else { - update_constant_part_mp2(tmp_pair.second); - iterate_pair(tmp_pair.second); - } - save(tmp_pair.second.function(), tmp_pair.second.name()); - const double pair_energy = CCOPS.compute_pair_correlation_energy(tmp_pair.second); - pair_energies.insert(tmp_pair.second.i, tmp_pair.second.j, pair_energy); - omega += pair_energy; - - } - if (world.rank() == 0) std::cout << "\nMP2 Pair Correlation Energies:\n"; - for (auto& a : pair_energies.allpairs) { - if (world.rank() == 0) - std::cout << std::fixed << std::setprecision(10) << "omega_" << a.first.first << a.first.second << "=" - << a.second << "\n"; - } - if (world.rank() == 0) std::cout << "sum =" << omega << "\n"; - return omega; -} /// add the coupling terms for local MP2 @@ -947,9 +881,9 @@ CC2::initialize_singles(CC_vecfunction& singles, const FuncType type, const int MADNESS_ASSERT(singles.size() == 0); bool restarted = false; - std::vector vs; + std::vector> vs; for (size_t i = parameters.freeze(); i < CCOPS.mo_ket().size(); i++) { - CCFunction single_i; + CCFunction single_i; single_i.type = type; single_i.i = i; std::string name; diff --git a/src/madness/chem/CC2.h b/src/madness/chem/CC2.h index 7753dce2200..c5d13aba5c0 100644 --- a/src/madness/chem/CC2.h +++ b/src/madness/chem/CC2.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -131,12 +132,15 @@ class CC2 : public OptimizationTargetInterface, public QCPropertyInterface { /// solve the CC2 ground state equations, returns the correlation energy void solve(); + std::vector solve_ccs(); - /// solve the MP2 equations (uncoupled -> Canonical Orbitals) - double - solve_mp2(Pairs& doubles); + double compute_mp3(const Pairs& mp2pairs) const { + MP3 mp3(CCOPS); + double mp3_contribution=mp3.mp3_energy_contribution_macrotask_driver(mp2pairs); + return mp3_contribution; + } double solve_cc2(CC_vecfunction& tau, Pairs& u); @@ -404,28 +408,6 @@ class CC2 : public OptimizationTargetInterface, public QCPropertyInterface { iterate_lrcc2_pairs(const CC_vecfunction& cc2_s, const Pairs& cc2_d, const CC_vecfunction lrcc2_s, Pairs& lrcc2_d); - bool update_constant_part_mp2(CCPair& pair) { - MADNESS_ASSERT(pair.ctype == CT_MP2); - MADNESS_ASSERT(pair.type == GROUND_STATE); - if (parameters.no_compute_mp2_constantpart()) { - pair.constant_part=real_factory_6d(world); - load(pair.constant_part,pair.name()+"_const"); - } - if (pair.constant_part.is_initialized()) return false; - - // make screening Operator - real_convolution_6d Gscreen = BSHOperator<6>(world, sqrt(-2.0 * CCOPS.get_epsilon(pair.i, pair.j)), - parameters.lo(), parameters.thresh_bsh_6D()); - Gscreen.modified() = true; - - const CCFunction& moi = CCOPS.mo_ket(pair.i); - const CCFunction& moj = CCOPS.mo_ket(pair.j); - - pair.constant_part = CCOPS.make_constant_part_mp2(moi, moj, &Gscreen); - save(pair.constant_part, pair.name() + "_const"); - return true; - } - bool update_constant_part_cc2_gs(const CC_vecfunction& tau, CCPair& pair) { MADNESS_ASSERT(pair.ctype == CT_CC2); MADNESS_ASSERT(pair.type == GROUND_STATE); diff --git a/src/madness/chem/CCPotentials.cc b/src/madness/chem/CCPotentials.cc index 478e489d44e..64f7b3d7f52 100644 --- a/src/madness/chem/CCPotentials.cc +++ b/src/madness/chem/CCPotentials.cc @@ -24,10 +24,12 @@ CCPotentials::CCPotentials(World& world_, std::shared_ptr nemo, const CCP //mo_ket_(make_mo_ket(nemo)), //mo_bra_(make_mo_bra(nemo)), //orbital_energies_(init_orbital_energies(nemo)) - g12(world, OT_G12, param), f12(world, OT_F12, param), +// g12(std::shared_ptrget_calc()->molecule), get_potentials(world, param), output(world) { + g12=std::shared_ptr>(new CCConvolutionOperator(world,OpType::OT_G12,param)); + f12=std::shared_ptr>(new CCConvolutionOperator(world,OpType::OT_F12,param)); output.debug = parameters.debug(); // reset_nemo(nemo); // g12.update_elements(mo_bra_, mo_ket_); @@ -83,16 +85,16 @@ CCPotentials::make_pair_gs(const real_function_6d& u, const CC_vecfunction& tau, else { pt = make_full_t_intermediate(tau); } - std::vector functions; - CCPairFunction u_part(u); + std::vector> functions; + CCPairFunction u_part(u); functions.push_back(u_part); if (parameters.decompose_Q()) { - CCPairFunction f_part(&f12, t(i), t(j)); + CCPairFunction f_part(f12, t(i), t(j)); functions.push_back(f_part); - CCPairFunction Ot1 = apply_Ot(f_part, pt, 1); - CCPairFunction Ot2 = apply_Ot(f_part, pt, 2); - CCPairFunction PQ = apply_Qt(Ot1, pt, 2, 0.5); - CCPairFunction QP = apply_Qt(Ot2, pt, 1, 0.5); + CCPairFunction Ot1 = apply_Ot(f_part, pt, 1); + CCPairFunction Ot2 = apply_Ot(f_part, pt, 2); + CCPairFunction PQ = apply_Qt(Ot1, pt, 2, 0.5); + CCPairFunction QP = apply_Qt(Ot2, pt, 1, 0.5); // assign signs PQ.invert_sign(); QP.invert_sign(); @@ -103,7 +105,7 @@ CCPotentials::make_pair_gs(const real_function_6d& u, const CC_vecfunction& tau, real_function_6d ftt = make_f_xy(t(i), t(j)); real_function_6d Qftt = apply_Q12t(ftt, pt); Qftt.truncate(); - CCPairFunction residual(Qftt); + CCPairFunction residual(Qftt); functions.push_back(residual); } CalcType ctype = CT_CC2; @@ -228,41 +230,41 @@ CCPotentials::make_pair_ex(const real_function_6d& u, const CC_vecfunction& tau, pt = make_full_t_intermediate(tau).copy(); } MADNESS_ASSERT(pt.size() == mo_ket_.size()); - std::vector functions; - CCPairFunction u_part(u); + std::vector> functions; + CCPairFunction u_part(u); functions.push_back(u_part); if (parameters.decompose_Q()) { - CCPairFunction f_xt(&f12, x(i), t(j)); + CCPairFunction f_xt(f12, x(i), t(j)); functions.push_back(f_xt); - CCPairFunction f_tx(&f12, t(i), x(j)); + CCPairFunction f_tx(f12, t(i), x(j)); functions.push_back(f_tx); { - CCPairFunction Ot1_xt = apply_Ot(f_xt, pt, 1); // O1t(f|xt>) - CCPairFunction OtQt_xt = apply_Qt(Ot1_xt, pt, 2, 0.5); // O1t(1-0.5*O2t)f|xt> + CCPairFunction Ot1_xt = apply_Ot(f_xt, pt, 1); // O1t(f|xt>) + CCPairFunction OtQt_xt = apply_Qt(Ot1_xt, pt, 2, 0.5); // O1t(1-0.5*O2t)f|xt> functions.push_back(OtQt_xt.invert_sign()); // - " } { - CCPairFunction Ot2_xt = apply_Ot(f_xt, pt, 2); // O2t(f|xt>) - CCPairFunction QtOt_xt = apply_Qt(Ot2_xt, pt, 1, 0.5); // (1-0.5*O1t)O2t(f|xt>) + CCPairFunction Ot2_xt = apply_Ot(f_xt, pt, 2); // O2t(f|xt>) + CCPairFunction QtOt_xt = apply_Qt(Ot2_xt, pt, 1, 0.5); // (1-0.5*O1t)O2t(f|xt>) functions.push_back(QtOt_xt.invert_sign()); // - " } { - CCPairFunction Ot1_tx = apply_Ot(f_tx, pt, 1); // O1t(f|tx>) - CCPairFunction OtQt_tx = apply_Qt(Ot1_tx, pt, 2, 0.5); // O1t(1-0.5*O2t)f|tx> + CCPairFunction Ot1_tx = apply_Ot(f_tx, pt, 1); // O1t(f|tx>) + CCPairFunction OtQt_tx = apply_Qt(Ot1_tx, pt, 2, 0.5); // O1t(1-0.5*O2t)f|tx> functions.push_back(OtQt_tx.invert_sign()); // - " } { - CCPairFunction Ot2_tx = apply_Ot(f_tx, pt, 2); // O2t(f|tx>) - CCPairFunction QtOt_tx = apply_Qt(Ot2_tx, pt, 1, 0.5); // (1-0.5*O1t)O2t(f|tx>) + CCPairFunction Ot2_tx = apply_Ot(f_tx, pt, 2); // O2t(f|tx>) + CCPairFunction QtOt_tx = apply_Qt(Ot2_tx, pt, 1, 0.5); // (1-0.5*O1t)O2t(f|tx>) functions.push_back(QtOt_tx.invert_sign()); // - " } if (parameters.QtAnsatz()) { - CCPairFunction ftt(&f12, t(i), t(j)); // f|tt> - CCPairFunction O1x_tt = apply_Ot(ftt, x, 1); // O1x(f|tt>) - CCPairFunction OxQt_tt = apply_Qt(O1x_tt, pt, 2); // O1xQt(f|tt>) + CCPairFunction ftt(f12, t(i), t(j)); // f|tt> + CCPairFunction O1x_tt = apply_Ot(ftt, x, 1); // O1x(f|tt>) + CCPairFunction OxQt_tt = apply_Qt(O1x_tt, pt, 2); // O1xQt(f|tt>) functions.push_back(OxQt_tt.invert_sign()); // - " - CCPairFunction O2x_tt = apply_Ot(ftt, x, 2); // O2x(f|tt>) - CCPairFunction QtOx_tt = apply_Qt(O2x_tt, pt, 1); // Q1tO2x(f|tt>) + CCPairFunction O2x_tt = apply_Ot(ftt, x, 2); // O2x(f|tt>) + CCPairFunction QtOx_tt = apply_Qt(O2x_tt, pt, 1); // Q1tO2x(f|tt>) functions.push_back(QtOx_tt.invert_sign()); // - " } } else { @@ -281,7 +283,7 @@ CCPotentials::make_pair_ex(const real_function_6d& u, const CC_vecfunction& tau, real_function_6d OxOt = Ot(O1x, 2); real_function_6d projector_part = (O1x - OxOt + O2x - OtOx); real_function_6d residual = function_part - projector_part; - CCPairFunction res(residual); + CCPairFunction res(residual); functions.push_back(res); } else { real_function_6d ftx = make_f_xy(t(i), x(j)); @@ -289,7 +291,7 @@ CCPotentials::make_pair_ex(const real_function_6d& u, const CC_vecfunction& tau, real_function_6d tmp = ftx + fxt; real_function_6d residual = apply_Q12t(tmp, mo_ket_); residual.truncate(); - CCPairFunction res(residual); + CCPairFunction res(residual); functions.push_back(res); } } @@ -313,40 +315,41 @@ CCPotentials::compute_pair_correlation_energy(const CCPair& u, const CC_vecfunct MADNESS_ASSERT(u.type == GROUND_STATE); if (singles.functions.empty()) MADNESS_ASSERT(u.ctype == CT_MP2); - output("Compute pair-correlation energy of pair " + u.name()); + const bool print_details=(world.rank()==0 and parameters.debug()); + if (parameters.debug()) output("Compute pair-correlation energy of pair " + u.name()); double result = 0.0; - const CCFunction& mobi = mo_bra_(u.i); - const CCFunction& mobj = mo_bra_(u.j); + const CCFunction& mobi = mo_bra_(u.i); + const CCFunction& mobj = mo_bra_(u.j); const bool symmetric = (u.i == u.j); for (size_t mm = 0; mm < u.functions.size(); mm++) { double tmp = 0.0; - const double part1 = make_xy_op_u(mobi, mobj, g12, u.functions[mm]); + const double part1 = make_xy_op_u(mobi, mobj, *g12, u.functions[mm]); if (symmetric) tmp = part1; else //if(world.rank()==0) std::cout << std::fixed << std::setprecision(10) << part1 << "\n"; { - const double part2 = make_xy_op_u(mobj, mobi, g12, u.functions[mm]); + const double part2 = make_xy_op_u(mobj, mobi, *g12, u.functions[mm]); tmp = 2.0 * (2.0 * part1 - part2); // non symmetric pairs -> offdiagonal -> count twice } result += tmp; - if (world.rank() == 0) + if (print_details) std::cout << std::setfill(' ') << std::setw(15) << "from " + u.functions[mm].name() + "=" << std::setfill(' ') << std::fixed << std::setprecision(10) << tmp << "\n"; } if (u.ctype == CT_CC2 && !singles.get_vecfunction().empty()) { MADNESS_ASSERT(singles.type == PARTICLE); - const double omega_s = 2.0 * mobi.inner(g12(mobj, singles(u.j)) * singles(u.i).function) - - mobi.inner(g12(mobj, singles(u.i)) * singles(u.j).function); - if (world.rank() == 0) + const double omega_s = 2.0 * mobi.inner((*g12)(mobj, singles(u.j)) * singles(u.i).function) - + mobi.inner((*g12)(mobj, singles(u.i)) * singles(u.j).function); + if (print_details) std::cout << std::setw(15) << "from singles=" << std::setfill(' ') << std::fixed << std::setprecision(10) << omega_s << "\n\n"; result += omega_s; } - if (world.rank() == 0) std::cout << "------------\n" << std::fixed << std::setprecision(10) << result << "\n\n"; + // if (world.rank() == 0) std::cout << "------------\n" << std::fixed << std::setprecision(10) << result << "\n\n"; - timer.info(); + timer.info(parameters.debug()); return result; } @@ -405,14 +408,14 @@ CCPotentials::compute_cis_expectation_value(const CC_vecfunction& x, const vecto double CCPotentials::compute_excited_pair_energy(const CCPair& d, const CC_vecfunction& x) const { const CC_vecfunction xbra(make_bra(x), RESPONSE, parameters.freeze()); - const CCFunction& xbi = xbra(d.i); - const CCFunction& mobj = mo_bra_(d.j); + const CCFunction& xbi = xbra(d.i); + const CCFunction& mobj = mo_bra_(d.j); double result = 0.0; - double s2b = 2.0 * make_xy_op_u(xbi, mobj, g12, d.functions) - make_xy_op_u(mobj, xbi, g12, d.functions); + double s2b = 2.0 * make_xy_op_u(xbi, mobj, *g12, d.functions) - make_xy_op_u(mobj, xbi, *g12, d.functions); double s2c = 0.0; for (const auto& ktmp : x.functions) { const size_t k = ktmp.first; - const real_function_3d j_igk = g12(mo_bra_(d.i), mo_ket_(k)) * mo_bra_(d.j).function; + const real_function_3d j_igk = (*g12)(mo_bra_(d.i), mo_ket_(k)) * mo_bra_(d.j).function; s2c -= 2.0 * make_xy_u(xbra(k), j_igk, d.functions) - make_xy_u(j_igk, xbra(k), d.functions); } result = s2b + s2c; @@ -438,23 +441,23 @@ CCPotentials::compute_cispd_energy(const CC_vecfunction& x, const Pairs for (const auto& ktmp : x.functions) { // s2b part: const size_t k = ktmp.first; - s2b += 2.0 * make_xy_op_u(xbra(i), mo_bra_(k), g12, get_pair_function(cispd, i, k)) - - make_xy_op_u(mo_bra_(k), xbra(i), g12, get_pair_function(cispd, i, k)); - const real_function_3d kgi = g12(mo_bra_(k), mo_ket_(i)); - const real_function_3d kgxi = g12(mo_bra_(k), x(i)); - const real_function_3d kgxk = g12(mo_bra_(k), x(k)); + s2b += 2.0 * make_xy_op_u(xbra(i), mo_bra_(k), *g12, get_pair_function(cispd, i, k)) - + make_xy_op_u(mo_bra_(k), xbra(i), *g12, get_pair_function(cispd, i, k)); + const real_function_3d kgi = (*g12)(mo_bra_(k), mo_ket_(i)); + const real_function_3d kgxi = (*g12)(mo_bra_(k), x(i)); + const real_function_3d kgxk = (*g12)(mo_bra_(k), x(k)); for (const auto& ltmp : x.functions) { // s2c part: const size_t l = ltmp.first; - const real_function_3d k_lgxk = mo_bra_(k).function * g12(mo_bra_(l), x(k)); + const real_function_3d k_lgxk = mo_bra_(k).function * (*g12)(mo_bra_(l), x(k)); const real_function_3d l_kgxk = mo_bra_(l).function * kgxk; const real_function_3d l_kgi = mo_bra_(l).function * kgi; const real_function_3d l_kgxi = mo_bra_(l).function * kgxi; s2c += 2.0 * make_xy_u(xbra(i), l_kgi, get_pair_function(cispd, k, l)) - make_xy_u(l_kgi, xbra(i), get_pair_function(cispd, k, l)); const double xil = xbra(i).function.inner(x(l).function); - s4a += xil * (2.0 * make_xy_op_u(mo_bra_(l), mo_bra_(k), g12, get_pair_function(mp2, i, k)) - - make_xy_op_u(mo_bra_(k), mo_bra_(l), g12, get_pair_function(mp2, i, k))); + s4a += xil * (2.0 * make_xy_op_u(mo_bra_(l), mo_bra_(k), *g12, get_pair_function(mp2, i, k)) - + make_xy_op_u(mo_bra_(k), mo_bra_(l), *g12, get_pair_function(mp2, i, k))); s4b += 2.0 * make_xy_u(xbra(i), l_kgxi, get_pair_function(mp2, k, l)) - make_xy_u(l_kgxi, xbra(i), get_pair_function(mp2, k, l)); s4c += 4.0 * make_xy_u(xbra(i), l_kgxk, get_pair_function(mp2, i, l)) - @@ -514,7 +517,7 @@ CCPotentials::fock_residue_6d(const CCPair& u) const { // make the coulomb and local Un part with the composite factory real_function_3d hartree_potential = real_factory_3d(world); for (const auto& tmp : mo_ket_.functions) - hartree_potential += g12(mo_bra_(tmp.first), mo_ket_(tmp.first)); + hartree_potential += (*g12)(mo_bra_(tmp.first), mo_ket_(tmp.first)); real_function_3d local_part = (2.0 * hartree_potential + nemo_->ncf->U2()); if (parameters.debug()) local_part.print_size("vlocal"); @@ -641,14 +644,12 @@ CCPotentials::fock_residue_6d_macrotask(World& world, const CCPair& u, const CCP real_function_6d x = CompositeFactory(world).ket(copy(Du)).V_for_particle1(copy(U1_axis)).thresh( tight_thresh).special_points(sp6d); x.fill_nuclear_cuspy_tree(op_mod, 1); - if (x.norm2() < tight_thresh) x.print_size("Un_axis_" + stringify(axis)); - - if (x.norm2() < tight_thresh) print("||Un|u>|| is below the threshold\n"); + if (parameters.debug()) x.print_size("Un_axis_" + stringify(axis)); Un1 += x; - Un1.truncate().reduce_rank(); - if (parameters.debug()) Un1.print_size("Un1"); } + if (parameters.debug()) Un1.print_size("Un1"); + if (u.i == u.j) { print(u.name() + " is a diagonal pair: Exploting permutation symmetry\n"); Un2 = madness::swap_particles(Un1); @@ -660,17 +661,14 @@ CCPotentials::fock_residue_6d_macrotask(World& world, const CCPair& u, const CCP double tight_thresh = parameters.thresh_6D(); real_function_6d x = CompositeFactory(world).ket(copy(Du)).V_for_particle2( copy(U1_axis)).thresh(tight_thresh).special_points(sp6d); - x.fill_nuclear_cuspy_tree(op_mod, 2); - if (x.norm2() < tight_thresh) x.print_size("Un_axis_" + stringify(axis)); - - if (x.norm2() < tight_thresh) print("||Un|u>|| is below the threshold\n"); - + x.fill_nuclear_cuspy_tree(op_mod, 2); + if (parameters.debug()) x.print_size("Un_axis_" + stringify(axis)); Un2 += x; - Un2.truncate().reduce_rank(); - if (parameters.debug()) Un2.print_size("Un2"); } + if (parameters.debug()) Un2.print_size("Un2"); } vphi += (Un1 + Un2); + if (parameters.debug()) vphi.print_size("before truncation (Un + J1 + J2)|u>"); vphi.truncate().reduce_rank(); if (parameters.debug()) vphi.print_size("(Un + J1 + J2)|u>"); @@ -681,28 +679,6 @@ CCPotentials::fock_residue_6d_macrotask(World& world, const CCPair& u, const CCP return vphi; } -madness::real_function_6d -CCPotentials::make_constant_part_mp2(const CCFunction& ti, const CCFunction& tj, - const real_convolution_6d *Gscreen) const { - output.section("Calculating Constant Part of MP2 pair " + ti.name() + tj.name()); - CCTimer time(world, "Calculating Constant Part of MP2"); - MADNESS_ASSERT(ti.type == HOLE); - MADNESS_ASSERT(tj.type == HOLE); - real_function_6d V = apply_Vreg(ti, tj, Gscreen); - print_size(V, "Vreg", parameters.debug()); - V = apply_Q12t(V, mo_ket_); - print_size(V, "QVreg"); - real_convolution_6d G = BSHOperator<6>(world, sqrt(-2.0 * get_epsilon(ti.i, tj.i)), parameters.lo(), - parameters.thresh_bsh_6D()); - G.destructive() = true; - real_function_6d GV = -2.0 * G(V); - print_size(GV, "GVreg", parameters.debug()); - GV = apply_Q12t(GV, mo_ket_); - print_size(GV, "GVreg"); - time.info(); - return GV; -} - madness::real_function_6d CCPotentials::make_constant_part_mp2_macrotask(World& world, const CCPair& pair, const std::vector& mo_ket, @@ -730,7 +706,7 @@ CCPotentials::make_constant_part_mp2_macrotask(World& world, const CCPair& pair, parameters.lo(), parameters.thresh_bsh_6D()); Gscreen.modified() = true; - print("Calculating Constant Part of MP2 pair " + i_name + j_name); + print("\nCalculating Constant Part of MP2 pair " + i_name + j_name); CCTimer time(world, "Calculating Constant Part of MP2"); MADNESS_ASSERT(i_type == HOLE); MADNESS_ASSERT(j_type == HOLE); @@ -772,9 +748,7 @@ CCPotentials::update_pair_mp2_macrotask(World& world, const CCPair& pair, const if (world.rank()==0) print(assign_name(pair.ctype) + "-Microiteration\n"); CCTimer timer_mp2(world, "MP2-Microiteration of pair " + pair.name()); - //print coupling vector - if (world.rank()==0) std::cout << "aaaaa coupling macrotask of pair" << pair.name() << " "; - mp2_coupling.print_size("coupling in macrotask"); + if (parameters.debug()) mp2_coupling.print_size("coupling in macrotask"); double bsh_eps = pair.bsh_eps; real_convolution_6d G = BSHOperator<6>(world, sqrt(-2.0 * bsh_eps), parameters.lo(), parameters.thresh_bsh_6D()); @@ -797,18 +771,19 @@ CCPotentials::update_pair_mp2_macrotask(World& world, const CCPair& pair, const //CCTimer timer_addup(world, "Add constant parts and update pair " + pair.name()); real_function_6d unew = GVmp2 + pair.constant_part; - unew.print_size("unew"); + if (parameters.debug()) unew.print_size("unew"); StrongOrthogonalityProjector Q(world); Q.set_spaces(mo_bra, mo_ket, mo_bra, mo_ket); unew = Q(unew); - unew.print_size("Q12unew"); - //unew.truncate().reduce_rank(); // already done in Q12 application at the end if (parameters.debug())unew.print_size("truncated-unew"); timer_mp2.info(); - const real_function_6d residue = (pair.function() - unew).truncate(); + real_function_6d residue = (pair.function() - unew); + // if (parameters.debug()) residue.print_size("bsh residual"); + residue.truncate(FunctionDefaults<6>::get_thresh()*0.1); + if (parameters.debug()) residue.print_size("bsh residual, truncated"); return residue; } @@ -828,8 +803,8 @@ CCPotentials::make_constant_part_cc2_gs(const CCPair& u, const CC_vecfunction& t const size_t j = u.j; const bool symmetric = (i == j); // make ti intermediates - const CCFunction ti = make_t_intermediate(tau(i)); - const CCFunction tj = make_t_intermediate(tau(j)); + const CCFunction ti = make_t_intermediate(tau(i)); + const CCFunction tj = make_t_intermediate(tau(j)); real_function_6d GV; real_convolution_6d G = BSHOperator<6>(world, sqrt(-2.0 * get_epsilon(ti.i, tj.i)), parameters.lo(), parameters.thresh_bsh_6D()); @@ -853,12 +828,12 @@ CCPotentials::make_constant_part_cc2_gs(const CCPair& u, const CC_vecfunction& t CCTimer time_Vcc(world, "G(Coulomb Coupling Potential)"); real_function_6d GVcc = real_factory_6d(world); // make the g12|titj> function as op_decomposed function (not constructed in 6D) - CCPairFunction gtt(&g12, ti, tj); + CCPairFunction gtt(g12, ti, tj); // make Otau(1)(g12|titj>) - CCPairFunction Otau1_gtt = apply_Ot(gtt, tau, 1); + CCPairFunction Otau1_gtt = apply_Ot(gtt, tau, 1); // make Otau1Q2 part and the Otau1Otau2. Otau1Otau2 part IS NOT used in the symmetry exploit - CCPairFunction OtauQ = apply_Qt(Otau1_gtt, mo_ket_, 2); - CCPairFunction OtauOtau = apply_Ot(Otau1_gtt, tau, 2); + CCPairFunction OtauQ = apply_Qt(Otau1_gtt, mo_ket_, 2); + CCPairFunction OtauOtau = apply_Ot(Otau1_gtt, tau, 2); // apply the Greens Operator const real_function_6d GVcc_1 = -2.0 * apply_G(OtauQ, G); const real_function_6d GVcc_3 = -2.0 * apply_G(OtauOtau, G); @@ -866,8 +841,8 @@ CCPotentials::make_constant_part_cc2_gs(const CCPair& u, const CC_vecfunction& t real_function_6d GVcc_2; if (symmetric) GVcc_2 = swap_particles(GVcc_1); else { - CCPairFunction Otau2_gtt = apply_Ot(gtt, tau, 2); - CCPairFunction QOtau = apply_Qt(Otau2_gtt, mo_ket_, 1); + CCPairFunction Otau2_gtt = apply_Ot(gtt, tau, 2); + CCPairFunction QOtau = apply_Qt(Otau2_gtt, mo_ket_, 1); GVcc_2 = -2.0 * apply_G(QOtau, G); } if (parameters.debug()) print_size(GVcc, "GVcc", parameters.debug()); @@ -905,8 +880,8 @@ CCPotentials::make_constant_part_cc2_Qt_gs(const CCPair& u, const CC_vecfunction const size_t j = u.j; const bool symmetric = (i == j); // make ti intermediates - const CCFunction ti = make_t_intermediate(tau(i)); - const CCFunction tj = make_t_intermediate(tau(j)); + const CCFunction ti = make_t_intermediate(tau(i)); + const CCFunction tj = make_t_intermediate(tau(j)); const CC_vecfunction t = make_full_t_intermediate(tau); MADNESS_ASSERT(t.size() == mo_ket_.size()); MADNESS_ASSERT(t.type == MIXED); @@ -920,15 +895,15 @@ CCPotentials::make_constant_part_cc2_Qt_gs(const CCPair& u, const CC_vecfunction CCTimer time_comm(world, "commutator"); const vector_real_function_3d Vtmp = get_potentials(tau, POT_singles_); const CC_vecfunction V(Vtmp, UNDEFINED, parameters.freeze()); - const CCPairFunction ftt(&f12, ti, tj); - const CCPairFunction O1ftt = apply_Ot(ftt, V, 1); - const CCPairFunction O1Q2ftt = apply_Qt(O1ftt, t, 2); + const CCPairFunction ftt(f12, ti, tj); + const CCPairFunction O1ftt = apply_Ot(ftt, V, 1); + const CCPairFunction O1Q2ftt = apply_Qt(O1ftt, t, 2); const real_function_6d part1 = -2.0 * apply_G(O1Q2ftt, G); real_function_6d part2; if (symmetric) part2 = swap_particles(part1); else { - const CCPairFunction O2ftt = apply_Ot(ftt, V, 2); - const CCPairFunction Q1O2ftt = apply_Qt(O2ftt, t, 1); + const CCPairFunction O2ftt = apply_Ot(ftt, V, 2); + const CCPairFunction Q1O2ftt = apply_Qt(O2ftt, t, 1); part2 = -2.0 * apply_G(Q1O2ftt, G); } const real_function_6d commutator = part1 + part2; @@ -963,10 +938,10 @@ CCPotentials::make_constant_part_cispd(const CCPair& u, const CC_vecfunction& x, MADNESS_ASSERT(u.ctype == CT_CISPD || u.ctype == CT_ADC2); const size_t i = u.i; const size_t j = u.j; - const CCFunction& xi = x(i); - const CCFunction& xj = x(j); - const CCFunction& moi = mo_ket_(i); - const CCFunction& moj = mo_ket_(j); + const CCFunction& xi = x(i); + const CCFunction& xj = x(j); + const CCFunction& moi = mo_ket_(i); + const CCFunction& moj = mo_ket_(j); const bool symmetric = (i == j); MADNESS_ASSERT(x.type == RESPONSE); MADNESS_ASSERT(u.bsh_eps == get_epsilon(i, j) + x.omega); @@ -999,14 +974,14 @@ CCPotentials::make_constant_part_cispd(const CCPair& u, const CC_vecfunction& x, real_function_6d GVcc; { time_cr.start(); - const CCPairFunction gij(&g12, moi, moj); - const CCPairFunction O1x_gij = apply_Ot(gij, x, 1); - const CCPairFunction OQ_part = apply_Qt(O1x_gij, mo_ket_, 2); + const CCPairFunction gij(g12, moi, moj); + const CCPairFunction O1x_gij = apply_Ot(gij, x, 1); + const CCPairFunction OQ_part = apply_Qt(O1x_gij, mo_ket_, 2); const real_function_6d GOQ = -2.0 * apply_G(OQ_part, G); if (symmetric) GVcc = GOQ + swap_particles(GOQ); else { - const CCPairFunction O2x_gij = apply_Ot(gij, x, 2); - const CCPairFunction QO_part = apply_Qt(O2x_gij, mo_ket_, 1); + const CCPairFunction O2x_gij = apply_Ot(gij, x, 2); + const CCPairFunction QO_part = apply_Qt(O2x_gij, mo_ket_, 1); const real_function_6d GQO = -2.0 * apply_G(QO_part, G); GVcc = GOQ + GQO; } @@ -1032,10 +1007,10 @@ CCPotentials::make_constant_part_cispd_Qt(const CCPair& u, const CC_vecfunction& MADNESS_ASSERT(u.ctype == CT_CISPD || u.ctype == CT_ADC2); const size_t i = u.i; const size_t j = u.j; - const CCFunction& xi = x(i); - const CCFunction& xj = x(j); - const CCFunction& moi = mo_ket_(i); - const CCFunction& moj = mo_ket_(j); + const CCFunction& xi = x(i); + const CCFunction& xj = x(j); + const CCFunction& moi = mo_ket_(i); + const CCFunction& moj = mo_ket_(j); const bool symmetric = (i == j); MADNESS_ASSERT(x.type == RESPONSE); MADNESS_ASSERT(u.bsh_eps == get_epsilon(i, j) + x.omega); @@ -1066,14 +1041,14 @@ CCPotentials::make_constant_part_cispd_Qt(const CCPair& u, const CC_vecfunction& Vtmp.truncate().reduce_rank(); if (parameters.debug()) Vtmp.print_size("Vreg-omega*f12|ij>"); - const CCPairFunction V(Vtmp); - const CCPairFunction O1x_V = apply_Ot(V, x, 1); - const CCPairFunction O1xQ2_V = apply_Qt(O1x_V, mo_ket_, 2); + const CCPairFunction V(Vtmp); + const CCPairFunction O1x_V = apply_Ot(V, x, 1); + const CCPairFunction O1xQ2_V = apply_Qt(O1x_V, mo_ket_, 2); const real_function_6d GOQ = -2.0 * apply_G(O1xQ2_V, G); if (symmetric) PR = GOQ + swap_particles(GOQ); else { - const CCPairFunction O2x_V = apply_Ot(V, x, 2); - const CCPairFunction Q1O2x_V = apply_Qt(O2x_V, mo_ket_, 1); + const CCPairFunction O2x_V = apply_Ot(V, x, 2); + const CCPairFunction Q1O2x_V = apply_Qt(O2x_V, mo_ket_, 1); const real_function_6d GQO = -2.0 * apply_G(Q1O2x_V, G); PR = GOQ + GQO; } @@ -1122,19 +1097,19 @@ CCPotentials::make_constant_part_cispd_Qt(const CCPair& u, const CC_vecfunction& const vector_real_function_3d Vxtmp = sub(world, get_potentials(x, POT_singles_), x.omega * x.get_vecfunction()); const CC_vecfunction Vx(Vxtmp, UNDEFINED, parameters.freeze()); - CCPairFunction ftt(&f12, moi, moj); + CCPairFunction ftt(f12, moi, moj); real_function_6d tmp1; real_function_6d tmp2; { - CCPairFunction OVx = apply_Ot(ftt, Vx, 1); - CCPairFunction OVxQt = apply_Qt(OVx, mo_ket_, 2); + CCPairFunction OVx = apply_Ot(ftt, Vx, 1); + CCPairFunction OVxQt = apply_Qt(OVx, mo_ket_, 2); real_function_6d part2 = -2.0 * apply_G(OVxQt, G); tmp1 = part2; } if (symmetric) tmp2 = swap_particles(tmp1); else { - CCPairFunction OVx = apply_Ot(ftt, Vx, 2); - CCPairFunction OVxQt = apply_Qt(OVx, mo_ket_, 1); + CCPairFunction OVx = apply_Ot(ftt, Vx, 2); + CCPairFunction OVxQt = apply_Qt(OVx, mo_ket_, 1); real_function_6d part2 = -2.0 * apply_G(OVxQt, G); tmp2 = part2; } @@ -1170,10 +1145,10 @@ CCPotentials::make_constant_part_cc2_ex(const CCPair& u, const CC_vecfunction& t const size_t i = u.i; const size_t j = u.j; const CC_vecfunction t = make_full_t_intermediate(tau); - const CCFunction& ti = t(i); - const CCFunction& tj = t(j); - const CCFunction& xi = x(i); - const CCFunction& xj = x(j); + const CCFunction& ti = t(i); + const CCFunction& tj = t(j); + const CCFunction& xi = x(i); + const CCFunction& xj = x(j); // use pair symmetry for diagonal pairs const bool symmetric = (i == j); // the Greens operator @@ -1212,43 +1187,43 @@ CCPotentials::make_constant_part_cc2_ex(const CCPair& u, const CC_vecfunction& t real_function_6d tmp2; // make the xt parts of the functional and the QtOx part of the projector response { - CCPairFunction gxt(&g12, xi, tj); + CCPairFunction gxt(g12, xi, tj); // make QOtau*g*|xt> - CCPairFunction O2tmp = apply_Ot(gxt, tau, 2); - CCPairFunction QO = apply_Qt(O2tmp, mo_ket_, 1); + CCPairFunction O2tmp = apply_Ot(gxt, tau, 2); + CCPairFunction QO = apply_Qt(O2tmp, mo_ket_, 1); const real_function_6d part1 = -2.0 * apply_G(QO, G); // make OtauQ*g*|xt> - CCPairFunction O1tmp = apply_Ot(gxt, tau, 1); - CCPairFunction OQ = apply_Qt(O1tmp, mo_ket_, 2); + CCPairFunction O1tmp = apply_Ot(gxt, tau, 1); + CCPairFunction OQ = apply_Qt(O1tmp, mo_ket_, 2); const real_function_6d part2 = -2.0 * apply_G(OQ, G); // OtauOtau*g*|xt> - CCPairFunction OO = apply_Ot(O1tmp, tau, 2); + CCPairFunction OO = apply_Ot(O1tmp, tau, 2); const real_function_6d part3 = -2.0 * apply_G(OO, G); // QtOx*g|titj> - CCPairFunction gtt(&g12, ti, tj); - CCPairFunction O2x = apply_Ot(gtt, x, 2); - CCPairFunction QtOx = apply_Qt(O2x, t, 1); + CCPairFunction gtt(g12, ti, tj); + CCPairFunction O2x = apply_Ot(gtt, x, 2); + CCPairFunction QtOx = apply_Qt(O2x, t, 1); const real_function_6d part4 = -2.0 * apply_G(QtOx, G); tmp1 = part1 + part2 - part3 + part4; // overall minus sign applied in the end } if (symmetric) tmp2 = swap_particles(tmp1); else { - CCPairFunction gtx(&g12, ti, xj); + CCPairFunction gtx(g12, ti, xj); // make QOtau*g*|tx> - CCPairFunction O2tmp = apply_Ot(gtx, tau, 2); - CCPairFunction QO = apply_Qt(O2tmp, mo_ket_, 1); + CCPairFunction O2tmp = apply_Ot(gtx, tau, 2); + CCPairFunction QO = apply_Qt(O2tmp, mo_ket_, 1); const real_function_6d part1 = -2.0 * apply_G(QO, G); // make OtauQ*g*|tx> - CCPairFunction O1tmp = apply_Ot(gtx, tau, 1); - CCPairFunction OQ = apply_Qt(O1tmp, mo_ket_, 2); + CCPairFunction O1tmp = apply_Ot(gtx, tau, 1); + CCPairFunction OQ = apply_Qt(O1tmp, mo_ket_, 2); const real_function_6d part2 = -2.0 * apply_G(OQ, G); // OtauOtau*g*|tx> - CCPairFunction OO = apply_Ot(O1tmp, tau, 2); + CCPairFunction OO = apply_Ot(O1tmp, tau, 2); const real_function_6d part3 = -2.0 * apply_G(OO, G); // OxQt*g|titj> - CCPairFunction gtt(&g12, ti, tj); - CCPairFunction O1x = apply_Ot(gtt, x, 1); - CCPairFunction OxQt = apply_Qt(O1x, t, 2); + CCPairFunction gtt(g12, ti, tj); + CCPairFunction O1x = apply_Ot(gtt, x, 1); + CCPairFunction OxQt = apply_Qt(O1x, t, 2); const real_function_6d part4 = -2.0 * apply_G(OxQt, G); tmp1 = part1 + part2 - part3 + part4; // overall minus sign applied in the end } @@ -1287,10 +1262,10 @@ CCPotentials::make_constant_part_cc2_Qt_ex(const CCPair& u, const CC_vecfunction const CC_vecfunction t = make_full_t_intermediate(tau); MADNESS_ASSERT(t.type == MIXED); MADNESS_ASSERT(t.size() == mo_ket_.size()); - const CCFunction& ti = t(i); - const CCFunction& tj = t(j); - const CCFunction& xi = x(i); - const CCFunction& xj = x(j); + const CCFunction& ti = t(i); + const CCFunction& tj = t(j); + const CCFunction& xi = x(i); + const CCFunction& xj = x(j); // use pair symmetry for diagonal pairs const bool symmetric = (i == j); // the Greens operator @@ -1327,18 +1302,18 @@ CCPotentials::make_constant_part_cc2_Qt_ex(const CCPair& u, const CC_vecfunction real_function_6d Vtt_tmp = apply_Vreg(ti, tj, Gscreen); real_function_6d titj = make_f_xy(ti, tj); Vtt_tmp = Vtt_tmp - x.omega * titj; - CCPairFunction Vtt(Vtt_tmp); + CCPairFunction Vtt(Vtt_tmp); real_function_6d tmp1; real_function_6d tmp2; { - CCPairFunction Ox = apply_Ot(Vtt, x, 1); - CCPairFunction OxQt = apply_Qt(Ox, t, 2); + CCPairFunction Ox = apply_Ot(Vtt, x, 1); + CCPairFunction OxQt = apply_Qt(Ox, t, 2); tmp1 = -2.0 * apply_G(OxQt, G); } if (symmetric) tmp2 = swap_particles(tmp1); else { - CCPairFunction Ox = apply_Ot(Vtt, x, 2); - CCPairFunction QtOx = apply_Qt(Ox, t, 1); + CCPairFunction Ox = apply_Ot(Vtt, x, 2); + CCPairFunction QtOx = apply_Qt(Ox, t, 1); tmp2 = -2.0 * apply_G(QtOx, G); } projector_response = tmp1 + tmp2; @@ -1352,11 +1327,11 @@ CCPotentials::make_constant_part_cc2_Qt_ex(const CCPair& u, const CC_vecfunction const vector_real_function_3d Vtmp = get_potentials(tau, POT_singles_); const CC_vecfunction V(Vtmp, UNDEFINED, parameters.freeze()); { - const CCPairFunction fxt(&f12, xi, tj); - const CCPairFunction O1V = apply_Ot(fxt, V, 1); - const CCPairFunction OQ = apply_Qt(O1V, t, 2); - const CCPairFunction O2V = apply_Ot(fxt, V, 2); - const CCPairFunction QO = apply_Qt(O2V, t, 1); + const CCPairFunction fxt(f12, xi, tj); + const CCPairFunction O1V = apply_Ot(fxt, V, 1); + const CCPairFunction OQ = apply_Qt(O1V, t, 2); + const CCPairFunction O2V = apply_Ot(fxt, V, 2); + const CCPairFunction QO = apply_Qt(O2V, t, 1); const real_function_6d tmp1 = -2.0 * apply_G(OQ, G); const real_function_6d tmp2 = -2.0 * apply_G(QO, G); part1 = tmp1 + tmp2; @@ -1364,11 +1339,11 @@ CCPotentials::make_constant_part_cc2_Qt_ex(const CCPair& u, const CC_vecfunction real_function_6d part2; // the tx parts if (symmetric) part2 = swap_particles(part1); else { - const CCPairFunction ftx(&f12, ti, xj); - const CCPairFunction O1V = apply_Ot(ftx, V, 1); - const CCPairFunction OQ = apply_Qt(O1V, t, 2); - const CCPairFunction O2V = apply_Ot(ftx, V, 2); - const CCPairFunction QO = apply_Qt(O2V, t, 1); + const CCPairFunction ftx(f12, ti, xj); + const CCPairFunction O1V = apply_Ot(ftx, V, 1); + const CCPairFunction OQ = apply_Qt(O1V, t, 2); + const CCPairFunction O2V = apply_Ot(ftx, V, 2); + const CCPairFunction QO = apply_Qt(O2V, t, 1); const real_function_6d tmp1 = -2.0 * apply_G(OQ, G); const real_function_6d tmp2 = -2.0 * apply_G(QO, G); part2 = tmp1 + tmp2; @@ -1392,25 +1367,25 @@ CCPotentials::make_constant_part_cc2_Qt_ex(const CCPair& u, const CC_vecfunction const vector_real_function_3d Vttmp = get_potentials(tau, POT_singles_); const CC_vecfunction Vx(Vxtmp, UNDEFINED, parameters.freeze()); const CC_vecfunction Vt(Vttmp, UNDEFINED, parameters.freeze()); - CCPairFunction ftt(&f12, ti, tj); + CCPairFunction ftt(f12, ti, tj); real_function_6d tmp1; real_function_6d tmp2; { - CCPairFunction Ox = apply_Ot(ftt, x, 1); - CCPairFunction OxOVt = apply_Ot(Ox, Vt, 2); + CCPairFunction Ox = apply_Ot(ftt, x, 1); + CCPairFunction OxOVt = apply_Ot(Ox, Vt, 2); real_function_6d part1 = -2.0 * apply_G(OxOVt, G); - CCPairFunction OVx = apply_Ot(ftt, Vx, 1); - CCPairFunction OVxQt = apply_Qt(OVx, t, 2); + CCPairFunction OVx = apply_Ot(ftt, Vx, 1); + CCPairFunction OVxQt = apply_Qt(OVx, t, 2); real_function_6d part2 = -2.0 * apply_G(OVxQt, G); tmp1 = part2 - part1; } if (symmetric) tmp2 = swap_particles(tmp1); else { - CCPairFunction Ox = apply_Ot(ftt, x, 2); - CCPairFunction OxOVt = apply_Ot(Ox, Vt, 1); + CCPairFunction Ox = apply_Ot(ftt, x, 2); + CCPairFunction OxOVt = apply_Ot(Ox, Vt, 1); real_function_6d part1 = -2.0 * apply_G(OxOVt, G); - CCPairFunction OVx = apply_Ot(ftt, Vx, 2); - CCPairFunction OVxQt = apply_Qt(OVx, t, 1); + CCPairFunction OVx = apply_Ot(ftt, Vx, 2); + CCPairFunction OVxQt = apply_Qt(OVx, t, 1); real_function_6d part2 = -2.0 * apply_G(OVxQt, G); tmp2 = part2 - part1; } @@ -1430,7 +1405,7 @@ CCPotentials::make_constant_part_cc2_Qt_ex(const CCPair& u, const CC_vecfunction } madness::real_function_6d -CCPotentials::apply_Vreg(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen) const { +CCPotentials::apply_Vreg(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen) const { output("Applying Vreg to |" + ti.name() + tj.name() + ">"); CCTimer timer(world, "Vreg|" + ti.name() + tj.name() + ">"); CCTimer time_f(world, "F-Part"); @@ -1473,7 +1448,7 @@ CCPotentials::apply_Vreg_macrotask(World& world, const std::vector\n"); + // print("Applying Vreg to |" + x_name + y_name + ">\n"); CCTimer timer(world, "Vreg|" + x_name + y_name + ">"); //CCTimer time_f(world, "F-Part"); //const real_function_6d F_part = apply_reduced_F(ti, tj, Gscreen); @@ -1518,7 +1493,7 @@ CCPotentials::apply_Vreg_macrotask(World& world, const std::vector& ti, const CCFunction& tj, const real_convolution_6d *Gscreen) const { //CC_Timer time(world,"(F-eij)|"+ti.name()+tj.name()+">"); // get singles potential const bool symmetric = (ti.type == tj.type && ti.i == tj.i); @@ -1537,7 +1512,7 @@ CCPotentials::apply_reduced_F(const CCFunction& ti, const CCFunction& tj, const madness::real_function_6d -CCPotentials::apply_transformed_Ue(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { +CCPotentials::apply_transformed_Ue(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { if (parameters.debug()) output("\nComputing Ue|" + x.name() + y.name() + ">\n"); const bool symmetric = (x.type == y.type && x.i == y.i); @@ -1600,7 +1575,7 @@ CCPotentials::apply_transformed_Ue(const CCFunction& x, const CCFunction& y, con const double a = inner(Uxy, tmp); const real_function_3d xx = (x.function * x.function * nemo_->ncf->square()); const real_function_3d yy = (y.function * y.function * nemo_->ncf->square()); - const real_function_3d gxx = g12(xx); + const real_function_3d gxx = (*g12)(xx); const double aa = inner(yy, gxx); const double error = std::fabs(a - aa); const double diff = a - aa; @@ -1627,7 +1602,7 @@ CCPotentials::apply_transformed_Ue_macrotask(World& world, const std::vector\n"); + if (parameters.debug()) print("Computing Ue|" + x_name + y_name + ">"); real_function_3d x_function=mo_ket[i]; real_function_3d y_function=mo_ket[j]; @@ -1795,7 +1770,7 @@ CCPotentials::apply_exchange_commutator_macrotask(World& world, const std::vecto madness::real_function_6d -CCPotentials::apply_exchange_commutator(const CCFunction& x, const CCFunction& y, +CCPotentials::apply_exchange_commutator(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { real_function_6d result = apply_exchange_commutator1(x, y, Gscreen); { @@ -1823,7 +1798,7 @@ CCPotentials::apply_exchange_commutator(const CCFunction& x, const CCFunction& y } madness::real_function_6d -CCPotentials::apply_exchange_commutator1(const CCFunction& x, const CCFunction& y, +CCPotentials::apply_exchange_commutator1(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { if (parameters.debug()) output("\nComputing [K,f]|" + x.name() + y.name() + ">\n"); @@ -1849,7 +1824,7 @@ CCPotentials::apply_exchange_commutator1(const CCFunction& x, const CCFunction& } double -CCPotentials::make_xy_gf_ab(const CCFunction& x, const CCFunction& y, const CCFunction& a, const CCFunction& b) const { +CCPotentials::make_xy_gf_ab(const CCFunction& x, const CCFunction& y, const CCFunction& a, const CCFunction& b) const { const real_function_3d xa = (x.function * a.function).truncate(); const real_function_3d x_gf_a = apply_gf(xa); const double result = y.function.inner(x_gf_a * b.function); @@ -1862,11 +1837,11 @@ CCPotentials::apply_gf(const real_function_3d& f) const { BSHOperatorPtr3D(world, parameters.gamma(), parameters.lo(), parameters.thresh_poisson())); double bsh_prefactor = 4.0 * constants::pi; double prefactor = 1.0 / (2.0 * parameters.gamma()); - return prefactor * (g12(f) - bsh_prefactor * (*fBSH)(f)).truncate(); + return prefactor * ((*g12)(f) - bsh_prefactor * (*fBSH)(f)).truncate(); } double -CCPotentials::make_xy_u(const CCFunction& x, const CCFunction& y, const std::vector& u) const { +CCPotentials::make_xy_u(const CCFunction& x, const CCFunction& y, const std::vector>& u) const { double result = 0.0; for (size_t mm = 0; mm < u.size(); mm++) { result += u[mm].make_xy_u(x, y); @@ -1875,8 +1850,8 @@ CCPotentials::make_xy_u(const CCFunction& x, const CCFunction& y, const std::vec } double -CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, - const CCPairFunction& u) const { +CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, + const CCPairFunction& u) const { double result = 0.0; if (u.component->is_pure()) { real_function_6d xy_op = CompositeFactory(world).particle1(copy(x.function)).particle2( @@ -1884,11 +1859,11 @@ CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCCon result = inner(u.get_function(), xy_op); } else if (u.component->is_decomposed()) { if (u.component->has_operator()) { - if (op.type() == OT_G12 and u.decomposed().get_operator_ptr()->type() == OT_F12) + if (op.type() == OpType::OT_G12 and u.decomposed().get_operator_ptr()->type() == OpType::OT_F12) result = make_xy_gf_ab(x, y, u.decomposed().get_a()[0], u.decomposed().get_b()[0]); - else if (op.type() == OT_F12 and u.decomposed().get_operator_ptr()->type() == OT_G12) + else if (op.type() == OpType::OT_F12 and u.decomposed().get_operator_ptr()->type() == OpType::OT_G12) result = make_xy_gf_ab(x, y, u.decomposed().get_a()[0], u.decomposed().get_b()[0]); - else if (op.type() == OT_F12 and u.decomposed().get_operator_ptr()->type() == OT_F12) + else if (op.type() == OpType::OT_F12 and u.decomposed().get_operator_ptr()->type() == OpType::OT_F12) result = make_xy_ff_ab(x, y, u.decomposed().get_a()[0], u.decomposed().get_b()[0]); else MADNESS_EXCEPTION(("xy_" + op.name() + u.name() + " not implemented").c_str(), 1); } else { @@ -1901,8 +1876,8 @@ CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCCon } double -CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, - const std::vector& u) const { +CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, + const std::vector>& u) const { double result = 0.0; for (size_t mm = 0; mm < u.size(); mm++) { const double tmp = make_xy_op_u(x, y, op, u[mm]); @@ -1912,8 +1887,8 @@ CCPotentials::make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCCon } double -CCPotentials::make_xy_op_ab(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, - const CCFunction& a, const CCFunction& b) const { +CCPotentials::make_xy_op_ab(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, + const CCFunction& a, const CCFunction& b) const { double result = 0.0; if (x.type == HOLE) { real_function_3d xopa = op(x, a); @@ -1925,7 +1900,7 @@ CCPotentials::make_xy_op_ab(const CCFunction& x, const CCFunction& y, const CCCo return result; } -std::vector +std::vector> CCPotentials::get_pair_function(const Pairs& pairs, const size_t i, const size_t j) const { if (i > j) { return swap_particles(pairs(j, i).functions); @@ -1935,17 +1910,17 @@ CCPotentials::get_pair_function(const Pairs& pairs, const size_t i, cons } madness::real_function_3d -CCPotentials::apply_s2b_operation(const CCFunction& bra, const CCPairFunction& u, const size_t particle) const { +CCPotentials::apply_s2b_operation(const CCFunction& bra, const CCPairFunction& u, const size_t particle) const { real_function_3d result; MADNESS_ASSERT(particle == 1 || particle == 2); if (u.is_pure()) { - result = u.dirac_convolution(bra, g12, particle); + result = u.dirac_convolution(bra, *g12, particle); } else if (u.is_decomposed_no_op()) { - result = u.dirac_convolution(bra, g12, particle); + result = u.dirac_convolution(bra, *g12, particle); } else if (u.is_op_decomposed()) { // retunrns _particle - CCFunction a; - CCFunction b; + CCFunction a; + CCFunction b; if (particle == 1) { a = u.get_a()[0]; b = u.get_b()[0]; @@ -2007,15 +1982,15 @@ CCPotentials::apply_Q12t(const real_function_6d& f, const CC_vecfunction& t) con return Q(f); } -madness::CCPairFunction -CCPotentials::apply_Qt(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle, const double c) const { +madness::CCPairFunction +CCPotentials::apply_Qt(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle, const double c) const { MADNESS_ASSERT(particle == 1 || particle == 2); MADNESS_ASSERT(f.is_decomposed_no_op()); // pure type is not needed and op_deomposed type can not be because the result would be (1-Ot)f12|xy> = f12|xy> - \sum_a|a1a2> a subtraction with different types if (particle == 1) { - CCPairFunction result(apply_Qt(f.get_a(), t, c), f.get_b()); + CCPairFunction result(apply_Qt(f.get_a(), t, c), f.get_b()); return result; } else { - CCPairFunction result(f.get_a(), apply_Qt(f.get_b(), t, c)); + CCPairFunction result(f.get_a(), apply_Qt(f.get_b(), t, c)); return result; } } @@ -2031,8 +2006,8 @@ CCPotentials::apply_Qt(const CC_vecfunction& f, const CC_vecfunction& ket_, cons return result; } -madness::CCPairFunction -CCPotentials::apply_Ot(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle) const { +madness::CCPairFunction +CCPotentials::apply_Ot(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle) const { MADNESS_ASSERT(particle == 1 || particle == 2); // get the right bra CC_vecfunction mbra; @@ -2043,38 +2018,38 @@ CCPotentials::apply_Ot(const CCPairFunction& f, const CC_vecfunction& t, const s if (f.is_pure()) { vector_real_function_3d projected; for (const auto& ktmp : t.functions) { - const CCFunction& bra = mbra(ktmp.first); + const CCFunction& bra = mbra(ktmp.first); const real_function_3d kf = f.project_out(bra, particle); projected.push_back(kf); } if (particle == 1) { - return CCPairFunction(copy(world, t.get_vecfunction()), projected); + return CCPairFunction(copy(world, t.get_vecfunction()), projected); } else { - return CCPairFunction(projected, copy(world, t.get_vecfunction())); + return CCPairFunction(projected, copy(world, t.get_vecfunction())); } } else if (f.is_decomposed_no_op()) { - if (particle == 1) return CCPairFunction(apply_projector(f.get_a(), t), f.get_b()); - else return CCPairFunction(f.get_a(), apply_projector(f.get_b(), t)); + if (particle == 1) return CCPairFunction(apply_projector(f.get_a(), t), f.get_b()); + else return CCPairFunction(f.get_a(), apply_projector(f.get_b(), t)); } else if (f.is_op_decomposed()) { if (particle == 1) { const vector_real_function_3d a = copy(world, t.get_vecfunction()); // const vector_real_function_3d b = mul(world, f.get_b()[0], f.op->operator()(mbra, f.get_a()[0])); const vector_real_function_3d b = mul(world, f.get_b()[0], f.decomposed().get_operator_ptr()->operator()(mbra, f.get_a()[0])); - return CCPairFunction(a, b); + return CCPairFunction(a, b); } else { const vector_real_function_3d a = mul(world, f.get_a()[0], f.decomposed().get_operator_ptr()->operator()(mbra, f.get_b()[0])); const vector_real_function_3d b = copy(world, t.get_vecfunction()); - return CCPairFunction(a, b); + return CCPairFunction(a, b); } } else MADNESS_EXCEPTION("Should not end up here", 1) ; - return CCPairFunction(vector_real_function_3d(), vector_real_function_3d()); + return CCPairFunction(vector_real_function_3d(), vector_real_function_3d()); } madness::real_function_6d -CCPotentials::apply_G(const CCPairFunction& u, const real_convolution_6d& G) const { +CCPotentials::apply_G(const CCPairFunction& u, const real_convolution_6d& G) const { CCTimer time(world, "Applying G on " + u.name()); return apply(G,u).get_function(); // real_function_6d result = real_function_6d(world); @@ -2088,7 +2063,7 @@ CCPotentials::apply_G(const CCPairFunction& u, const real_convolution_6d& G) con // const real_function_6d tmp = G(u.get_a()[k], u.get_b()[k]); // result += tmp; // } -// } else error("Apply_G to CCPairFunction of type other than pure or decomposed"); +// } else error("Apply_G to CCPairFunction of type other than pure or decomposed"); // // time.info(true, result.norm2()); // if (result.norm2() == 0.0) output.warning("Gab is Zero"); @@ -2401,10 +2376,10 @@ CCPotentials::fock_residue_closed_shell(const CC_vecfunction& singles) const { // vecfuncT J = mul(world, intermediates_.get_hartree_potential(), tau); vector_real_function_3d J; for (const auto& tmpi : singles.functions) { - const CCFunction& taui = tmpi.second; + const CCFunction& taui = tmpi.second; real_function_3d hartree_potential = real_function_3d(world); for (const auto& tmpk : mo_ket_.functions) - hartree_potential += g12(mo_bra_(tmpk.first), tmpk.second); + hartree_potential += (*g12)(mo_bra_(tmpk.first), tmpk.second); const real_function_3d Ji = hartree_potential * taui.function; J.push_back(Ji); } @@ -2414,7 +2389,7 @@ CCPotentials::fock_residue_closed_shell(const CC_vecfunction& singles) const { CCTimer timer_K(world, "K"); vector_real_function_3d vK; for (const auto& tmpi : singles.functions) { - const CCFunction& taui = tmpi.second; + const CCFunction& taui = tmpi.second; const real_function_3d Ki = K(taui); vK.push_back(Ki); } @@ -2453,15 +2428,15 @@ CCPotentials::K_macrotask(World& world, const std::vector& mo_ result += madness::swap_particles(result); } else result += apply_K_macrotask(world, mo_ket, mo_bra, u, 2, parameters); - result.print_size("K|u>"); + if (parameters.debug()) result.print_size("K|u>"); return (result.truncate(parameters.tight_thresh_6D())); } madness::real_function_3d -CCPotentials::K(const CCFunction& f) const { +CCPotentials::K(const CCFunction& f) const { real_function_3d result = real_factory_3d(world); for (const auto& k_iterator : mo_ket_.functions) { - result += g12(mo_bra_(k_iterator.first), f) * mo_ket_(k_iterator.first).function; + result += (*g12)(mo_bra_(k_iterator.first), f) * mo_ket_(k_iterator.first).function; } return result; } @@ -2487,7 +2462,7 @@ CCPotentials::apply_K(const real_function_6d& u, const size_t& particle) const { real_function_6d copyu = copy(u); real_function_6d X = (multiply(copyu, copy(mo_bra_(k).function), particle)).truncate(); // real_function_6d Y=(*poisson)(X); - real_function_6d Y = g12(X, particle); // overwrite X to save space + real_function_6d Y = (*g12)(X, particle); // overwrite X to save space result += (multiply(copy(Y), copy(mo_ket_(k).function), particle)).truncate(); // this will destroy X, but I d not intend to use it again so I choose here to save this copy } @@ -2517,7 +2492,7 @@ CCPotentials::apply_K_macrotask(World& world, const std::vector& x, const CCFunction& y) const { bool symmetric = false; if ((x.type == y.type) && (x.i == y.i)) symmetric = true; @@ -2530,22 +2505,22 @@ CCPotentials::apply_Kf(const CCFunction& x, const CCFunction& y) const { } madness::real_function_6d -CCPotentials::apply_fK(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { +CCPotentials::apply_fK(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { const bool symmetric = (x.type == y.type && x.i == y.i); const real_function_3d Kx = K(x); - const real_function_6d fKphi0b = make_f_xy(CCFunction(Kx, x.i, UNDEFINED), y, Gscreen); + const real_function_6d fKphi0b = make_f_xy(CCFunction(Kx, x.i, UNDEFINED), y, Gscreen); real_function_6d fKphi0a; if (symmetric) fKphi0a = swap_particles(fKphi0b); else { real_function_3d Ky = K(y); - fKphi0a = make_f_xy(x, CCFunction(Ky, y.i, UNDEFINED), Gscreen); + fKphi0a = make_f_xy(x, CCFunction(Ky, y.i, UNDEFINED), Gscreen); } const real_function_6d fKphi0 = (fKphi0a + fKphi0b); return fKphi0; } madness::real_function_6d -CCPotentials::make_f_xy(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { +CCPotentials::make_f_xy(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen) const { std::string screen = ""; if (Gscreen != NULL) screen = "screened "; @@ -2559,7 +2534,7 @@ CCPotentials::make_f_xy(const CCFunction& x, const CCFunction& y, const real_con if (x.type != UNDEFINED && y.type != UNDEFINED) { CCTimer timer_db(world, "f|xy> sanity check"); const double test1 = (mo_bra_(y.i).function).inner(fxy.project_out(mo_bra_(x.i).function, 0)); - const double test2 = (mo_bra_(y.i).function).inner(f12(mo_bra_(x.i), x) * y.function); + const double test2 = (mo_bra_(y.i).function).inner((*f12)(mo_bra_(x.i), x) * y.function); const double sanity = test1 - test2; if (fabs(sanity) > FunctionDefaults<6>::get_thresh()) { if (world.rank() == 0) @@ -2620,11 +2595,11 @@ CCPotentials::ccs_unprojected(const CC_vecfunction& ti, const CC_vecfunction& tk for (const auto& itmp : ti.functions) { real_function_3d kgtk = real_factory_3d(world); for (const auto& ktmp : tk.functions) - kgtk += g12(mo_bra_(ktmp.first), ktmp.second); + kgtk += (*g12)(mo_bra_(ktmp.first), ktmp.second); const real_function_3d kgtk_ti = kgtk * ti(itmp.first).function; real_function_3d kgti_tk = real_factory_3d(world); for (const auto& ktmp : tk.functions) - kgti_tk += g12(mo_bra_(ktmp.first), ti(itmp.first)) * tk(ktmp.first).function; + kgti_tk += (*g12)(mo_bra_(ktmp.first), ti(itmp.first)) * tk(ktmp.first).function; const real_function_3d resulti = 2.0 * kgtk_ti - kgti_tk; result.push_back(resulti); } @@ -2654,8 +2629,8 @@ CCPotentials::x_s3a(const CC_vecfunction& x, const CC_vecfunction& t) const { for (const auto& ktmp : mo_ket_.functions) { // unfrozen summation !!!!!! important !!!! const size_t k = ktmp.first; - const double gpart = make_xy_op_ab(x(i), mo_bra_(k), g12, t(i), mo_ket_(k)); - const double xpart = make_xy_op_ab(x(i), mo_bra_(k), g12, mo_ket_(k), t(i)); + const double gpart = make_xy_op_ab(x(i), mo_bra_(k), *g12, t(i), mo_ket_(k)); + const double xpart = make_xy_op_ab(x(i), mo_bra_(k), *g12, mo_ket_(k), t(i)); pot += (2.0 * gpart - xpart); } } @@ -2683,8 +2658,8 @@ CCPotentials::x_s3c(const CC_vecfunction& x, const CC_vecfunction& t) const { const size_t i = itmp.first; for (const auto& ktmp : t.functions) { const size_t k = ktmp.first; - result += (2.0 * make_xy_op_ab(x(i), mo_bra_(k), g12, mo_ket_(i), t(k)) - - make_xy_op_ab(x(i), mo_bra_(k), g12, t(k), mo_ket_(i))); + result += (2.0 * make_xy_op_ab(x(i), mo_bra_(k), *g12, mo_ket_(i), t(k)) - + make_xy_op_ab(x(i), mo_bra_(k), *g12, t(k), mo_ket_(i))); } } return result; @@ -2699,8 +2674,8 @@ CCPotentials::x_s5b(const CC_vecfunction& x, const CC_vecfunction& t1, const CC_ const size_t i = itmp.first; for (const auto& ktmp : t1.functions) { const size_t k = ktmp.first; - result += (2.0 * make_xy_op_ab(x(i), mo_bra_(k), g12, t1(i), t2(k)) - - make_xy_op_ab(x(i), mo_bra_(k), g12, t2(k), t1(i))); + result += (2.0 * make_xy_op_ab(x(i), mo_bra_(k), *g12, t1(i), t2(k)) - + make_xy_op_ab(x(i), mo_bra_(k), *g12, t2(k), t1(i))); } } return result; @@ -2717,8 +2692,8 @@ CCPotentials::x_s5c(const CC_vecfunction& x, const CC_vecfunction& t1, const CC_ const size_t k = ktmp.first; for (const auto& ltmp : t2.functions) { const size_t l = ltmp.first; - result += (2.0 * make_xy_op_ab(mo_bra_(l), mo_bra_(k), g12, mo_ket_(i), t1(k)) - - make_xy_op_ab(mo_bra_(l), mo_bra_(k), g12, t1(k), mo_ket_(i))) * + result += (2.0 * make_xy_op_ab(mo_bra_(l), mo_bra_(k), *g12, mo_ket_(i), t1(k)) - + make_xy_op_ab(mo_bra_(l), mo_bra_(k), *g12, t1(k), mo_ket_(i))) * x(i).function.inner(t2(l).function); } } @@ -2738,8 +2713,8 @@ CCPotentials::x_s6(const CC_vecfunction& x, const CC_vecfunction& t1, const CC_v const size_t k = ktmp.first; for (const auto& ltmp : t2.functions) { const size_t l = ltmp.first; - result += (2.0 * make_xy_op_ab(mo_bra_(l), mo_bra_(k), g12, t3(i), t1(k)) - - make_xy_op_ab(mo_bra_(l), mo_bra_(k), g12, t1(k), t3(i))) * + result += (2.0 * make_xy_op_ab(mo_bra_(l), mo_bra_(k), *g12, t3(i), t1(k)) - + make_xy_op_ab(mo_bra_(l), mo_bra_(k), *g12, t1(k), t3(i))) * x(i).function.inner(t2(l).function); } } @@ -2754,8 +2729,8 @@ CCPotentials::x_s2b(const CC_vecfunction& x, const Pairs& u) const { const size_t i = itmp.first; for (const auto& ktmp : x.functions) { const size_t k = ktmp.first; - result += (2.0 * make_xy_op_u(x(i), mo_bra_(k), g12, get_pair_function(u, i, k)) - - make_xy_op_u(mo_bra_(k), x(i), g12, get_pair_function(u, i, k))); + result += (2.0 * make_xy_op_u(x(i), mo_bra_(k), *g12, get_pair_function(u, i, k)) - + make_xy_op_u(mo_bra_(k), x(i), *g12, get_pair_function(u, i, k))); } } return result; @@ -2768,7 +2743,7 @@ CCPotentials::x_s2c(const CC_vecfunction& x, const Pairs& u) const { const size_t i = itmp.first; for (const auto& ktmp : x.functions) { const size_t k = ktmp.first; - const real_function_3d kgi = g12(mo_bra_(k), mo_ket_(i)); + const real_function_3d kgi = (*g12)(mo_bra_(k), mo_ket_(i)); for (const auto& ltmp : x.functions) { const size_t l = ltmp.first; real_function_3d l_kgi = (mo_bra_(l).function * kgi).truncate(); @@ -2790,8 +2765,8 @@ CCPotentials::x_s4a(const CC_vecfunction& x, const CC_vecfunction& t, const Pair const size_t k = ktmp.first; for (const auto& ltmp : x.functions) { const size_t l = ltmp.first; - result += (2.0 * make_xy_op_u(mo_bra_(l), mo_bra_(k), g12, get_pair_function(u, i, k)) - - make_xy_op_u(mo_bra_(k), mo_bra_(l), g12, get_pair_function(u, i, k))) * + result += (2.0 * make_xy_op_u(mo_bra_(l), mo_bra_(k), *g12, get_pair_function(u, i, k)) - + make_xy_op_u(mo_bra_(k), mo_bra_(l), *g12, get_pair_function(u, i, k))) * x(i).function.inner(t(l).function); } } @@ -2807,7 +2782,7 @@ CCPotentials::x_s4b(const CC_vecfunction& x, const CC_vecfunction& t, const Pair const size_t i = itmp.first; for (const auto& ktmp : x.functions) { const size_t k = ktmp.first; - const real_function_3d kgti = g12(mo_bra_(k), t(i)); + const real_function_3d kgti = (*g12)(mo_bra_(k), t(i)); for (const auto& ltmp : x.functions) { const size_t l = ltmp.first; real_function_3d l_kgti = (mo_bra_(l).function * kgti).truncate(); @@ -2827,10 +2802,10 @@ CCPotentials::x_s4c(const CC_vecfunction& x, const CC_vecfunction& t, const Pair const size_t i = itmp.first; for (const auto& ktmp : x.functions) { const size_t k = ktmp.first; - const real_function_3d kgtk = g12(mo_bra_(k), t(k)); + const real_function_3d kgtk = (*g12)(mo_bra_(k), t(k)); for (const auto& ltmp : x.functions) { const size_t l = ltmp.first; - const real_function_3d lgtk = g12(mo_bra_(l), t(k)); + const real_function_3d lgtk = (*g12)(mo_bra_(l), t(k)); const real_function_3d k_lgtk = (mo_bra_(k).function * lgtk).truncate(); const real_function_3d l_kgtk = (mo_bra_(l).function * kgtk).truncate(); result += (4.0 * make_xy_u(x(i), l_kgtk, get_pair_function(u, i, l)) - @@ -2857,7 +2832,7 @@ CCPotentials::s2b(const CC_vecfunction& singles, const Pairs& doubles) c real_function_3d resulti_r = real_factory_3d(world); for (const auto& ktmp : singles.functions) { const size_t k = ktmp.first; - std::vector uik = get_pair_function(doubles, i, k); + std::vector> uik = get_pair_function(doubles, i, k); // check if the first function in the vector is really the pure 6D part MADNESS_ASSERT(uik[0].is_pure()); if (recalc_u_part) { @@ -2897,11 +2872,11 @@ CCPotentials::s2c(const CC_vecfunction& singles, const Pairs& doubles) c real_function_3d resulti_r = real_factory_3d(world); for (const auto& ktmp : singles.functions) { const size_t k = ktmp.first; - const real_function_3d kgi = g12(mo_bra_(k), mo_ket_(i)); + const real_function_3d kgi = (*g12)(mo_bra_(k), mo_ket_(i)); for (const auto& ltmp : singles.functions) { const size_t l = ltmp.first; const real_function_3d l_kgi = mo_bra_(l).function * kgi; - std::vector ukl = get_pair_function(doubles, k, l); + std::vector> ukl = get_pair_function(doubles, k, l); // check if the first function in the vector is really the pure 6D part MADNESS_ASSERT(ukl[0].is_pure()); if (recalc_u_part) { @@ -2961,12 +2936,12 @@ CCPotentials::s4b(const CC_vecfunction& singles, const Pairs& doubles) c real_function_3d resulti = real_factory_3d(world); for (const auto& ktmp : singles.functions) { const size_t k = ktmp.first; - const real_function_3d kgi = g12(mo_bra_(k), singles(i)); + const real_function_3d kgi = (*g12)(mo_bra_(k), singles(i)); vector_real_function_3d l_kgi = mul_sparse(world, kgi, active_mo_bra, parameters.thresh_3D()); truncate(world, l_kgi); for (const auto& ltmp : singles.functions) { const size_t l = ltmp.first; - const std::vector ukl = get_pair_function(doubles, k, l); + const std::vector> ukl = get_pair_function(doubles, k, l); for (size_t mm = 0; mm < ukl.size(); mm++) { resulti += -2.0 * ukl[mm].project_out(l_kgi[l - parameters.freeze()], 2); resulti += ukl[mm].project_out(l_kgi[l - parameters.freeze()], 1); @@ -2992,20 +2967,20 @@ CCPotentials::s4c(const CC_vecfunction& singles, const Pairs& doubles) c real_function_3d kgtauk = real_factory_3d(world); for (const auto& ktmp : singles.functions) { const size_t k = ktmp.first; - kgtauk += g12(mo_bra_(k), singles(k)); + kgtauk += (*g12)(mo_bra_(k), singles(k)); } vector_real_function_3d l_kgtauk = mul(world, kgtauk, active_mo_bra); truncate(world, l_kgtauk); for (const auto& ltmp : singles.functions) { const size_t l = ltmp.first; - const std::vector uil = get_pair_function(doubles, i, l); + const std::vector> uil = get_pair_function(doubles, i, l); for (size_t mm = 0; mm < uil.size(); mm++) { part1 += uil[mm].project_out(l_kgtauk[l - parameters.freeze()], 2); part2 += uil[mm].project_out(l_kgtauk[l - parameters.freeze()], 1); } for (const auto& ktmp : singles.functions) { const size_t k = ktmp.first; - const real_function_3d k_lgtauk = (mo_bra_(k).function * g12(mo_bra_(l), singles(k))).truncate(); + const real_function_3d k_lgtauk = (mo_bra_(k).function * (*g12)(mo_bra_(l), singles(k))).truncate(); for (size_t mm = 0; mm < uil.size(); mm++) { part3 += uil[mm].project_out(k_lgtauk, 2); part4 += uil[mm].project_out(k_lgtauk, 1); @@ -3051,7 +3026,7 @@ CC_vecfunction CCPotentials::make_t_intermediate(const CC_vecfunction& tau, cons CC_vecfunction result(returntype); for (const auto& itmp:tau.functions) { const size_t i = itmp.first; - CCFunction t(mo_ket_(i).function + factor * tau(i).function, i, MIXED); + CCFunction t(mo_ket_(i).function + factor * tau(i).function, i, MIXED); result.insert(i, t); } @@ -3078,7 +3053,7 @@ CC_vecfunction CCPotentials::make_full_t_intermediate(const CC_vecfunction& tau) if (int(i) < parameters.freeze()) { result.insert(i, mo_ket_(i)); } else { - CCFunction t(mo_ket_(i).function + tau(i).function, i, MIXED); + CCFunction t(mo_ket_(i).function + tau(i).function, i, MIXED); result.insert(i, t); } } @@ -3088,30 +3063,30 @@ CC_vecfunction CCPotentials::make_full_t_intermediate(const CC_vecfunction& tau) /// makes the t intermediates /// t_i = mo_ket_(i) + tau /// i = tau.i -CCFunction CCPotentials::make_t_intermediate(const CCFunction& tau) const { +CCFunction CCPotentials::make_t_intermediate(const CCFunction& tau) const { MADNESS_ASSERT(tau.type == PARTICLE); - const CCFunction t(mo_ket_(tau.i).function + tau.function, tau.i, MIXED); + const CCFunction t(mo_ket_(tau.i).function + tau.function, tau.i, MIXED); return t; } /// forms the regularized functions from Q and Qt Ansatz for CIS(D) where tau=0 and t=mo so that Qt=Q -void CCPotentials::test_pair_consistency(const CCPairFunction& u, const size_t i, const size_t j, +void CCPotentials::test_pair_consistency(const CCPairFunction& u, const size_t i, const size_t j, const CC_vecfunction& x) const { if (parameters.QtAnsatz()) { // u(QAnsatz) = u(QtAnsatz) - OxQftt - QOxftt - std::vector v1; + std::vector> v1; v1.push_back(u); - std::vector v2; + std::vector> v2; v2.push_back(u); - CCPairFunction ftt(&f12, mo_ket_(i), mo_ket_(j)); - CCPairFunction O1xftt = apply_Ot(ftt, x, 1); - CCPairFunction OxQftt = apply_Qt(O1xftt, mo_ket_, 2); - CCPairFunction OxQ = OxQftt.invert_sign(); + CCPairFunction ftt(f12, mo_ket_(i), mo_ket_(j)); + CCPairFunction O1xftt = apply_Ot(ftt, x, 1); + CCPairFunction OxQftt = apply_Qt(O1xftt, mo_ket_, 2); + CCPairFunction OxQ = OxQftt.invert_sign(); v2.push_back(OxQ); - CCPairFunction O2xftt = apply_Ot(ftt, x, 2); - CCPairFunction QOxftt = apply_Qt(O2xftt, mo_ket_, 1); - CCPairFunction QOx = QOxftt.invert_sign(); + CCPairFunction O2xftt = apply_Ot(ftt, x, 2); + CCPairFunction QOxftt = apply_Qt(O2xftt, mo_ket_, 1); + CCPairFunction QOx = QOxftt.invert_sign(); v2.push_back(QOx); CCPair p1(i, j, EXCITED_STATE, CT_CISPD, v1); @@ -3125,16 +3100,16 @@ void CCPotentials::test_pair_consistency(const CCPairFunction& u, const size_t i } else { // u(QtAnsatz) = u(QAnsatz) + OxQftt - QOxftt - std::vector v1; + std::vector> v1; v1.push_back(u); - std::vector v2; + std::vector> v2; v2.push_back(u); - CCPairFunction ftt(&f12, mo_ket_(i), mo_ket_(j)); - CCPairFunction O1xftt = apply_Ot(ftt, x, 1); - CCPairFunction OxQftt = apply_Qt(O1xftt, mo_ket_, 2); + CCPairFunction ftt(f12, mo_ket_(i), mo_ket_(j)); + CCPairFunction O1xftt = apply_Ot(ftt, x, 1); + CCPairFunction OxQftt = apply_Qt(O1xftt, mo_ket_, 2); v2.push_back(OxQftt); - CCPairFunction O2xftt = apply_Ot(ftt, x, 2); - CCPairFunction QOxftt = apply_Qt(O2xftt, mo_ket_, 1); + CCPairFunction O2xftt = apply_Ot(ftt, x, 2); + CCPairFunction QOxftt = apply_Qt(O2xftt, mo_ket_, 1); v2.push_back(QOxftt); CCPair p1(i, j, EXCITED_STATE, CT_CISPD, v1); @@ -3165,8 +3140,8 @@ bool CCPotentials::test_compare_pairs(const CCPair& pair1, const CCPair& pair2) } else output("Test Passed, diff=" + std::to_string(diff)); // test energy integration - double energy_1 = make_xy_op_u(mo_bra_(pair1.i), mo_bra_(pair1.j), g12, pair1.functions); - double energy_2 = make_xy_op_u(mo_bra_(pair2.i), mo_bra_(pair2.j), g12, pair2.functions); + double energy_1 = make_xy_op_u(mo_bra_(pair1.i), mo_bra_(pair1.j), *g12, pair1.functions); + double energy_2 = make_xy_op_u(mo_bra_(pair2.i), mo_bra_(pair2.j), *g12, pair2.functions); double diff_energy = energy_1 - energy_2; if (world.rank() == 0) std::cout << std::fixed << std::setprecision(10) @@ -3195,7 +3170,7 @@ bool CCPotentials::test_compare_pairs(const CCPair& pair1, const CCPair& pair2) // make a single 6D functions of the pair real_function_6d CCPotentials::make_6D_pair(const CCPair& pair) const { - std::vector functions = pair.functions; + std::vector> functions = pair.functions; real_function_6d result = real_factory_6d(world); for (const auto& f:functions) { if (f.is_pure()) result += f.pure().get_function(); @@ -3207,10 +3182,10 @@ real_function_6d CCPotentials::make_6D_pair(const CCPair& pair) const { result += ab; } } else if (f.is_op_decomposed()) { - MADNESS_ASSERT(f.get_operator().type() == OT_F12); + MADNESS_ASSERT(f.get_operator().type() == OpType::OT_F12); real_function_6d fxy = make_f_xy(f.get_a()[0], f.get_b()[0]); result += fxy; - } else MADNESS_EXCEPTION("Unknown type of CCPairFunction", 1); + } else MADNESS_EXCEPTION("Unknown type of CCPairFunction", 1); } return result; } @@ -3235,8 +3210,8 @@ void CCPotentials::test_pairs() { CCPair rr_3D = (make_pair_gs(u, mo_ket_, i, j)); real_function_6d rr_tmp = make_f_xy(t(i), t(j)); real_function_6d rr_6D0 = apply_Q12t(rr_tmp, mo_ket_); - CCPairFunction rr_6D1(rr_6D0); - std::vector rr_6D2(1, rr_6D1); + CCPairFunction rr_6D1(rr_6D0); + std::vector> rr_6D2(1, rr_6D1); CCPair rr_6D(i, j, GROUND_STATE, CT_CC2, rr_6D2); regrestest = test_compare_pairs(rr_3D, rr_6D); } @@ -3280,8 +3255,8 @@ void CCPotentials::test_pairs() { MADNESS_EXCEPTION("No Test for QtAnsatz right now", 1); } } - CCPairFunction rr_6D1(rr_6D0); - std::vector rr_6D2(1, rr_6D1); + CCPairFunction rr_6D1(rr_6D0); + std::vector> rr_6D2(1, rr_6D1); CCPair rr_6D(i, j, GROUND_STATE, CT_MP2, rr_6D2); regrestest = test_compare_pairs(rr_3D, rr_6D); } @@ -3412,15 +3387,12 @@ void CCPotentials::test_singles_potential() const { void CCPotentials::test() { output.section("Testing enums"); - PairFormat test1 = PT_DECOMPOSED; CalcType test2 = CT_MP2; - OpType test3 = OT_G12; + OpType test3 = OpType::OT_G12; FuncType test4 = HOLE; CCState test5 = GROUND_STATE; PotentialType test6 = POT_F3D_; - assign_name(test1); assign_name(test2); - assign_name(test3); assign_name(test4); assign_name(test5); assign_name(test6); @@ -3435,8 +3407,8 @@ void CCPotentials::test() { else output.warning("Test Failed: Norm1 = " + std::to_string(norma) + ", Norm2 = " + std::to_string(normb)); } { - CCFunction mo = mo_ket_(0); - CCFunction mo1 = mo * 2.0; + CCFunction mo = mo_ket_(0); + CCFunction mo1 = mo * 2.0; double norma = mo.function.norm2(); double normb = mo1.function.norm2(); if (2.0 * norma == normb) output("Test Passed"); @@ -3461,7 +3433,7 @@ void CCPotentials::test() { } - output.section("Testing Overlaps of CCPairFunction"); + output.section("Testing Overlaps of CCPairFunction"); // f^2 = 1/(4y^2)(1 - 2*f2(y) + f2(2y)) , f2(2y) =f2(y)^2 const double y = parameters.gamma(); SeparatedConvolution f = SlaterF12Operator(world, y, parameters.lo(), parameters.thresh_bsh_3D()); @@ -3490,7 +3462,7 @@ void CCPotentials::test() { const double lo = parameters.thresh_6D(); const double hi = parameters.thresh_3D(); { - CCPairFunction fab(&f12, a, b); + CCPairFunction fab(f12, a, b); const double test1 = overlap(fab, fab); const double prefactor = 1.0 / (4 * y * y); const double test2 = prefactor * (aR.inner(a) * bR.inner(b) - 2.0 * bb.inner(af2a) + bb.inner(affa)); @@ -3504,7 +3476,7 @@ void CCPotentials::test() { << ", test=" << test2 << ", diff=" << diff << "\n"; } { - CCPairFunction ab(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); + CCPairFunction ab(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); const double test1 = overlap(ab, ab); const double test2 = double(mo_ket_.size()); // mos are normed const double diff = test1 - test2; @@ -3515,7 +3487,7 @@ void CCPotentials::test() { if (fabs(diff) > hi) passed_hi = false; } { - CCPairFunction ab(ab_6d); + CCPairFunction ab(ab_6d); const double test1 = overlap(ab, ab); const double test2 = double(mo_ket_.size()); // mos are normed const double diff = test1 - test2; @@ -3526,8 +3498,8 @@ void CCPotentials::test() { if (fabs(diff) > hi) passed_hi = false; } { - CCPairFunction ab1(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); - CCPairFunction ab2(ab_6d); + CCPairFunction ab1(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); + CCPairFunction ab2(ab_6d); const double test1 = overlap(ab1, ab2); const double test2 = double(mo_ket_.size()); // mos are normed const double diff = test1 - test2; @@ -3539,8 +3511,8 @@ void CCPotentials::test() { } { // the next tests evaulate in different ways - CCPairFunction fab(fab_6d); - CCPairFunction ab2(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); + CCPairFunction fab(fab_6d); + CCPairFunction ab2(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); const double test1 = overlap(fab, ab2); const double test2 = bb.inner(afa); const double diff = test1 - test2; @@ -3551,8 +3523,8 @@ void CCPotentials::test() { if (fabs(diff) > hi) passed_hi = false; } { - CCPairFunction fab(fab_6d); - CCPairFunction ab2(ab_6d); + CCPairFunction fab(fab_6d); + CCPairFunction ab2(ab_6d); const double test1 = overlap(fab, ab2); const double test2 = bb.inner(afa); const double diff = test1 - test2; @@ -3563,8 +3535,8 @@ void CCPotentials::test() { if (fabs(diff) > hi) passed_hi = false; } { - CCPairFunction fab(&f12, a, b); - CCPairFunction ab2(ab_6d); + CCPairFunction fab(f12, a, b); + CCPairFunction ab2(ab_6d); const double test1 = overlap(fab, ab2); const double test2 = bb.inner(afa); const double diff = test1 - test2; @@ -3575,8 +3547,8 @@ void CCPotentials::test() { if (fabs(diff) > hi) passed_hi = false; } { - CCPairFunction fab(&f12, a, b); - CCPairFunction ab2(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); + CCPairFunction fab(f12, a, b); + CCPairFunction ab2(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); const double test1 = overlap(fab, ab2); const double test2 = bb.inner(afa); const double diff = test1 - test2; diff --git a/src/madness/chem/CCPotentials.h b/src/madness/chem/CCPotentials.h index a60380fed56..05a1fe202d7 100644 --- a/src/madness/chem/CCPotentials.h +++ b/src/madness/chem/CCPotentials.h @@ -33,7 +33,7 @@ class CCPotentials { ~CCPotentials() {}; /// forms the regularized functions from Q and Qt Ansatz for CIS(D) where tau=0 and t=mo so that Qt=Q - void test_pair_consistency(const CCPairFunction& u, const size_t i, const size_t j, const CC_vecfunction& x) const; + void test_pair_consistency(const CCPairFunction& u, const size_t i, const size_t j, const CC_vecfunction& x) const; bool test_compare_pairs(const CCPair& pair1, const CCPair& pair2) const; @@ -109,7 +109,7 @@ class CCPotentials { } /// returns a specific mo - CCFunction mo_ket(const size_t& i) const { + CCFunction mo_ket(const size_t& i) const { return mo_ket_(i); } @@ -119,7 +119,7 @@ class CCPotentials { } /// returns a specific mo multiplied with the squared nuclear correlation factor - CCFunction mo_bra(const size_t& i) const { + CCFunction mo_bra(const size_t& i) const { return mo_bra_(i); } @@ -152,7 +152,7 @@ class CCPotentials { /// makes the t intermediates /// t_i = mo_ket_(i) + tau /// i = tau.i - CCFunction make_t_intermediate(const CCFunction& tau) const; + CCFunction make_t_intermediate(const CCFunction& tau) const; private: /// Helper function to initialize the const mo_bra and ket elements adn orbital energies @@ -259,14 +259,6 @@ class CCPotentials { const std::vector& U1, const real_function_3d& U2); - /// Function evaluates the consant part of the ground state for MP2 - /// @param[out]The result is \f$ Q12(G(Q12(Vreg|titj>))) \f$ - /// @param[in] ti, first particle -> should be HOLE state - /// @param[in] tj, second particle -> should be HOLE state - /// @param[in] Gscreen pointer to bsh operator (in order to screen), has to be in modified NS form - real_function_6d - make_constant_part_mp2(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen = NULL) const; - /// Static version of make_constant_part_mp2 to be called from macrotask. static madness::real_function_6d make_constant_part_mp2_macrotask(World& world, const CCPair& pair, const std::vector& mo_ket, @@ -337,7 +329,7 @@ class CCPotentials { /// @param[in] pointer to bsh operator (in order to screen) /// @param[out] the regularization potential (unprojected), see equation above real_function_6d - apply_Vreg(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen = NULL) const; + apply_Vreg(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen = NULL) const; /// Static version of apply_Vreg to be used from a macrotask. Will eventually replace former. madness::real_function_6d @@ -359,7 +351,7 @@ class CCPotentials { /// @param[in] tj, second function in the ket ... /// @param[in] pointer to bsh operator (in order to screen) real_function_6d - apply_reduced_F(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen = NULL) const; + apply_reduced_F(const CCFunction& ti, const CCFunction& tj, const real_convolution_6d *Gscreen = NULL) const; /// Apply Ue on a tensor product of two 3d functions: Ue(1,2) |x(1)y(2)> (will be either |ij> or |\tau_i\tau_j> or mixed forms) @@ -373,7 +365,7 @@ class CCPotentials { /// @param[in] The BSH operator to screen: Has to be in NS form, Gscreen->modified == true /// @return R^-1U_eR|x,y> the transformed electronic smoothing potential applied on |x,y> : real_function_6d - apply_transformed_Ue(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; + apply_transformed_Ue(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; /// Static version of apply_transformed_Ue for the use in a macrotask. /// Will eventually replace the former. @@ -396,7 +388,7 @@ class CCPotentials { /// the f12K|xy> part will be screened with the BSH while the Kf12|xy> can not be screened with the BSH operator but maybe with the coulomb /// @return R^-1U_eR|x,y> the transformed electronic smoothing potential applied on |x,y> : real_function_6d - apply_exchange_commutator(const CCFunction& x, const CCFunction& y, + apply_exchange_commutator(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; real_function_6d @@ -408,7 +400,7 @@ class CCPotentials { /// This applies the exchange commutator, see apply_exchange_commutator function for information real_function_6d - apply_exchange_commutator1(const CCFunction& x, const CCFunction& y, + apply_exchange_commutator1(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; /// Helper Function which performs the operation \f$ \f$ @@ -417,9 +409,9 @@ class CCPotentials { /// @param[in] function a, /// @param[in] function b, double - make_xy_gf_ab(const CCFunction& x, const CCFunction& y, const CCFunction& a, const CCFunction& b) const; + make_xy_gf_ab(const CCFunction& x, const CCFunction& y, const CCFunction& a, const CCFunction& b) const; - double make_xy_ff_ab(const CCFunction& x, const CCFunction& y, const CCFunction& a, const CCFunction& b) const { + double make_xy_ff_ab(const CCFunction& x, const CCFunction& y, const CCFunction& a, const CCFunction& b) const { error("xy_ff_ab not yet implemented"); return 0.0; } @@ -433,22 +425,22 @@ class CCPotentials { /// loops over every entry in the vector and accumulates results /// helper function for CIS(D) energy double - make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, - const std::vector& u) const; + make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, + const std::vector>& u) const; /// returns for a vector of CCPairFunction /// the result is accumulated for every vercotr /// helper functions for CIS(D) energy double - make_xy_u(const CCFunction& x, const CCFunction& y, const std::vector& u) const; + make_xy_u(const CCFunction& x, const CCFunction& y, const std::vector>& u) const; /// Functions which operate with the CCPairFunction structure /// @param[in] function x, if nuclear correlation is used make sure this is the correct bra function /// @param[in] function y, if nuclear correlation is used make sure this is the correct bra function /// @param[in] CCPairFunction u, double - make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, - const CCPairFunction& u) const; + make_xy_op_u(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, + const CCPairFunction& u) const; /// Helper Function which returns /// @return @@ -457,19 +449,19 @@ class CCPotentials { /// @param[in] function a, /// @param[in] function b, double - make_xy_op_ab(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, const CCFunction& a, - const CCFunction& b) const; + make_xy_op_ab(const CCFunction& x, const CCFunction& y, const CCConvolutionOperator& op, const CCFunction& a, + const CCFunction& b) const; /// get the correct pair function as vector of CCPairFunction functions /// @param[in] The pair functions /// @param[out] The demanded pair function as vector of CCPairFunction functions (includes regularization tails) - std::vector + std::vector> get_pair_function(const Pairs& pairs, const size_t i, const size_t j) const; /// returns _2 real_function_3d - apply_s2b_operation(const CCFunction& bra, const CCPairFunction& u, const size_t particle) const; + apply_s2b_operation(const CCFunction& bra, const CCPairFunction& u, const size_t particle) const; /// dummy to avoid confusion and for convenience real_function_6d swap_particles(const real_function_6d& f) const { @@ -477,8 +469,8 @@ class CCPotentials { } /// swap the particles of the CCPairFunction and return a new vector of swapped functions - std::vector swap_particles(const std::vector& f) const { - std::vector swapped; + std::vector> swap_particles(const std::vector>& f) const { + std::vector> swapped; for (size_t i = 0; i < f.size(); i++) swapped.push_back(f[i].swap_particles()); return swapped; } @@ -487,7 +479,7 @@ class CCPotentials { /// @param[in] 6D function 1 /// @param[in] 6D function 2 double - overlap(const CCPairFunction& f1, const CCPairFunction& f2) const { + overlap(const CCPairFunction& f1, const CCPairFunction& f2) const { return inner(f1,f2,nemo_->ncf->square()); }; @@ -521,8 +513,8 @@ class CCPotentials { /// Apply the Qt projector on a CCPairFunction /// works in principle like apply_Ot - CCPairFunction - apply_Qt(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle, const double c = 1.0) const; + CCPairFunction + apply_Qt(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle, const double c = 1.0) const; /// Apply Ot projector on decomposed or op_decomposed 6D function /// The function does not work with type==pure right now (not needed) @@ -533,14 +525,14 @@ class CCPotentials { /// for CCPairFunction type == op_decomposd the function si f=op|xy> and we have for particle==1 /// \f$ a_k = t_k \f$ /// \f$ b_k = *y \f$ - CCPairFunction - apply_Ot(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle) const; + CCPairFunction + apply_Ot(const CCPairFunction& f, const CC_vecfunction& t, const size_t particle) const; /// Apply the Greens Operator to a CCPairFunction /// For CCPairFunction only type pure and type decomposed is supported /// for the op_decomposed type a pure function can be constructed (not needed therefore not implemented yet) real_function_6d - apply_G(const CCPairFunction& u, const real_convolution_6d& G) const; + apply_G(const CCPairFunction& u, const real_convolution_6d& G) const; /// Apply BSH Operator and count time real_function_6d apply_G(const real_function_6d& f, const real_convolution_6d& G) const { @@ -637,7 +629,7 @@ class CCPotentials { /// the K operator runs over ALL orbitals (also the frozen ones) real_function_3d - K(const CCFunction& f) const; + K(const CCFunction& f) const; /// static version of k above for access from macrotask. will eventually replace former. real_function_3d @@ -675,7 +667,7 @@ class CCPotentials { /// Apply the Exchange operator on a tensor product multiplied with f12 /// !!! Prefactor of (-1) is not inclued in K here !!!! real_function_6d - apply_Kf(const CCFunction& x, const CCFunction& y) const; + apply_Kf(const CCFunction& x, const CCFunction& y) const; /// Apply fK on a tensor product of two 3D functions /// fK|xy> = fK_1|xy> + fK_2|xy> @@ -683,11 +675,11 @@ class CCPotentials { /// @param[in] y, the second 3D function in |xy> structure holds index i and type (HOLE, PARTICLE, MIXED, UNDEFINED) /// @param[in] BSH operator to screen, has to be in modified NS form, Gscreen->modified()==true; real_function_6d - apply_fK(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; + apply_fK(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; /// Creates a 6D function with the correlation factor and two given CCFunctions real_function_6d - make_f_xy(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; + make_f_xy(const CCFunction& x, const CCFunction& y, const real_convolution_6d *Gscreen = NULL) const; real_function_6d static make_f_xy_macrotask( World& world, const real_function_3d& x_ket, const real_function_3d& y_ket, @@ -794,9 +786,9 @@ class CCPotentials { // update the intermediates void update_intermediates(const CC_vecfunction& t) { - g12.update_elements(mo_bra_, t); + g12->update_elements(mo_bra_, t); // g12.sanity(); - f12.update_elements(mo_bra_, t); + f12->update_elements(mo_bra_, t); // f12.sanity(); } @@ -813,7 +805,7 @@ class CCPotentials { } } -private: +protected: // member variables /// MPI World World& world; @@ -829,9 +821,9 @@ class CCPotentials { std::vector orbital_energies_; /// the coulomb operator with all intermediates public: - CCConvolutionOperator g12; + std::shared_ptr> g12; /// the f12 operator with all intermediates - CCConvolutionOperator f12; + std::shared_ptr> f12; /// the correlation factor, holds necessary regularized potentials CorrelationFactor corrfac; /// Manager for stored intermediate potentials which are s2c, s2b and the whole singles potentials without fock-residue for GS and EX state diff --git a/src/madness/chem/CCStructures.cc b/src/madness/chem/CCStructures.cc index 793962d3dcc..bf18a7aeaff 100644 --- a/src/madness/chem/CCStructures.cc +++ b/src/madness/chem/CCStructures.cc @@ -63,36 +63,12 @@ CCTimer::info(const bool debug, const double norm) { } } -void -CCFunction::info(World& world, const std::string& msg) const { - if (world.rank() == 0) { - std::cout << "Information about 3D function: " << name() << " " << msg << std::endl; - std::cout << std::setw(10) << std::setfill(' ') << std::setw(50) << " |f| : " << function.norm2() - << std::endl; - std::cout << std::setw(10) << std::setfill(' ') << std::setw(50) << " |error|: " << current_error << std::endl; - } -} - -std::string -CCFunction::name() const { - if (type == HOLE) { - return "phi" + stringify(i); - } else if (type == PARTICLE) { - return "tau" + stringify(i); - } else if (type == MIXED) { - return "t" + stringify(i); - } else if (type == RESPONSE) { - return "x" + stringify(i); - } else { - return "function" + stringify(i); - } -} madness::CC_vecfunction CC_vecfunction::copy() const { - std::vector vn; + std::vector> vn; for (auto x : functions) { - const CCFunction fn(madness::copy(x.second.function), x.second.i, x.second.type); + const CCFunction fn(madness::copy(x.second.function), x.second.i, x.second.type); vn.push_back(fn); } CC_vecfunction result(vn, type); @@ -161,7 +137,7 @@ CCIntermediatePotentials::operator()(const CC_vecfunction& f, const PotentialTyp } madness::real_function_3d -CCIntermediatePotentials::operator()(const CCFunction& f, const PotentialType& type) const { +CCIntermediatePotentials::operator()(const CCFunction& f, const PotentialType& type) const { output("Getting " + assign_name(type) + " for " + f.name()); real_function_3d result = real_factory_3d(world); if (type == POT_singles_ and (f.type == PARTICLE or f.type == MIXED)) @@ -299,12 +275,13 @@ void CCParameters::sanity_check(World& world) const { } } -real_function_3d -CCConvolutionOperator::operator()(const CCFunction& bra, const CCFunction& ket, const bool use_im) const { - real_function_3d result; +template +Function +CCConvolutionOperator::operator()(const CCFunction& bra, const CCFunction& ket, const bool use_im) const { + Function result; if (not use_im) { if (world.rank() == 0) - std::cout << "Recalculating <" << bra.name() << "|" << assign_name(operator_type) << "|" << ket.name() + std::cout << "Recalculating <" << bra.name() << "|" << name() << "|" << ket.name() << ">\n"; result = ((*op)(bra.function * ket.function)).truncate(); } else if (bra.type == HOLE and ket.type == HOLE and not imH.allpairs.empty()) result = imH(bra.i, ket.i); @@ -320,54 +297,61 @@ CCConvolutionOperator::operator()(const CCFunction& bra, const CCFunction& ket, return result; } -real_function_6d CCConvolutionOperator::operator()(const real_function_6d& u, const size_t particle) const { - MADNESS_ASSERT(particle == 1 or particle == 2); - MADNESS_ASSERT(operator_type == OT_G12); - MADNESS_ASSERT(op); +template +Function CCConvolutionOperator::operator()(const Function& u, const size_t particle) const { + MADNESS_CHECK(particle == 1 or particle == 2); + MADNESS_CHECK(op); op->particle() = particle; return (*op)(u); } -real_function_3d -CCConvolutionOperator::operator()(const CCFunction& bra, const real_function_6d& u, const size_t particle) const { - MADNESS_ASSERT(particle == 1 or particle == 2); - MADNESS_ASSERT(operator_type == OT_G12); - MADNESS_ASSERT(op); - const real_function_6d tmp = multiply(copy(u), copy(bra.function), particle); +template +Function +CCConvolutionOperator::operator()(const CCFunction& bra, const Function& u, const size_t particle) const { + MADNESS_CHECK(particle == 1 or particle == 2); + MADNESS_CHECK(op); + const Function tmp = multiply(copy(u), copy(bra.function), particle); op->particle() = particle; - const real_function_6d g_tmp = (*op)(tmp); - const real_function_3d result = g_tmp.dirac_convolution<3>(); + const Function g_tmp = (*op)(tmp); + const Function result = g_tmp.dirac_convolution(); return result; } - -void CCConvolutionOperator::update_elements(const CC_vecfunction& bra, const CC_vecfunction& ket) { - const std::string operation_name = "<" + assign_name(bra.type) + "|" + name() + "|" + assign_name(ket.type) + ">"; - if (world.rank() == 0) - std::cout << "updating operator elements: " << operation_name << " (" << bra.size() << "x" << ket.size() << ")" - << std::endl; - if (bra.type != HOLE) - error("Can not create intermediate of type " + operation_name + " , bra-element has to be of type HOLE"); - op.reset(init_op(operator_type, parameters)); - intermediateT xim; - for (auto tmpk : bra.functions) { - const CCFunction& k = tmpk.second; - for (auto tmpl : ket.functions) { - const CCFunction& l = tmpl.second; - real_function_3d kl = (bra(k).function * l.function); - real_function_3d result = ((*op)(kl)).truncate(); - result.reconstruct(); // for sparse multiplication - xim.insert(k.i, l.i, result); +template +void CCConvolutionOperator::update_elements(const CC_vecfunction& bra, const CC_vecfunction& ket) { + if constexpr (NDIM==3) { + constexpr std::size_t LDIM=NDIM/2; + const std::string operation_name = "<" + assign_name(bra.type) + "|" + name() + "|" + assign_name(ket.type) + ">"; + if (world.rank() == 0) + std::cout << "updating operator elements: " << operation_name << " (" << bra.size() << "x" << ket.size() << ")" + << std::endl; + if (bra.type != HOLE) + error("Can not create intermediate of type " + operation_name + " , bra-element has to be of type HOLE"); + op.reset(init_op(type(), parameters)); + intermediateT xim; + for (auto tmpk : bra.functions) { + const CCFunction& k = tmpk.second; + for (auto tmpl : ket.functions) { + const CCFunction& l = tmpl.second; + Function kl = (bra(k).function * l.function); + Function result = ((*op)(kl)).truncate(); + result.reconstruct(); // for sparse multiplication + xim.insert(k.i, l.i, result); + } } + if (ket.type == HOLE) imH = xim; + else if (ket.type == PARTICLE) imP = xim; + else if (ket.type == RESPONSE) imR = xim; + else error("Can not create intermediate of type <" + assign_name(bra.type) + "|op|" + assign_name(ket.type) + ">"); + } else { + std::string msg="update_elements not implemented for NDIM="+std::to_string(NDIM); + MADNESS_EXCEPTION(msg.c_str(),1); } - if (ket.type == HOLE) imH = xim; - else if (ket.type == PARTICLE) imP = xim; - else if (ket.type == RESPONSE) imR = xim; - else error("Can not create intermediate of type <" + assign_name(bra.type) + "|op|" + assign_name(ket.type) + ">"); } -void CCConvolutionOperator::clear_intermediates(const FuncType& type) { +template +void CCConvolutionOperator::clear_intermediates(const FuncType& type) { if (world.rank() == 0) std::cout << "Deleting all intermediates \n"; switch (type) { @@ -388,7 +372,8 @@ void CCConvolutionOperator::clear_intermediates(const FuncType& type) { } } -size_t CCConvolutionOperator::info() const { +template +size_t CCConvolutionOperator::info() const { const size_t size_imH = size_of(imH); const size_t size_imP = size_of(imP); const size_t size_imR = size_of(imR); @@ -404,63 +389,13 @@ size_t CCConvolutionOperator::info() const { return size_imH + size_imP + size_imR; } -SeparatedConvolution * -CCConvolutionOperator::init_op(const OpType& type, const Parameters& parameters) const { - switch (type) { - case OT_G12 : { - if (world.rank() == 0) - std::cout << "Creating " << assign_name(type) << " Operator with thresh=" << parameters.thresh_op - << " and lo=" << parameters.lo << std::endl; - return CoulombOperatorPtr(world, parameters.lo, parameters.thresh_op); - } - case OT_F12 : { - if (world.rank() == 0) - std::cout << "Creating " << assign_name(type) << " Operator with thresh=" << parameters.thresh_op - << " and lo=" << parameters.lo << " and Gamma=" << parameters.gamma << std::endl; - return SlaterF12OperatorPtr(world, parameters.gamma, parameters.lo, parameters.thresh_op); - } - case OT_SLATER : { - if (world.rank() == 0) - std::cout << "Creating " << assign_name(type) << " Operator with thresh=" << parameters.thresh_op - << " and lo=" << parameters.lo << " and Gamma=" << parameters.gamma << std::endl; - return SlaterOperatorPtr(world, parameters.gamma, parameters.lo, parameters.thresh_op); - } - case OT_BSH : { - if (world.rank() == 0) - std::cout << "Creating " << assign_name(type) << " Operator with thresh=" << parameters.thresh_op - << " and lo=" << parameters.lo << " and Gamma=" << parameters.gamma << std::endl; - return BSHOperatorPtr3D(world, parameters.gamma, parameters.lo, parameters.thresh_op); - } - case OT_ONE : { - if (world.rank() == 0) - std::cout << "Creating " << assign_name(type) << " Operator " << std::endl; - return nullptr; - } - default : { - error("Unknown operatorype " + assign_name(type)); - MADNESS_EXCEPTION("error", 1); - } - } - -} - -/// Assigns strings to enums for formated output -std::string -assign_name(const PairFormat& input) { - switch (input) { - case PT_FULL: - return "full"; - case PT_DECOMPOSED: - return "decomposed"; - case PT_OP_DECOMPOSED: - return "operator-decomposed"; - default: { - MADNESS_EXCEPTION("Unvalid enum assignement!", 1); - return "undefined"; - } - } - MADNESS_EXCEPTION("assign_name:pairtype, should not end up here", 1); - return "unknown pairtype"; +template +SeparatedConvolution * +CCConvolutionOperator::init_op(const OpType& type, const CCConvolutionOperator::Parameters& parameters) const { + bool debug=false; + bool printme=(world.rank()==0) and debug; + if (printme) print("init_op: creating",type,"with thresh, lo, gamma",parameters.thresh_op,parameters.lo,parameters.gamma); + return new SeparatedConvolution(world,OperatorInfo(parameters.gamma,parameters.lo,parameters.thresh_op,type)); } /// Assigns strings to enums for formated output @@ -480,31 +415,6 @@ assign_name(const CCState& input) { return "unknown pairtype"; } -/// Assigns strings to enums for formated output -std::string -assign_name(const OpType& input) { - switch (input) { - case OT_G12: - return "g12"; - case OT_F12: - return "f12"; - case OT_SLATER: - return "slater"; - case OT_FG12: - return "fg12"; - case OT_BSH: - return "bsh"; - case OT_ONE: - return "identity"; - default: { - MADNESS_EXCEPTION("Unvalid enum assignement!", 1); - return "undefined"; - } - } - MADNESS_EXCEPTION("assign_name:optype, should not end up here", 1); - return "unknown operatortype"; -} - /// Assigns enum to string CalcType assign_calctype(const std::string name) { @@ -616,15 +526,6 @@ assign_name(const FuncType& inp) { return "???"; } -/// Returns the size of an intermediate -double -size_of(const intermediateT& im) { - double size = 0.0; - for (const auto& tmp : im.allpairs) { - size += get_size(tmp.second); - } - return size; -} std::vector MacroTaskMp2ConstantPart::operator() (const std::vector& pair, const std::vector& mo_ket, @@ -659,6 +560,14 @@ MacroTaskMp2UpdatePair::operator() (const std::vector &pair, return result; } +template class CCConvolutionOperator; +template class CCConvolutionOperator; +template class CCConvolutionOperator; + +template class CCFunction; +template class CCFunction; +template class CCFunction; + }// end namespace madness diff --git a/src/madness/chem/CCStructures.h b/src/madness/chem/CCStructures.h index e4eb3ba4cd1..e64fb0cd47a 100644 --- a/src/madness/chem/CCStructures.h +++ b/src/madness/chem/CCStructures.h @@ -21,21 +21,9 @@ namespace madness { -/// Operatortypes used by the CCConvolutionOperator Class -enum OpType { - OT_UNDEFINED, - OT_ONE, /// indicates the identity - OT_G12, /// 1/r - OT_SLATER, /// exp(r) - OT_F12, /// 1-exp(r) - OT_FG12, /// (1-exp(r))/r - OT_F212, /// (1-exp(r))^2 - OT_BSH /// exp(r)/r -}; - /// Calculation Types used by CC2 enum CalcType { - CT_UNDEFINED, CT_MP2, CT_CC2, CT_LRCCS, CT_LRCC2, CT_CISPD, CT_ADC2, CT_TDHF, CT_TEST + CT_UNDEFINED, CT_MP2, CT_MP3, CT_CC2, CT_LRCCS, CT_LRCC2, CT_CISPD, CT_ADC2, CT_TDHF, CT_TEST }; /// Type of Pairs used by CC_Pair2 class enum CCState { @@ -62,18 +50,10 @@ enum PotentialType { POT_singles_ }; -/// Assigns strings to enums for formated output -std::string -assign_name(const PairFormat& input); - /// Assigns strings to enums for formated output std::string assign_name(const CCState& input); -/// Assigns strings to enums for formated output -std::string -assign_name(const OpType& input); - /// Assigns enum to string CalcType assign_calctype(const std::string name); @@ -230,10 +210,11 @@ struct CCParameters : public QCCalculationParametersBase { set_derived_values(); }; + void initialize_parameters() { double thresh=1.e-3; double thresh_operators=1.e-6; - initialize < std::string > ("calc_type", "mp2", "the calculation type", {"mp2", "cc2", "cis", "lrcc2", "cispd", "adc2", "test"}); + initialize < std::string > ("calc_type", "mp2", "the calculation type", {"mp2", "mp3", "cc2", "cis", "lrcc2", "cispd", "adc2", "test"}); initialize < double > ("lo", 1.e-7, "the finest length scale to be resolved by 6D operators"); initialize < double > ("dmin", 1.0, "defines the depth of the special level"); initialize < double > ("thresh_6d", thresh, "threshold for the 6D wave function"); @@ -286,6 +267,7 @@ struct CCParameters : public QCCalculationParametersBase { CalcType calc_type() const { std::string value = get("calc_type"); if (value == "mp2") return CT_MP2; + if (value == "mp3") return CT_MP3; if (value == "cc2") return CT_CC2; if (value == "cis") return CT_LRCCS; if (value == "lrcc2") return CT_LRCC2; @@ -475,8 +457,10 @@ struct Pairs { /// getter // at instead of [] operator bc [] inserts new element if nothing is found while at throws out of range error + // back to before T& operator()(int i, int j) { - return allpairs.at(std::make_pair(i, j)); + // return allpairs.at(std::make_pair(i, j)); + return allpairs[std::make_pair(i, j)]; } /// setter @@ -498,11 +482,22 @@ struct Pairs { }; /// f12 and g12 intermediates of the form (with op=f12 or op=g12) will be saved using the pair structure -typedef Pairs intermediateT; +template +using intermediateT = Pairs>; /// Returns the size of an intermediate +//double +//size_of(const intermediateT& im); +/// Returns the size of an intermediate +template double -size_of(const intermediateT& im); +size_of(const intermediateT& im) { + double size = 0.0; + for (const auto& tmp : im.allpairs) { + size += get_size(tmp.second); + } + return size; +} @@ -516,12 +511,12 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { CC_vecfunction(const vector_real_function_3d& v) : type(UNDEFINED), omega(0.0), current_error(99.9), delta(0.0) { for (size_t i = 0; i < v.size(); i++) { - CCFunction tmp(v[i], i, type); + CCFunction tmp(v[i], i, type); functions.insert(std::make_pair(i, tmp)); } } - CC_vecfunction(const std::vector& v) : type(UNDEFINED), omega(0.0), current_error(99.9), delta(0.0) { + CC_vecfunction(const std::vector>& v) : type(UNDEFINED), omega(0.0), current_error(99.9), delta(0.0) { for (size_t i = 0; i < v.size(); i++) { functions.insert(std::make_pair(v[i].i, v[i])); } @@ -530,7 +525,7 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { CC_vecfunction(const vector_real_function_3d& v, const FuncType& type) : type(type), omega(0.0), current_error(99.9), delta(0.0) { for (size_t i = 0; i < v.size(); i++) { - CCFunction tmp(v[i], i, type); + CCFunction tmp(v[i], i, type); functions.insert(std::make_pair(i, tmp)); } } @@ -540,12 +535,12 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { current_error(99.9), delta(0.0) { for (size_t i = 0; i < v.size(); i++) { - CCFunction tmp(v[i], freeze + i, type); + CCFunction tmp(v[i], freeze + i, type); functions.insert(std::make_pair(freeze + i, tmp)); } } - CC_vecfunction(const std::vector& v, const FuncType type_) + CC_vecfunction(const std::vector>& v, const FuncType type_) : type(type_), omega(0.0), current_error(99.9), delta(0.0) { for (auto x:v) functions.insert(std::make_pair(x.i, x)); } @@ -589,7 +584,7 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { template void serialize(const Archive& ar) { - typedef std::vector> CC_functionvec; + typedef std::vector>> CC_functionvec; auto map2vector = [] (const CC_functionmap& map) { return CC_functionvec(map.begin(), map.end()); @@ -613,7 +608,7 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { } } - typedef std::map CC_functionmap; + typedef std::map> CC_functionmap; CC_functionmap functions; FuncType type; @@ -630,27 +625,27 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { } /// getter - const CCFunction& operator()(const CCFunction& i) const { + const CCFunction& operator()(const CCFunction& i) const { return functions.find(i.i)->second; } /// getter - const CCFunction& operator()(const size_t& i) const { + const CCFunction& operator()(const size_t& i) const { return functions.find(i)->second; } /// getter - CCFunction& operator()(const CCFunction& i) { + CCFunction& operator()(const CCFunction& i) { return functions[i.i]; } /// getter - CCFunction& operator()(const size_t& i) { + CCFunction& operator()(const size_t& i) { return functions[i]; } /// setter - void insert(const size_t& i, const CCFunction& f) { + void insert(const size_t& i, const CCFunction& f) { functions.insert(std::make_pair(i, f)); } @@ -658,7 +653,7 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { void set_functions(const vector_real_function_3d& v, const FuncType& type, const size_t& freeze) { functions.clear(); for (size_t i = 0; i < v.size(); i++) { - CCFunction tmp(v[i], freeze + i, type); + CCFunction tmp(v[i], freeze + i, type); functions.insert(std::make_pair(freeze + i, tmp)); } } @@ -711,6 +706,7 @@ struct CC_vecfunction : public archive::ParallelSerializableObject { /// The structure can hold intermediates for g12 and f12 of type : with type=HOLE,PARTICLE or RESPONSE /// some 6D operations are also included /// The structure does not know if nuclear correlation facors are used, so the corresponding bra states have to be prepared beforehand +template struct CCConvolutionOperator { /// parameter class @@ -731,6 +727,11 @@ struct CCConvolutionOperator { double lo = 1.e-6; int freeze = 0; double gamma = 1.0; /// f12 exponent + + template + void serialize(archiveT& ar) { + ar & thresh_op & lo & freeze & gamma; + } }; @@ -738,75 +739,35 @@ struct CCConvolutionOperator { /// @param[in] optype: the operatortype (can be g12_ or f12_) /// @param[in] param: the parameters of the current CC-Calculation (including function and operator thresholds and the exponent for f12) CCConvolutionOperator(World& world, const OpType type, Parameters param) : parameters(param), world(world), - operator_type(type), - op(init_op(operator_type, parameters)) { + op(init_op(type, parameters)) { } CCConvolutionOperator(const CCConvolutionOperator& other) = default; - friend bool can_combine(const CCConvolutionOperator& left, const CCConvolutionOperator& right) { - return (combine_OT(left,right).first!=OT_UNDEFINED); +protected: + + friend CCConvolutionOperator combine(const CCConvolutionOperator& a, const CCConvolutionOperator& b) { + auto info= SeparatedConvolution::combine_OT((*a.get_op()),(*b.get_op())); + Parameters param; + param.gamma=info.mu; + param.thresh_op=info.thresh; + param.lo=info.lo; + param.freeze=a.parameters.freeze; + return CCConvolutionOperator(a.world, info.type, param); } - friend std::pair combine_OT(const CCConvolutionOperator& left, const CCConvolutionOperator& right) { - OpType type=OT_UNDEFINED; - Parameters param=left.parameters; - if ((left.type()==OT_F12) and (right.type()==OT_G12)) { - type=OT_FG12; - } - if ((left.type()==OT_G12) and (right.type()==OT_F12)) { - type=OT_FG12; - param.gamma=right.parameters.gamma; - } - if ((left.type()==OT_F12) and (right.type()==OT_F12)) { - type=OT_F212; - // keep the original gamma - // (f12)^2 = (1- slater12)^2 = 1/(4 gamma) (1 - 2 exp(-gamma) + exp(-2 gamma)) - MADNESS_CHECK(right.parameters.gamma == left.parameters.gamma); - } - return std::make_pair(type,param); - } - - - /// combine 2 convolution operators to one - - /// @return a vector of pairs: factor and convolution operator - friend std::vector> combine(const CCConvolutionOperator& left, const CCConvolutionOperator& right) { - MADNESS_CHECK(can_combine(left,right)); - MADNESS_CHECK(left.world.id()==right.world.id()); - auto [type,param]=combine_OT(left,right); - std::vector> result; - if (type==OT_FG12) { - // fg = (1 - exp(-gamma r12)) / r12 = 1/r12 - exp(-gamma r12)/r12 = coulomb - bsh - - // coulombfit return 1/r - // we need 1/(2 gamma) 1/r - result.push_back(std::make_pair(1.0/(2.0*param.gamma),CCConvolutionOperator(left.world, OT_G12, param))); - - // bshfit returns 1/(4 pi) exp(-gamma r)/r - // we need 1/(2 gamma) exp(-gamma r)/r - const double factor = 4.0 * constants::pi /(2.0*param.gamma); - result.push_back(std::make_pair(-factor,CCConvolutionOperator(left.world, OT_BSH, param))); - } else if (type==OT_F212) { -// we use the slater operator which is S = e^(-y*r12), y=gamma -// the f12 operator is: 1/2y*(1-e^(-y*r12)) = 1/2y*(1-S) -// so the squared f12 operator is: f*f = 1/(4*y*y)(1-2S+S*S), S*S = S(2y) = e(-2y*r12) -// we have then: = 1/(4*y*y)*( - 2* + ) -// we have then: =( - 1/(4*y*y)*2* - MADNESS_CHECK(left.parameters.gamma==right.parameters.gamma); - const double prefactor = 1.0 / (4.0 * param.gamma); // Slater has no 1/(2 gamma) per se. - Parameters param2=param; - param2.gamma*=2.0; - result.push_back(std::make_pair(1.0*prefactor,CCConvolutionOperator(left.world, OT_ONE, param))); - result.push_back(std::make_pair(-2.0*prefactor,CCConvolutionOperator(left.world, OT_SLATER, left.parameters))); - result.push_back(std::make_pair(1.0*prefactor,CCConvolutionOperator(left.world, OT_SLATER, param2))); - } - return result; + friend std::shared_ptr combine(const std::shared_ptr& a, + const std::shared_ptr& b) { + if (a and (not b)) return a; + if ((not a) and b) return b; + if ((not a) and (not b)) return nullptr; + return std::shared_ptr(new CCConvolutionOperator(combine(*a,*b))); } +public: /// @param[in] f: a 3D function /// @param[out] the convolution op(f), no intermediates are used - real_function_3d operator()(const real_function_3d& f) const { + Function operator()(const Function& f) const { if (op) return ((*op)(f)).truncate(); return f; } @@ -814,46 +775,51 @@ struct CCConvolutionOperator { /// @param[in] bra a CC_vecfunction /// @param[in] ket a CC_function /// @param[out] vector[i] = - vector_real_function_3d operator()(const CC_vecfunction& bra, const CCFunction& ket) const { + std::vector> operator()(const CC_vecfunction& bra, const CCFunction& ket) const { MADNESS_CHECK(op); - vector_real_function_3d result; - if (bra.type == HOLE) { - for (const auto& ktmp:bra.functions) { - const CCFunction& brai = ktmp.second; - const real_function_3d tmpi = this->operator()(brai, ket); - result.push_back(tmpi); + std::vector> result; + if constexpr (NDIM == 3) { + if (bra.type == HOLE) { + for (const auto& ktmp: bra.functions) { + const CCFunction& brai = ktmp.second; + const Function tmpi = this->operator()(brai, ket); + result.push_back(tmpi); + } + } else { + std::vector> tmp = mul(world, ket.function, bra.get_vecfunction()); + result = apply(world, (*op), tmp); + truncate(world, result); } } else { - vector_real_function_3d tmp = mul(world, ket.function, bra.get_vecfunction()); - result = apply(world, (*op), tmp); - truncate(world, result); + MADNESS_EXCEPTION("not implemented", 1); } + return result; } // @param[in] f: a vector of 3D functions // @param[out] the convolution of op with each function, no intermeditates are used - vector_real_function_3d operator()(const vector_real_function_3d& f) const { - if (op) return apply(world, (*op), f); - return f; + std::vector> operator()(const std::vector>& f) const { + MADNESS_CHECK(op); + return apply(world, (*op), f); } // @param[in] bra: a 3D CC_function, if nuclear-correlation factors are used they have to be applied before // @param[in] ket: a 3D CC_function, // @param[in] use_im: default is true, if false then no intermediates are used // @param[out] the convolution = op(bra*ket), if intermediates were calculated before the operator uses them - real_function_3d operator()(const CCFunction& bra, const CCFunction& ket, const bool use_im = true) const; + Function operator()(const CCFunction& bra, const CCFunction& ket, const bool use_im = true) const; // @param[in] u: a 6D-function // @param[out] the convolution \int g(r,r') u(r,r') dr' (if particle==2) and g(r,r') u(r',r) dr' (if particle==1) // @param[in] particle: specifies on which particle of u the operator will act (particle ==1 or particle==2) - real_function_6d operator()(const real_function_6d& u, const size_t particle) const; + Function operator()(const Function& u, const size_t particle) const; // @param[in] bra: a 3D-CC_function, if nuclear-correlation factors are used they have to be applied before // @param[in] u: a 6D-function // @param[in] particle: specifies on which particle of u the operator will act (particle ==1 or particle==2) // @param[out] the convolution _particle - real_function_3d operator()(const CCFunction& bra, const real_function_6d& u, const size_t particle) const; + Function operator()(const CCFunction& bra, const Function& u, const size_t particle) const; /// @param[in] bra: a vector of CC_functions, the type has to be HOLE /// @param[in] ket: a vector of CC_functions, the type can be HOLE,PARTICLE,RESPONSE @@ -861,7 +827,11 @@ struct CCConvolutionOperator { void update_elements(const CC_vecfunction& bra, const CC_vecfunction& ket); /// @param[out] prints the name of the operator (convenience) which is g12 or f12 or maybe other things like gf in the future - std::string name() const { return assign_name(operator_type); } + std::string name() const { + std::stringstream ss; + ss << type(); + return ss.str(); + } /// @param[in] the type of which intermediates will be deleted /// e.g if(type==HOLE) then all intermediates of type will be deleted @@ -870,6 +840,16 @@ struct CCConvolutionOperator { /// prints out information (operatorname, number of stored intermediates ...) size_t info() const; + friend hashT hash_value(CCConvolutionOperator& op) { + hashT h; + hash_combine(h, op.parameters.thresh_op); + hash_combine(h, op.parameters.lo); + hash_combine(h, op.parameters.freeze); + hash_combine(h, op.parameters.gamma); + hash_combine(h, int(op.type())); + return h; + } + /// sanity check .. doens not do so much void sanity() const { print_intermediate(HOLE); } @@ -877,59 +857,63 @@ struct CCConvolutionOperator { void print_intermediate(const FuncType type) const { if (type == HOLE) for (const auto& tmp:imH.allpairs) - tmp.second.print_size(" intermediate"); else if (type == PARTICLE) for (const auto& tmp:imP.allpairs) - tmp.second.print_size(" intermediate"); else if (type == RESPONSE) for (const auto& tmp:imR.allpairs) - tmp.second.print_size(" intermediate"); } /// create a TwoElectronFactory with the operatorkernel - TwoElectronFactory get_kernel() const { - if (type() == OT_G12) return TwoElectronFactory(world).dcut(1.e-7); - else if (type() == OT_F12) return TwoElectronFactory(world).dcut(1.e-7).f12().gamma(parameters.gamma); - else if (type() == OT_FG12) return TwoElectronFactory(world).dcut(1.e-7).BSH().gamma(parameters.gamma); - else error("no kernel of type " + name() + " implemented"); - return TwoElectronFactory(world); + TwoElectronFactory get_kernel() const { + auto factory=TwoElectronFactory(world); + factory.set_info(op->info); + return factory; } - OpType type() const { return operator_type; } + OpType type() const { return get_op()->info.type; } const Parameters parameters; - std::shared_ptr get_op() const {return op;}; + std::shared_ptr> get_op() const {return op;}; private: /// the world World& world; - /// the operatortype, currently this can be g12_ or f12_ - const OpType operator_type = OT_UNDEFINED; /// @param[in] optype: can be f12_ or g12_ depending on which operator shall be intitialzied /// @param[in] parameters: parameters (thresholds etc) /// initializes the operators - SeparatedConvolution *init_op(const OpType& type, const Parameters& parameters) const; + SeparatedConvolution *init_op(const OpType& type, const Parameters& parameters) const; - std::shared_ptr op; - intermediateT imH; - intermediateT imP; - intermediateT imR; + std::shared_ptr> op; + intermediateT imH; + intermediateT imP; + intermediateT imR; /// @param[in] msg: output message /// the function will throw an MADNESS_EXCEPTION void error(const std::string& msg) const { if (world.rank() == 0) - std::cout << "\n\n!!!!ERROR in CCConvolutionOperator " << assign_name(operator_type) << ": " << msg + std::cout << "\n\n!!!!ERROR in CCConvolutionOperator " << name() << ": " << msg << "!!!!!\n\n" << std::endl; MADNESS_EXCEPTION(msg.c_str(), 1); } +public: }; +template +std::shared_ptr> CCConvolutionOperatorPtr(World& world, const OpType type, + typename CCConvolutionOperator::Parameters param) { + return std::shared_ptr>(new CCConvolutionOperator(world,type,param)); +} + + class CCPair : public archive::ParallelSerializableObject { public: CCPair(){}; @@ -937,7 +921,7 @@ class CCPair : public archive::ParallelSerializableObject { CCPair(const size_t ii, const size_t jj, const CCState t, const CalcType c) : type(t), ctype(c), i(ii), j(jj), bsh_eps(12345.6789) {}; - CCPair(const size_t ii, const size_t jj, const CCState t, const CalcType c, const std::vector& f) + CCPair(const size_t ii, const size_t jj, const CCState t, const CalcType c, const std::vector>& f) : type(t), ctype(c), i(ii), j(jj), functions(f), bsh_eps(12345.6789) {}; CCPair(const CCPair& other) : type(other.type), ctype(other.ctype), i(other.i), j(other.j), @@ -1016,7 +1000,7 @@ class CCPair : public archive::ParallelSerializableObject { } /// the functions which belong to the pair - std::vector functions; + std::vector> functions; /// the constant part real_function_6d constant_part; @@ -1047,8 +1031,8 @@ struct CCIntermediatePotentials { operator()(const CC_vecfunction& f, const PotentialType& type) const; /// fetch the potential for a single function - real_function_3d - operator()(const CCFunction& f, const PotentialType& type) const; + Function + operator()(const CCFunction& f, const PotentialType& type) const; /// deltes all stored potentials void clear_all() { @@ -1117,9 +1101,9 @@ class MacroTaskMp2ConstantPart : public MacroTaskOperationBase { public: MacroTaskMp2ConstantPart(){partitioner.reset(new ConstantPartPartitioner());} - typedef std::tuple&, const std::vector&, - const std::vector&, const CCParameters&, const real_function_3d&, - const std::vector&, const std::vector& > argtupleT; + typedef std::tuple&, const std::vector>&, + const std::vector>&, const CCParameters&, const Function&, + const std::vector>&, const std::vector& > argtupleT; using resultT = std::vector; @@ -1129,9 +1113,9 @@ class MacroTaskMp2ConstantPart : public MacroTaskOperationBase { return result; } - resultT operator() (const std::vector& pair, const std::vector& mo_ket, - const std::vector& mo_bra, const CCParameters& parameters, - const real_function_3d& Rsquare, const std::vector& U1, + resultT operator() (const std::vector& pair, const std::vector>& mo_ket, + const std::vector>& mo_bra, const CCParameters& parameters, + const Function& Rsquare, const std::vector>& U1, const std::vector& argument) const; }; @@ -1158,8 +1142,8 @@ class MacroTaskMp2UpdatePair : public MacroTaskOperationBase { typedef std::tuple&, const std::vector&, const CCParameters&, const std::vector< madness::Vector >&, - const std::vector&, const std::vector&, - const std::vector&, const real_function_3d&> argtupleT; + const std::vector>&, const std::vector>&, + const std::vector>&, const Function&> argtupleT; using resultT = std::vector; @@ -1171,8 +1155,8 @@ class MacroTaskMp2UpdatePair : public MacroTaskOperationBase { resultT operator() (const std::vector& pair, const std::vector& mp2_coupling, const CCParameters& parameters, const std::vector< madness::Vector >& all_coords_vec, - const std::vector& mo_ket, const std::vector& mo_bra, - const std::vector& U1, const real_function_3d& U2) const; + const std::vector>& mo_ket, const std::vector>& mo_bra, + const std::vector>& U1, const Function& U2) const; }; }//namespace madness diff --git a/src/madness/chem/CMakeLists.txt b/src/madness/chem/CMakeLists.txt index 14b375a22f1..bf05ce39b9b 100644 --- a/src/madness/chem/CMakeLists.txt +++ b/src/madness/chem/CMakeLists.txt @@ -10,6 +10,7 @@ set(MADCHEM_HEADERS BSHApply.h CalculationParameters.h CC2.h + mp3.h ccpairfunction.h CCPotentials.h CCStructures.h @@ -24,6 +25,7 @@ set(MADCHEM_HEADERS gth_pseudopotential.h GuessFactory.h localizer.h + lowrankfunction.h masks_and_boxes.h molecularbasis.h molecular_functors.h @@ -62,6 +64,7 @@ set(MADCHEM_SOURCES AC.cc atomutil.cc CC2.cc + mp3.cc ccpairfunction.cc CCPotentials.cc CCStructures.cc @@ -141,8 +144,8 @@ if(BUILD_TESTING) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # The list of unit test source files set(CHEM_TEST_SOURCES_SHORT test_pointgroupsymmetry.cc test_masks_and_boxes.cc - test_qc.cc test_MolecularOrbitals.cc test_BSHApply.cc test_projector.cc) - set(CHEM_TEST_SOURCES_LONG test_localizer.cc test_ccpairfunction.cc) + test_qc.cc test_MolecularOrbitals.cc test_BSHApply.cc test_projector.cc) + set(CHEM_TEST_SOURCES_LONG test_localizer.cc test_ccpairfunction.cc test_low_rank_function.cc) if (LIBXC_FOUND) list(APPEND CHEM_TEST_SOURCES_SHORT test_dft.cc ) list(APPEND CHEM_TEST_SOURCES_LONG test_SCFOperators.cc) diff --git a/src/madness/chem/PNOF12Potentials.cpp b/src/madness/chem/PNOF12Potentials.cpp index d59d8a9ea15..99fc2bdcfc5 100644 --- a/src/madness/chem/PNOF12Potentials.cpp +++ b/src/madness/chem/PNOF12Potentials.cpp @@ -81,8 +81,6 @@ F12Potentials::F12Potentials(World& world,const Nemo& nemo, const BasisFunctions fop = std::shared_ptr < real_convolution_3d > (SlaterF12OperatorPtr(world, param.gamma(), lo, eps)); slaterop = std::shared_ptr < real_convolution_3d > (SlaterF12OperatorPtr(world, param.gamma(), lo, eps)); slaterop_sq = std::shared_ptr < real_convolution_3d > (SlaterF12OperatorPtr(world, param.gamma() * 2.0, lo, eps)); - slaterop->is_slaterf12 = false; - slaterop_sq->is_slaterf12 = false; fop = std::shared_ptr < real_convolution_3d > (SlaterF12OperatorPtr(world, param.gamma(), lo, eps)); //test diff --git a/src/madness/chem/SCF.cc b/src/madness/chem/SCF.cc index 4c2ed155da2..dbd3c68e0db 100644 --- a/src/madness/chem/SCF.cc +++ b/src/madness/chem/SCF.cc @@ -157,33 +157,35 @@ tensorT Q2(const tensorT& s) { void SCF::output_calc_info_schema() const { nlohmann::json j = {}; - vec_pair_ints int_vals; - vec_pair_T double_vals; - vec_pair_tensor_T double_tensor_vals; - - - int_vals.emplace_back("calcinfo_nmo", param.nmo_alpha() + param.nmo_beta()); - int_vals.emplace_back("calcinfo_nalpha", param.nalpha()); - int_vals.emplace_back("calcinfo_nbeta", param.nbeta()); - int_vals.emplace_back("calcinfo_natom", molecule.natom()); - int_vals.emplace_back("k", FunctionDefaults<3>::get_k()); - - to_json(j, int_vals); -// double_vals.push_back({"return_energy", value(molecule.get_all_coords().flat())}); - double_vals.emplace_back("return_energy", current_energy); - to_json(j, double_vals); - double_tensor_vals.emplace_back("scf_eigenvalues_a", aeps); - if (param.nbeta() != 0 && !param.spin_restricted()) { - double_tensor_vals.emplace_back("scf_eigenvalues_b", beps); - } + World& world=amo.front().world(); + if (world.rank()==0) { + vec_pair_ints int_vals; + vec_pair_T double_vals; + vec_pair_tensor_T double_tensor_vals; + + + int_vals.emplace_back("calcinfo_nmo", param.nmo_alpha() + param.nmo_beta()); + int_vals.emplace_back("calcinfo_nalpha", param.nalpha()); + int_vals.emplace_back("calcinfo_nbeta", param.nbeta()); + int_vals.emplace_back("calcinfo_natom", molecule.natom()); + int_vals.emplace_back("k", FunctionDefaults<3>::get_k()); + + to_json(j, int_vals); + // double_vals.push_back({"return_energy", value(molecule.get_all_coords().flat())}); + double_vals.emplace_back("return_energy", current_energy); + to_json(j, double_vals); + double_tensor_vals.emplace_back("scf_eigenvalues_a", aeps); + if (param.nbeta() != 0 && !param.spin_restricted()) { + double_tensor_vals.emplace_back("scf_eigenvalues_b", beps); + } - to_json(j, double_tensor_vals); - param.to_json(j); - e_data.to_json(j); + to_json(j, double_tensor_vals); + param.to_json(j); + e_data.to_json(j); -// output_schema(param.prefix()+".calc_info", j); - World& world=amo.front().world(); - if (world.rank()==0) update_schema(param.prefix()+".calc_info", j); + // output_schema(param.prefix()+".calc_info", j); + update_schema(param.prefix()+".calc_info", j); + } } void scf_data::add_data(std::map values) { diff --git a/src/madness/chem/TDHF.cc b/src/madness/chem/TDHF.cc index c892840ea3e..72b89e28f73 100644 --- a/src/madness/chem/TDHF.cc +++ b/src/madness/chem/TDHF.cc @@ -69,7 +69,7 @@ void TDHF::initialize() { msg.section("Initialize TDHF Class"); msg.debug = parameters.debug(); - g12=std::make_shared(world, OT_G12, parameters.get_ccc_parameters(get_calcparam().lo())); + g12=std::make_shared>(world, OpType::OT_G12, parameters.get_ccc_parameters(get_calcparam().lo())); const double old_thresh = FunctionDefaults<3>::get_thresh(); if (old_thresh > parameters.thresh() * 0.1 and old_thresh > 1.e-5) { diff --git a/src/madness/chem/TDHF.h b/src/madness/chem/TDHF.h index 107a2b5de2b..ec98cdb2fcf 100644 --- a/src/madness/chem/TDHF.h +++ b/src/madness/chem/TDHF.h @@ -196,8 +196,8 @@ class TDHF : public QCPropertyInterface { std::size_t guess_maxiter() const { return get("guess_maxiter"); } /// make parameters for convolution operator - typename CCConvolutionOperator::Parameters get_ccc_parameters(const double lo) const { - typename CCConvolutionOperator::Parameters result; + typename CCConvolutionOperator::Parameters get_ccc_parameters(const double lo) const { + typename CCConvolutionOperator::Parameters result; result.freeze = freeze(); result.lo = lo; result.thresh_op = thresh(); @@ -514,7 +514,7 @@ class TDHF : public QCPropertyInterface { TDHFParameters parameters; /// Operator Structure which can handle intermediates (use for exchange with GS orbitals) /// Can be replaced by another potential manager - std::shared_ptr g12; + std::shared_ptr> g12; /// MO bra and ket CC_vecfunction mo_ket_; CC_vecfunction mo_bra_; diff --git a/src/madness/chem/ccpairfunction.cc b/src/madness/chem/ccpairfunction.cc index 23029a31b12..91f186e4ad2 100644 --- a/src/madness/chem/ccpairfunction.cc +++ b/src/madness/chem/ccpairfunction.cc @@ -5,55 +5,296 @@ #include #include #include +#include +#include using namespace madness; namespace madness { - -madness::CCPairFunction -CCPairFunction::invert_sign() { +template +madness::CCPairFunction +CCPairFunction::invert_sign() { (*this)*=(-1.0); return *this; } +template +bool CCPairFunction::is_convertible_to_pure_no_op() const { + if (has_operator()) { + const auto type=get_operator().type(); + if (not (type==OpType::OT_SLATER or type==OpType::OT_F12)) return false; + } + return true; +}; + + +template +void CCPairFunction::convert_to_pure_no_op_inplace() { + pureT result; + if (is_pure_no_op()) { + return; + } else if (is_pure()) { + result= CompositeFactory(world()) + .g12(get_operator().get_kernel()) + .ket(get_function()); + } else if (is_decomposed()) { + result= CompositeFactory(world()) + .g12(get_operator().get_kernel()) + .particle1(get_a()) + .particle2(get_b()); + } + result.fill_tree(); + result.truncate(FunctionDefaults::get_thresh()*0.1); + component.reset(new TwoBodyFunctionPureComponent(result)); +}; + +template +std::vector> CCPairFunction::op_pure_to_pure(const std::vector>& other) { + World& world=other.front().world(); + std::vector> result; + Function pure=FunctionFactory(world); + for (const auto& c : other) { + if (c.is_pure_no_op()) { + pure+=c.get_function(); + } else if (c.is_op_pure()) { + Function tmp=CompositeFactory(world).g12(c.get_operator().get_kernel()).ket(c.get_function()); + tmp.fill_tree(); + pure+=tmp; + } else if (c.is_decomposed()) { + result.push_back(c); + } + } + if (pure.is_initialized()) { + pure.truncate(FunctionDefaults::get_thresh()*0.1); + result.push_back(CCPairFunction(pure)); + } + return result; +} + +/// turn decomposed functions with operator into decomposed functions using LowRankFunction +template +std::vector> CCPairFunction::op_dec_to_dec(const std::vector>& other, + const std::vector::LDIM>>& centers) { + LowRankFunctionParameters lrparameters; + lrparameters.set_derived_value("tol",1.e-10); + auto builder = LowRankFunctionFactory(lrparameters,centers); +// builder.set_volume_element(3.e-2); + if (other.front().world().rank()==0) { + builder.parameters.print("lrparameters"); + print("centers",centers); + } + std::vector> result; + for (const auto& c : other) { + if (c.is_op_decomposed()) { + LRFunctorF12 functor(c.get_operator_ptr()->get_op(),c.get_a(),c.get_b()); + LowRankFunction tmp=builder.project(functor); +// double l2error=tmp.l2error(functor); + tmp.optimize(functor); + result.push_back(CCPairFunction(tmp.get_g(),tmp.get_h())); + } else { + result.push_back(c); + } + } + return result; +} + +/// turn decomposed functions with operator into pure functions +template +std::vector> CCPairFunction::op_dec_to_pure(const std::vector>& other) { + LowRankFunctionParameters lrparameters; + std::vector> result; + for (const auto& c : other) { + if (c.is_op_decomposed()) { + CCPairFunction tmp=copy(c); + tmp.convert_to_pure_no_op_inplace(); + result.push_back(tmp); + } else { + result.push_back(c); + } + } + return result; + +} + +/// turn decomposed functions with operator into decomposed functions using LowRankFunction +template +std::vector> CCPairFunction::remove_linearly_dependent_terms(const std::vector>& other, + double thresh) { + if (thresh<0.0) thresh=FunctionDefaults<3>::get_thresh()*0.1; + std::vector> result; + for (const auto& c : other) { + if (c.is_pure()) result.push_back(c); + else if (c.is_decomposed()) { + + LowRankFunction lrf(c.get_a(),c.get_b(),thresh,"canonical"); + lrf.reorthonormalize(); + result.push_back(CCPairFunction(c.get_operator_ptr(),lrf.get_g(),lrf.get_h())); + + } else { + MADNESS_EXCEPTION("you should not be here",1); + } + } + + return result; +} + +/// collect all terms with of similiar type: pure, op_pure, decomposed, op_decomposed +template +std::vector> CCPairFunction::collect_same_types(const std::vector>& other) { + + if (other.size()==0) return other; + if (is_collected(other)) return other; + + World& world=other.front().world(); + + /// vector includes OT_ONE, meaning no operator + std::vector>> op_pure(OT_SIZE); + std::vector>> op_decomposed_a(OT_SIZE); + std::vector>> op_decomposed_b(OT_SIZE); + std::vector>> ops(OT_SIZE); + + // collect terms of the same type + for (const auto& c : other) { + int iop= (c.has_operator()) ? int(c.get_operator().type()) : OT_ONE; + ops[iop]=c.get_operator_ptr(); + if (c.is_decomposed()) { + op_decomposed_a[iop]=append(op_decomposed_a[iop],c.get_a()); + op_decomposed_b[iop]=append(op_decomposed_b[iop],c.get_b()); + } else if (c.is_pure()) { + op_pure[iop].push_back(c.get_function()); + } + } + + std::vector> result; + + // accumulate all terms of the same type + for (int opint=OT_ONE; opint0) { + auto op=ops[opint]; + if (op_pure[opint].size()>1) { + // Function tmp=CompositeFactory(world).ket(op_pure[opint]); + // tmp.fill_tree(); + Tensor c(op_pure[opint].size(),1); + c=1.0; + Function tmp=transform_reconstructed(world, op_pure[opint],c,true)[0]; + result.push_back(CCPairFunction(op,tmp)); + } else { + MADNESS_CHECK_THROW(op_pure[opint].size()==1,"op_pure[opint].size()!=1"); + result.push_back(CCPairFunction(op,copy(op_pure[opint].front()))); + } + } + if (op_decomposed_a[opint].size()>0) { + result.push_back(CCPairFunction(ops[opint],op_decomposed_a[opint],op_decomposed_b[opint])); + } + } + return result; + +} +template +bool CCPairFunction::is_collected(const std::vector>& other) { + + // simply count the occurence of each term + std::map counter; + for (const auto& c : other) { + int index=0; + if (c.has_operator()) index=int(c.get_operator().type()) *100; + if (c.is_op_pure()) index+=1; + if (c.is_op_decomposed()) index+=2; + if (c.is_pure_no_op()) index+=3; + if (c.is_decomposed_no_op()) index+=4; + counter[index]++; + } + for (const auto& c : counter) if (c.second>1) return false; + return true; +} + +template +std::vector> CCPairFunction::consolidate(const std::vector>& other, + const std::vector& options, + const std::vector::LDIM>>& centers) const { + + // return only one term of a hi-dim function + bool one_term=find(options.begin(),options.end(),"one_term")!=options.end(); + // convert op_pure functions to pure + bool op_pure_to_pure=find(options.begin(),options.end(),"op_pure_to_pure")!=options.end(); + // convert op_dec functions to dec (via LowRankFunctions + bool op_dec_to_dec=find(options.begin(),options.end(),"op_dec_to_dec")!=options.end(); + // convert op_dec functions to pure (via fill_tree) + bool op_dec_to_pure=find(options.begin(),options.end(),"op_dec_to_pure")!=options.end(); + // reorthogonalize decomposed functions and op_decomposed functions + bool lindep=find(options.begin(),options.end(),"remove_lindep")!=options.end(); + + // always collect all terms of the same type + auto result= is_collected(other) ? other : collect_same_types(other); + if (lindep) result=CCPairFunction::remove_linearly_dependent_terms(result); + + if (op_dec_to_dec) result=CCPairFunction::op_dec_to_dec(result,centers); + if (op_dec_to_pure) result=CCPairFunction::op_dec_to_pure(result); + if (op_pure_to_pure) result=CCPairFunction::op_pure_to_pure(result); + + if (not is_collected(result)) result=collect_same_types(result); + + + return result; +} + +/// multiplication with a 2-particle function +template +CCPairFunction& CCPairFunction::multiply_with_op_inplace(const std::shared_ptr::LDIM>> op) { + if (has_operator()) { + auto newop=combine(get_operator_ptr(),op); + reset_operator(newop); + } else { + reset_operator(op); + } + return *this; +} + +template double -CCPairFunction::make_xy_u(const CCFunction& xx, const CCFunction& yy) const { - double result = 0.0; +CCPairFunction::make_xy_u(const CCFunction& xx, const CCFunction& yy) const { + T result = 0.0; if (is_pure()) { World& world=xx.function.world(); - real_function_6d ij = CompositeFactory(world).particle1(madness::copy(xx.function)) + Function ij = CompositeFactory(world).particle1(madness::copy(xx.function)) .particle2( madness::copy(yy.function)); result = inner(pure().get_function(), ij); } else if (is_decomposed_no_op()) { for (size_t i = 0; i < get_a().size(); i++) result += (xx.function.inner(get_a()[i])) * (yy.function.inner(get_b()[i])); } else if (is_op_decomposed()) { - const CCConvolutionOperator& op=*decomposed().get_operator_ptr(); + const CCConvolutionOperator& op=*decomposed().get_operator_ptr(); result = yy.function.inner(op(xx, get_a()[0]) * get_b()[0]); } return result; } -real_function_3d CCPairFunction::project_out(const CCFunction& f, const size_t particle) const { +template +Function::LDIM> +CCPairFunction::project_out(const CCFunction& f, const size_t particle) const { MADNESS_ASSERT(particle == 1 or particle == 2); - real_function_3d result; - if (is_pure()) { - result = pure().get_function().project_out(f.function, particle - 1); // this needs 0 or 1 for particle but we give 1 or 2 + Function result; + if (is_pure_no_op()) { + result = pure().get_function().project_out(f.function, + particle - 1); // this needs 0 or 1 for particle but we give 1 or 2 + } else if (is_op_pure()) { + MADNESS_EXCEPTION("implement CCPairFunction::project_out for op_pure",1); } else if (is_decomposed_no_op()) { result = project_out_decomposed(f.function, particle); } else if (is_op_decomposed()) { result = project_out_op_decomposed(f, particle); } - if (not result.is_initialized()) MADNESS_EXCEPTION("Result of project out on CCPairFunction was not initialized", + if (not result.is_initialized()) MADNESS_EXCEPTION("Result of project out on CCPairFunction was not initialized", 1); return result; } // result is: _particle -real_function_3d -CCPairFunction::dirac_convolution(const CCFunction& x, const CCConvolutionOperator& op, const size_t particle) const { - real_function_3d result; +template +Function::LDIM> +CCPairFunction::dirac_convolution(const CCFunction& x, const CCConvolutionOperator::LDIM>& op, const size_t particle) const { + Function::LDIM> result; if (is_pure()) { result = op(x, pure().get_function(), particle); } else if (is_decomposed_no_op()) { @@ -64,45 +305,149 @@ CCPairFunction::dirac_convolution(const CCFunction& x, const CCConvolutionOperat return result; } -// CCPairFunction CCPairFunction::swap_particles() const { -// switch (type) { -// default: MADNESS_EXCEPTION("Undefined enum", 1); -// case PT_FULL: -// return swap_particles_pure(); -// break; -// case PT_DECOMPOSED: -// return swap_particles_decomposed(); -// break; -// case PT_OP_DECOMPOSED: -// return swap_particles_op_decomposed(); -// break; -// } -// MADNESS_EXCEPTION("swap_particles in CCPairFunction: we should not end up here", 1); -// } - - - -real_function_3d inner(const CCPairFunction& c, const real_function_3d& f, - const std::tuple v1, const std::tuple v2) { - auto v11=std::array({std::get<0>(v1),std::get<1>(v1),std::get<2>(v1)}); - auto v22=std::array({std::get<0>(v2),std::get<1>(v2),std::get<2>(v2)}); - - return c.partial_inner(f,v11,v22); -} +template +CCPairFunction CCPairFunction::partial_inner(const CCPairFunction& other, + const std::array::LDIM>& v1, + const std::array::LDIM>& v2) const { + auto a012=std::array(); + auto a345=std::array(); + for (int i=0; iis_op_pure()); // not implemented yet + MADNESS_CHECK(not other.is_op_pure()); // not implemented yet + + auto integration_index=[&a012,&a345](auto v) {return (v==a012) ? 0l : 1l;}; + auto remaining_index=[&integration_index](auto v) {return (integration_index(v)+1)%2;}; + + CCPairFunction result; + if (this->is_pure()) { + if (other.is_pure()) { + Function tmp=madness::innerXX(this->get_function(),other.get_function(),v1,v2); + return CCPairFunction(tmp); + + } else if (other.is_decomposed_no_op()) { + // \int \sum_i f(1,2) a_i(1) b_i(3) d1 = \sum_i b_i(3) \int a_i(1) f(1,2) d1 + std::vector> tmp; + auto avec=other.get_vector(integration_index(v2)); + change_tree_state(avec,redundant); + for (auto& a : other.get_vector(integration_index(v2))) { +// tmp.push_back(innerXX<3>(this->get_function(),a,v1,a012)); // a012 is correct, referring to 3D function + tmp.push_back(this->get_function().project_out(a,integration_index(v1))); + } + return CCPairFunction(tmp,other.get_vector(remaining_index(v2))); + + } else if (other.is_op_decomposed()) { + + // \int \sum_i h(1,2) f(1,3) c_i(1) d_i(3) d1 + // = \sum_i d_i(3) \int h_c_i(1,2) f(1,3) d1 + // = \sum_i d_i(3) H_i(3,2) + const auto& h=this->pure().get_function(); + const auto& c=other.get_vector(integration_index(v2)); + const auto& d=other.get_vector(remaining_index(v2)); + auto& op=*(other.get_operator().get_op()); + op.particle()=integration_index(v1)+1; + + const std::vector> tmp=partial_mul(h,c,integration_index(v1)+1); + auto H=madness::apply(world(),op,tmp); + Function result=FunctionFactory(world()); +// const vector_real_function_6d result=partial_mul(H,d,integration_index(v1)+1); + for (int i=0; i(result); + } else { + MADNESS_EXCEPTION("confused CCPairFunction",1); + } + + } else if (this->is_decomposed_no_op()) { + if (other.is_pure()) { + return other.partial_inner(*this,v2,v1); + } else if (other.is_decomposed_no_op()) { + // \int \sum_i a_i(1) b_i(2) \sum_j c_j(1) d_j(3) d1 + // = \sum_ij b_i(2) d_j(3) + // = \sum_i b~_i(2) d~_i(3) // SVD decomposition of S_ac + Tensor ovlp=matrix_inner(world(),this->get_vector(integration_index(v1)),other.get_vector(integration_index(v2))); + Tensor< typename Tensor::scalar_type > s; + Tensor U,VT; + svd(ovlp,U,s,VT); + for (int i=0; iget_vector(remaining_index(v1)),U); + auto right=transform(world(),other.get_vector(remaining_index(v2)),transpose(VT)); + return CCPairFunction(left,right); + + } else if (other.is_op_decomposed()) { + // \int \sum_ij a_i(1) b_i(2) f(1,3) c_j(1) d_j(3) d1 + // = \sum_ij b_i(2) d_j(3) \int ac_ij(1) f(1,3) d1 + // = \sum_i b_i(2) \sum_j d_j(3) g_ij(3) + // = \sum_i b_i(2) h_i(3) + const auto& a=this->get_vector(integration_index(v1)); + const auto& b=this->get_vector(remaining_index(v1)); + const auto& c=other.get_vector(integration_index(v2)); + const auto& d=other.get_vector(remaining_index(v2)); + const auto& op=*(other.get_operator().get_op()); + std::decay_t h(a.size()); // /same type as a, without reference& + for (int i=0; i(b,h); + } else { + MADNESS_EXCEPTION("confused CCPairFunction",1); + } + + } else if (this->is_op_decomposed()) { + if (other.is_pure()) { + return other.partial_inner(*this,v2,v1); + } else if (other.is_decomposed_no_op()) { + return other.partial_inner(*this,v2,v1); + } else if (other.is_op_decomposed()) { + if (this->is_convertible_to_pure_no_op()) { + CCPairFunction tmp=copy(*this); + tmp.convert_to_pure_no_op_inplace(); + return tmp.partial_inner(other,v1,v2); + } else if (other.is_convertible_to_pure_no_op()) { + CCPairFunction tmp=copy(other); + tmp.convert_to_pure_no_op_inplace(); + return this->partial_inner(tmp,v1,v2); + } else { + MADNESS_EXCEPTION("no partial_inner for this combination: ",1); + } + } else { + MADNESS_EXCEPTION("confused CCPairFunction",1); + } + } else { + MADNESS_EXCEPTION("confused CCPairFunction",1); + } + return result; +} -real_function_3d CCPairFunction::partial_inner(const real_function_3d& f, - const std::array& v1, - const std::array& v2) const { - auto a012=std::array{0,1,2}; - auto a345=std::array{3,4,5}; +template +Function::LDIM> CCPairFunction::partial_inner( + const Function::LDIM>& f, + const std::array::LDIM>& v1, + const std::array::LDIM >& v2) const { +// auto a012=std::array{0,1,2}; +// auto a345=std::array{3,4,5}; + auto a012=std::array(); + auto a345=std::array(); + for (int i=0; iis_op_pure()); // not implemented yet int particle=-1; if (v1== a012) particle=0; if (v1== a345) particle=1; - real_function_3d result; + Function::LDIM> result; if (is_pure()) { result = pure().get_function().project_out(f, particle); @@ -111,23 +456,26 @@ real_function_3d CCPairFunction::partial_inner(const real_function_3d& f, } else if (is_op_decomposed()) { result = project_out_op_decomposed(f, particle+1); } else { - MADNESS_EXCEPTION("confused state in CCPairFunction::partial_inner",1); + MADNESS_EXCEPTION("confused state in CCPairFunction::partial_inner",1); } return result; } -real_function_3d CCPairFunction::project_out_decomposed(const real_function_3d& f, const size_t particle) const { +template +Function::LDIM> CCPairFunction::project_out_decomposed( + const Function& f, const size_t particle) const { World& world=f.world(); - real_function_3d result = real_factory_3d(world); - const std::pair decompf = assign_particles(particle); + Function result = FunctionFactory(world); + const std::pair>, std::vector>> decompf = assign_particles(particle); Tensor c = inner(world, f, decompf.first); for (size_t i = 0; i < get_a().size(); i++) result += c(i) * decompf.second[i]; return result; } -real_function_3d CCPairFunction::project_out_op_decomposed(const CCFunction& f, const size_t particle) const { +template +Function::LDIM> CCPairFunction::project_out_op_decomposed(const CCFunction& f, const size_t particle) const { World& world=f.get().world(); - const CCConvolutionOperator& op=*decomposed().get_operator_ptr(); + const CCConvolutionOperator& op=*decomposed().get_operator_ptr(); if (particle == 1) { // return op(f, get_a()[0]) * get_b()[0]; // result(2) = < f(1) | op(1,2) | a_i(1) b_i(2) > @@ -137,24 +485,27 @@ real_function_3d CCPairFunction::project_out_op_decomposed(const CCFunction& f, return sum(world,mul(world,op(f.f()* get_b()),get_a())); } else { MADNESS_EXCEPTION("project_out_op_decomposed: particle must be 1 or 2", 1); - return real_factory_3d(world); + return FunctionFactory(world); } } -real_function_3d CCPairFunction::dirac_convolution_decomposed(const CCFunction& bra, const CCConvolutionOperator& op, +template +Function::LDIM> CCPairFunction::dirac_convolution_decomposed(const CCFunction& bra, + const CCConvolutionOperator& op, const size_t particle) const { World& world=bra.function.world(); - const std::pair f = assign_particles(particle); - const vector_real_function_3d braa = mul(world, bra.function, f.first); - const vector_real_function_3d braga = op(braa); - real_function_3d result = real_factory_3d(world); + const std::pair>, std::vector>> f = assign_particles(particle); + const std::vector> braa = mul(world, bra.function, f.first); + const std::vector> braga = op(braa); + Function result = FunctionFactory(world); for (size_t i = 0; i < braga.size(); i++) result += braga[i] * f.second[i]; return result; } -const std::pair -CCPairFunction::assign_particles(const size_t particle) const { +template +const std::pair::LDIM>>, std::vector::LDIM>>> +CCPairFunction::assign_particles(const size_t particle) const { if (particle == 1) { return std::make_pair(get_a(), get_b()); } else if (particle == 2) { @@ -165,188 +516,219 @@ CCPairFunction::assign_particles(const size_t particle) const { } } +/// compute the inner product of this and other -double CCPairFunction::inner_internal(const CCPairFunction& other, const real_function_3d& R2) const { - const CCPairFunction& f1=*this; - const CCPairFunction& f2=other; +/// there are 4 possible components: pure/decomposed with and without operator, gives us 16 pair combinations.. +template +double CCPairFunction::inner_internal(const CCPairFunction& other, const Function& R2) const { + const CCPairFunction& f1=*this; + const CCPairFunction& f2=other; - double thresh=FunctionDefaults<6>::get_thresh(); + double thresh=FunctionDefaults<6>::get_thresh()*0.1; double result = 0.0; - if (f1.is_pure() and f2.is_pure()) { - CCTimer tmp(world(), "making R1R2|u>"); - if (R2.is_initialized()) { - real_function_6d R1u = multiply(::copy(f1.pure().get_function()), ::copy(R2), 1); - real_function_6d R1R2u = multiply(R1u, ::copy(R2), 2); // R1u function now broken -// tmp.info(); - result = f2.pure().get_function().inner(R1R2u); + if (f1.is_pure() and f2.is_pure()) { // these are 4 combinations pure/pure + pureT bra=f1.get_function(); + pureT ket=f2.get_function(); + // include the operator(s), if any + auto op=combine(f1.get_operator_ptr(),f2.get_operator_ptr()); + Function tmp1; + if (op) { + if (R2.is_initialized()) { + tmp1 = CompositeFactory(world()).g12(op->get_kernel()).ket(ket).particle1(R2).particle2(R2); + } else { + tmp1 = CompositeFactory(world()).g12(op->get_kernel()).ket(ket); + } } else { - result = f2.pure().get_function().inner(f1.pure().get_function()); - } - } else if (f1.is_pure() and f2.is_decomposed()) { // with or without operator - vector_real_function_3d a = R2.is_initialized() ? R2 * f2.get_a() : copy(world(), f2.get_a()); - vector_real_function_3d b = R2.is_initialized() ? R2 * f2.get_b() : copy(world(), f2.get_b()); - real_function_6d op; - if (f2.has_operator()) { - if (f2.decomposed().get_operator_ptr()->type() == OT_F12) { - op = TwoElectronFactory(world()).dcut(f2.get_operator().parameters.lo).gamma( - f2.get_operator().parameters.gamma).f12().thresh(thresh); - } else if (f2.get_operator().type() == OT_G12) { - op = TwoElectronFactory(world()).dcut(f2.get_operator().parameters.lo).thresh(thresh); + if (R2.is_initialized()) { + tmp1 = CompositeFactory(world()).ket(ket).particle1(R2).particle2(R2); } else { - MADNESS_EXCEPTION(("6D Overlap with operatortype " + assign_name(f2.get_operator().type()) + " not supported").c_str(), 1); + tmp1 = CompositeFactory(world()).ket(ket); } } - for (size_t i=0; i(world()).g12(op).particle1(x).particle2(y); - else opxy= CompositeFactory(world()).particle1(x).particle2(y); - result+= f1.pure().get_function().inner(opxy); + result=inner(bra,tmp1); + } else if (f1.is_pure() and f2.is_decomposed()) { // with or without operator + const std::vector> a = R2.is_initialized() ? R2 * f2.get_a() : copy(world(), f2.get_a()); + const std::vector> b = R2.is_initialized() ? R2 * f2.get_b() : copy(world(), f2.get_b()); + const pureT& bra=f1.get_function(); + + auto op=combine(f1.get_operator_ptr(),f2.get_operator_ptr()); + if (op) { + double bla=0.0; + for (int i=0; i tmp = CompositeFactory(world()).g12(op->get_kernel()).particle1(a[i]).particle2(b[i]); + bla += inner(bra, tmp); + } + result+=bla; + } else { // no operators + for (int i=0; i tmp = CompositeFactory(world()).particle1(a[i]).particle2(b[i]); + result+=inner(bra,tmp); + } } } else if (f1.is_decomposed() and f2.is_pure()) { // with or without op result= f2.inner_internal(f1,R2); - } else if (f1.is_decomposed_no_op() and f2.is_decomposed_no_op()) { + + } else if (f1.is_decomposed() and f2.is_decomposed()) { MADNESS_ASSERT(f1.get_a().size() == f1.get_b().size()); MADNESS_ASSERT(f2.get_a().size() == f2.get_b().size()); - vector_real_function_3d a = R2.is_initialized() ? R2* f2.get_a() : f2.get_a(); - vector_real_function_3d b = R2.is_initialized() ? R2* f2.get_b() : f2.get_b(); - // = \sum_ij = \sum_ij - result = (matrix_inner(world(), a, f1.get_a()).emul(matrix_inner(world(), b, f1.get_b()))).sum(); - } else if (f1.is_decomposed_no_op() and f2.is_op_decomposed()) { - MADNESS_ASSERT(f1.get_a().size() == f1.get_b().size()); - // = - const vector_real_function_3d& a1 = f1.get_a(); - const vector_real_function_3d& b1 = f1.get_b(); - const vector_real_function_3d a2 = R2.is_initialized() ? R2 * f2.get_a() : copy(world(),f2.get_a()); - const vector_real_function_3d b2 = R2.is_initialized() ? R2 * f2.get_b() : copy(world(),f2.get_b()); - for (size_t i = 0; i < a1.size(); i++) { - vector_real_function_3d aa = truncate(a1[i] * a2); - vector_real_function_3d aopx = f2.get_operator()(aa); - vector_real_function_3d bb = truncate(b1[i] * b2); - result += inner(bb,aopx); - } + const std::vector>& a1 = f1.get_a(); + const std::vector>& b1 = f1.get_b(); + const std::vector> a2 = R2.is_initialized() ? R2* f2.get_a() : f2.get_a(); + const std::vector> b2 = R2.is_initialized() ? R2* f2.get_b() : f2.get_b(); + - } else if (f1.is_op_decomposed() and f2.is_decomposed_no_op()) { - return f2.inner_internal(f1,R2); - - } else if (f1.is_op_decomposed() and f2.is_op_decomposed()) { - MADNESS_CHECK(can_combine(f1.get_operator(),f2.get_operator())); - MADNESS_CHECK(f1.get_a().size()==1); - std::vector> ops=combine(f1.get_operator(),f2.get_operator()); - CCPairFunction bra(f1.get_a(),f1.get_b()); - for (const auto& op : ops) { - CCPairFunction ket; - if (op.second.get_op()) ket = CCPairFunction(&op.second,f2.get_a(),f2.get_b()); - else ket = CCPairFunction(f2.get_a(),f2.get_b()); - - double tmp=op.first * inner(ket,bra,R2); - print("inner",bra.name(true),ket.name()," : ",tmp); - result+=tmp; +// MADNESS_EXCEPTION("still to debug",1); + auto op=combine(f1.get_operator_ptr(),f2.get_operator_ptr()); + if (not op) { + // = \sum_ij = \sum_ij + result = (matrix_inner(world(), a1, a2)).trace(matrix_inner(world(),b1,b2)); + } else { + // = + result=0.0; + for (size_t i = 0; i < a1.size(); i++) { + std::vector> aa = truncate(a1[i] * a2); + std::vector> bb = truncate(b1[i] * b2); + std::vector> aopx = (*op)(aa); + result += inner(bb, aopx); + } } } else MADNESS_EXCEPTION( - ("CCPairFunction Overlap not supported for combination " + f1.name() + " and " + f2.name()).c_str(), 1) - - ; + ("CCPairFunction Overlap not supported for combination " + f1.name() + " and " + f2.name()).c_str(), 1) ; return result; } -CCPairFunction apply(const ProjectorBase& projector, const CCPairFunction& argument) { - auto result=madness::apply(projector,std::vector (1,argument)); - MADNESS_CHECK(result.size()==1); - return result[0]; -} - -std::vector apply(const ProjectorBase& projector, const std::vector& argument) { +template +std::vector> CCPairFunction::apply(const ProjectorBase& projector, const std::vector>& argument) { if (argument.size()==0) return argument; World& world=argument.front().world(); - if (auto P=dynamic_cast*>(&projector)) MADNESS_CHECK(P->get_particle()==0 or P->get_particle()==1); - if (auto Q=dynamic_cast*>(&projector)) MADNESS_CHECK(Q->get_particle()==0 or Q->get_particle()==1); - std::vector result; + constexpr std::size_t LDIM=CCPairFunction::LDIM; +// print("apply projector on argument with terms",argument.size()); + if (auto P=dynamic_cast*>(&projector)) { +// print("P->get_particle()",P->get_particle()); + MADNESS_CHECK_THROW(P->get_particle()==0 or P->get_particle()==1,"P Projector particle must be 0 or 1 in CCPairFunction"); + } + if (auto Q=dynamic_cast*>(&projector)) { +// print("Q->get_particle()",Q->get_particle()); + MADNESS_CHECK_THROW(Q->get_particle()==0 or Q->get_particle()==1,"Q Projector particle must be 0 or 1 in CCPairFunction"); + } + std::vector> result; for (const auto& pf : argument) { if (pf.is_pure()) { - if (auto SO=dynamic_cast*>(&projector)) { + MADNESS_CHECK(not pf.has_operator()); // not yet implemented + if (auto SO=dynamic_cast*>(&projector)) { auto tmp=(*SO)(pf.get_function()); - auto tmp2=CCPairFunction(tmp); + auto tmp2=CCPairFunction(tmp); result.push_back(tmp2); - } else if (auto P=dynamic_cast*>(&projector)) { - result.push_back(CCPairFunction((*P)(pf.get_function(),P->get_particle()+1))); + } else if (auto P=dynamic_cast*>(&projector)) { + result.push_back(CCPairFunction((*P)(pf.get_function(),P->get_particle()+1))); - } else if (auto Q=dynamic_cast*>(&projector)) { - result.push_back(CCPairFunction((*Q)(pf.get_function(),Q->get_particle()+1))); + } else if (auto Q=dynamic_cast*>(&projector)) { + result.push_back(CCPairFunction((*Q)(pf.get_function(),Q->get_particle()+1))); } else { - MADNESS_EXCEPTION("CCPairFunction: unknown projector type",1); + MADNESS_EXCEPTION("CCPairFunction: unknown projector type",1); } } else if (pf.is_decomposed_no_op()) { // pair function is sum_i | a_i b_i > - if (auto SO=dynamic_cast*>(&projector)) { + if (auto SO=dynamic_cast*>(&projector)) { // Q12 | kl > = (1-O1)(1-O2) |kl> = |(1-O1)k (1-O2)l> - QProjector Q1(world,SO->bra1(),SO->ket1()); - QProjector Q2(world,SO->bra2(),SO->ket2()); - result.push_back(CCPairFunction(Q1(pf.get_a()),Q2(pf.get_b()))); + QProjector Q1(world,SO->bra1(),SO->ket1()); + QProjector Q2(world,SO->bra2(),SO->ket2()); + result.push_back(CCPairFunction(Q1(pf.get_a()),Q2(pf.get_b()))); - } else if (auto P=dynamic_cast*>(&projector)) { + } else if (auto P=dynamic_cast*>(&projector)) { // P1 | kl > = P1 |kl> = |P1 k l> - if (P->get_particle()==0) result.push_back(CCPairFunction((*P)(pf.get_a()),pf.get_b())); + if (P->get_particle()==0) result.push_back(CCPairFunction((*P)(pf.get_a()),pf.get_b())); // P2 | kl > = P2 |kl> = |k P2 l> - if (P->get_particle()==1) result.push_back(CCPairFunction(pf.get_a(),(*P)(pf.get_b()))); + if (P->get_particle()==1) result.push_back(CCPairFunction(pf.get_a(),(*P)(pf.get_b()))); - } else if (auto Q=dynamic_cast*>(&projector)) { + } else if (auto Q=dynamic_cast*>(&projector)) { // Q1 | kl > = Q1 |kl> = |Q1 k l> - if (Q->get_particle()==0) result.push_back(CCPairFunction((*Q)(pf.get_a()),pf.get_b())); + if (Q->get_particle()==0) result.push_back(CCPairFunction((*Q)(pf.get_a()),pf.get_b())); // P2 | kl > = Q2 |kl> = |k Q2 l> - if (Q->get_particle()==1) result.push_back(CCPairFunction(pf.get_a(),(*Q)(pf.get_b()))); + if (Q->get_particle()==1) result.push_back(CCPairFunction(pf.get_a(),(*Q)(pf.get_b()))); } else { - MADNESS_EXCEPTION("CCPairFunction: unknown projector type",1); + MADNESS_EXCEPTION("CCPairFunction: unknown projector type",1); } } else if (pf.is_op_decomposed()) { - if (auto SO=dynamic_cast*>(&projector)) { + if (auto SO=dynamic_cast*>(&projector)) { +// CCTimer t(world,"SO block"); // Q12 = 1 - O1 (1 - 1/2 O2) - O2 (1 - 1/2 O1) - QProjector Q1(world,SO->bra1(),SO->ket1()); +// print("entering SO block"); + QProjector Q1(world,SO->bra1(),SO->ket1()); Q1.set_particle(0); - QProjector Q2(world,SO->bra2(),SO->ket2()); + QProjector Q2(world,SO->bra2(),SO->ket2()); Q2.set_particle(1); - auto tmp=Q1(Q2(std::vector({pf}))); + + Projector O1(SO->bra1(),SO->ket1()); + O1.set_particle(0); + Projector O2(SO->bra2(),SO->ket2()); + O2.set_particle(1); + +// auto arg=std::vector>({pf}); +// auto o1arg=O1(arg); +// auto o2arg=O2(arg); +// auto o1o2arg=O1(o2arg); +// +// result.push_back(pf); +// for (auto& t: o1arg) result.push_back(-1.0*t); +// for (auto& t: o2arg) result.push_back(-1.0*t); +// for (auto& t: o1o2arg) result.push_back(t); + + + auto tmp=Q1(Q2(std::vector>({pf}))); +// auto tmp=Q2(Q1(std::vector>({pf}))); +// print("result of SO"); +// for (auto& t: tmp) t.print_size(); for (auto& t: tmp) result.push_back(t); +// for (auto& t: result) t.print_size(); - } else if (auto P=dynamic_cast*>(&projector)) { - std::vector tmp= zero_functions_compressed(world,P->get_ket_vector().size()); + } else if (auto P=dynamic_cast*>(&projector)) { +// CCTimer t(world,"P block"); +// print("entering P block"); + std::vector> tmp= zero_functions_compressed(world,P->get_ket_vector().size()); - // per term a_i b_k: + // per term a_i b_i: // P1 f |a b> = \sum_k |k(1)> |f_ak(2)*b(2)> for (std::size_t i=0; i a=pf.get_a()[i]; + Function b=pf.get_b()[i]; if (P->get_particle()==1) std::swap(a,b); - std::vector ka=a*P->get_bra_vector(); - real_convolution_3d& op=*(pf.get_operator().get_op()); - std::vector f_ka=apply(world,op,ka); - std::vector b_f_ka=f_ka*b; + std::vector> ka=a*P->get_bra_vector(); + SeparatedConvolution& op=*(pf.get_operator().get_op()); + std::vector> f_ka=madness::apply(world,op,ka); + std::vector> b_f_ka=f_ka*b; tmp+=b_f_ka; } truncate(world,tmp); - if (P->get_particle()==0) result.push_back(CCPairFunction(P->get_ket_vector(),tmp)); - if (P->get_particle()==1) result.push_back(CCPairFunction(tmp,P->get_ket_vector())); +// print("size of tmp",tmp.size()); + + if (P->get_particle()==0) result.push_back(CCPairFunction(P->get_ket_vector(),tmp)); + if (P->get_particle()==1) result.push_back(CCPairFunction(tmp,P->get_ket_vector())); +// t.print(); - } else if (auto Q=dynamic_cast*>(&projector)) { + } else if (auto Q=dynamic_cast*>(&projector)) { +// CCTimer t(world,"Q block"); // Q1 f12 |a_i b_i> = f12 |a_i b_i> - \sum_k |k(1) a_i(2)*f_(kb_i)(2) > result.push_back(pf); +// print("entering Q block"); // reuse the projector code above - std::vector tmp=madness::apply(Q->get_P_projector(),std::vector(1,pf)); + std::vector> tmp=madness::apply(Q->get_P_projector(),std::vector>(1,pf)); +// for (auto& t : tmp) t.print_size(); for (auto& t : tmp) { t*=-1.0; result.push_back(t); } +// t.print(); } else { - MADNESS_EXCEPTION("CCPairFunction: unknown projector type",1); + MADNESS_EXCEPTION("CCPairFunction: unknown projector type",1); } } else { - MADNESS_EXCEPTION("confused type in CCPairFunction",1); + MADNESS_EXCEPTION("confused type in CCPairFunction",1); } } @@ -355,4 +737,8 @@ std::vector apply(const ProjectorBase& projector, const std::vec }; +template class CCPairFunction; +template class CCPairFunction; +template class CCPairFunction; + } // namespace madness diff --git a/src/madness/chem/ccpairfunction.h b/src/madness/chem/ccpairfunction.h index f4fd93238d1..b50893abb3e 100644 --- a/src/madness/chem/ccpairfunction.h +++ b/src/madness/chem/ccpairfunction.h @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -16,6 +17,7 @@ namespace madness { +template struct CCConvolutionOperator; class ProjectorBase; @@ -23,52 +25,67 @@ class ProjectorBase; /// Types of Functions used by CC_function class enum FuncType { UNDEFINED, HOLE, PARTICLE, MIXED, RESPONSE }; -/// FuncTypes used by the CC_function_6d structure -enum PairFormat { PT_UNDEFINED, PT_FULL, PT_DECOMPOSED, PT_OP_DECOMPOSED }; - - - /// structure for a CC Function 3D which holds an index and a type // the type is defined by the enum FuncType (definition at the start of this file) +template struct CCFunction : public archive::ParallelSerializableObject { CCFunction() : current_error(99), i(99), type(UNDEFINED) {}; - CCFunction(const real_function_3d& f) : current_error(99), function(f), i(99), type(UNDEFINED) {}; - - CCFunction(const real_function_3d& f, const size_t& ii) : current_error(99), function(f), i(ii), type(UNDEFINED) {}; + CCFunction(const Function& f) : current_error(99), function(f), i(99), type(UNDEFINED) {}; - CCFunction(const real_function_3d& f, const size_t& ii, const FuncType& type_) : current_error(99), function(f), +// CCFunction(const Function& f, const size_t& ii) : current_error(99), function(f), i(ii), type(UNDEFINED) {}; +// + CCFunction(const Function& f, const size_t& ii, const FuncType& type_) : current_error(99), function(f), i(ii), type(type_) {}; CCFunction(const CCFunction& other) : current_error(other.current_error), function(other.function), i(other.i), type(other.type) {}; double current_error; - real_function_3d function; + Function function; - real_function_3d get() const { return function; } + Function get() const { return function; } - real_function_3d f() const { return function; } + Function f() const { return function; } - void set(const real_function_3d& other) { function = other; } + void set(const Function& other) { function = other; } size_t i; FuncType type; - void info(World& world, const std::string& msg = " ") const; + void info(World& world, const std::string& msg = " ") const { + if (world.rank() == 0) { + std::cout << "Information about 3D function: " << name() << " " << msg << std::endl; + std::cout << std::setw(10) << std::setfill(' ') << std::setw(50) << " |f| : " << function.norm2() + << std::endl; + std::cout << std::setw(10) << std::setfill(' ') << std::setw(50) << " |error|: " << current_error << std::endl; + } + }; - std::string name() const; + std::string name() const { + if (type == HOLE) { + return "phi" + stringify(i); + } else if (type == PARTICLE) { + return "tau" + stringify(i); + } else if (type == MIXED) { + return "t" + stringify(i); + } else if (type == RESPONSE) { + return "x" + stringify(i); + } else { + return "function" + stringify(i); + } + }; double inner(const CCFunction& f) const { return inner(f.function); } - double inner(const real_function_3d& f) const { + double inner(const Function& f) const { return function.inner(f); } /// scalar multiplication CCFunction operator*(const double& fac) const { - real_function_3d fnew = fac * function; + Function fnew = fac * function; return CCFunction(fnew, i, type); } @@ -95,28 +112,34 @@ class TwoBodyFunctionComponentBase { virtual void swap_particles_inplace() = 0; virtual bool is_pure() const {return false;} virtual bool is_decomposed() const {return false;} - virtual bool has_operator() const {return false;} + virtual bool has_operator() const = 0; +// virtual void set_operator(const std::shared_ptr op) = 0; +// virtual const std::shared_ptr get_operator_ptr() const = 0; virtual void print_size() const = 0; virtual std::string name(const bool transpose=false) const = 0; virtual World& world() const =0; virtual std::shared_ptr clone() = 0; virtual ~TwoBodyFunctionComponentBase() {} + virtual hashT hash() const = 0; }; /// a two-body, explicitly 6-dimensional function -template +template class TwoBodyFunctionPureComponent : public TwoBodyFunctionComponentBase { + static constexpr std::size_t LDIM=NDIM/2; + static_assert(NDIM%2==0,"NDIM must be even"); public: TwoBodyFunctionPureComponent() = default; - explicit TwoBodyFunctionPureComponent(const Function& f) : u(f) {} + explicit TwoBodyFunctionPureComponent(const Function& f) : u(f) {} + explicit TwoBodyFunctionPureComponent(const std::shared_ptr> op, const Function& f) + : u(f), op(op) {} /// deep copy std::shared_ptr clone() override { - TwoBodyFunctionPureComponent result; - result.u=madness::copy(u); - return std::make_shared>(result); + TwoBodyFunctionPureComponent result(op,madness::copy(u)); + return std::make_shared>(result); } template @@ -127,17 +150,21 @@ class TwoBodyFunctionPureComponent : public TwoBodyFunctionComponentBase { bool is_pure() const override {return true;} - World& world() const override {return u.world();}; + bool has_operator() const override {return op!=nullptr;} - void serialize() {} + World& world() const override {return u.world();}; void print_size() const override { u.print_size(name(false)); } std::string name(const bool transpose) const override { - if (transpose) return "< u |"; - return "|u>"; + if (transpose) { + if (has_operator()) return "< u |"+get_operator_ptr()->name(); + return "< u |"; + } + if (has_operator()) return get_operator_ptr()->name() + "| u >"; + return "| u >"; } @@ -152,37 +179,50 @@ class TwoBodyFunctionPureComponent : public TwoBodyFunctionComponentBase { u=swap_particles(u); } - real_function_6d& get_function() { + const std::shared_ptr> get_operator_ptr() const {return op;}; + + void set_operator(const std::shared_ptr> op1) {op=op1;} + + Function& get_function() { return u; } + hashT hash() const override { + hashT h1=hash_value(u.get_impl()); + if (op) hash_combine(h1,hash_value(*op)); + return h1; + } + private: /// pure 6D function - real_function_6d u; + Function u; + std::shared_ptr> op; }; /// holds two vectors a and b of low-dimensional functions forming a high-dim function by a sum of outer products /// f(1,2) = sum_i |a_i b_i > -template +template class TwoBodyFunctionSeparatedComponent : public TwoBodyFunctionComponentBase { + static constexpr std::size_t LDIM=NDIM/2; + static_assert(NDIM%2==0,"NDIM must be even"); public: TwoBodyFunctionSeparatedComponent() = default; - TwoBodyFunctionSeparatedComponent(const std::vector>& a, - const std::vector>& b) : a(a), b(b), op(nullptr) {}; + TwoBodyFunctionSeparatedComponent(const std::vector>& a, + const std::vector>& b) : a(a), b(b), op(nullptr) {}; - TwoBodyFunctionSeparatedComponent(const std::vector>& a, - const std::vector>& b, - const CCConvolutionOperator* op) : a(a), b(b), op(op) {}; + TwoBodyFunctionSeparatedComponent(const std::vector>& a, + const std::vector>& b, + const std::shared_ptr> op) : a(a), b(b), op(op) {}; TwoBodyFunctionSeparatedComponent(const TwoBodyFunctionSeparatedComponent& other) = default; /// deep copy std::shared_ptr clone() override { - TwoBodyFunctionSeparatedComponent result(madness::copy(world(),a),madness::copy(world(),b),op); - return std::make_shared>(result); + TwoBodyFunctionSeparatedComponent result(madness::copy(world(),a),madness::copy(world(),b),op); + return std::make_shared>(result); } template @@ -209,32 +249,61 @@ class TwoBodyFunctionSeparatedComponent : public TwoBodyFunctionComponentBase { std::string name(const bool transpose) const override { if (transpose) { - if (has_operator()) return "name(); - return "name(); + return "name() + "|xy>"; - return "|ab>"; + if (has_operator()) return get_operator_ptr()->name() + "| ab>"; + return "| ab>"; }; void serialize() {} - template - TwoBodyFunctionPureComponent apply(const SeparatedConvolution* op, const int particle=0) {} + hashT hash() const override { + hashT h1=0; + for (const auto& aa : a) hash_combine(h1,hash_value(aa.get_impl())); + for (const auto& bb : b) hash_combine(h1,hash_value(bb.get_impl())); + // print("hashvalue of TwoBodyFunctionSeparatedComponent: ",h1); + + if (op) hash_combine(h1,hash_value(*op)); + return h1; + } + + + template + TwoBodyFunctionPureComponent apply(const SeparatedConvolution* op, const int particle=0) { + MADNESS_EXCEPTION("TwoBodyFunctionPureComponent apply not yet implemented",1); + } /// return f(2,1) void swap_particles_inplace() override { std::swap(a,b); } - std::vector> get_a() const {return a;} - std::vector> get_b() const {return b;} - const CCConvolutionOperator* get_operator_ptr() const {return op;}; + long rank() const { + MADNESS_CHECK(a.size()==b.size()); + return a.size(); + } + + std::vector> get_a() const {return a;} + std::vector> get_b() const {return b;} + std::vector> get_vector(const int i) const { + MADNESS_CHECK(i==0 or i==1); + if (i==0) return a; + else if (i==1) return b; + else { + MADNESS_EXCEPTION("confused index in TwoBodyFunctionSeparatedComponent",1); + } + } + + const std::shared_ptr> get_operator_ptr() const {return op;}; + + void set_operator(const std::shared_ptr> op1) {op=op1;} private: - std::vector> a; - std::vector> b; - const CCConvolutionOperator* op=nullptr; + std::vector> a; + std::vector> b; + std::shared_ptr> op; }; @@ -264,42 +333,72 @@ class TwoBodyFunctionSeparatedComponent : public TwoBodyFunctionComponentBase { * - mul_partial */ -struct CCPairFunction { +/// a 6D function, either in full or low rank form, possibly including an 2-particle function + +/** + * the function is stored as + * - pure: full rank form, 6D + * - op_pure: full rank form, 6D with an 2-particle function f(1,2) |u> + * - decomposed: sum of two vectors of 3D functions \sum_i |a_i(1) b_i(2)> + * - op_decomposed: as above, with an 2-particle function: f(1,2) \sum_i |a_i b_i> + * +**/ +template +struct CCPairFunction : public archive::ParallelSerializableObject { + static constexpr std::size_t LDIM=NDIM/2; + static_assert(NDIM%2==0,"NDIM must be even"); + +using pureT=Function; -using T=double; public: /// empty ctor CCPairFunction() = default; - /// takes a deep copy of the argument function - explicit CCPairFunction(const real_function_6d& ket) { - component.reset(new TwoBodyFunctionPureComponent(copy(ket))); + /// takes a shallow copy of the argument function + explicit CCPairFunction(const Function& ket) { + component.reset(new TwoBodyFunctionPureComponent(ket)); + } + + /// takes a shallow copy of the argument function + explicit CCPairFunction(const std::shared_ptr> op_, const Function& ket) { + component.reset(new TwoBodyFunctionPureComponent(op_,ket)); } /// takes a deep copy of the argument functions - explicit CCPairFunction(const vector_real_function_3d& f1, const vector_real_function_3d& f2) { + explicit CCPairFunction(const std::vector>& f1, const std::vector>& f2) { World& world=f1.front().world(); - component.reset(new TwoBodyFunctionSeparatedComponent(copy(world,f1),copy(world,f2))); + component.reset(new TwoBodyFunctionSeparatedComponent(copy(world,f1),copy(world,f2))); } /// takes a deep copy of the argument functions - explicit CCPairFunction(const std::pair& f) : + explicit CCPairFunction(const Function& f1, const Function& f2) : + CCPairFunction(std::vector>({f1}),std::vector>({f2})) { + } + + /// takes a deep copy of the argument functions + explicit CCPairFunction(const std::pair>, std::vector>>& f) : CCPairFunction(f.first,f.second) { } /// takes a deep copy of the argument functions - explicit CCPairFunction(const CCConvolutionOperator *op_, const CCFunction& f1, const CCFunction& f2) : - CCPairFunction(op_,std::vector({f1.function}),std::vector({f2.function})) { + explicit CCPairFunction(const std::shared_ptr> op_, const CCFunction& f1, const CCFunction& f2) : + CCPairFunction(op_,std::vector>({f1.function}),std::vector>({f2.function})) { } /// takes a deep copy of the argument functions - explicit CCPairFunction(const CCConvolutionOperator *op_, const std::vector& f1, - const std::vector& f2) { + explicit CCPairFunction(const std::shared_ptr> op_, const std::vector>& f1, + const std::vector>& f2) { World& world=f1.front().world(); - component.reset(new TwoBodyFunctionSeparatedComponent(copy(world,f1),copy(world,f2),op_)); + component.reset(new TwoBodyFunctionSeparatedComponent(copy(world,f1),copy(world,f2),op_)); } + /// takes a deep copy of the argument functions + explicit CCPairFunction(const std::shared_ptr> op_, const Function& f1, + const Function& f2) : CCPairFunction(op_,std::vector>({f1}), + std::vector>({f2})) { + }; + /// shallow assignment operator CCPairFunction& operator()(const CCPairFunction& other) { component=other.component; @@ -316,6 +415,55 @@ using T=double; return result; } + bool is_assigned() const { + return component.get(); + } + + friend hashT hash_value(const CCPairFunction& f) { + if (not f.is_assigned()) { return hashT(); } + return f.component->hash(); + } + +private: + std::vector consolidate(const std::vector& other, + const std::vector& options, + const std::vector>& centers) const; + + /// turn decomposed functions with operator into decomposed functions using LowRankFunction + static std::vector op_dec_to_dec(const std::vector& other, + const std::vector>& centers); + + /// turn pure functions with operator into pure functions without operators + static std::vector op_pure_to_pure(const std::vector& other); + + /// turn decomposed functions with operator into pure functions without operators + static std::vector op_dec_to_pure(const std::vector& other); + + /// remove linear dependent terms in the low-rank parts + static std::vector remove_linearly_dependent_terms(const std::vector& other, + double thresh=-1.0); + + static std::vector collect_same_types(const std::vector& other); + +public: + // check if all types (pure. op_pure, decomposed, op_decomposed, with various ops) occur only once + static bool is_collected(const std::vector>& other); + + /// collect the terms into a compact format + + /// @param[in] other: a vector of CCPairFunctions + /// @param[in] options: a vector of strings which can be "one_term", "op_pure_to_pure", "svd" + /// @param[in] centers: a vector of 3D-vectors which are the centers of the grid for low-rank functions + /// TODO: implement a function for removing linearly dependent terms without orthonormalization + friend std::vector consolidate(const std::vector& other, + const std::vector options, + const std::vector> centers=std::vector>()) { + + if (other.size()>0) return other.front().consolidate(other,options,centers); // workaround + return other; + }; + + void info() const { print_size(); } World& world() const { @@ -323,12 +471,12 @@ using T=double; return component->world(); } - Function& get_function() { + Function& get_function() { MADNESS_CHECK(component and (component->is_pure())); return pure().get_function(); } - Function& get_function() const { + Function& get_function() const { MADNESS_CHECK(component and (component->is_pure())); return pure().get_function(); } @@ -337,46 +485,129 @@ using T=double; /// deep copy necessary otherwise: shallow copy errors CCPairFunction invert_sign(); + /// scalar multiplication: f*fac CCPairFunction operator*(const double fac) const { CCPairFunction result=copy(*this); result*=fac; return result; } + /// scalar multiplication: fac*f friend CCPairFunction operator*(const double fac, const CCPairFunction& f) { - return fac*f; + return f*fac; + } + + /// multiplication with a 2-particle function + friend CCPairFunction operator*(const std::shared_ptr> op, const CCPairFunction& f) { + CCPairFunction result=copy(f); + return result.multiply_with_op_inplace(op); } + /// multiplication with a 2-particle function + friend std::vector operator*(const std::shared_ptr> op, + const std::vector& f) { + std::vector result; + for (auto& ff : f) { + result.push_back(copy(ff)); + result.back().multiply_with_op_inplace(op); + } + return result; + } + + friend std::vector multiply(const std::vector& other, const Function f, + const std::array& v1) { + std::vector result; + for (auto& o : other) { + double cpu0=cpu_time(); +// std::cout << "multiply " << o.name(); + result.push_back(multiply(o,f,v1)); + double cpu1=cpu_time(); +// std::cout << " done after " << cpu1-cpu0 << std::endl; + } + return result; + } + + /// multiplication with a 2-particle function + CCPairFunction operator*(const std::shared_ptr> op) { + CCPairFunction result=copy(*this); + return result.multiply_with_op_inplace(op); + } + + CCPairFunction& multiply_with_op_inplace(const std::shared_ptr> op); + + + bool has_operator() const {return component->has_operator();} bool is_pure() const {return component->is_pure();} + bool is_op_pure() const {return is_pure() and has_operator();} + bool is_pure_no_op() const {return is_pure() and (not has_operator());} bool is_decomposed() const {return component->is_decomposed();} - bool is_decomposed_no_op() const {return component->is_decomposed() and (not component->has_operator());} bool is_op_decomposed() const {return component->is_decomposed() and component->has_operator();} - bool has_operator() const {return component->has_operator();} + bool is_decomposed_no_op() const {return component->is_decomposed() and (not component->has_operator());} - TwoBodyFunctionPureComponent& pure() const { - if (auto ptr=dynamic_cast*>(component.get())) return *ptr; + TwoBodyFunctionPureComponent& pure() const { + if (auto ptr=dynamic_cast*>(component.get())) return *ptr; MADNESS_EXCEPTION("bad cast in TwoBodyFunction",1); } - TwoBodyFunctionSeparatedComponent& decomposed() const { - if (auto ptr=dynamic_cast*>(component.get())) return *ptr; + TwoBodyFunctionSeparatedComponent& decomposed() const { + if (auto ptr=dynamic_cast*>(component.get())) return *ptr; MADNESS_EXCEPTION("bad cast in TwoBodyFunction",1); } - vector_real_function_3d get_a() const { + std::vector> get_a() const { MADNESS_CHECK(component->is_decomposed()); return decomposed().get_a(); } - vector_real_function_3d get_b() const { + + std::vector> get_b() const { MADNESS_CHECK(component->is_decomposed()); return decomposed().get_b(); } - const CCConvolutionOperator& get_operator() const { - MADNESS_CHECK(is_op_decomposed()); - return *decomposed().get_operator_ptr(); + std::vector> get_vector(const int i) const { + MADNESS_CHECK(component->is_decomposed()); + return decomposed().get_vector(i); + } + + const CCConvolutionOperator& get_operator() const { + MADNESS_CHECK(component and component->has_operator()); + if (is_pure()) return *(pure().get_operator_ptr()); + if (is_decomposed()) return *(decomposed().get_operator_ptr()); + MADNESS_EXCEPTION("bad cast in TwoBodyFunction",1); + return *(decomposed().get_operator_ptr()); + } + + const std::shared_ptr> get_operator_ptr() const { + MADNESS_CHECK(component); + if (is_pure()) return (pure().get_operator_ptr()); + if (is_decomposed()) return (decomposed().get_operator_ptr()); + MADNESS_EXCEPTION("bad cast in TwoBodyFunction",1); +// return component->get_operator_ptr(); + } + + void reset_operator(const std::shared_ptr> op) { + MADNESS_CHECK(component); + if (is_pure()) pure().set_operator(op); + else if (is_decomposed()) decomposed().set_operator(op); + else { + MADNESS_EXCEPTION("bad cast in TwoBodyFunction",1); + } } + /// can this be converted to a pure representation (depends on the operator, if present) + bool is_convertible_to_pure_no_op() const; + + /// out-of-place conversion to pure function + CCPairFunction to_pure() const { + auto tmp=copy(*this); + MADNESS_CHECK(tmp.is_convertible_to_pure_no_op()); + tmp.convert_to_pure_no_op_inplace(); + return tmp; + } + + /// convert this into a pure hi-dim function + void convert_to_pure_no_op_inplace(); + CCPairFunction& operator*=(const double fac) { if (component->is_pure()) pure()*=fac; if (component->is_decomposed()) decomposed()*=fac; @@ -393,18 +624,64 @@ using T=double; return component->name(transpose); } + typename Tensor::scalar_type norm2() const { + if (component->is_pure()) return pure().get_function().norm2(); + if (component->is_decomposed()) { + Function R2; + auto tmp= inner_internal(*this,R2); + typename Tensor::scalar_type result=std::real(tmp); + typename Tensor::scalar_type imag=std::imag(tmp); + if ((imag>1.e-14) or (result<-1.e-14)) { + MADNESS_EXCEPTION("bad norm in TwoBodyFunction",1); + } + return sqrt(std::abs(result)); + } + MADNESS_EXCEPTION("bad cast in TwoBodyFunction",1); + return 0.0; + } + + /// multiply CCPairFunction with a 3D function of one of the two particles + friend CCPairFunction multiply(const CCPairFunction& other, const Function& f, + const std::array& v1) { + auto a012=std::array(); + auto a345=std::array(); + for (int i=0; i(other.get_operator_ptr(), f * other.get_a(), copy(world, other.get_b())); + } else { + return CCPairFunction(other.get_operator_ptr(), copy(world, other.get_a()), f * other.get_b()); + } + } else if (other.is_pure()) { + auto tmp=multiply(other.get_function(),f,particle+1); + return CCPairFunction(other.get_operator_ptr(),tmp); + } else { + MADNESS_EXCEPTION("confused CCPairFunction in multiply",1); + } + }; + /// @param[in] f: a 3D-CC_function /// @param[in] particle: the particle on which the operation acts /// @param[out] _particle (projection from 6D to 3D) - real_function_3d project_out(const CCFunction& f, const size_t particle) const; + Function project_out(const CCFunction& f, const size_t particle) const; + + /// result is: _particle - // result is: _particle /// @param[in] x: a 3D-CC_function /// @param[in] op: a CC_convoltion_operator which is currently either f12 or g12 /// @param[in] particle: the particle on which the operation acts (can be 1 or 2) /// @param[out] the operator is applied and afterwards a convolution with the delta function makes a 3D-function: _particle - real_function_3d - dirac_convolution(const CCFunction& x, const CCConvolutionOperator& op, const size_t particle) const; + Function + dirac_convolution(const CCFunction& x, const CCConvolutionOperator& op, const size_t particle) const; /// @param[out] particles are interchanged, if the function was u(1,2) the result is u(2,1) CCPairFunction swap_particles() const { @@ -414,26 +691,34 @@ using T=double; }; double - make_xy_u(const CCFunction& xx, const CCFunction& yy) const; + make_xy_u(const CCFunction& xx, const CCFunction& yy) const; - double inner_internal(const CCPairFunction& other, const real_function_3d& R2) const; + /// compute the inner product of this and other + double inner_internal(const CCPairFunction& other, const Function& R2) const; - friend double inner(const CCPairFunction& a, const CCPairFunction& b, const real_function_3d& R2) { + friend double inner(const CCPairFunction& a, const CCPairFunction& b, const Function& R2) { return a.inner_internal(b,R2); } friend double inner(const CCPairFunction& a, const CCPairFunction& b) { - real_function_3d R2; + Function R2; return a.inner_internal(b,R2); } - friend double inner(const std::vector& va, const std::vector& vb) { - real_function_3d R2; + friend double inner(const std::vector& va, const std::vector& vb, + const Function R2=Function()) { + double wall0=cpu_time(); +// Function R2; double result=0.0; for (auto& a : va) { for (auto& b : vb) { double tmp=a.inner_internal(b,R2); - print("result from inner",a.name(true),b.name(),tmp); + double wall1=cpu_time(); + std::size_t bufsize=256; + char buf[bufsize]; + snprintf(buf,bufsize,"result from inner %10s %10s %12.8f %4.1fs",a.name(true).c_str(),b.name().c_str(),tmp,wall1-wall0); + print(std::string(buf)); + wall0=wall1; result+=tmp; } } @@ -441,72 +726,191 @@ using T=double; } + friend std::vector swap_particles(const std::vector& argument) { + std::vector result; + for (auto& a : argument) result.push_back(a.swap_particles()); + return result; + }; + public: /// the 3 types of 6D-function that occur in the CC potential which coupled doubles to singles std::shared_ptr component; -// World& world; -// /// the type of the given 6D-function -// const PairFormat type; -// /// if type==decomposed this is the first particle -// vector_real_function_3d a; -// /// if type==decomposed this is the second particle -// vector_real_function_3d b; -// /// if type==op_decomposed_ this is the symmetric 6D-operator (g12 or f12) in u=op12|xy> -// const CCConvolutionOperator *op; -// /// if type==op_decomposed_ this is the first particle in u=op12|xy> -// CCFunction x; -// /// if type==op_decomposed_ this is the second particle in u=op12|xy> -// CCFunction y; -// /// if type=pure_ this is just the MRA 6D-function -// real_function_6d u; - /// @param[in] f: a 3D-CC_function /// @param[in] particle: the particle on which the operation acts /// @param[out] _particle (projection from 6D to 3D) for the case that u=|ab> so _particle = *|b> if particle==1 - real_function_3d project_out_decomposed(const real_function_3d& f, const size_t particle) const; + Function project_out_decomposed(const Function& f, const size_t particle) const; /// @param[in] f: a 3D-CC_function /// @param[in] particle: the particle on which the operation acts /// @param[out] _particle (projection from 6D to 3D) for the case that u=op|xy> so _particle = *|y> if particle==1 - real_function_3d project_out_op_decomposed(const CCFunction& f, const size_t particle) const; + Function project_out_op_decomposed(const CCFunction& f, const size_t particle) const; /// @param[in] x: a 3D-CC_function /// @param[in] op: a CC_convoltion_operator which is currently either f12 or g12 /// @param[in] particle: the particle on which the operation acts (can be 1 or 2) /// @param[out] the operator is applied and afterwards a convolution with the delta function makes a 3D-function: _particle /// in this case u=|ab> and the result is _1 = *|b> for particle==1 - real_function_3d - dirac_convolution_decomposed(const CCFunction& x, const CCConvolutionOperator& op, const size_t particle) const; + Function + dirac_convolution_decomposed(const CCFunction& x, const CCConvolutionOperator& op, const size_t particle) const; /// small helper function that gives back (a,b) or (b,a) depending on the value of particle - const std::pair assign_particles(const size_t particle) const; + const std::pair>, std::vector>> assign_particles(const size_t particle) const; - template - friend CCPairFunction apply(const SeparatedConvolution& G, const CCPairFunction& argument) { + static std::vector> apply(const ProjectorBase& P, const std::vector>& argument); + + /// apply the operator on a CCPairfunction, both with the same dimension + + /// note there is another function, where the operator works only on some dimensions of the CCPairFunction! + /// @return result(x) = \int op(x,x') arg(x') dx': a CCPairfunction with the same dimension as the argument + friend CCPairFunction apply(const SeparatedConvolution& G, const CCPairFunction& argument) { + CCPairFunction result; + timer t1(argument.world()); if (argument.is_pure()) { - return CCPairFunction(G(argument.get_function())); + result=CCPairFunction(G(argument.get_function())); } else if (argument.is_decomposed_no_op()) { - real_function_6d result=real_factory_6d(argument.world()).compressed(); + Function result1=real_factory_6d(argument.world()).compressed(); MADNESS_ASSERT(argument.get_a().size() == argument.get_b().size()); + MADNESS_CHECK_THROW(G.particle()==-1,"G must be a two-particle operator in apply(CCPairFunction)"); for (size_t k = 0; k < argument.get_a().size(); k++) { - const real_function_6d tmp = G(argument.get_a()[k], argument.get_b()[k]); - result += tmp; + const Function tmp = G(argument.get_a()[k], argument.get_b()[k]); + result1 += tmp; } - return CCPairFunction(result); + result=CCPairFunction(result1); } else { MADNESS_EXCEPTION("unknown type in CCPairFunction::apply",1); } - return CCPairFunction(); + t1.end("applying G to " + argument.name()); + return result; }; - real_function_3d partial_inner(const real_function_3d& f, - const std::array& v1, - const std::array& v2) const; + Function partial_inner(const Function& f, + const std::array& v1, + const std::array& v2) const; + + CCPairFunction partial_inner(const CCPairFunction& other, + const std::array& v1, + const std::array& v2) const; + +}; + +namespace archive { +template +struct ArchiveLoadImpl< ParallelInputArchive, CCPairFunction > { + static inline void load(const ParallelInputArchive& ar, CCPairFunction& p) { + constexpr std::size_t LDIM=CCPairFunction::LDIM; + bool exists, is_pure, has_operator; + ar & exists; + if (exists) { + ar & is_pure & has_operator; + if (is_pure) { + Function f; + ar & f; + p=CCPairFunction(f); + } else { + std::size_t sz; + ar & sz; + std::vector> a(sz),b(sz); + for (auto& aa : a) ar & aa; + for (auto& bb : b) ar & bb; + p=CCPairFunction(a,b); + } + + // store construction parameters of the operator, not the operator itself + if (has_operator) { + auto param=typename CCConvolutionOperator::Parameters(); + OpType type; + ar & param & type; + auto op=std::make_shared>(*ar.get_world(),type,param); + p.reset_operator(op); + } + } + } +}; + +template +struct ArchiveStoreImpl< ParallelOutputArchive, CCPairFunction > { + static inline void store(const ParallelOutputArchive& ar, const CCPairFunction& f) { + bool exists=f.is_assigned(); + ar & exists; + if (exists) { + ar & f.is_pure() & f.has_operator(); + if (f.is_pure()) ar & f.get_function(); + if (f.is_decomposed()) { + auto avec=f.get_a(); + auto bvec=f.get_b(); + ar & avec.size(); + for (const auto& a : avec) ar & a; + for (const auto& b : bvec) ar & b; + } + // store construction parameters of the operator, not the operator itself + if (f.has_operator()) { + ar & f.get_operator().parameters & f.get_operator().type(); + } + } + } }; +} + +/// apply the operator to the argument + +/// the operator is applied to one particle only, the other one is left untouched +/// note the ordering of the particles, cf the corresponding comment in mra.h +/// op.particle==1 : op(f(x,y)) = op(x,x') f(x',y) = result(x,y); +/// op.particle==2 : op(f(x,y)) = op(y,y') f(x,y') = result(y,x); +template +CCPairFunction apply(const SeparatedConvolution& op, const CCPairFunction& arg) { + bool convert_to_pure=(arg.has_operator() or arg.is_pure()); + CCPairFunction result; + World& world = arg.world(); + + if (convert_to_pure) { + auto tmp=arg.to_pure().get_function(); + tmp=op(tmp); + + result=(CCPairFunction(tmp)); + // !! confusing ordering of the result variables!! + if (op.particle()==2) result=result.swap_particles(); + + } else if (arg.is_decomposed_no_op()) { + MADNESS_CHECK(op.particle()==1 or op.particle()==2); + if (op.particle()==1) { + auto tmp= madness::apply(world,op,arg.get_a()); + result=(CCPairFunction(tmp,arg.get_b())); + } else if (op.particle()==2) { + auto tmp= madness::apply(world,op,arg.get_b()); + result=(CCPairFunction(tmp,arg.get_a())); + } + + } else { + MADNESS_CHECK_THROW(false,"confused type in apply(CCPairFunction)"); + } + + return result; +} + +/// apply the operator to the argument + +/// the operator is applied to one particle only, the other one is left untouched +/// note the ordering of the particles, cf the corresponding comment in mra.h +/// op.particle==1 : op(f(x,y)) = op(x,x') f(x',y) = result(x,y); +/// op.particle==2 : op(f(x,y)) = op(y,y') f(x,y') = result(y,x); +template +std::vector> apply(const SeparatedConvolution& op, const std::vector>& argument) { + std::vector> result; + for (const auto& arg : argument) result.push_back(madness::apply(op, arg)); + return result; +} + +template +CCPairFunction apply(const ProjectorBase& projector, const CCPairFunction& argument) { + auto result=madness::apply(projector,std::vector> (1,argument)); + MADNESS_CHECK(result.size()==1); + return result[0]; +} /// apply the projector on the argument function, potentially yielding a vector of CCPairfunctions as result @@ -514,15 +918,99 @@ using T=double; /// Q12 f12 |ij> = (1 - O1) (1 - O2) f12 i(1) j(2) /// = f12 ij - \sum_k k(1) f_ik(2) j(2) - \sum_k k(2) f_ij(1)j(1) /// which is a pure function and a decomposed function -std::vector apply(const ProjectorBase& P, const std::vector& argument); +template +std::vector> apply(const ProjectorBase& projector, const std::vector>& argument) { + return CCPairFunction::apply(projector,argument); +} + + +template +Function::LDIM>inner(const CCPairFunction& c, const Function::LDIM>& f, + const std::tuple v1, const std::tuple v2) { + constexpr std::size_t LDIM=CCPairFunction::LDIM; + auto v11=std::array({std::get<0>(v1),std::get<1>(v1),std::get<2>(v1)}); + auto v22=std::array({std::get<0>(v2),std::get<1>(v2),std::get<2>(v2)}); + + return c.partial_inner(f,v11,v22); +} + +template +Function::LDIM>inner(const CCPairFunction& c, const Function::LDIM>& f, + const std::array::LDIM>& v1, + const std::array::LDIM>& v2) { + return c.partial_inner(f,v1,v2); +} + +template +CCPairFunction inner(const CCPairFunction& c1, const CCPairFunction& c2, + const std::tuple v1, const std::tuple v2) { + constexpr std::size_t LDIM=CCPairFunction::LDIM; + auto v11=std::array({std::get<0>(v1),std::get<1>(v1),std::get<2>(v1)}); + auto v22=std::array({std::get<0>(v2),std::get<1>(v2),std::get<2>(v2)}); + + return c1.partial_inner(c2,v11,v22); +} + +template +CCPairFunction inner(const CCPairFunction& c1, const CCPairFunction& c2, + const std::array::LDIM>& v1, + const std::array::LDIM>& v2) { + return c1.partial_inner(c2,v1,v2); +} + +template +std::vector> inner(const std::vector>& c1, + const std::vector>& c2, + const std::tuple v1, const std::tuple v2) { + constexpr std::size_t LDIM=CCPairFunction::LDIM; + auto v11=std::array({std::get<0>(v1),std::get<1>(v1),std::get<2>(v1)}); + auto v22=std::array({std::get<0>(v2),std::get<1>(v2),std::get<2>(v2)}); + return inner(c1,c2,v11,v22); +} + +template +std::vector> inner(const std::vector>& c1, + const std::vector>& c2, + const std::array::LDIM>& v1, + const std::array::LDIM>& v2) { + std::vector> result; + for (const auto& cc1 : c1) { + for (const auto& cc2 : c2) { + print("inner of ",cc1.name(), cc2.name()); + result.push_back(inner(cc1,cc2,v1,v2)); + } + } + return result; +} + + +template +std::vector >& operator+=(std::vector >& rhs, + const std::vector >& lhs) { + for (const auto& l : lhs) rhs.push_back(l); + return rhs; +} + +template +std::vector >& operator-=(std::vector >& rhs, + const std::vector >& lhs) { + for (const auto& l : lhs) rhs.push_back(-1.0*l); + return rhs; +} -/// convenience function -CCPairFunction apply(const ProjectorBase& P, const CCPairFunction& argument); +template +std::vector > operator*(const double fac, const std::vector >& arg) { + std::vector> result; + for (const auto& l : arg) result.push_back(fac*l); + return result; +} -real_function_3d inner(const CCPairFunction& c, const real_function_3d& f, - const std::tuple v1, const std::tuple v2={0,1,2}); +template +bool is_collected(const std::vector>& other) { + return CCPairFunction::is_collected(other); +} } // namespace madness #endif //MADNESS_CCPAIRFUNCTION_H diff --git a/src/madness/chem/lowrankfunction.h b/src/madness/chem/lowrankfunction.h new file mode 100644 index 00000000000..10b2aeff235 --- /dev/null +++ b/src/madness/chem/lowrankfunction.h @@ -0,0 +1,1070 @@ +// +// Created by Florian Bischoff on 8/10/23. +// + +#ifndef MADNESS_LOWRANKFUNCTION_H +#define MADNESS_LOWRANKFUNCTION_H + + +#include +#include +#include +#include +#include + + +namespace madness { + + struct LowRankFunctionParameters : QCCalculationParametersBase { + + LowRankFunctionParameters() : QCCalculationParametersBase() { + + // initialize with: key, value, comment (optional), allowed values (optional) + initialize("radius",2.0,"the radius"); + initialize("gamma",1.0,"the exponent of the correlation factor"); + initialize("volume_element",0.1,"volume covered by each grid point"); + initialize("tol",1.e-8,"rank-reduced cholesky tolerance"); + initialize("f12type","Slater","correlation factor",{"Slater","SlaterF12"}); + initialize("orthomethod","cholesky","orthonormalization",{"cholesky","canonical","symmetric"}); + initialize("transpose","slater2","transpose of the matrix",{"slater1","slater2"}); + initialize("gridtype","random","the grid type",{"random","cartesian","spherical"}); + initialize("rhsfunctiontype","exponential","the type of function",{"exponential"}); + initialize("optimize",1,"number of optimization iterations"); + } + + void read_and_set_derived_values(World& world, const commandlineparser& parser, std::string tag) { + read_input_and_commandline_options(world,parser,tag); + } + + double radius() const {return get("radius");} + double gamma() const {return get("gamma");} + double volume_element() const {return get("volume_element");} + double tol() const {return get("tol");} + int optimize() const {return get("optimize");} + std::string gridtype() const {return get("gridtype");} + std::string orthomethod() const {return get("orthomethod");} + std::string rhsfunctiontype() const {return get("rhsfunctiontype");} + std::string f12type() const {return get("f12type");} + }; + + + class gridbase { + public: + double get_volume_element() const {return volume_element;} + double get_radius() const {return radius;} + + // visualize the grid in xyz format + template + void visualize(const std::string filename, const std::vector>& grid) const { + print("visualizing grid to file",filename); + print("a total of",grid.size(),"grid points"); + std::ofstream file(filename); + for (const auto& r : grid) { + // formatted output + file << std::fixed << std::setprecision(6); + for (int i=0; i + class randomgrid : public gridbase { + public: + randomgrid(const double volume_element, const double radius, const Vector origin=Vector(0.0)) + : gridbase(), origin(origin) { + this->volume_element=volume_element; + this->radius=radius; + } + + std::vector> get_grid() const { + std::vector> grid; + long npoint_within_volume=volume()/volume_element; + if (do_print) print("npoint_within_volume",npoint_within_volume); + + auto cell = FunctionDefaults::get_cell(); + auto is_in_cell = [&cell](const Vector& r) { + for (int d = 0; d < NDIM; ++d) if (r[d] < cell(d, 0) or r[d] > cell(d, 1)) return false; + return true; + }; + double rad=radius; + auto o=origin; + auto is_in_sphere = [&rad,&o](const Vector& r) { + return ((r-o).normf() get_origin() const { + return origin; + } + + private: + + double volume() const { + MADNESS_CHECK(NDIM>0 and NDIM<4); + if (NDIM==1) return 2.0*radius; + if (NDIM==2) return constants::pi*radius*radius; + if (NDIM==3) return 4.0 / 3.0 * constants::pi * std::pow(radius, 3.0); + } + + static Vector gaussian_random_distribution(const Vector origin, double variance) { + std::random_device rd{}; + std::mt19937 gen{rd()}; + Vector result; + for (int i = 0; i < NDIM; ++i) { + std::normal_distribution<> d{origin[i], variance}; + result[i]=d(gen); + } + + return result; + } + + Vector origin; + + }; + + template + struct cartesian_grid { + Vector lovec,hivec; + std::vector stride; + long index=0; + long n_per_dim; + long total_n; + Vector increment; + + cartesian_grid(const double volume_per_gridpoint, const double radius) { + double length_per_gridpoint=std::pow(volume_per_gridpoint,1.0/NDIM); + n_per_dim=ceil(2.0*radius/length_per_gridpoint); + print("length per gridpoint, n_per_dim",length_per_gridpoint,n_per_dim); + print("volume_per_gridpoint",std::pow(length_per_gridpoint,NDIM)); + initialize(-radius,radius); + print("increment",increment); + } + + + cartesian_grid(const long n_per_dim, const double lo, const double hi) + : n_per_dim(n_per_dim) { + initialize(lo,hi); + } + + cartesian_grid(const cartesian_grid& other) : lovec(other.lovec), + hivec(other.hivec), stride(other.stride), index(0), n_per_dim(other.n_per_dim), + total_n(other.total_n), increment(other.increment) { + } + + cartesian_grid& operator=(const cartesian_grid& other) { + cartesian_grid tmp(other); + std::swap(*this,other); + return *this; + } + + void initialize(const double lo, const double hi) { + lovec.fill(lo); + hivec.fill(hi); + increment=(hivec-lovec)*(1.0/double(n_per_dim-1)); + stride=std::vector(NDIM,1l); + total_n=std::pow(n_per_dim,NDIM); + for (long i=NDIM-2; i>=0; --i) stride[i]=n_per_dim*stride[i+1]; + + } + + double volume_per_gridpoint() const{ + double volume=1.0; + for (int i=0; i get_coordinates() const { + Vector tmp(NDIM); + for (int idim=0; idim + class molecular_grid : public gridbase { + + public: + /// ctor takes molecule and grid + molecular_grid(const std::vector> origins, randomgrid grid) { + for (const auto& coords : origins) { + atomicgrid.push_back(randomgrid(grid.get_volume_element(),grid.get_radius(),coords)); + } + } + + molecular_grid(const Molecule& molecule, randomgrid grid) : molecular_grid(molecule.get_all_coords_vec(),grid) {} + + std::vector> get_grid() const { + std::vector> grid; + for (const auto& atomic : atomicgrid) { + print("atom sites",atomic.get_origin()); + auto atomgrid=atomic.get_grid(); + grid.insert(grid.end(),atomgrid.begin(),atomgrid.end()); + } + return grid; + } + + private: + std::vector> atomicgrid; + + }; + +template +struct particle { + std::array dims; + + /// default constructor + particle() = default; + + /// convenience for particle 1 (the left/first particle) + static particle particle1() { + particle p; + for (int i=0; i(1,p)) {} + particle(const int p1, const int p2) : particle(std::vector({p1,p2})) {} + particle(const int p1, const int p2,const int p3) : particle(std::vector({p1,p2,p3})) {} + particle(const std::vector p) { + for (int i=0; i get_array() const { + return dims; + } + + + /// assuming two particles only + bool is_first() const {return dims[0]==0;} + /// assuming two particles only + bool is_last() const {return dims[0]==(PDIM);} + + template + typename std::enable_if_t> + get_tuple() const {return std::tuple(dims[0]);} + + template + typename std::enable_if_t> + get_tuple() const {return std::tuple(dims[0],dims[1]);} + + template + typename std::enable_if_t> + get_tuple() const {return std::tuple(dims[0],dims[1],dims[2]);} +}; + +template +std::ostream& operator<<(std::ostream& os, const particle& p) { + os << "("; + for (auto i=0; i +struct LRFunctorBase { + + virtual ~LRFunctorBase() {}; + virtual std::vector> inner(const std::vector>& rhs, + const particle p1, const particle p2) const =0; + + virtual Function inner(const Function& rhs, const particle p1, const particle p2) const { + return inner(std::vector>({rhs}),p1,p2)[0]; + } + + virtual T operator()(const Vector& r) const =0; + virtual typename Tensor::scalar_type norm2() const { + MADNESS_EXCEPTION("L2 norm not implemented",1); + } + + virtual World& world() const =0; + friend std::vector> inner(const LRFunctorBase& functor, const std::vector>& rhs, + const particle p1, const particle p2) { + return functor.inner(rhs,p1,p2); + } + friend Function inner(const LRFunctorBase& functor, const Function& rhs, + const particle p1, const particle p2) { + return functor.inner(rhs,p1,p2); + } + +}; + +template +struct LRFunctorF12 : public LRFunctorBase { +// LRFunctorF12() = default; + LRFunctorF12(const std::shared_ptr> f12, const std::vector>& a, + const std::vector>& b) : f12(f12), a(a), b(b) { + + // if a or b are missing, they are assumed to be 1 + // you may not provide a or b, but if you do they have to have the same size because they are summed up + if (a.size()>0 and b.size()>0) + MADNESS_CHECK_THROW(a.size()==b.size(), "a and b must have the same size"); + if (a.size()==0) this->a.resize(b.size()); + if (b.size()==0) this->b.resize(a.size()); + MADNESS_CHECK_THROW(this->a.size()==this->b.size(), "a and b must have the same size"); + } + + /// delegate to the other ctor with vector arguments + LRFunctorF12(const std::shared_ptr> f12, + const Function& a, const Function& b) + : LRFunctorF12(f12,std::vector>({a}), std::vector>({b})) {} + + +private: + std::shared_ptr> f12; ///< a two-particle function + std::vector> a,b; ///< the lo-dim functions +public: + + World& world() const {return f12->get_world();} + std::vector> inner(const std::vector>& rhs, + const particle p1, const particle p2) const { + + std::vector> result; + // functor is now \sum_i a_i(1) b_i(2) f12 + // result(1) = \sum_i \int a_i(1) f(1,2) b_i(2) rhs(2) d2 + // = \sum_i a_i(1) \int f(1,2) b_i(2) rhs(2) d2 + World& world=rhs.front().world(); + + const int nbatch=30; + for (int i=0; i> rhs_batch; + auto begin= rhs.begin()+i; + auto end= (i+nbatch)(world,rhs_batch.size()); + + if (a.size()==0) tmp2=apply(world,*(f12),rhs_batch); + + for (int ia=0; ia::scalar_type norm2() const { + const Function one = FunctionFactory(world()).f( + [](const Vector& r) { return 1.0; }); + std::vector> pre, post; + std::size_t sz = a.size(); + if (sz == 0) { + pre = std::vector>(1, one); + post = std::vector>(1, one); + } else { + pre = (a.front().is_initialized()) ? a : std::vector>(sz, one); + post = (b.front().is_initialized()) ? b : std::vector>(sz, one); + } + + const SeparatedConvolution& f12a=*(f12); + const SeparatedConvolution f12sq= SeparatedConvolution::combine(f12a,f12a); + + // \int f(1,2)^2 d1d2 = \int f(1,2)^2 pre(1)^2 post(2)^2 d1 d2 + // || \sum_i f(1,2) a_i(1) b_i(2) || = \int ( \sum_{ij} a_i(1) a_j(1) f(1,2)^2 b_i(1) b_j(2) ) d1d2 + std::vector> aa,bb; + for (std::size_t i=0; i::scalar_type term1 =madness::inner(mul(world(),post,post),f12sq(mul(world(),pre,pre))); + typename Tensor::scalar_type term1 =madness::inner(bb,f12sq(aa)); + return sqrt(term1); + + } + + T operator()(const Vector& r) const { + + if (a.size()==0) return 0.0; + auto split = [](const Vector& r) { + Vector first, second; + for (int i=0; iinfo.mu; + auto [first,second]=split(r); + + + double result=0.0; + for (std::size_t ia=0; iainfo.type==OT_SLATER) result1*=exp(-gamma*(first-second).normf()); + else if (f12->info.type==OT_GAUSS) result1*=exp(-gamma* madness::inner(first-second,first-second)); + else { + MADNESS_EXCEPTION("no such operator_type",1); + } + result+=result1; + } + return result; + + } +}; + +template +struct LRFunctorPure : public LRFunctorBase { + LRFunctorPure() = default; + LRFunctorPure(const Function& f) : f(f) {} + World& world() const {return f.world();} + + Function f; ///< a hi-dim function + + std::vector> inner(const std::vector>& rhs, + const particle p1, const particle p2) const { + return madness::innerXX(f,rhs,p1.get_array(),p2.get_array()); +// std::vector> result; +// for (const auto& r : rhs) result.push_back(madness::inner(f,r,p1.get_tuple(),p2.get_tuple())); +// return result; + } + + T operator()(const Vector& r) const { + return f(r); + } + + typename Tensor::scalar_type norm2() const { + return f.norm2(); + } +}; + + + /// LowRankFunction represents a hi-dimensional (NDIM) function as a sum of products of low-dimensional (LDIM) functions + + /// f(1,2) = \sum_i g_i(1) h_i(2) + /// a LowRankFunction can be created from a hi-dim function directly, or from a composite like f(1,2) phi(1) psi(2), + /// where f(1,2) is a two-particle function (e.g. a Slater function) + template + class LowRankFunction { + public: + + World& world; + double rank_revealing_tol=1.e-8; // rrcd tol + std::string orthomethod="canonical"; + bool do_print=false; + std::vector> g,h; + const particle p1=particle::particle1(); + const particle p2=particle::particle2(); + + LowRankFunction(World& world) : world(world) {} + + LowRankFunction(std::vector> g, std::vector> h, + double tol, std::string orthomethod) : world(g.front().world()), + rank_revealing_tol(tol), orthomethod(orthomethod), g(g), h(h) { + + } + + /// shallow copy ctor + LowRankFunction(const LowRankFunction& other) : world(other.world), + g(other.g), h(other.h), + rank_revealing_tol(other.rank_revealing_tol), orthomethod(other.orthomethod) { + } + + /// deep copy + friend LowRankFunction copy(const LowRankFunction& other) { + return LowRankFunction(madness::copy(other.g),madness::copy(other.h),other.rank_revealing_tol,other.orthomethod); + } + + LowRankFunction& operator=(const LowRankFunction& f) { // Assignment required for storage in vector + LowRankFunction ff(f); + std::swap(ff.g,g); + std::swap(ff.h,h); + return *this; + } + + /// function evaluation + T operator()(const Vector& r) const { + Vector first, second; + for (int i=0; i result=copy(*this); + result+=b; + return result; + } + /// subtraction + LowRankFunction operator-(const LowRankFunction& b) const { + LowRankFunction result=copy(*this); + result-=b; + return result; + } + + /// in-place addition + LowRankFunction& operator+=(const LowRankFunction& b) { + + g=append(g,copy(b.g)); + h=append(h,copy(b.h)); + return *this; + } + + /// in-place subtraction + LowRankFunction& operator-=(const LowRankFunction& b) { + g=append(g,-1.0*b.g); // operator* implies deep copy of b.g + h=append(h,copy(b.h)); + return *this; + } + + /// scale by a scalar + template + LowRankFunction operator*(const Q a) const { + return LowRankFunction,NDIM>(g * a, Q(h),rank_revealing_tol,orthomethod); + } + + /// out-of-place scale by a scalar (no type conversion) + LowRankFunction operator*(const T a) const { + return LowRankFunction(g * a, h,rank_revealing_tol,orthomethod); + } + + /// multiplication with a scalar + friend LowRankFunction operator*(const T a, const LowRankFunction& other) { + return other*a; + } + + /// in-place scale by a scalar (no type conversion) + LowRankFunction& operator*=(const T a) { + g=g*a; + return *this; + } + + /// l2 norm + typename TensorTypeData::scalar_type norm2() const { + auto tmp1=matrix_inner(world,h,h); + auto tmp2=matrix_inner(world,g,g); + return sqrt(tmp1.trace(tmp2)); + } + + std::vector> get_functions(const particle& p) const { + MADNESS_CHECK(p.is_first() or p.is_last()); + if (p.is_first()) return g; + return h; + } + + std::vector> get_g() const {return g;} + std::vector> get_h() const {return h;} + + long rank() const {return g.size();} + + /// return the size in GByte + double size() const { + double sz=get_size(world,g); + sz+=get_size(world,h); + return sz; + } + + Function reconstruct() const { + auto fapprox=hartree_product(g[0],h[0]); + for (int i=1; i> orthonormalize(const std::vector>& g) const { + + double tol=rank_revealing_tol; + std::vector> g2; + auto ovlp=matrix_inner(world,g,g); + if (orthomethod=="canonical") { + tol*=0.01; + print("orthonormalizing with method/tol",orthomethod,tol); + g2=orthonormalize_canonical(g,ovlp,tol); + } else if (orthomethod=="cholesky") { + print("orthonormalizing with method/tol",orthomethod,tol); + g2=orthonormalize_rrcd(g,ovlp,tol); + } + else { + MADNESS_EXCEPTION("no such orthomethod",1); + } + double tight_thresh=FunctionDefaults<3>::get_thresh()*0.1; + return truncate(g2,tight_thresh); + } + + + /// optimize the lrf using the lrfunctor + + /// @param[in] nopt number of iterations (wrt to Alg. 4.3 in Halko) + void optimize(const LRFunctorBase& lrfunctor1, const long nopt=1) { + timer t(world); + t.do_print=do_print; + for (int i=0; i ovlp_g = matrix_inner(world, g, g); + Tensor ovlp_h = matrix_inner(world, h, h); + auto [eval_g, evec_g] = syev(ovlp_g); + auto [eval_h, evec_h] = syev(ovlp_h); + + // get relevant part of the eigenvalues and eigenvectors + // eigenvalues are sorted in ascending order + auto get_slice = [](auto eval, double thresh) { + // remove small/negative eigenvalues + eval.screen(thresh); + Slice s; + for (int i=0; i=0.0,"negative eigenvalues in reorthonormalize"); + if (eval[i]>thresh) { + return s=Slice(i,-1); // from i to the end + break; + } + } + return s; + }; + + Slice gslice=get_slice(eval_g,1.e-13); + Slice hslice=get_slice(eval_h,1.e-13); + + Tensor Xplus=copy(evec_g(_,gslice)); + Tensor Xminus=copy(evec_g(_,gslice)); + Tensor Yplus=copy(evec_h(_,hslice)); + Tensor Yminus=copy(evec_h(_,hslice)); + eval_g=copy(eval_g(gslice)); + eval_h=copy(eval_h(hslice)); + + for (int i=0; i M=madness::inner(Xplus,Yplus,0,0); // (X+)^T Y+ + auto [U,s,VT]=svd(M); + + // truncate + typename Tensor::scalar_type s_accumulated=0.0; + int i=s.size()-1; + for (;i>=0; i--) { + s_accumulated+=s[i]; + if (s_accumulated>thresh) { + i++; + break; + } + } + for (int j=0; j XX=madness::inner(Xminus,U,1,0); + Tensor YY=madness::inner(Yminus,VT,1,1); + + g=truncate(transform(world,g,XX)); + h=truncate(transform(world,h,YY)); + } + + + double check_orthonormality(const std::vector>& v) const { + Tensor ovlp=matrix_inner(world,v,v); + return check_orthonormality(ovlp); + } + + double check_orthonormality(const Tensor& ovlp) const { + timer t(world); + t.do_print=do_print; + Tensor ovlp2=ovlp; + for (int i=0; i& lrfunctor1) const { + + timer t(world); + t.do_print=do_print; + + // \int f(1,2)^2 d1d2 + double term1 = lrfunctor1.norm2(); + term1=term1*term1; + t.tag("computing term1"); + + // \int f(1,2) pre(1) post(2) \sum_i g(1) h(2) d1d2 +// double term2=madness::inner(pre*g,f12(post*h)); + double term2=madness::inner(g,inner(lrfunctor1,h,p2,p1)); + t.tag("computing term2"); + + // g functions are orthonormal + // \int gh(1,2)^2 d1d2 = \int \sum_{ij} g_i(1) g_j(1) h_i(2) h_j(2) d1d2 + // = \sum_{ij} \int g_i(1) g_j(1) d1 \int h_i(2) h_j(2) d2 + // = \sum_{ij} delta_{ij} \int h_i(2) h_j(2) d2 + // = \sum_{i} \int h_i(2) h_i(2) d2 + double zero=check_orthonormality(g); + if (zero>1.e-10) print("g is not orthonormal",zero); + double term3a=madness::inner(h,h); + auto tmp1=matrix_inner(world,h,h); + auto tmp2=matrix_inner(world,g,g); + double term3=tmp1.trace(tmp2); +// print("term3/a/diff",term3a,term3,term3-term3a); + t.tag("computing term3"); + + double arg=term1-2.0*term2+term3; + if (arg<0.0) { + print("negative l2 error"); + arg*=-1.0; +// throw std::runtime_error("negative argument in l2error"); + } + double error=sqrt(arg)/sqrt(term1); + if (world.rank()==0 and do_print) { + print("term1,2,3, error",term1, term2, term3, " --",error); + } + + return error; + } + + }; + + // This interface is necessary to compute inner products + template + double inner(const LowRankFunction& a, const LowRankFunction& b) { + World& world=a.world; + return (matrix_inner(world,a.g,b.g).emul(matrix_inner(world,a.h,b.h))).sum(); + } + + + +// template +// LowRankFunction inner(const Function& lhs, const LowRankFunction& rhs, +// const std::tuple v1, const std::tuple v2) { +// World& world=rhs.world; +// // int lhs(1,2) rhs(2,3) d2 = \sum \int lhs(1,2) g_i(2) h_i(3) d2 +// // = \sum \int lhs(1,2) g_i(2) d2 h_i(3) +// LowRankFunction result(world); +// result.h=rhs.h; +// decltype(rhs.g) g; +// for (int i=0; i + LowRankFunction inner(const Function& f1, const LowRankFunction& f2, + const particle p1, const particle p2) { + auto result=inner(f2,f1,p2,p1); + std::swap(result.g,result.h); + return result; + } + + /// lrf(1,3) = inner(lrf(1,2), full(2,3)) + + /// @param[in] f1 the first function + /// @param[in] f2 the second function + /// @param[in] p1 the integration variable of the first function + /// @param[in] p2 the integration variable of the second function + template + LowRankFunction inner(const LowRankFunction& f1, const Function& f2, + const particle p1, const particle p2) { + static_assert(TensorTypeData::iscomplex==false, "complex inner in LowRankFunction not implemented"); + World& world=f1.world; + static_assert(2*PDIM==NDIM); + // int f(1,2) k(2,3) d2 = \sum \int g_i(1) h_i(2) k(2,3) d2 + // = \sum g_i(1) \int h_i(2) k(2,3) d2 + LowRankFunction result(world); + if (p1.is_last()) { // integrate over 2: result(1,3) = lrf(1,2) f(2,3) + result.g = f1.g; + change_tree_state(f1.h,reconstructed); + result.h=innerXX(f2,f1.h,p2.get_array(),particle::particle1().get_array()); + } else if (p1.is_first()) { // integrate over 1: result(2,3) = lrf(1,2) f(1,3) + result.g = f1.h; // correct! second variable of f1 becomes first variable of result + change_tree_state(f1.g,reconstructed); + result.h=innerXX(f2,f1.g,p2.get_array(),particle::particle1().get_array()); + } + return result; + } + + /// lrf(1,3) = inner(lrf(1,2), lrf(2,3)) + + /// @param[in] f1 the first function + /// @param[in] f2 the second function + /// @param[in] p1 the integration variable of the first function + /// @param[in] p2 the integration variable of the second function + template + LowRankFunction inner(const LowRankFunction& f1, const LowRankFunction& f2, + const particle p1, const particle p2) { + World& world=f1.world; + static_assert(2*PDIM==NDIM); + + // inner(lrf(1,2) ,lrf(2,3) ) = \sum_ij g1_i(1) h2_j(3) + auto matrix=matrix_inner(world,f2.get_functions(p2),f1.get_functions(p1)); + auto htilde=transform(world,f2.get_functions(p2.complement()),matrix); + auto gg=copy(world,f1.get_functions(p1.complement())); + return LowRankFunction(gg,htilde,f1.rank_revealing_tol,f1.orthomethod); + } + + /// f(1) = inner(lrf(1,2), f(2)) + + /// @param[in] f1 the first function + /// @param[in] vf vector of the second functions + /// @param[in] p1 the integration variable of the first function + /// @param[in] p2 the integration variable of the second function, dummy variable for consistent notation + template + std::vector> inner(const LowRankFunction& f1, const std::vector>& vf, + const particle p1, const particle p2=particle::particle1()) { + World& world=f1.world; + static_assert(2*PDIM==NDIM); + MADNESS_CHECK(p2.is_first()); + + // inner(lrf(1,2), f_k(2) ) = \sum_i g1_i(1) + auto matrix=matrix_inner(world,f1.get_functions(p1),vf); + return transform(world,f1.get_functions(p1.complement()),matrix); + } + + /// f(1) = inner(lrf(1,2), f(2)) + + /// @param[in] f1 the first function + /// @param[in] vf the second function + /// @param[in] p1 the integration variable of the first function + /// @param[in] p2 the integration variable of the second function, dummy variable for consistent notation + template + Function inner(const LowRankFunction& f1, const Function& f2, + const particle p1, const particle p2=particle::particle1()) { + return inner(f1,std::vector>({f2}),p1,p2)[0]; + } + + template + class LowRankFunctionFactory { + public: + + const particle p1=particle::particle1(); + const particle p2=particle::particle2(); + + LowRankFunctionParameters parameters; + std::vector> origins; ///< origins of the molecular grid + + LowRankFunctionFactory() = default; + LowRankFunctionFactory(const LowRankFunctionParameters param, const std::vector> origins={}) + : parameters(param), origins(origins) {} + + LowRankFunctionFactory(const LowRankFunctionParameters param, const Molecule& molecule) + : LowRankFunctionFactory(param,molecule.get_all_coords_vec()){} + + LowRankFunctionFactory(const LowRankFunctionFactory& other) = default; + + LowRankFunctionFactory& set_radius(const double radius) { + parameters.set_user_defined_value("radius",radius); + return *this; + } + LowRankFunctionFactory& set_volume_element(const double volume_element) { + parameters.set_user_defined_value("volume_element",volume_element); + return *this; + } + LowRankFunctionFactory& set_rank_revealing_tol(const double rrtol) { + parameters.set_user_defined_value("tol",rrtol); + return *this; + } + LowRankFunctionFactory& set_orthomethod(const std::string orthomethod) { + parameters.set_user_defined_value("orthomethod",orthomethod); + return *this; + } + + LowRankFunction project(const LRFunctorBase& lrfunctor) const { + World& world=lrfunctor.world(); + bool do_print=true; + timer t1(world); + t1.do_print=do_print; + auto orthomethod=parameters.orthomethod(); + auto rank_revealing_tol=parameters.tol(); + + // get sampling grid + std::vector> grid; + randomgrid rgrid(parameters.volume_element(),parameters.radius()); + if (origins.size()>0) { + molecular_grid mgrid(origins,rgrid); + grid=mgrid.get_grid(); + } else { + grid=rgrid.get_grid(); + } + if (world.rank()==0) print("grid size",grid.size()); + + auto Y=Yformer(lrfunctor,grid,parameters.rhsfunctiontype()); + t1.tag("Yforming"); + + auto ovlp=matrix_inner(world,Y,Y); // error in symmetric matrix_inner, use non-symmetric form here! + t1.tag("compute ovlp"); + auto g=truncate(orthonormalize_rrcd(Y,ovlp,rank_revealing_tol)); + t1.tag("rrcd/truncate/thresh"); + auto sz=get_size(world,g); + if (world.rank()==0 and do_print) print("gsize",sz); +// check_orthonormality(g); + + if (world.rank()==0 and do_print) { + print("Y.size()",Y.size()); + print("g.size()",g.size()); + } + + auto h=truncate(inner(lrfunctor,g,p1,p1)); + t1.tag("Y backprojection with truncation"); + return LowRankFunction(g,h,parameters.tol(),parameters.orthomethod()); + + } + + /// apply a rhs (delta or exponential) on grid points to the hi-dim function and form Y = A_ij w_j (in Halko's language) + std::vector> Yformer(const LRFunctorBase& lrfunctor1, const std::vector>& grid, + const std::string rhsfunctiontype, const double exponent=30.0) const { + + World& world=lrfunctor1.world(); + std::vector> Y; + if (rhsfunctiontype=="exponential") { + std::vector> omega; + double coeff=std::pow(2.0*exponent/constants::pi,0.25*LDIM); + for (const auto& point : grid) { + omega.push_back(FunctionFactory(world) + .functor([&point,&exponent,&coeff](const Vector& r) + { + auto r_rel=r-point; + return coeff*exp(-exponent*madness::inner(r_rel,r_rel)); + })); + } + Y=inner(lrfunctor1,omega,p2,p1); + } else { + MADNESS_EXCEPTION("confused rhsfunctiontype",1); + } + auto norms=norm2s(world,Y); + std::vector> Ynormalized; + + for (int i=0; iparameters.tol()) Ynormalized.push_back(Y[i]); + normalize(world,Ynormalized); + return Ynormalized; + } + + }; + + +} // namespace madness + +#endif //MADNESS_LOWRANKFUNCTION_H diff --git a/src/madness/chem/molecule.h b/src/madness/chem/molecule.h index 4a575273bb4..341401c86f4 100644 --- a/src/madness/chem/molecule.h +++ b/src/madness/chem/molecule.h @@ -107,6 +107,16 @@ class Atom { void serialize(Archive& ar) { ar & x & y & z & q & atomic_number & mass & pseudo_atom; } + hashT hash() const { + hashT h=hash_value(x); + hash_combine(h,y); + hash_combine(h,z); + hash_combine(h,q); + hash_combine(h,atomic_number); + hash_combine(h,mass); + hash_combine(h,pseudo_atom); + return h; + } }; std::ostream& operator<<(std::ostream& s, const Atom& atom); @@ -521,6 +531,13 @@ class Molecule { void serialize(Archive& ar) { ar & atoms & rcut & core_pot & parameters & pointgroup_ & field; } + + hashT hash() const { + hashT h= hash_range(atoms.begin(),atoms.end()); + hash_combine(h,hash_range(rcut.begin(),rcut.end())); + hash_combine(h,pointgroup_); + return h; + } [[nodiscard]] json to_json() const; }; diff --git a/src/madness/chem/mp2.cc b/src/madness/chem/mp2.cc index c05323218e9..0131703ed2a 100644 --- a/src/madness/chem/mp2.cc +++ b/src/madness/chem/mp2.cc @@ -50,6 +50,9 @@ #include #include #include +#include +#include +#include #include @@ -234,32 +237,18 @@ double MP2::value(const Tensor& x) { return correlation_energy; } - // DEBUG: INTEGRAL TEST - for (int i = param.freeze(); i < hf->nocc(); ++i) { - for (int j = i; j < hf->nocc(); ++j) { - ElectronPair test = make_pair(i, j); - if (world.rank() == 0) { - std::cout << "\n-----------------------------------------\n"; - std::cout << "<" << i << j << "|gQf|" << i << j << "> =" << test.ij_gQf_ij << std::endl; - std::cout << "<" << j << i << "|gQf|" << i << j << "> =" << test.ji_gQf_ij << std::endl; - } - } - } - // DEBUG END - // compute the 0th order term and do some coarse pre-iterations for (int i = param.freeze(); i < hf->nocc(); ++i) { for (int j = i; j < hf->nocc(); ++j) { pairs(i, j) = make_pair(i, j); // initialize - solve_residual_equations(pairs(i, j), param.econv() * 0.5, - param.dconv()); - correlation_energy += pairs(i, j).e_singlet - + pairs(i, j).e_triplet; + solve_residual_equations(pairs(i, j), param.econv() * 0.5, param.dconv()); + correlation_energy += pairs(i, j).e_singlet + pairs(i, j).e_triplet; } } if (world.rank() == 0) { printf("current decoupled mp2 energy %12.8f\n", correlation_energy); } + if (param.no_compute()) return correlation_energy; correlation_energy = 0.0; if (hf->get_calc().param.do_localize()) { @@ -280,12 +269,325 @@ double MP2::value(const Tensor& x) { return correlation_energy; } +double MP2::mp3() const { + + print_header2("computing the MP3 correlation energy"); + print_header3("prepare the cluster function"); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + + auto R2 = hf->nemo_ptr->ncf->square(); + auto R = hf->nemo_ptr->ncf->function(); + + double mp3_energy = 0.0; + // load converged MP1 wave functions + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = i; j < hf->nocc(); ++j) { +// pairs(i, j) = make_pair(i, j); // initialize + ClusterFunction tmp; + tmp.push_back(CCPairFunction(pairs(i, j).function)); + CCPairFunction ij(hf->nemo(i), hf->nemo(j)); + + CCConvolutionOperator::Parameters cparam; + cparam.thresh_op *= 0.1; + auto f12 = CCConvolutionOperatorPtr(world, OpType::OT_F12, cparam); + auto vfij = Q12(std::vector>({f12 * ij})); + for (auto& p: vfij) tmp.push_back(p); + + clusterfunctions(i, j) = tmp; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } +// Pairs clusterfunctions_R2; +// for (int i=param.freeze(); inocc(); ++i) { +// for (int j=i; jnocc(); ++j) { +// { +// auto tmp1 = multiply(clusterfunctions(i, j), R2, {0, 1, 2}); +// auto tmp2 = multiply(tmp1, R2, {3, 4, 5}); +// for (auto& t: tmp2) clusterfunctions_R2(i, j).push_back(t); +// if (i!=j) { +// for (const auto& t : clusterfunctions_R2(i,j)) { +// clusterfunctions_R2(j, i).push_back(t.swap_particles()); +// } +// } +// } +// } +// } + + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = i; j < hf->nocc(); ++j) { + print("info of clusterfunction ", i, j); + for (auto& c: clusterfunctions(i, j)) c.info(); + } + } + + print_header3("recompute the MP2 energy"); + + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + timer t2(world, "recompute MP2"); + + // recompute MP2 energy + double mp2_energy=0.0; + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = i; j < hf->nocc(); ++j) { + + auto bra=CCPairFunction(hf->nemo(i),hf->nemo(j)); + double direct=inner({bra},g12*clusterfunctions(i,j),R2); + double exchange=inner({bra},g12*clusterfunctions(j,i),R2); + double fac=(i==j) ? 0.5: 1.0; + double pair_energy=fac*(4.0*direct-2.0*exchange); + printf("MP2 energy for pair %2d %2d: %12.8f\n",i,j,pair_energy); + mp2_energy+=pair_energy; + } + } + printf("total mp2 energy %12.8f\n",mp2_energy); + t2.tag("recompute MP2 energy"); + + print_header3("compute the MP3 energy"); + + // compute the term (2) + madness::Pairs gij; + + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = param.freeze(); j < hf->nocc(); ++j) { + gij.insert(i,j,(*g12)(hf->nemo(i)*hf->R2orbital(j))); + } + } + + double term_CD=0.0; + double term_GHIJ=0.0; + double term_KLMN=0.0; + double term_EF=0.0; + + if (1) { + print_header3("compute term_CD of the MP3 energy with R2_bra"); + // compute the MP3 energy + if (1) { + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = i; j < hf->nocc(); ++j) { + auto bra = clusterfunctions(i, j); + double tmp1 = inner(bra, g12 * clusterfunctions(i, j), R2); + double tmp2 = inner(bra, g12 * clusterfunctions(j, i), R2); +// auto bra = clusterfunctions_R2(i, j); +// double tmp1 = inner(bra, g12 * clusterfunctions(i, j)); +// double tmp2 = inner(bra, g12 * clusterfunctions(j, i)); + double fac = (i == j) ? 0.5 : 1.0; + double tmp = fac * (4.0 * tmp1 - 2.0 * tmp2); + printf("mp3 energy: term_CD %2d %2d: %12.8f\n", i, j, tmp); + term_CD += tmp; + } + } + } + printf("MP3 energy: term_CD %12.8f\n", term_CD); + t2.tag("CD term"); + + + // compute intermediates for terms G, I, H, and J + + // \sum_j tau_ij(1,2) * phi_j(2) + std::vector tau_kk_i(hf->nocc()); + // \sum_j tau_ij(1,2) * phi_j(1) + std::vector tau_ij_j(hf->nocc()); + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = param.freeze(); j < hf->nocc(); ++j) { + + auto tmp2=multiply(clusterfunctions(i,j),hf->R2orbital(i),{0,1,2}); + for (auto& t : tmp2) tau_kk_i[j].push_back(t); + + // auto tmp1 = multiply(clusterfunctions(i, j), hf->R2orbital(j), {0, 1, 2}); + // for (auto& t: tmp1) tau_kk_i[i].push_back(t); + + auto tmp3 = multiply(clusterfunctions(i, j), hf->R2orbital(j), {0, 1, 2}); + for (auto& t: tmp3) tau_ij_j[i].push_back(t); + } + } + print("info on tau_kk_i, consolidated with op_pure_to_pure"); + for (int i = param.freeze(); i < hf->nocc(); ++i) { + tau_kk_i[i]=consolidate(tau_kk_i[i],{"op_pure_to_pure","op_dec_to_dec"}); + for (auto& c: tau_kk_i[i]) c.info(); + } + print("info on tau_ij_j, consolidated with op_pure_to_pure"); + for (int i = param.freeze(); i < hf->nocc(); ++i) { + tau_ij_j[i]=consolidate(tau_ij_j[i],{"op_pure_to_pure","op_dec_to_dec"}); + for (auto& c: tau_ij_j[i]) c.info(); + } + + t2.tag("GHIJ term prep"); + + // terms G, I, H, J of Bartlett/Silver 1975 + real_convolution_3d& g = *(g12->get_op()); + for (int i = param.freeze(); i < hf->nocc(); ++i) { + + // tmp(1,2) = g(1,1') | tau_ij(1',2) j(2) > + timer t4(world, "gtau"); + g.set_particle(1); + auto gtau_same = g(tau_kk_i[i]); + t4.tag("compute gtau_same"); + // gtau_same=consolidate(gtau_same,{"op_pure_to_pure"}); + // t4.tag("consolidate gtau_same"); + + // tmp(1',2) = g(1',1) | tau_ij(1,2) j(1) > + g.set_particle(1); + auto gtau_other = g(tau_ij_j[i]); // < tau_ij(1,2) j(1) | g(1,1') | + t4.tag("compute gtau_other"); + // gtau_other=consolidate(gtau_other,{"op_pure_to_pure"}); + // t4.tag("consolidate gtau_other"); + + auto bra_kk_i = multiply(tau_kk_i[i],R2,{3,4,5}); + auto bra_ij_j = multiply(tau_ij_j[i],R2,{3,4,5}); + + double G = inner(bra_kk_i, gtau_same); + printf("G %12.8f\n", G); + + double H = inner(bra_ij_j, gtau_other); + printf("H %12.8f\n", H); + + double I = inner(bra_kk_i, gtau_other); + printf("I %12.8f\n", I); + + double J = inner(bra_ij_j, gtau_same); + printf("J %12.8f\n", J); + + t4.tag("compute inner products"); + double tmp = (8.0 * G - 4.0 * I + 2.0 * H - 4.0 * J); + printf("mp3 energy: term_GHIJ %2d %12.8f\n", i, tmp); + term_GHIJ += tmp; + } + printf("MP3 energy: term_GHIJ %12.8f\n", term_GHIJ); + t2.tag("GHIJ term"); + } + + + if (1) { // new KLMN algorithm + timer multiply_KLMN(world,"multiplication in KLMN term"); + multiply_KLMN.interrupt(); + timer inner_KLMN(world,"inner in KLMN term"); + inner_KLMN.interrupt(); + // prepare intermediates for terms K, L, M, N of Bartlett/Silver 1975 + // tau_g_ij(1,2) = \sum_k tau_ik(1,1') g_jk(2) + Pairs tau_ik_g_kj, tau_kj_g_ki; + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = param.freeze(); j < hf->nocc(); ++j) { + multiply_KLMN.resume(); + std::vector> tmp1, tmp2; + for (int k = param.freeze(); k < hf->nocc(); ++k) { + tmp1+=multiply(clusterfunctions(i,k), gij(k, j), {3, 4, 5}); + tmp2+=multiply(clusterfunctions(k,j), gij(k, i), {3, 4, 5}); + } + tmp1=consolidate(tmp1,{}); + tmp2=consolidate(tmp2,{}); + tau_ik_g_kj(i,j)=tmp1; + tau_kj_g_ki(i,j)=tmp2; + multiply_KLMN.interrupt(); + + inner_KLMN.resume(); + double K = inner(clusterfunctions(i,j), tau_ik_g_kj(i,j), R2); + double L = inner(clusterfunctions(i,j), tau_kj_g_ki(i,j), R2); + double M = inner(clusterfunctions(j,i), tau_kj_g_ki(i,j), R2); + double N = inner(clusterfunctions(j,i), tau_ik_g_kj(i,j), R2); + inner_KLMN.interrupt(); + + double tmp = -4 * K - 4 * L + 2 * M + 2 * N; + printf("mp3 energy: term_KLMN with particle=1 %2d %2d %12.8f\n", i, j, tmp); + term_KLMN += tmp; + } + } + printf("MP3 energy: term_KLMN (KLMN) %12.8f\n", term_KLMN); + multiply_KLMN.print("multiplication in KLMN term"); + inner_KLMN.print("inner in KLMN term"); + t2.tag("KLMN term"); + } + + if (0) { // old KLMN algorithm + timer multiply_KLMN(world,"multiplication in KLMN term"); + multiply_KLMN.interrupt(); + timer inner_KLMN(world,"inner in KLMN term"); + inner_KLMN.interrupt(); + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = param.freeze(); j < hf->nocc(); ++j) { + double tmp = 0.0; + for (int k = param.freeze(); k < hf->nocc(); ++k) { + + auto tau_ik = clusterfunctions(i, k); + auto tau_kj = clusterfunctions(k, j); + auto tau_ij = clusterfunctions(i, j); + auto tau_ji = clusterfunctions(j, i); + multiply_KLMN.resume(); + auto tau_ik_gkj = multiply(tau_ik, gij(k, j), {3, 4, 5}); + auto tau_kj_gki = multiply(tau_kj, gij(k, i), {3, 4, 5}); + multiply_KLMN.interrupt(); +// auto tau_ik_gkj = multiply(tau_ik,gij(k,j),{0,1,2}); +// auto tau_kj_gki = multiply(tau_kj,gij(k,i),{0,1,2}); + + inner_KLMN.resume(); + double K = inner(tau_ij, tau_ik_gkj, R2); + double L = inner(tau_ij, tau_kj_gki, R2); + double M = inner(tau_ji, tau_kj_gki, R2); + double N = inner(tau_ji, tau_ik_gkj, R2); + inner_KLMN.interrupt(); + + tmp += -4 * K - 4 * L + 2 * M + 2 * N; + } + printf("mp3 energy: term_KLMN with particle=1 %2d %2d %12.8f\n", i, j, tmp); + term_KLMN += tmp; + } + } + printf("MP3 energy: term_KLMN (KLMN), old algorithm %12.8f\n", term_KLMN); + multiply_KLMN.print("multiplication in KLMN term"); + inner_KLMN.print("inner in KLMN term"); + t2.tag("KLMN term"); + } + + print_header3("computing term EF of the MP3 energy with R2_bra"); + for (int i = param.freeze(); i < hf->nocc(); ++i) { + for (int j = param.freeze(); j < hf->nocc(); ++j) { + double tmp=0.0; + for (int k=param.freeze(); knocc(); ++k) { + for (int l=param.freeze(); lnocc(); ++l) { + auto bra_ik = clusterfunctions(i,k); + auto bra_ki = clusterfunctions(k,i); + double ovlp_E=inner(bra_ik,clusterfunctions(j,l),R2); + double ovlp_F=inner(bra_ki,clusterfunctions(j,l),R2); + auto ket_i=hf->nemo(i); + auto ket_k=hf->nemo(k); + auto bra_j=hf->R2orbital(j); + auto bra_l=hf->R2orbital(l); + + double g_jlik=inner(bra_j*ket_i, (*g12)(bra_l*ket_k)); +// print("",g_jlik); + tmp+=(2.0*ovlp_E - ovlp_F)*g_jlik; + } + } + printf("mp3 energy: term_EF %2d %2d %12.8f\n",i,j,tmp); + term_EF+=tmp; + } + } + printf("MP3 energy: term_EF %12.8f\n",term_EF); + t2.tag("EF term"); + + printf("term_CD %12.8f\n",term_CD); + printf("term_GHIJ %12.8f\n",term_GHIJ); + printf("term_KLMN %12.8f\n",term_KLMN); + printf("term_EF %12.8f\n",term_EF); + mp3_energy=term_CD+term_GHIJ+term_KLMN+term_EF; + printf("MP3 energy contribution %12.8f\n",mp3_energy); + + + return mp3_energy; +} + /// solve the residual equation for electron pair (i,j) void MP2::solve_residual_equations(ElectronPair& result, const double econv, const double dconv) const { - if (result.converged) { + if (result.converged or param.no_compute()) { result.print_energy(); return; } @@ -498,23 +800,6 @@ void MP2::increment(ElectronPair& pair, real_convolution_6d& green) { } } -/// swap particles 1 and 2 - -/// param[in] f a function of 2 particles f(1,2) -/// return the input function with particles swapped g(1,2) = f(2,1) -real_function_6d MP2::swap_particles(const real_function_6d& f) const { - - // this could be done more efficiently for SVD, but it works decently - std::vector map(6); - map[0] = 3; - map[1] = 4; - map[2] = 5; // 2 -> 1 - map[3] = 0; - map[4] = 1; - map[5] = 2; // 1 -> 2 - return mapdim(f, map); -} - double MP2::asymmetry(const real_function_6d& f, const std::string s) const { return 0.0; const real_function_6d ff = swap_particles(f); @@ -525,13 +810,6 @@ double MP2::asymmetry(const real_function_6d& f, const std::string s) const { return diff; } -void MP2::test(const std::string filename) { - if (world.rank() == 0) - printf("starting coupling at time %8.1fs\n", wall_time()); - if (world.rank() == 0) - printf("ending coupling at time %8.1fs\n", wall_time()); -} - /// compute the matrix element /// scales quartically. I think I can get this down to cubically by @@ -540,6 +818,7 @@ void MP2::test(const std::string filename) { /// @return the energy double MP2::compute_gQf(const int i, const int j, ElectronPair& pair) const { + CCTimer t1(world,"gQf old"); // for clarity of notation const int k = pair.i; const int l = pair.j; @@ -619,6 +898,33 @@ double MP2::compute_gQf(const int i, const int j, ElectronPair& pair) const { if (world.rank() == 0) printf("<%d%d | g Q12 f | %d%d> %12.8f\n", i, j, k, l, e); + print("gQf old",t1.reset()); + + CCTimer timer(world,"gQf with ccpairfunction"); + CCConvolutionOperator::Parameters param; + auto f=CCConvolutionOperatorPtr(world,OpType::OT_F12,param); + auto g=CCConvolutionOperatorPtr(world,OpType::OT_G12,param); +// print("operator constructor",timer.reset()); + + CCPairFunction fij(f,ket_i,ket_j); + CCPairFunction gij(g,bra_k,bra_l); +// CCPairFunction ij({psi},{psi}); + std::vector> vfij={fij}; + std::vector> vgij={gij}; +// std::vector vij={ij}; +// print("g/f ij constructor",timer.reset()); + +// StrongOrthogonalityProjector SO(world); +// SO.set_spaces({psi},{psi},{psi},{psi}); + std::vector> Qfij=Q12(vfij); +// std::vector Qgij=Q12(vgij); +// print("SO application",timer.reset()); + + double result1=inner(vgij,Qfij); + print("inner",timer.reset()); + + print(")",result1); + return e; } diff --git a/src/madness/chem/mp2.h b/src/madness/chem/mp2.h index 72e13dce929..02ec72b96f1 100644 --- a/src/madness/chem/mp2.h +++ b/src/madness/chem/mp2.h @@ -331,6 +331,7 @@ class MP2 : public OptimizationTargetInterface, public QCPropertyInterface { initialize < int > ("freeze", 0); initialize < int > ("maxsub", 2); initialize < bool > ("restart", true); + initialize < bool > ("no_compute", false); initialize < int > ("maxiter", 5); } @@ -364,6 +365,7 @@ class MP2 : public OptimizationTargetInterface, public QCPropertyInterface { int i() const { return this->get >("pair")[0]; } /// convenience function int j() const { return this->get >("pair")[1]; } /// convenience function int restart() const { return this->get("restart"); } /// convenience function + int no_compute() const { return this->get("no_compute"); } /// convenience function int maxiter() const { return this->get("maxiter"); } /// convenience function int maxsub() const { return this->get("maxsub"); } /// convenience function bool do_oep() const { return do_oep1;} @@ -462,6 +464,8 @@ class MP2 : public OptimizationTargetInterface, public QCPropertyInterface { return hf->orbital_energy(i) + hf->orbital_energy(j); } + double mp3() const; + /// solve the residual equation for electron pair (i,j) /// \todo Parameter documentation. Below are un-doxygenated comments that no longer seem relevant? @@ -497,16 +501,8 @@ class MP2 : public OptimizationTargetInterface, public QCPropertyInterface { /// param[in] green the Green's function void increment(ElectronPair& pair, real_convolution_6d& green); - /// swap particles 1 and 2 - - /// param[in] f a function of 2 particles f(1,2) - /// return the input function with particles swapped g(1,2) = f(2,1) - real_function_6d swap_particles(const real_function_6d& f) const; - double asymmetry(const real_function_6d& f, const std::string s) const; - void test(const std::string filename); - /// compute the matrix element /// scales quartically. I think I can get this down to cubically by diff --git a/src/madness/chem/mp3.cc b/src/madness/chem/mp3.cc new file mode 100644 index 00000000000..76c0e7d3e51 --- /dev/null +++ b/src/madness/chem/mp3.cc @@ -0,0 +1,984 @@ +// +// Created by Florian Bischoff on 2/15/24. +// + +#include "mp3.h" + +namespace madness { +double MP3::compute_mp3_cd(const Pairs& mp2pairs) const { + print_header3("compute term_CD of the MP3 energy with R2_bra"); + // compute the MP3 energy + std::size_t nocc=mo_ket().size(); + print("freeze, nocc",parameters.freeze(),nocc); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + const auto& R2=nemo_->R_square; + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + double result = 0.0; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + auto bra = clusterfunctions(i, j); + double tmp1 = inner(bra, g12 * clusterfunctions(i, j), R2); + double tmp2 = inner(bra, g12 * clusterfunctions(j, i), R2); + double fac = (i == j) ? 0.5 : 1.0; + double tmp = fac * (4.0 * tmp1 - 2.0 * tmp2); + printf("mp3 energy: term_CD %2d %2d: %12.8f\n", i, j, tmp); + result+= tmp; + } + } + printf("MP3 energy: term_CD %12.8f\n", result); + return result; +}; + +double MP3::compute_mp3_ef(const Pairs& mp2pairs) const { + + // prepare cluster functions + std::size_t nocc=mo_ket().size(); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + + + + const auto& R2=nemo_->R_square; + double result=0.0; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + + + print_header3("computing term EF of the MP3 energy with R2_bra"); + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + double tmp=0.0; + for (int k=parameters.freeze(); k< nocc; ++k) { + for (int l=parameters.freeze(); l",g_jlik); + tmp+=(2.0*ovlp_E - ovlp_F)*g_jlik; + } + } + printf("mp3 energy: term_EF %2d %2d %12.8f\n",i,j,tmp); + result+=tmp; + } + } + printf("MP3 energy: term_EF %12.8f\n",result); + return result; +}; + +/// permutations in physisists notation: = (ik | jl), loop over ik all_permutations; +/// for (int ik=0; ik make_all_eri_permutations() const{ + permutation p1(i,j,k,l); + permutation p2(k,j,i,l); + permutation p3(i,l,k,j); + permutation p4(k,l,i,j); + permutation p5(j,i,l,k); + permutation p6(l,i,j,k); + permutation p7(j,k,l,i); + permutation p8(l,k,j,i); + std::vector result({p1,p2,p3,p4,p5,p6,p7,p8}); + return remove_duplicates(result); + } + + /// <\tau_{ij} | \tau_{kl}> = <\tau_{ji} | \tau_{lk}> = <\tau_{kl} | \tau_{ij}>= <\tau_{lk} | \tau_{ji}> + std::vector make_all_tau_permutations() const{ + permutation p1(i,j,k,l); + permutation p2(j,i,l,k); + permutation p3(k,l,i,j); + permutation p4(l,k,j,i); + return remove_duplicates({p1,p2,p3,p4}); + } + + bool operator<(const permutation& other) const { + return std::tie(i,j,k,l) < std::tie(other.i,other.j,other.k,other.l); + } + bool operator==(const permutation& other) const { + return std::tie(i,j,k,l) == std::tie(other.i,other.j,other.k,other.l); + } + bool operator!=(const permutation& other) const { + return (not (*this==other)); + } + static std::vector remove_duplicates(std::vector v) { + // remove duplicates + std::sort(v.begin(), v.end(),[](permutation a, permutation b) { return a < b; }); + auto last = std::unique(v.begin(), v.end()); + v.erase(last, v.end()); + return v; + } +}; +std::ostream& operator<<(std::ostream& os, const permutation& p) { + os << "(" << p.i << p.j << p.k << p.l << ")"; + return os; +} +/// compute the EF term of the MP3 energy with permutational symmetry + +/// there is the usual 8-fold permutational symmetry for 2-electron integrals +/// = = = < il | kj > +/// = = = = < kj | il > +/// implemented as loop over (ij) = \sum_i& mp2pairs) const { + print_header3("computing term EF of the MP3 energy with R2_bra"); + // prepare cluster functions + std::size_t nocc=mo_ket().size(); + std::size_t nfrozen=parameters.freeze(); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + + const auto& R2=nemo_->R_square; + double result=0.0; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + // number of pairs + auto npair = [&nocc, &nfrozen]() { return (nocc - nfrozen) * (nocc - nfrozen + 1) / 2; }; + + // turn composite index ij into i and j, taking care of frozen orbitals + PairVectorMap map=PairVectorMap::triangular_map(nfrozen,nocc); + auto ij_to_i_and_j = [&map](const int ij) { return map.map[ij]; }; + + /// = (ik | jl) + std::vector all_tau_permutations; + // loop over unique pairs (ij) + for (int ij=0; ij> tmp_tau; + // loop over all k and l + for (int k=nfrozen; k (2* - ) + // = (2*(ik|jl) - (jk|il)) + const auto& ket_i=nemo_orbital[i]; + const auto& ket_k=nemo_orbital[k]; + const auto& ket_l=nemo_orbital[l]; + const auto& bra_i=R2_orbital[i]; + const auto& bra_j=R2_orbital[j]; + const auto& bra_l=R2_orbital[l]; + double g_ikjl=inner(bra_i*ket_k, (*g12)(bra_j*ket_l)); + double g_jkil=inner(bra_j*ket_k, (*g12)(bra_l*ket_i)); + // double ovlp=inner(clusterfunctions(i,j),clusterfunctions(k,l),R2); + tmp_tau+=weight*(2.0* g_ikjl - g_jkil)*clusterfunctions(k,l); + // tmp+=weight*ovlp*(2.0* g_ikjl - g_jkil); + } + } + tmp_tau=consolidate(tmp_tau,{"remove_lindep"}); + tmp=inner(clusterfunctions(i,j),tmp_tau,R2); + printf("mp3 energy: term_EF %2d %2d %12.8f\n",i,j,tmp); + result+=tmp; + } + printf("MP3 energy: term_EF %12.8f\n",result); + + // sanity check + int npermutations=all_tau_permutations.size(); + all_tau_permutations=permutation::remove_duplicates(all_tau_permutations); + int nuniquepermutations=all_tau_permutations.size(); + int ntotalpermutations=std::pow(nocc-nfrozen,4); + MADNESS_CHECK_THROW(npermutations==nuniquepermutations,"incorrect number of unique permutations"); + MADNESS_CHECK_THROW(npermutations==ntotalpermutations,"incorrect number of unique permutations"); + + return result; +}; + +double MP3::compute_mp3_ef_low_scaling(const Pairs& mp2pairs, + const Pairs>> clusterfunctions) const { + + print_header3("computing term EF of the MP3 energy with R2_bra, low-scaling version"); + + std::size_t nocc=mo_ket().size(); + std::size_t nfrozen=parameters.freeze(); + + const auto& R2=nemo_->R_square; + double result=0.0; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + // number of pairs + auto npair = [&nocc, &nfrozen]() { return (nocc - nfrozen) * (nocc - nfrozen + 1) / 2; }; + + // turn composite index ij into i and j, taking care of frozen orbitals + PairVectorMap map=PairVectorMap::triangular_map(nfrozen,nocc); + auto ij_to_i_and_j = [&map](const int ij) { return map.map[ij]; }; + + timer timer_sum(world); + timer_sum.interrupt(); + timer timer_inner(world); + timer_inner.interrupt(); + timer timer_consolidate(world); + timer_consolidate.interrupt(); + + /// = (ik | jl) + std::vector all_tau_permutations; + // loop over unique pairs (ij) + for (int i=nfrozen; i> sigma; + for (int k=nfrozen; k& mp2pairs, + const Pairs>> clusterfunctions) const { + typedef std::vector> ClusterFunction; + + // prepare cluster functions + std::size_t nocc=mo_ket().size(); + double result=0.0; + + const auto& R2=nemo_->R_square; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + timer t2(world); + + + // compute intermediates for terms G, I, H, and J + + // \sum_j tau_ij(1,2) * phi_j(2) + std::vector tau_kk_i(nocc); + // \sum_j tau_ij(1,2) * phi_j(1) + std::vector tau_ij_j(nocc); + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + auto tmp2 = multiply(clusterfunctions(i, j), R2_orbital[i], {0, 1, 2}); + for (auto& t: tmp2) tau_kk_i[j].push_back(t); + + auto tmp3 = multiply(clusterfunctions(i, j), R2_orbital[j], {0, 1, 2}); + for (auto& t: tmp3) tau_ij_j[i].push_back(t); + } + } + std::vector consolidation={"op_pure_to_pure","remove_lindep"}; + print("consolidating with ",consolidation); + for (int i = parameters.freeze(); i < nocc; ++i) { + tau_kk_i[i] = consolidate(tau_kk_i[i],consolidation); + tau_ij_j[i] = consolidate(tau_ij_j[i], consolidation); + // for (auto& c: tau_kk_i[i]) c.info(); + } + + t2.tag("GHIJ term prep"); + + // terms G, I, H, J of Bartlett/Silver 1975 + real_convolution_3d& g = *(g12->get_op()); + for (int i = parameters.freeze(); i < nocc; ++i) { + // tmp(1,2) = g(1,1') | tau_ij(1',2) j(2) > + timer t4(world, "gtau"); + g.set_particle(1); + auto gtau_same = g(tau_kk_i[i]); + t4.tag("compute gtau_same"); + + // tmp(1',2) = g(1',1) | tau_ij(1,2) j(1) > + g.set_particle(1); + auto gtau_other = g(tau_ij_j[i]); // < tau_ij(1,2) j(1) | g(1,1') | + t4.tag("compute gtau_other"); + + auto bra_kk_i = multiply(tau_kk_i[i], R2, {3, 4, 5}); + auto bra_ij_j = multiply(tau_ij_j[i], R2, {3, 4, 5}); + + double G = inner(bra_kk_i, gtau_same); + printf("G %12.8f\n", G); + + double H = inner(bra_ij_j, gtau_other); + printf("H %12.8f\n", H); + + double I = inner(bra_kk_i, gtau_other); + printf("I %12.8f\n", I); + + double J = inner(bra_ij_j, gtau_same); + printf("J %12.8f\n", J); + + t4.tag("compute inner products"); + double tmp = (8.0 * G - 4.0 * I + 2.0 * H - 4.0 * J); + printf("mp3 energy: term_GHIJ %2d %12.8f\n", i, tmp); + result += tmp; + } + printf("MP3 energy: term_GHIJ %12.8f\n", result); + t2.tag("GHIJ term"); + return result; +}; + + +double MP3::compute_mp3_ghij_fast(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const { + + print_header3("entering compute_mp3_ghij_fast"); + + // prepare cluster functions + std::size_t nocc=mo_ket().size(); + typedef std::vector> ClusterFunction; + double result=0.0; + + const auto& R2=nemo_->R_square; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + const Molecule molecule=nemo_->molecule(); + timer t2(world); + + // compute intermediates for terms G, I, H, and J + + // \sum_j tau_ij(1,2) * phi_j(2) + std::vector tau_kk_i(nocc); + // \sum_j tau_ij(1,2) * phi_j(1) + std::vector tau_ij_j(nocc); + + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + auto tmp2 = multiply(clusterfunctions(i, j), R2_orbital[i], {0, 1, 2}); + for (auto& t: tmp2) tau_kk_i[j].push_back(t); + + auto tmp3 = multiply(clusterfunctions(i, j), R2_orbital[j], {0, 1, 2}); + for (auto& t: tmp3) tau_ij_j[i].push_back(t); + } + } + + std::vector consolidation={"op_pure_to_pure","remove_lindep","op_dec_to_dec"}; + print("consolidating with ",consolidation); + std::vector intermediate(nocc); + for (int i = parameters.freeze(); i < nocc; ++i) { + intermediate[i]=2.0*tau_kk_i[i]; + intermediate[i]-=tau_ij_j[i]; + intermediate[i]=consolidate(intermediate[i],consolidation,molecule.get_all_coords_vec()); + } + + t2.tag("GHIJ term prep"); + + // terms G, I, H, J of Bartlett/Silver 1975 + real_convolution_3d& g = *(g12->get_op()); + g.set_particle(1); + for (int i = parameters.freeze(); i < nocc; ++i) { + // tmp(1,2) = g(1,1') | tau_ij(1',2) j(2) > + timer t4(world, "gtau"); + auto gintermediate = g(intermediate[i]); + t4.tag("compute gintermediate"); + auto bra_intermediate = multiply(intermediate[i], R2, {3, 4, 5}); + t4.tag("multiply"); + double tmp = 2.0*inner(bra_intermediate, gintermediate); + printf("mp3 energy: term_GHIJ %2d %12.8f\n", i, tmp); + t4.tag("inner"); + result += tmp; + } + printf("MP3 energy: term_GHIJ %12.8f\n", result); + t2.tag("GHIJ term"); + return result; +}; + +double MP3::compute_mp3_klmn_fast(const Pairs& mp2pairs) const { + + // prepare cluster functions + std::size_t nocc=mo_ket().size(); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + double result=0.0; + + const auto& R2=nemo_->R_square; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + // compute the term (2) + madness::Pairs gij; + + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + gij.insert(i,j,(*g12)(nemo_orbital[i]*R2_orbital[j])); + } + } + + + timer multiply_KLMN(world, "multiplication in KLMN term"); + multiply_KLMN.interrupt(); + timer inner_KLMN(world, "inner in KLMN term"); + inner_KLMN.interrupt(); + // prepare intermediates for terms K, L, M, N of Bartlett/Silver 1975 + // tau_g_ij(1,2) = \sum_k tau_ik(1,1') g_jk(2) + Pairs tau_ik_g_kj, tau_kj_g_ki; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + multiply_KLMN.resume(); + std::vector> rhs; + for (int k = parameters.freeze(); k < nocc; ++k) { + rhs += +2.0 * multiply(clusterfunctions(j, k), gij(k, i), {0, 1, 2}); // M + rhs += +2.0 * multiply(clusterfunctions(k, i), gij(k, j), {0, 1, 2}); // N + rhs += -4.0 * multiply(clusterfunctions(i, k), gij(k, j), {3, 4, 5}); // K + rhs += -4.0 * multiply(clusterfunctions(k, j), gij(k, i), {3, 4, 5}); // L + } + rhs = consolidate(rhs, {}); + multiply_KLMN.interrupt(); + + inner_KLMN.resume(); + double tmp = inner(clusterfunctions(i, j), rhs, R2); + inner_KLMN.interrupt(); + + printf("mp3 energy: term_KLMN with particle=1 %2d %2d %12.8f\n", i, j, tmp); + result += tmp; + } + } + printf("MP3 energy: term_KLMN (KLMN) %12.8f\n", result); + multiply_KLMN.print("multiplication in KLMN term"); + inner_KLMN.print("inner in KLMN term"); + + return result; + +}; +double MP3::compute_mp3_klmn(const Pairs& mp2pairs) const { + + // prepare cluster functions + std::size_t nocc=mo_ket().size(); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + double result=0.0; + + const auto& R2=nemo_->R_square; + const std::vector& nemo_orbital=mo_ket().get_vecfunction(); + const std::vector& R2_orbital=mo_bra().get_vecfunction(); + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + // compute the term (2) + madness::Pairs gij; + + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + gij.insert(i,j,(*g12)(nemo_orbital[i]*R2_orbital[j])); + } + } + + + timer multiply_KLMN(world, "multiplication in KLMN term"); + multiply_KLMN.interrupt(); + timer inner_KLMN(world, "inner in KLMN term"); + inner_KLMN.interrupt(); + // prepare intermediates for terms K, L, M, N of Bartlett/Silver 1975 + // tau_g_ij(1,2) = \sum_k tau_ik(1,1') g_jk(2) + Pairs tau_ik_g_kj, tau_kj_g_ki; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + multiply_KLMN.resume(); + std::vector> tmp1, tmp2; + for (int k = parameters.freeze(); k < nocc; ++k) { + tmp1 += multiply(clusterfunctions(i, k), gij(k, j), {3, 4, 5}); + tmp2 += multiply(clusterfunctions(k, j), gij(k, i), {3, 4, 5}); + } + tmp1 = consolidate(tmp1, {}); + tmp2 = consolidate(tmp2, {}); + tau_ik_g_kj(i, j) = tmp1; + tau_kj_g_ki(i, j) = tmp2; + multiply_KLMN.interrupt(); + + inner_KLMN.resume(); + double K = inner(clusterfunctions(i, j), tau_ik_g_kj(i, j), R2); + double L = inner(clusterfunctions(i, j), tau_kj_g_ki(i, j), R2); + double M = inner(clusterfunctions(j, i), tau_kj_g_ki(i, j), R2); + double N = inner(clusterfunctions(j, i), tau_ik_g_kj(i, j), R2); + inner_KLMN.interrupt(); + + double tmp = -4 * K - 4 * L + 2 * M + 2 * N; + printf("mp3 energy: term_KLMN with particle=1 %2d %2d %12.8f\n", i, j, tmp); + result += tmp; + } + } + printf("MP3 energy: term_KLMN (KLMN) %12.8f\n", result); + multiply_KLMN.print("multiplication in KLMN term"); + inner_KLMN.print("inner in KLMN term"); + + return result; + +}; + +double MP3::mp3_test(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const { + print_header2("entering mp3 test"); + + auto R2 = nemo_->ncf->square(); + const int nocc=mo_ket().size(); + std::vector nemo_orbital=mo_ket().get_vecfunction(); + std::vector R2_orbital=mo_bra().get_vecfunction(); + // std::vector nemo_orbital=mo_ket().get_vecfunction(); + std::vector> ij; + int i=1, j=1; + ij.push_back(CCPairFunction(nemo_orbital[i],nemo_orbital[j])); + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + double eri=0.0; + for (int i=0; i> ii; + ii.push_back(CCPairFunction(nemo_orbital[i], nemo_orbital[j])); + double tmp = inner(ii, g12 * ii, R2); + print("eri for pair", i,j, tmp); + eri += tmp; + } + } + print("total eri",eri); + + print("eri(1,1)",eri); + double mp2_energy=inner(clusterfunctions(i,j),ij,R2); + print("mp2 energy for pair ", i, j, mp2_energy); + + double mp3_contrib=inner(clusterfunctions(i,j),clusterfunctions(i,j),R2); + print("",mp3_contrib); + + return 0.0; +} + +double MP3::compute_mp3_cd(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument) { + + CCConvolutionOperator::Parameters cparam(parameters); + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + auto bra = pair_square(i, j); + double tmp1 = inner(bra, g12 * pair_square(i, j), Rsquare); + double tmp2 = inner(bra, g12 * pair_square(j, i), Rsquare); + double fac = (i == j) ? 0.5 : 1.0; + double result = fac * (4.0 * tmp1 - 2.0 * tmp2); + printf("mp3 energy: term_CD %2ld %2ld: %12.8f\n", i, j, result); + return result; +} + +double MP3::compute_mp3_ef(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument) { + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + int nfrozen=parameters.freeze(); + int nocc=mo_ket.size(); + + /// = (ik | jl) + std::vector all_tau_permutations; + std::vector> tmp_tau; + + // loop over all k and l + for (int k=nfrozen; k (2* - ) + // = (2*(ik|jl) - (jk|il)) + const auto& ket_i=mo_ket[i]; + const auto& ket_k=mo_ket[k]; + const auto& ket_l=mo_ket[l]; + const auto& bra_i=mo_bra[i]; + const auto& bra_j=mo_bra[j]; + const auto& bra_l=mo_bra[l]; + double g_ikjl=inner(bra_i*ket_k, (*g12)(bra_j*ket_l)); + double g_jkil=inner(bra_j*ket_k, (*g12)(bra_l*ket_i)); + tmp_tau+=weight*(2.0* g_ikjl - g_jkil)*pair_square(k,l); + } + } + tmp_tau=consolidate(tmp_tau,{"remove_lindep"}); + double result=inner(pair_square(i,j),tmp_tau,Rsquare); + printf("mp3 energy: term_EF %2ld %2ld %12.8f\n",i,j,result); + + // can't do sanity check, because we are working on a single pair +// int npermutations=all_tau_permutations.size(); +// all_tau_permutations=permutation::remove_duplicates(all_tau_permutations); +// int nuniquepermutations=all_tau_permutations.size(); +// int ntotalpermutations=std::pow(nocc-nfrozen,4); +// MADNESS_CHECK_THROW(npermutations==nuniquepermutations,"incorrect number of unique permutations"); +// MADNESS_CHECK_THROW(npermutations==ntotalpermutations,"incorrect number of unique permutations"); + + return result; +} + +double MP3::compute_mp3_ghij(World& world, + const long i, const long jj, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument) { + + // this scales linearly, use only the diagaonal contributions + if (i!=jj) return 0.0; + + print_header3("entering compute_mp3_ghij_fast"); + + // prepare cluster functions + std::size_t nocc=mo_ket.size(); + typedef std::vector> ClusterFunction; + double result=0.0; + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + timer t2(world); + + // compute intermediates for terms G, I, H, and J + + // \sum_j tau_ij(1,2) * phi_j(2) + ClusterFunction tau_kk_i; + // \sum_j tau_ij(1,2) * phi_j(1) + ClusterFunction tau_ij_j; + + for (int ii = parameters.freeze(); ii < nocc; ++ii) { + // for (int j = parameters.freeze(); j < nocc; ++j) { + auto tmp2 = multiply(pair_square(ii, i), mo_bra[ii], {0, 1, 2}); + for (auto& t: tmp2) tau_kk_i.push_back(t); + // } + } + + // for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = parameters.freeze(); j < nocc; ++j) { + auto tmp3 = multiply(pair_square(i, j), mo_bra[j], {0, 1, 2}); + for (auto& t: tmp3) tau_ij_j.push_back(t); + } + // } + + std::vector consolidation={"op_pure_to_pure","remove_lindep","op_dec_to_dec"}; + // std::vector consolidation={"op_pure_to_pure","remove_lindep","op_dec_to_pure"}; + print("consolidating with ",consolidation); + ClusterFunction intermediate; + // for (int i = parameters.freeze(); i < nocc; ++i) { + intermediate=2.0*tau_kk_i; + intermediate-=tau_ij_j; + intermediate=consolidate(intermediate,consolidation,molecule.get_all_coords_vec()); + // } + + t2.tag("GHIJ term prep"); + + // terms G, I, H, J of Bartlett/Silver 1975 + real_convolution_3d& g = *(g12->get_op()); + g.set_particle(1); + // for (int i = parameters.freeze(); i < nocc; ++i) { + // tmp(1,2) = g(1,1') | tau_ij(1',2) j(2) > + timer t4(world, "gtau"); + auto gintermediate = g(intermediate); + t4.tag("compute gintermediate"); + auto bra_intermediate = multiply(intermediate, Rsquare, {3, 4, 5}); + t4.tag("multiply"); + double tmp = 2.0*inner(bra_intermediate, gintermediate); + printf("mp3 energy: term_GHIJ %2ld %12.8f\n", i, tmp); + t4.tag("inner"); + result += tmp; + // } + t2.tag("GHIJ term"); + return result; +} + + +double MP3::compute_mp3_klmn(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument) { + + // prepare cluster functions + std::size_t nocc=mo_ket.size(); + typedef std::vector> ClusterFunction; + double result=0.0; + + CCConvolutionOperator::Parameters cparam; + auto g12=CCConvolutionOperatorPtr(world,OpType::OT_G12,cparam); + + // compute the term (2) + madness::Pairs gij; + + for (int k = parameters.freeze(); k < nocc; ++k) { + for (int l = parameters.freeze(); l < nocc; ++l) { + gij.insert(k,l,(*g12)(mo_ket[k]*mo_bra[l])); + } + } + + + timer multiply_KLMN(world, "multiplication in KLMN term"); + multiply_KLMN.interrupt(); + timer inner_KLMN(world, "inner in KLMN term"); + inner_KLMN.interrupt(); + + // for (int i = parameters.freeze(); i < nocc; ++i) { + // for (int j = parameters.freeze(); j < nocc; ++j) { + multiply_KLMN.resume(); + std::vector> rhs; + for (int k = parameters.freeze(); k < nocc; ++k) { + rhs += +2.0 * multiply(pair_square(j, k), gij(k, i), {0, 1, 2}); // M + rhs += +2.0 * multiply(pair_square(k, i), gij(k, j), {0, 1, 2}); // N + rhs += -4.0 * multiply(pair_square(i, k), gij(k, j), {3, 4, 5}); // K + rhs += -4.0 * multiply(pair_square(k, j), gij(k, i), {3, 4, 5}); // L + } + rhs = consolidate(rhs, {}); + multiply_KLMN.interrupt(); + + inner_KLMN.resume(); + double tmp = inner(pair_square(i, j), rhs, Rsquare); + inner_KLMN.interrupt(); + + printf("mp3 energy: term_KLMN with particle=1 %2ld %2ld %12.8f\n", i, j, tmp); + result += tmp; + // } + // } + multiply_KLMN.print("multiplication in KLMN term"); + inner_KLMN.print("inner in KLMN term"); + + return result; +} + +double MP3::mp3_energy_contribution(const Pairs& mp2pairs) const { + + print_header2("computing the MP3 correlation energy"); + print("mp2pairs.size()",mp2pairs.allpairs.size()); + + timer t2(world); + std::size_t nocc=mo_ket().size(); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + + t2.tag("make cluster functions"); +// mp3_test(mp2pairs,clusterfunctions); + double term_CD=0.0, term_EF=0.0, term_GHIJ=0.0, term_KLMN=0.0; + term_CD=compute_mp3_cd(mp2pairs); + t2.tag("CD term"); + + term_GHIJ=compute_mp3_ghij_fast(mp2pairs,clusterfunctions); + t2.tag("GHIJ term"); + term_KLMN=compute_mp3_klmn_fast(mp2pairs); + t2.tag("KLMN term fast"); + term_EF=compute_mp3_ef_with_permutational_symmetry(mp2pairs); + t2.tag("EF term, permutational symmetry"); + + printf("term_CD %12.8f\n",term_CD); + printf("term_GHIJ %12.8f\n",term_GHIJ); + printf("term_KLMN %12.8f\n",term_KLMN); + printf("term_EF %12.8f\n",term_EF); + double mp3_energy=term_CD+term_GHIJ+term_KLMN+term_EF; + printf("MP3 energy contribution %12.8f\n",mp3_energy); + return mp3_energy; +} + +double MP3::mp3_energy_contribution_macrotask_driver(const Pairs& mp2pairs) const { + + if (world.rank()==0) { + print_header2("computing the MP3 correlation energy, macrotask version"); + print("mp2pairs.size()",mp2pairs.allpairs.size()); + } + + // compute all ij pairs + timer t2(world); + std::size_t nocc=mo_ket().size(); + typedef std::vector> ClusterFunction; + Pairs clusterfunctions; + for (int i = parameters.freeze(); i < nocc; ++i) { + for (int j = i; j < nocc; ++j) { + clusterfunctions(i,j)=mp2pairs(i,j).functions; + if (i!=j) { + for (const auto& t : clusterfunctions(i,j)) { + clusterfunctions(j, i).push_back(t.swap_particles()); + } + } + } + } + // turn Pair into vector for cloud and stuff -- will be reversed later on + PairVectorMap square_map=PairVectorMap::quadratic_map(parameters.freeze(),mo_ket().size()); + auto clusterfunc_vec=Pairs>>::pairs2vector(clusterfunctions,square_map); + + t2.tag("make cluster functions"); + + // create dummy scheduling vector of length npair=nocc*(nocc+1)/2, for the macrotask scheduler, triangular form + std::vector ij_triangular(mp2pairs.allpairs.size()); + // create dummy scheduling vector of length nact for the macrotask scheduler, square form + std::vector nact(nocc-parameters.freeze()); + std::vector dummy; + + + // get mos + const std::vector& ket=mo_ket().get_vecfunction(); + const std::vector& bra=mo_bra().get_vecfunction(); + + auto taskq=std::shared_ptr(new MacroTaskQ(world,world.size(),3)); + // taskq->set_printlevel(20); + // taskq->cloud.set_debug(true); + MacroTaskMP3 task_triangular("triangular"); + MacroTaskMP3 task_square("square"); + MacroTask macrotask_triangular(world,task_triangular,taskq); + MacroTask macrotask_square(world,task_square,taskq); + auto ghij_future=macrotask_triangular(std::string("ghij"), ij_triangular, dummy, clusterfunc_vec, ket, bra, parameters, nemo_->molecule(), nemo_->R_square, std::vector()); + auto klmn_future=macrotask_square(std::string("klmn"), nact, nact, clusterfunc_vec, ket, bra, parameters, nemo_->molecule(), nemo_->R_square, std::vector()); + auto cd_future=macrotask_triangular(std::string("cd"), ij_triangular, dummy, clusterfunc_vec, ket, bra, parameters, nemo_->molecule(), nemo_->R_square, std::vector()); + auto ef_future=macrotask_triangular(std::string("ef"), ij_triangular, dummy, clusterfunc_vec, ket, bra, parameters, nemo_->molecule(), nemo_->R_square, std::vector()); + taskq->print_taskq(); + taskq->run_all(); + + double term_CD=cd_future->get(); + double term_EF=ef_future->get(); + double term_GHIJ=ghij_future->get(); + double term_KLMN=klmn_future->get(); + + printf("term_CD %12.8f\n",term_CD); + printf("term_GHIJ %12.8f\n",term_GHIJ); + printf("term_KLMN %12.8f\n",term_KLMN); + printf("term_EF %12.8f\n",term_EF); + double mp3_energy=term_CD+term_GHIJ+term_KLMN+term_EF; + printf("MP3 energy contribution %12.8f\n",mp3_energy); + return mp3_energy; + // return 0.0; +} +} diff --git a/src/madness/chem/mp3.h b/src/madness/chem/mp3.h new file mode 100644 index 00000000000..b31c4eb27e2 --- /dev/null +++ b/src/madness/chem/mp3.h @@ -0,0 +1,205 @@ +// +// Created by Florian Bischoff on 2/15/24. +// + +#ifndef MP3_H +#define MP3_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace madness { +class MP3 : public CCPotentials { +public: + + MP3(World& world, const std::shared_ptr nemo, const CCParameters& param) + : CCPotentials(world,nemo,param) {} + MP3(const CCPotentials& ops) : CCPotentials(ops) {} + + double mp3_energy_contribution(const Pairs& mp2pairs) const; + + /// compute the MP3 energy contribution, macrotask version + double mp3_energy_contribution_macrotask_driver(const Pairs& mp2pairs) const; + +private: + /// helper class for calculating the MP3 energy contributions + class MacroTaskMP3 : public MacroTaskOperationBase { + + class Partitioner : public MacroTaskPartitioner { + public: + Partitioner(const std::string shape) { + min_batch_size=1; + max_batch_size=1; + if (shape=="triangular") dimension=1; + else if (shape=="square") dimension=2; + else { + std::string msg = "Unknown partitioning shape: " + shape; + MADNESS_EXCEPTION(msg.c_str(), 1); + } + }; + }; + + public: + MacroTaskMP3(const std::string shape) { + partitioner.reset(new Partitioner(shape)); + } + + typedef std::tuple< + const std::string&, + const std::vector&, // dummy vector of size npair or nocc for scheduling + const std::vector&, // dummy vector of size npair or nocc for scheduling + const std::vector>>& , // all pairs ij + const std::vector>&, + const std::vector>&, + const CCParameters&, + const Molecule&, + const Function&, + const std::vector& > argtupleT; + + using resultT =std::shared_ptr>; + + resultT allocator(World& world, const argtupleT& argtuple) const { + return std::shared_ptr>(new ScalarResult(world)); + } + + resultT operator() (const std::string& diagram, // which diagram to calculate + const std::vector& ij_vec, // dummy vector of size npair or nocc + const std::vector& j_vec, // dummy vector of size 0 or nocc + const std::vector>>& pair_square, // all pairs ij + const std::vector>& mo_ket, // the orbitals + const std::vector>& mo_bra, // the orbitals*R2 + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument) const { + + // the partitioner will break the input vector of pairs into single pairs + MADNESS_CHECK(ij_vec.size()==1); + MADNESS_CHECK(batch.result.size()==1); + + // nact=active occupied orbitals + const long nact=mo_ket.size()-parameters.freeze(); + MADNESS_CHECK(pair_square.size()==nact*nact); + + // loop over pairs idimension==1); + + // determine the orbital indices i and j for the pair + int i=0, j=0; + if (is_triangular) { + // the batch index is the ij composite index [0,nact*(nact+1)-1] + const long ij=batch.result.begin; + // turn composite index ij into i and j, taking care of frozen orbitals + PairVectorMap tri_map=PairVectorMap::triangular_map(parameters.freeze(),mo_ket.size()); + auto ij_to_i_and_j = [&tri_map](const int ij) { return tri_map.map[ij]; }; + auto [ii,jj]=ij_to_i_and_j(ij); + i=ii; + j=jj; + } else { + MADNESS_CHECK(partitioner->dimension==2); + MADNESS_CHECK(j_vec.size()==ij_vec.size()); + i=batch.input[0].begin+parameters.freeze(); + j=batch.input[1].begin+parameters.freeze(); + } + print("i,j,parameters.freeze()=",i,j,parameters.freeze()); + + // convert vector of vectors back to Pairs + PairVectorMap square_map=PairVectorMap::quadratic_map(parameters.freeze(),mo_ket.size()); + auto clusterfunctions=Pairs>>::vector2pairs(pair_square,square_map); + + double result=0.0; + World& world=Rsquare.world(); + if (diagram=="cd") + result= MP3::compute_mp3_cd(world,i,j,clusterfunctions,mo_ket,mo_bra,parameters,molecule,Rsquare,argument); + else if (diagram=="ef") + result= MP3::compute_mp3_ef(world,i,j,clusterfunctions,mo_ket,mo_bra,parameters,molecule,Rsquare,argument); + else if (diagram=="ghij") + result= MP3::compute_mp3_ghij(world,i,j,clusterfunctions,mo_ket,mo_bra,parameters,molecule,Rsquare,argument); + else if (diagram=="klmn") + result= MP3::compute_mp3_klmn(world,i,j,clusterfunctions,mo_ket,mo_bra,parameters,molecule,Rsquare,argument); + else { + std::string msg = "Unknown MP3 diagram: " + diagram; + MADNESS_EXCEPTION(msg.c_str(), 1); + } + auto result1=std::shared_ptr>(new ScalarResult(world)); + *result1=result; + return result1; + + }; + + + }; + + + double compute_mp3_cd(const Pairs& mp2pairs) const; + double compute_mp3_ef(const Pairs& mp2pairs) const; + double compute_mp3_ef_with_permutational_symmetry(const Pairs& mp2pairs) const; + double compute_mp3_ef_low_scaling(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const; + double compute_mp3_ef_as_overlap(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const; + double compute_mp3_ghij(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const; + double compute_mp3_ghij_fast(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const; + double compute_mp3_klmn(const Pairs& mp2pairs) const; + double compute_mp3_klmn_fast(const Pairs& mp2pairs) const; + double mp3_test(const Pairs& mp2pairs, const Pairs>> clusterfunctions) const; + + /// compute the cd term for single pair ij + static double compute_mp3_cd(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument); + + /// compute the ef term for single pair ij + static double compute_mp3_ef(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument); + + /// compute the ghij term for single pair ij + /// + /// the term actually scales linearly with the number of occupied orbitals i, so for all i!=j return zero + static double compute_mp3_ghij(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument); + + /// compute the klmn term for single pair ij + static double compute_mp3_klmn(World& world, + const long i, const long j, + const Pairs>>& pair_square, + const std::vector>& mo_ket, + const std::vector>& mo_bra, + const CCParameters& parameters, + const Molecule& molecule, + const Function& Rsquare, + const std::vector& argument); + +}; +} + + +#endif //MP3_H diff --git a/src/madness/chem/projector.h b/src/madness/chem/projector.h index a42043afe5e..4d71db2a96c 100644 --- a/src/madness/chem/projector.h +++ b/src/madness/chem/projector.h @@ -25,10 +25,13 @@ namespace madness { virtual std::string type() const = 0; }; + template struct CCPairFunction; - std::vector apply(const ProjectorBase& P, const std::vector& argument); -/// simple projector class + template + std::vector> apply(const ProjectorBase& P, const std::vector>& argument); + + /// simple projector class /// use this class to project a function or a set of functions on /// another space of function. The projector can handle different sets of @@ -113,10 +116,10 @@ namespace madness { operator()(const Function& f, size_t particle1=size_t(-1)) const { Function result = FunctionFactory(f.world()); if (particle1==size_t(-1)) particle1=particle; - MADNESS_ASSERT(particle1 == 1 or particle1 == 2); + MADNESS_CHECK_THROW(particle1 == 1 or particle1 == 2, "particle must be 1 or 2"); for (size_t i = 0; i < mo_ket_.size(); i++) { Function tmp1 = mo_ket_[i]; - Function tmp2 = f.project_out(mo_bra_[i], particle - 1); + Function tmp2 = f.project_out(mo_bra_[i], particle1 - 1); Function tmp12; if (particle1 == 1) { tmp12 = CompositeFactory(f.world()).particle1(copy(tmp1)).particle2(copy(tmp2)); @@ -181,7 +184,7 @@ namespace madness { return result; } - real_function_6d operator()(const real_function_6d& f, const size_t particle) const { + Function operator()(const Function& f, const size_t particle) const { return f-O(f,particle); } diff --git a/src/madness/chem/structure_library b/src/madness/chem/structure_library index 74e11333fa5..d9d24122eec 100644 --- a/src/madness/chem/structure_library +++ b/src/madness/chem/structure_library @@ -300,66 +300,62 @@ h 1.42650808202133 0.00000000000000 -0.35501194545850 end structure=water2 -geometry -units angs - h -1.958940 -0.032063 0.725554 - h -0.607485 0.010955 0.056172 - o -1.538963 0.004548 -0.117331 - h 1.727607 0.762122 -0.351887 - h 1.704312 -0.747744 -0.399151 - o 1.430776 -0.003706 0.113495 +geometry + h -3.70186011075500 -0.06059028899871 1.37109835461971 + h -1.14798027983604 0.02070194978576 0.10614969633645 + o -2.90821859864409 0.00859447445236 -0.22172345689761 + h 3.26470409525617 1.44020185984707 -0.66497005972273 + h 3.22068292499060 -1.41303137750844 -0.75428607566744 + o 2.70377479750559 -0.00700332504847 0.21447446745186 end structure=water3 -geometry -units angs - o -0.167787 1.645761 0.108747 - h 0.613411 1.102620 0.113724 - h -0.093821 2.209720 -0.643619 - o 1.517569 -0.667424 -0.080674 - h 1.989645 -1.098799 0.612047 - h 0.668397 -1.091798 -0.139744 - o -1.350388 -0.964879 -0.092208 - h -1.908991 -1.211298 0.626207 - h -1.263787 -0.018107 -0.055536 +geometry + o -0.31707147865848 3.11003757018401 0.20550204777292 + h 1.15917879689952 2.08364982864237 0.21490721473629 + h -0.17729599551346 4.17576563036006 -1.21626364392172 + o 2.86778979775713 -1.26124857451507 -0.15245176604442 + h 3.75988415166526 -2.07642918508861 1.15660121045426 + h 1.26308727804237 -2.06319921243228 -0.26407788871397 + o -2.55186349313518 -1.82335706137257 -0.17424786726112 + h -3.60747018014350 -2.28902148531211 1.18335973249592 + h -2.38821132030115 -0.03421727108816 -0.10494783051594 end structure=water4 -geometry -units angs - o 0.216486 -1.989005 0.035297 - h -0.603911 -1.505274 -0.033552 - h 0.138672 -2.535875 0.799566 - h 1.505276 -0.603920 0.033505 - o 1.989004 0.216483 -0.035296 - h 2.535922 0.138690 -0.799534 - o -1.989001 -0.216487 -0.035297 - h -2.535947 -0.138688 -0.799513 - h -1.505273 0.603920 0.033493 - o -0.216485 1.989007 0.035294 - h -0.138694 2.535879 0.799565 - h 0.603919 1.505277 -0.033513 +geometry + o 0.40909925160388 -3.75867472694021 0.06670166331246 + h -1.14122639863710 -2.84455561495330 -0.06340409121058 + h 0.26205210229952 -4.79210925723138 1.51096076516684 + h 2.84455939440557 -1.14124340617230 0.06331527408233 + o 3.75867283721408 0.40909358242548 -0.06669977358633 + h 4.79219807435963 0.26208611736991 -1.51090029393059 + o -3.75866716803568 -0.40910114133001 -0.06670166331246 + h -4.79224531751295 -0.26208233791764 -1.51086060968180 + h -2.84455372522717 1.14124340617230 0.06329259736874 + o -0.40909736187775 3.75867850639247 0.06669599413407 + h -0.26209367627444 4.79211681613591 1.51095887544071 + h 1.14124151644616 2.84456128413170 -0.06333039189140 end structure=water5 -geometry -units angs - o -0.571270 2.322980 -0.003174 - o 2.038411 1.261129 -0.102281 - o 1.831197 -1.536089 0.156570 - o -0.890568 -2.217987 -0.100067 - o -2.398873 0.159801 -0.005022 - h -0.541626 2.994645 0.657473 - h 2.545757 1.390343 -0.886606 - h 2.127985 -1.971116 0.938635 - h -1.172378 -2.646489 -0.891417 - h -3.019733 0.318743 0.686131 - h 0.319714 1.986524 -0.080413 - h 2.008500 0.316696 0.040400 - h 0.922213 -1.803155 0.031130 - h -1.429035 -1.432872 -0.018620 - h -1.832579 0.928012 -0.044919 +geometry + o -1.07954384793358 4.38979601217069 -0.00599799074578 + o 3.85203853626156 2.38318842823994 -0.19328307859768 + o 3.46046082536179 -2.90278752573817 0.29587442062591 + o -1.68292962271170 -4.19138799630063 -0.18909922493947 + o -4.53321299757378 0.30198012576126 -0.00949020463935 + h -1.02352480645032 5.65905891521533 1.24244390976672 + h 4.81078353087656 2.62736750077462 -1.67544252777321 + h 4.02130886488866 -3.72486941614902 1.77376308874112 + h -2.21547334422020 -5.00113942369439 -1.68453400019852 + h -5.70646836443716 0.60233697677437 1.29659968128296 + h 0.60417190084940 3.75398631640452 -0.15195854752373 + h 3.79551493790081 0.59846870738035 0.07634493576858 + h 1.74273000618687 -3.40746912514341 0.05882717451673 + h -2.70048478430824 -2.70773566348012 -0.03518670059433 + h -3.46307242687744 1.75368852803147 -0.08488460816309 end diff --git a/src/madness/chem/test_ccpairfunction.cc b/src/madness/chem/test_ccpairfunction.cc index 505c0828c29..d1dbad93ab2 100644 --- a/src/madness/chem/test_ccpairfunction.cc +++ b/src/madness/chem/test_ccpairfunction.cc @@ -8,88 +8,146 @@ #include #include #include +#include #include #include +#include using namespace madness; +bool longtest=false; +template struct data { - real_function_3d f1,f2,f3,f4,f5; - real_function_6d f; - - std::shared_ptr f12; - data() {} - - data(World& world, CCParameters& parameters) { - f12.reset(new CCConvolutionOperator(world,OT_F12,parameters)); - if (not f1.is_initialized()) initialize(world); - } - - void initialize(World& world) { - auto g1 = [](const coord_3d& r) { return exp(-1.0 * inner(r, r)); }; - auto g2 = [](const coord_3d& r) { return exp(-2.0 * inner(r, r)); }; - auto g3 = [](const coord_3d& r) { return exp(-3.0 * inner(r, r)); }; - auto g4 = [](const coord_3d& r) { return exp(-4.0 * inner(r, r)); }; - auto g5 = [](const coord_3d& r) { return exp(-5.0 * inner(r, r)); }; - f1=real_factory_3d(world).f(g1); - f2=real_factory_3d(world).f(g2); - f3=real_factory_3d(world).f(g3); - f4=real_factory_3d(world).f(g4); - f5=real_factory_3d(world).f(g5); - - auto g = [](const coord_6d& r) { - double r1=r[0]*r[0] + r[1]*r[1] + r[2]*r[2]; - double r2=r[3]*r[3] + r[4]*r[4] + r[5]*r[5]; + static constexpr std::size_t LDIM=NDIM/2; + Function f1,f2,f3,f4,f5; + Function f12,f23; + World& world; + CCParameters parameters; + + std::shared_ptr> f12_op; + + data(World& world, const CCParameters& parameters) : world(world), parameters(parameters){} + + bool is_initialized() const { + return f1.is_initialized(); + } + + void initialize() { + print("initializing data for NDIM=",NDIM); + f12_op.reset(new CCConvolutionOperator(world,OT_F12,parameters)); + auto g1 = [](const Vector& r) { return exp(-1.0 * inner(r, r)); }; + auto g2 = [](const Vector& r) { return exp(-2.0 * inner(r, r)); }; + auto g3 = [](const Vector& r) { return exp(-3.0 * inner(r, r)); }; + auto g4 = [](const Vector& r) { return exp(-4.0 * inner(r, r)); }; + auto g5 = [](const Vector& r) { return exp(-5.0 * inner(r, r)); }; + f1=FunctionFactory(world).f(g1); + f2=FunctionFactory(world).f(g2); + f3=FunctionFactory(world).f(g3); + f4=FunctionFactory(world).f(g4); + f5=FunctionFactory(world).f(g5); + + auto g = [](const Vector& r) { + double r1=0.0, r2=0.0; + for (int i=0; i& r) { + double r1=0.0, r2=0.0; + for (int i=0; i(world); + f23=FunctionFactory(world); + std::string name_f12="test_ccpairfunction_f12_ndim_"+std::to_string(NDIM); + std::string name_f23="test_ccpairfunction_f23_ndim_"+std::to_string(NDIM); +// try { +// load(f12,name_f12); +// } catch (...) { + f12 = FunctionFactory(world).f(g); +// save(f12,name_f12); +// } +// try { +// load(f23,name_f23); +// } catch (...) { + f23 = FunctionFactory(world).f(g23); +// save(f23,name_f23); +// } } void clear() { - f12.reset(); + f12_op.reset(); f1.clear(); f2.clear(); f3.clear(); f4.clear(); f5.clear(); - f.clear(); + f12.clear(); + f23.clear(); } - auto get_functions() const { - return std::make_tuple(f1,f2,f3,f4,f5,f); + /// get some standard functions + + /// f1: exp(-1.0 r^2) + /// f2: exp(-2.0 r^2) + /// f3: exp(-3.0 r^2) + /// f4: exp(-4.0 r^2) + /// f5: exp(-5.0 r^2) + /// f12: exp(-r_1^2 - 2 r_2^2) + /// f23: exp(-r_1^2 - 2 r_2^2) + exp(-2 r_1^2 - 3 r_2^2) + auto get_functions() { + if (not is_initialized()) initialize(); + return std::make_tuple(f1,f2,f3,f4,f5,f12); } - auto get_ccpairfunctions() const { - CCPairFunction p1; - CCPairFunction p2(f); - CCPairFunction p3({f1,f2},{f1,f3}); - CCPairFunction p4(f12.get(),{f1,f2},{f1,f3}); - return std::make_tuple(p1,p2,p3); + /// get some standard ccpairfunctions + + /// p1: pure, corresponds to f12 + /// p2: dec, corresponds to f23 + /// p3: op_dec, corresponds to f23 + /// p4: pure, corresponds to f23 + /// p5: op_pure, corresponds to f23 + auto get_ccpairfunctions() { + if (not is_initialized()) initialize(); + CCPairFunction p1(copy(f12)); + CCPairFunction p2({f1,f2},{f2,f3}); + CCPairFunction p3(f12_op,{f1,f2},{f2,f3}); + CCPairFunction p4(copy(f23)); // two-term, corresponds to p2 + CCPairFunction p5(f12_op,copy(f23)); // two-term, corresponds to p2 + return std::make_tuple(p1,p2,p3,p4,p5); } }; -data data1; -int test_constructor(World& world, std::shared_ptr ncf, const Molecule& molecule, - const CCParameters& parameter) { - int success=0; - test_output t1("CCPairFunction::constructor"); +template +int test_constructor(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + test_output t1("constructor"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; - real_function_6d f=real_factory_6d(world); - auto [f1,f2,f3,f4,f5,ff]=data1.get_functions(); + Function f=FunctionFactory(world); + auto [f1,f2,f3,f4,f5,ff]=data.get_functions(); - vector_real_function_3d a= zero_functions(world,3); - vector_real_function_3d b= zero_functions(world,3); - CCConvolutionOperator f12(world, OT_F12, parameter); + std::vector> a= zero_functions(world,LDIM); + std::vector> b= zero_functions(world,LDIM); + auto f12=CCConvolutionOperatorPtr(world, OT_F12, parameter); t1.checkpoint(true,"preparation"); - CCPairFunction p1; - CCPairFunction p2(f); - CCPairFunction p3({f1,f2},{f1,f3}); - CCPairFunction p4(&f12,{f1,f2},{f2,f3}); + auto f_copy=copy(f); + CCPairFunction p1; + CCPairFunction p2(f_copy); + CCPairFunction p3({f1,f2},{f1,f3}); + CCPairFunction p4(f12,{f1,f2},{f2,f3}); t1.checkpoint(true,"construction"); { @@ -98,7 +156,7 @@ int test_constructor(World& world, std::shared_ptr ncf MADNESS_CHECK(!p2.is_op_decomposed()); auto p = p2.pure(); auto ff = p2.pure().get_function(); - MADNESS_CHECK(!(ff.get_impl()==f.get_impl())); // deep copy of f + MADNESS_CHECK((ff.get_impl()==f_copy.get_impl())); // shallow copy of f } t1.checkpoint(true,"checks on pure"); @@ -122,7 +180,7 @@ int test_constructor(World& world, std::shared_ptr ncf t1.checkpoint(true,"checks on op_decomposed"); { - CCPairFunction tmp; + CCPairFunction tmp; tmp=p2; MADNESS_CHECK(tmp.is_pure()); MADNESS_CHECK(tmp.component==p2.component); @@ -133,315 +191,812 @@ int test_constructor(World& world, std::shared_ptr ncf } t1.checkpoint(true,"checks on assignment"); - t1.end(); - return (t1.get_final_success()) ? 0 : 1; + return t1.end(); +} + +template +int test_load_store(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + test_output t1("load/store of "); + +// t1.set_cout_to_terminal(); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); // p2-p5 correspond to f23 + auto [f1,f2,f3,f4,f5,ff]=data.get_functions(); + + auto compute_diff_norm = [](const CCPairFunction& f1, const CCPairFunction f2) { + std::vector> diff; + diff+={f1}; + diff-={f2}; + double in = inner(diff,diff); + if (in<0) return -sqrt(-in); + return sqrt(in); + }; + + std::string fname="ccpairfunction_test"; + { + archive::ParallelOutputArchive ar(world, fname, 1); + ar & f1; + ar & p2; + ar & p3; + ar & p4; + ar & p5; + } + { + archive::ParallelInputArchive ar(world, fname, 1); + CCPairFunction g2, g3, g4, g5; + ar & f1; + ar & g2; + ar & g3; + ar & g4; + ar & g5; + + double n2=compute_diff_norm(g2,p2); + double n3=compute_diff_norm(g3,p3); + double n4=compute_diff_norm(g4,p4); + double n5=compute_diff_norm(g5,p5); + t1.checkpoint(n2,FunctionDefaults::get_thresh(),"store/load "+p2.name()); + t1.checkpoint(n3,FunctionDefaults::get_thresh(),"store/load "+p3.name()); + t1.checkpoint(n4,FunctionDefaults::get_thresh(),"store/load "+p4.name()); + t1.checkpoint(n5,FunctionDefaults::get_thresh(),"store/load "+p5.name()); + } + t1.checkpoint(true,"checks on load/store"); + return t1.end(); +} + + +template +int test_operator_apply(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + test_output t1("test_operator_apply"); +// t1.set_cout_to_terminal(); + + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + + double exponent=1.0; // corresponds to the exponent of data::f1 and data::ff +// double coefficient=pow(1.0/constants::pi*exponent,0.5*3); + double coefficient=1.0; + const Vector center(0.0); + + auto Gaussian = [¢er, &exponent, &coefficient](const Vector& r) { + return coefficient * exp(-exponent*inner(r-center,r-center)); + }; + + exponent=1.5; + Function i=FunctionFactory(world).functor(Gaussian); + exponent=2.5; + Function j=FunctionFactory(world).functor(Gaussian); + + auto gop= BSHOperator(world,0.5,1.e-5,std::min(1.e-4,FunctionDefaults::get_thresh())); + + auto [f1,f2,f3,f4,f5,ff]=data.get_functions(); + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); // p2-p5 correspond to f23 + // p2: dec, corresponds to f23 + // p3: op_dec, corresponds to f23 + // p4: pure, corresponds to f23 + // p5: op_pure, corresponds to f23 + + // integrate over particle 2: | u(1,3)> = g(1,2) uu(2,3), project u(1,3) onto bra = + // reference for p1, p3: + // = = + // + // note that the ccpairfunctions always have 2 terms + // CCPairFunction p2({f1,f2},{f2,f3}); + // with l=f1, k=f2; and l=f2, k=f3 + + auto& fop=*(data.f12_op->get_op()); + Function gi=gop(i); + + Function k_1=f1; + Function l_1=f2; + Function f_lj_1=fop(l_1*j); + Function f_kj_1=fop(k_1*j); + + Function k_2=f2; + Function l_2=f3; + Function f_lj_2=fop(l_2*j); + Function f_kj_2=fop(k_2*j); + + CCPairFunction bra(i,j); + + double ref_p2p4=(gi*k_1*f_lj_1).trace() + (gi*k_2*f_lj_2).trace(); + double ref_p1p3=(gi*k_1).trace() * (j*l_1).trace() + (gi*k_2).trace() * (j*l_2).trace(); + std::vector reference({ref_p1p3,ref_p2p4,ref_p1p3,ref_p2p4}); + + int counter=0; + + for (auto& p : {p2,p3,p4,p5}) { + gop.particle() = 1; + auto tmp1=gop(p); + double result=inner(bra,tmp1); + double ref=reference[counter++]; + print("p=",p.name(),"result=",result,"reference=",ref); + t1.checkpoint(result,ref,FunctionDefaults::get_thresh(),"op(1) to "+p.name()); + } + + // integrate over particle 3: | u(1,2)> = g(1,3) uu(2,3), project u(1,2) onto bra = + // reference for p1, p3: + // = = + // note the ordering of the indices in the ket! the above line is equivalent to + // = + ref_p2p4=(gi*l_1*f_kj_1).trace() + (gi*l_2*f_kj_2).trace(); + ref_p1p3=(gi*l_1).trace() * (j*k_1).trace() + (gi*l_2).trace() * (j*k_2).trace(); + reference=std::vector({ref_p1p3,ref_p2p4,ref_p1p3,ref_p2p4}); + + counter=0; + CCPairFunction bra_ji(j,i); + for (auto& p : {p2,p3,p4,p5}) { + gop.particle() = 2; + auto tmp1=gop(p); + double result=inner(bra,tmp1); + double ref=reference[counter++]; + print("p=",p.name(),"result=",result,"reference=",ref); + t1.checkpoint(result,ref,FunctionDefaults::get_thresh(),"op(2) to "+p.name()); + } + + + + return t1.end(); } -int test_overlap(World& world, std::shared_ptr ncf, const Molecule& molecule, + +template +int test_transformations(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + test_output t1("test_transformations"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + +// auto data=get_data(world,parameter); + auto [f1,f2,f3,f4,f5,ff]=data.get_functions(); + auto f12=CCConvolutionOperatorPtr(world, OT_F12, parameter); + auto g12=CCConvolutionOperatorPtr(world, OT_G12, parameter); + + auto compute_diff_norm = [](const CCPairFunction& f1, const CCPairFunction f2) { + std::vector> diff; + diff+={f1}; + diff-={f2}; + return sqrt(inner(diff,diff)); + }; + + CCPairFunction p1(copy(ff)); + t1.checkpoint(p1.is_pure(),"is_pure"); + t1.checkpoint(p1.is_pure_no_op(),"is_pure_no_op"); + + CCPairFunction p2(f12,ff); + t1.checkpoint(p2.is_pure(),"is_pure"); + t1.checkpoint(p2.is_op_pure(),"is_op_pure"); + t1.checkpoint(p2.is_convertible_to_pure_no_op(),"is_convertible_to_pure_no_op"); + CCPairFunction p3=copy(p2); + p3.convert_to_pure_no_op_inplace(); + t1.checkpoint(p2.is_op_pure(),"is_op_pure"); + t1.checkpoint(p3.is_pure_no_op(),"is_pure_no_op"); + + CCPairFunction p4(g12,copy(ff)); + t1.checkpoint(p4.is_pure(),"is_pure"); + t1.checkpoint(p4.is_op_pure(),"is_op_pure"); + t1.checkpoint(not p4.is_convertible_to_pure_no_op(),"not is_convertible_to_pure_no_op"); + + // convert f12 f1 f2 to pure_op_op + CCPairFunction p5(f12,f1,f2); + t1.checkpoint(not p5.is_pure(),"is_pure"); + t1.checkpoint(p5.is_op_decomposed(),"is_op_decomposed"); + t1.checkpoint(p5.is_convertible_to_pure_no_op(),"is_convertible_to_pure_no_op"); + CCPairFunction p6=copy(p5); + p6.convert_to_pure_no_op_inplace(); + t1.checkpoint(p6.is_pure_no_op(),"is_pure_no_op"); + double d6=compute_diff_norm(p5,p6); + t1.checkpoint(d6,FunctionDefaults::get_thresh()*50,"numerics"); + + // convert \sum_i f12 f1_i f2_i to pure_op_op + CCPairFunction p7(f12,{f1,f2,f3},{f1,f2,f3}); + t1.checkpoint(not p7.is_pure(),"is_pure"); + t1.checkpoint(p7.is_op_decomposed(),"is_op_decomposed"); + t1.checkpoint(p7.is_convertible_to_pure_no_op(),"is_convertible_to_pure_no_op"); + CCPairFunction p8=copy(p7); + p8.convert_to_pure_no_op_inplace(); + t1.checkpoint(p8.is_pure_no_op(),"is_pure_no_op"); + double d8=compute_diff_norm(p7,p8); + t1.checkpoint(d8,FunctionDefaults::get_thresh()*50,"numerics"); + + + + return t1.end(); +} + +template +int test_multiply_with_f12(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameters) { + test_output t1("test_multiply_with_f12"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + + // p1: pure, corresponds to f12 + // p2: dec, corresponds to f23 + // p3: op_dec, corresponds to f23 + // p4: pure, corresponds to f23 + // p5: op_pure, corresponds to f23 +// auto data=get_data(world,parameters); + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); // p2-p5 correspond to f23 + auto f12=data.f12_op; + + double thresh=FunctionDefaults::get_thresh(); + + // decomposed + CCPairFunction tmp1=f12*p2; // should now be identical to p3 + CCPairFunction tmp2=p2*f12; // should now be identical to p3 + double ref=inner(p2,p3); + + double r1=inner(p2,tmp1); + bool good=(fabs(ref-r1) +int test_multiply(World& world, std::shared_ptr ncf, data& data, const CCParameters& parameters) { - CCTimer timer(world, "testing"); - auto R2 = ncf->square(); + test_output t1("test_multiply<"+std::to_string(NDIM)+">"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + + // consistency check, relies on CCPairFunction::inner to work correctly + double thresh=FunctionDefaults::get_thresh(); +// auto data=get_data(world,parameters); + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); // p2-p5 correspond to f23 + auto [f1,f2,f3,f4,f5,f]=data.get_functions(); + + auto particle1=std::array(); + auto particle2=std::array(); + for (int i=0; i = + CCPairFunction bra(f1,f2); + CCPairFunction bra1(f1*f2,f2); + CCPairFunction bra2(f1,f2*f2); + for (auto& p : {p2,p3,p4,p5}) { - CorrelationFactor corrfac(world, 1.0, 1.e-7, molecule); + auto tmp1=multiply(p,f2,particle1); + double ovlp1=inner(bra,tmp1); + double ref1=p.has_operator() ? inner(bra1,p3) : inner(bra1,p2); - auto g = [](const coord_3d& r) { return exp(-1.0 * r.normf()); }; - real_function_3d f1 = real_factory_3d(world).f(g); - real_function_3d R2f = R2 * f1; - double norm = inner(f1, R2f); - f1.scale(1.0 / sqrt(norm)); - R2f.scale(1.0 / sqrt(norm)); - CC_vecfunction mo_ket_(std::vector(1, f1), HOLE); - CC_vecfunction mo_bra_(std::vector(1, R2f), HOLE); + bool good=(fabs(ovlp1-ref1) +int test_inner(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameters) { + test_output t1("test_inner<"+std::to_string(NDIM)+">"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; +// t1.set_cout_to_terminal(); + + /// f1: exp(-1.0 r^2) + /// f2: exp(-2.0 r^2) + /// f3: exp(-3.0 r^2) + /// f4: exp(-4.0 r^2) + /// f5: exp(-5.0 r^2) + /// f: exp(-r_1^2 - 2 r_2^2) + /// f23: exp(-r_1^2 - 2 r_2^2) + exp(-2 r_1^2 - 3 r_2^2) +// auto data=get_data(world,parameters); + auto [f1,f2,f3,f4,f5,f]=data.get_functions(); + /// p1: pure, corresponds to f12 + /// p2: dec, corresponds to f23 + /// p3: op_dec, corresponds to f23 + /// p4: pure, corresponds to f23 + /// p5: op_pure, corresponds to f23 + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); + auto f12 = *(data.f12_op); + + /// results + auto a=std::vector>({f1,f2}); + auto b=std::vector>({f2,f3}); + std::vector> a_ij_functions, b_ij_functions; + for (int i=0; i = \sum_{ij} + double ab_ab=aij.trace(bij); + + // = \sum_{ij} < _1(2) | b_ib_j(2) > + double ab_f_ab=dot(world,f12(a_ij_functions),b_ij_functions).trace(); + + // = \sum_{ij} < _2 | b_ib_j(2) > + // f^2 = 1/(4y^2)(1 - 2*f(y) + f2(2y)) , f2(2y) =f2(y)^2 // operator apply of SlaterF12Operator includes a factor of 1/(2 gamma) and the identity // operator apply of SlaterOperator has no further terms const double y = parameters.gamma(); - SeparatedConvolution f = SlaterF12Operator(world, y, parameters.lo(), parameters.thresh_bsh_3D()); - SeparatedConvolution f2 = SlaterOperator(world, y, parameters.lo(), parameters.thresh_bsh_3D()); - SeparatedConvolution ff = SlaterOperator(world, 2.0 * y, parameters.lo(), parameters.thresh_bsh_3D()); - - real_function_3d a = mo_ket_(0).function; - real_function_3d b = mo_ket_(0).function; - real_function_6d fab_6d = CompositeFactory(world).g12(corrfac.f()).particle1(copy(a)).particle2( - copy(b)); - fab_6d.fill_tree().truncate().reduce_rank(); - fab_6d.print_size("fab_6d"); - - real_function_3d aR = mo_bra_(0).function; - real_function_3d bR = mo_bra_(0).function; - - const real_function_3d aa = (aR * a).truncate(); - const real_function_3d bb = (bR * b).truncate(); - const real_function_3d af2a = f2(aa); - const real_function_3d affa = ff(aa); - const real_function_3d afa = f(aa); + SeparatedConvolution fop= SlaterOperator(world, y, parameters.lo(), parameters.thresh_bsh_3D()); + SeparatedConvolution fsq = SlaterOperator(world, 2.0 * y, parameters.lo(), parameters.thresh_bsh_3D()); const double prefactor = 1.0 / (4 * y * y); - const double term1= prefactor * (aR.inner(a) * bR.inner(b)); - const double term2= prefactor * 2.0 * bb.inner(af2a) ; - const double term3= prefactor * bb.inner(affa); - const double ab_f2_ab = prefactor * (aR.inner(a) * bR.inner(b) - 2.0 * bb.inner(af2a) + bb.inner(affa)); - const double ab_f_ab = inner(f(aa), bb); - - const long rank = world.rank(); - auto printer = [&rank](std::string msg, const double result, const double reference, const double time) { - if (rank == 0) { - long len = std::max(0l, long(40 - msg.size())); - msg += std::string(len, ' '); - std::cout << msg << std::fixed << std::setprecision(8) << "result, ref, diff " - << result << " " << reference << " " << std::setw(9) << std::setprecision(2) << std::scientific - << result - reference - << ", elapsed time " << std::fixed << std::setw(5) << std::setprecision(2) << time << std::endl; + const double ab_f2_ab = prefactor*( ab_ab + -2.0*dot(world,apply(world,fop,a_ij_functions),b_ij_functions).trace() + +dot(world,apply(world,fsq,a_ij_functions),b_ij_functions).trace() ); + + + for (auto& ket : {p2, p3, p4, p5}) { + for (auto& bra : {p2, p3, p4, p5}) { + double ref=0.0; + if (bra.has_operator() and ket.has_operator()) ref=ab_f2_ab; + if (bra.has_operator() and (not ket.has_operator())) ref=ab_f_ab; + if ((not bra.has_operator()) and ket.has_operator()) ref=ab_f_ab; + if ((not bra.has_operator()) and (not ket.has_operator())) ref=ab_ab; + double result=inner(bra,ket); + + print(bra.name(true)+ket.name(),"ref, result, diff", ref, result, ref-result); + double thresh=FunctionDefaults::get_thresh(); + bool good=(fabs(result-ref) lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - printer(" op_dec/op_dec : ", test1, ab_f2_ab, timer.reset()); - success=(fabs(diff) < hi); +template +int test_partial_inner_6d(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + + test_output t1("test_partial_inner6d<"+std::to_string(NDIM)+">"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; +// t1.set_cout_to_terminal(); +// auto data=get_data<6>(world,parameter); + auto [f1,f2,f3,f4,f5,f] = data.get_functions(); + + std::vector> a = {f1, f2}; + std::vector> b = {f2, f3}; + + auto f12=CCConvolutionOperatorPtr(world, OT_F12, parameter); + + auto [p1,p2,p3,p4,nil]=data.get_ccpairfunctions(); + CCPairFunction p11({f1},{f1}); + CCPairFunction p12({f1},{f2}); + + double g11=inner(f1,f1); + double g22=inner(f2,f2); + double g12=inner(f1,f2); + double g13=inner(f1,f3); + double g23=inner(f2,f3); + double g33=inner(f3,f3); + Function gf11=(*f12)(f1*f1); + Function gf12=(*f12)(f1*f2); + Function gf22=(*f12)(f2*f2); + Function gf13=(*f12)(f1*f3); + Function gf23=(*f12)(f2*f3); + + t1.checkpoint(true,"prep"); + + auto particle1=std::array(); + auto particle2=std::array(); + for (int i=0; i dec/dec : ", test1, double(mo_ket_.size()), timer.reset()); - if (fabs(diff) > lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - success=(fabs(diff) < hi); - } - if (not success) isuccess++; - t1.checkpoint(success, "dec/dec"); - { - CCPairFunction fab(fab_6d); - const double test1 = inner(fab, fab, R2); - const double diff = test1 - ab_f2_ab; - printer(" pure/pure : ", test1, ab_f2_ab, timer.reset()); - if (fabs(diff) > lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - success=(fabs(diff) < hi_loose); + + // p1 = p12 = e(-r1 -2r2) + // p2 = e(-r1) * e(-2r2) + e(-2r1) * e(-3e2) separated + // p4 = e(-r1) * e(-2r2) + e(-2r1) * e(-3e2) 6d + for (auto test_p1 : {p2,p4}) { + for (auto test_p2 : {p2,p4}) { + CCPairFunction r1=inner(test_p1,test_p2,particle1,particle1); + CCPairFunction r2=inner(test_p1,test_p2,particle1,particle2); + CCPairFunction r3=inner(test_p1,test_p2,particle2,particle1); + CCPairFunction r4=inner(test_p1,test_p2,particle2,particle2); + + double n1=inner(r1,p11); + double n2=inner(r2,p11); + double n3=inner(r3,p11); + double n4=inner(r4,p11); + + double ref_n1=g11*g12*g12 + g12*g12*g13 + g12*g13*g12 + g22*g13*g13; + double ref_n2=g12*g12*g11 + g13*g12*g12 + g22*g13*g11 + g23*g13*g12; + double ref_n3=ref_n2; + double ref_n4=g22*g11*g11 + g23*g11*g12 + g23*g12*g11 + g33*g12*g12; + + bool good=fabs(n1-ref_n1)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+test_p2.name()+" -- 1"); + good=fabs(n2-ref_n2)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+test_p2.name()+" -- 2"); + good=fabs(n3-ref_n3)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+test_p2.name()+" -- 3"); + good=fabs(n4-ref_n4)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+test_p2.name()+" -- 4"); + + } } - if (not success) isuccess++; - t1.checkpoint(success, "pure/pure"); - { - CCPairFunction ab1(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); - CCPairFunction fab(&f12, a, b); - timer.reset(); - const double test1 = inner(ab1, fab, R2); - const double test2 = ab_f_ab; - const double diff = test1 - test2; - printer(" dec/op_dec : ", test1, ab_f_ab, timer.reset()); - if (fabs(diff) > lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - success=(fabs(diff) < hi); - } - if (not success) isuccess++; - t1.checkpoint(success, "dec/op_dec"); + // test < sth | f(1,2) a(1)b(2) > + // CCPairFunction p2({f1,f2},{f2,f3}); + // CCPairFunction p4(f23); // two-term, corresponds to p2 + CCPairFunction p5(f12,std::vector>({f1}),std::vector>({f2})); + for (auto& test_p1 : {p2, p4}) { + CCPairFunction r1=inner(test_p1,p5,particle1,particle1); + CCPairFunction r2=inner(test_p1,p5,particle1,particle2); + CCPairFunction r3=inner(test_p1,p5,particle2,particle1); + CCPairFunction r4=inner(test_p1,p5,particle2,particle2); + + double ref_n1=inner(gf11,f2*f1) * g12 + inner(gf12,f1*f2) * g13; + double ref_n2=inner(gf12,f1*f1) * g12 + inner(gf22,f1*f1) * g13; + double ref_n3=inner(gf12,f2*f1) * g11 + inner(gf13,f1*f2) * g12; + double ref_n4=inner(gf22,f1*f1) * g11 + inner(gf23,f1*f1) * g12; + + double n1=inner(r1,p11); + double n2=inner(r2,p11); + double n3=inner(r3,p11); + double n4=inner(r4,p11); +// print("n1, ref_n1",n1,ref_n1, n1-ref_n1); +// print("n2, ref_n2",n2,ref_n2, n2-ref_n2); +// print("n3, ref_n3",n3,ref_n3, n3-ref_n3); +// print("n4, ref_n4",n4,ref_n4, n4-ref_n4); + + bool good=fabs(n1-ref_n1)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+p5.name()+" -- 1"); + good=fabs(n2-ref_n2)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+p5.name()+" -- 2"); + good=fabs(n3-ref_n3)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+p5.name()+" -- 3"); + good=fabs(n4-ref_n4)::get_thresh(); + t1.checkpoint(good,test_p1.name(true)+p5.name()+" -- 4"); - { - // the next tests evaulate in different ways - CCPairFunction fab(fab_6d); - CCPairFunction ab2(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); - const double test1 = inner(fab, ab2, R2); - const double test2 = bb.inner(afa); - const double diff = test1 - test2; - printer(" dec/pure", test1, test2, timer.reset()); - if (fabs(diff) > lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - - success=(fabs(diff) < hi_loose); - } - if (not success) isuccess++; - t1.checkpoint(success, "dec/pure"); -// { -// CCPairFunction fab(fab_6d); -// CCPairFunction ab2(ab_6d); -// const double test1 = overlap(fab, ab2); -// const double test2 = bb.inner(afa); -// const double diff = test1 - test2; -// if (world.rank() == 0) -// std::cout << "Overlap Test 6 : " << std::fixed << std::setprecision(10) << "result=" << test1 -// << ", test=" << test2 << ", diff=" << diff << "\n"; -// if (fabs(diff) > lo) passed_lo = false; -// if (fabs(diff) > hi) passed_hi = false; -// } - { - CCPairFunction fab(&f12, a, b); - CCPairFunction ab2(fab_6d); - const double test1 = inner(fab, ab2, R2); - const double test2 = ab_f2_ab; - const double diff = test1 - test2; - printer(" op_dec/pure : ", test1, test2, timer.reset()); - if (fabs(diff) > lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - success=(fabs(diff) < hi_loose); // be a bit loose here .. - } - if (not success) isuccess++; - t1.checkpoint(success, "op_dec/pure"); + } - { - CCPairFunction fab(&f12, a, b); - CCPairFunction ab2(mo_ket_.get_vecfunction(), mo_ket_.get_vecfunction()); - timer.reset(); - const double test1 = inner(fab, ab2, R2); -// const double test2 = bb.inner(afa); - const double test2 = ab_f_ab; - const double diff = test1 - test2; - printer(" op_dec/dec : ", test1, test2, timer.reset()); - if (fabs(diff) > lo) passed_lo = false; - if (fabs(diff) > hi) passed_hi = false; - success=(fabs(diff) < hi); - } - if (not success) isuccess++; - t1.checkpoint(success, "op_dec/dec"); - - return (t1.get_final_success()) ? 0 : 1; + // test < a(1) b(2) f(1,2) | f(1,3) c(1)d(3) > + // CCPairFunction p3(f12_op.get(),{f1,f2},{f2,f3}); + // CCPairFunction p5(&f12,{f1},{f2}); + if (longtest) { + CCPairFunction r1=inner(p3,p5,particle1,particle1); + double n1=inner(r1,p11); + p3.convert_to_pure_no_op_inplace(); + p5.convert_to_pure_no_op_inplace(); + print("n1",n1); + CCPairFunction r1a=inner(p3,p5,particle1,particle1); + double n1a=inner(r1a,p11); + print("n1a",n1a); + print("diff",n1-n1a); + CCPairFunction r2=inner(p3,p5,particle1,particle2); + CCPairFunction r3=inner(p3,p5,particle2,particle1); + CCPairFunction r4=inner(p3,p5,particle2,particle2); + + } + return t1.end(); } +template +int test_partial_inner_3d(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { -int test_partial_inner(World& world, std::shared_ptr ncf, const Molecule& molecule, - const CCParameters& parameter) { - int success=0; + test_output t1("test_partial_inner<"+std::to_string(NDIM)+">"); +// t1.set_cout_to_terminal(); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + auto [f1,f2,f3,f4,f5,f] = data.get_functions(); - CCTimer timer(world, "testing"); - test_output t1("CCPairFunction::test_partial_inner"); - auto [f1,f2,f3,f4,f5,f] = data1.get_functions(); + auto particle1=std::array(); + auto particle2=std::array(); + for (int i=0; i a = {f1, f2}; - std::vector b = {f2, f3}; + std::vector> a = {f1, f2}; + std::vector> b = {f2, f3}; - CCConvolutionOperator f12(world, OT_F12, parameter); + auto f12=CCConvolutionOperatorPtr(world, OT_F12, parameter); - CCPairFunction p1(f); - CCPairFunction p2(a,b); - CCPairFunction p3(&f12,a,b); + CCPairFunction p1(copy(f)); // e(-r1 - 2r2) + CCPairFunction p2(a,b); + CCPairFunction p3(f12,a,b); + CCPairFunction p11({f1},{f1}); + CCPairFunction p12({f1},{f2}); double g11=inner(f1,f1); double g22=inner(f2,f2); double g12=inner(f1,f2); double g13=inner(f1,f3); double g23=inner(f2,f3); - real_function_3d gf11=f12(f1*f1); - real_function_3d gf12=f12(f1*f2); - real_function_3d gf13=f12(f1*f3); + Function gf11=(*f12)(f1*f1); + Function gf12=(*f12)(f1*f2); + Function gf13=(*f12)(f1*f3); print("g11, g22",g11,g22); - print("time in preparation",timer.reset()); + double thresh=FunctionDefaults::get_thresh(); + t1.checkpoint(true,"prep"); - // test pure + // test pure/3d { - real_function_3d r=inner(p1,f1,{0,1,2},{0,1,2}); + double aa=inner(p1,p1); + print("aa",aa); + // < e1 e2 | e1 >_1 = | e2 > ||e1|| + Function r=inner(p1,f1,particle1,particle1); double norm=inner(r,f2); double ref_norm=g11 * g22; - print("norm, ref_norm",norm,ref_norm); - bool good=fabs(norm-ref_norm::get_thresh()); - t1.checkpoint(good,"pure -- 1"); + print("norm ",norm); + print("ref_norm", ref_norm); + Function r_swap=inner(p1,f1,particle2,particle1); + double norm_swap=inner(r_swap,f2); + print("norm1 swap",norm_swap); + t1.checkpoint(norm,ref_norm,thresh,"pure -- 1"); } - // test pure + // test pure/3d { - real_function_3d r=inner(p1,f1,{3,4,5},{0,1,2}); + Function r=inner(p1,f1,particle2,particle1); double norm=inner(r,f2); double ref_norm=g12 * g12; - print("norm, ref_norm",norm,ref_norm); - bool good=fabs(norm-ref_norm::get_thresh()); - t1.checkpoint(good,"pure -- 2"); + t1.checkpoint(norm, ref_norm, thresh,"pure -- 2"); } // test decomposed { - real_function_3d r=inner(p2,f1,{0,1,2},{0,1,2}); + Function r=inner(p2,f1,particle1,particle1); double norm=inner(r,f2); double ref_norm=g11 * g22 + g12 * g23; - print("norm, ref_norm",norm,ref_norm); - bool good=fabs(norm-ref_norm::get_thresh()); - t1.checkpoint(good,"decomposed -- 1"); + t1.checkpoint(norm, ref_norm, thresh,"decomposed -- 1"); } // test decomposed { - real_function_3d r=inner(p2,f1,{3,4,5},{0,1,2}); + Function r=inner(p2,f1,particle2,particle1); double norm=inner(r,f2); double ref_norm=g12 * g12 + g13 * g22; - print("norm, ref_norm",norm,ref_norm); - bool good=fabs(norm-ref_norm::get_thresh()); - t1.checkpoint(good,"decomposed -- 2"); + t1.checkpoint(norm, ref_norm, thresh,"decomposed -- 2"); } // test op_decomposed { // < f1 f2 | f | f1 f2 > + < f1 f2 | f | f2 f3> - real_function_3d r=inner(p3,f1,{0,1,2},{0,1,2}); + Function r=inner(p3,f1,particle1,particle1); double norm=inner(r,f2); double ref_norm=inner(gf11*f2,f2) + inner(gf12*f2,f3); - print("norm, ref_norm",norm,ref_norm); - bool good=fabs(norm-ref_norm::get_thresh()); - t1.checkpoint(good,"op_decomposed -- 1"); + t1.checkpoint(norm, ref_norm, thresh,"op_decomposed -- 1"); } // test op_decomposed { // < f1 f2 | f | f2 f1 > + < f1 f2 | f | f3 f2> - real_function_3d r=inner(p3,f1,{3,4,5},{0,1,2}); + Function r=inner(p3,f1,particle2,particle1); double norm=inner(r,f2); double ref_norm=inner(gf12*f1,f2) + inner(gf13*f2,f2); - print("norm, ref_norm",norm,ref_norm); - bool good=fabs(norm-ref_norm::get_thresh()); - t1.checkpoint(good,"op_decomposed -- 2"); + t1.checkpoint(norm, ref_norm, thresh,"op_decomposed -- 2"); } + return t1.end(); +} + - return (t1.get_final_success()) ? 0 : 1; +template +int test_consolidate(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + test_output t1("CCPairFunction::test_consolidate"); + static_assert(NDIM % 2 == 0, "NDIM must be even"); + constexpr std::size_t LDIM = NDIM / 2; +// t1.set_cout_to_terminal(); + + /// f12: exp(-r_1^2 - 2 r_2^2) + /// f23: exp(-r_1^2 - 2 r_2^2) + exp(-2 r_1^2 - 3 r_2^2) + /// p1: pure, corresponds to f12 + /// p2: dec, corresponds to f23 + /// p3: op_dec, corresponds to f23 + /// p4: pure, corresponds to f23 + /// p5: op_pure, corresponds to f23 + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); + + // collect all terms of similar type, no conversions + for (const auto& p : {p1,p4,p5}) { + auto tmp=std::vector>({p,p}); + double r0=inner(p,{p1}); + double r1=inner(tmp,{p1}); + auto tmp1=consolidate(tmp,{}); + double r2=inner(tmp1,{p1}); + t1.checkpoint(tmp1.size()==1 && tmp.size()==2,"vector size"); + t1.checkpoint(2.0*r0,r1,FunctionDefaults::get_thresh(),"consolidate"); + t1.checkpoint(2.0*r0,r2,FunctionDefaults::get_thresh(),"consolidate"); + } + + // convert op_pure to pure + for (const auto& p : {p5}) { + auto tmp=std::vector>({p}); + t1.checkpoint(tmp.front().is_op_pure(),"correct initial type: op_pure"); + auto tmp1=consolidate(tmp,{"op_pure_to_pure"}); + t1.checkpoint(tmp1.front().is_pure_no_op(),"correct final type: pure"); + t1.checkpoint(is_collected(tmp1),"is_collected"); + + double r0=inner(p,{p1}); + double r1=inner(tmp,{p1}); + t1.checkpoint(r0,r1,FunctionDefaults::get_thresh(),"correct numbers"); + } + + // convert op_decomposed to decomposed + for (const auto& p : {p3}) { + auto tmp=std::vector>({p}); + t1.checkpoint(tmp.front().is_op_decomposed(),"correct initial type: op_decomposed"); + auto tmp1=consolidate(tmp,{"op_dec_to_dec"}); + t1.checkpoint(tmp1.front().is_decomposed_no_op(),"correct final type: decomposed"); + + double r0=inner(p,{p1}); + double r1=inner(tmp,{p1}); + t1.checkpoint(r0,r1,FunctionDefaults::get_thresh(),"correct numbers"); + } + + // remove linear dependencies + for (const auto& p : {p3}) { + auto tmp=std::vector>({p}); + tmp+=tmp; + t1.checkpoint(tmp.size()==2,"correct number of terms"); + t1.checkpoint(tmp.front().get_a().size()==2,"correct number of vectors in a"); + t1.checkpoint(tmp.front().is_op_decomposed(),"correct initial type: op_decomposed"); + auto tmp1=consolidate(tmp,{"remove_lindep"}); + t1.checkpoint(tmp1.front().is_decomposed(),"correct final type: decomposed"); + t1.checkpoint(tmp1.size()==1,"correct number of terms"); + t1.checkpoint(tmp1.front().get_a().size()==2,"correct number of vectors in a"); + + double r0=2*inner(p,{p1}); + double r1=inner(tmp,{p1}); + t1.checkpoint(r0,r1,FunctionDefaults::get_thresh(),"correct numbers"); + } + + + + // some random composition of the above + // a vector of numerically identical terms is created, then a random permutation is applied, and the result is checked + for (int i=0; i<5; ++i) { + std::vector> pvec={p2,p3,p4,p5}; // numerically all the same + + std::random_device rd; // a seed source for the random number engine + std::mt19937 gen(rd()); // mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> distrib(0, 3); + std::vector mapping(5); + for (int i=0; i<5; ++i) mapping[i]=distrib(gen); + print("mapping",mapping); + + std::vector> tmp; + for (auto m: mapping) tmp.push_back(pvec[m]); + + double n0=inner({p1},tmp); + + auto tmp1=consolidate(tmp,{}); // collect only, no conversions + print("tmp"); + for (auto& c : tmp) c.print_size(); + print("tmp1"); + for (auto& c : tmp1) c.print_size(); + + t1.checkpoint(is_collected(tmp1),"random is_collected"); + double n1=inner({p1},tmp1); + t1.checkpoint(n0,n1,FunctionDefaults::get_thresh(),"random collect numerics"); + + tmp1=consolidate(tmp1,{"op_pure_to_pure"}); // + print("tmp1 after op_pure_to_pure"); + for (auto& c : tmp1) c.print_size(); + t1.checkpoint(is_collected(tmp1),"random is_collected"); + double n2=inner({p1},tmp1); + t1.checkpoint(n0,n2,FunctionDefaults::get_thresh(),"random op_pure_to_pure numerics"); + + tmp1=consolidate(tmp1,{"op_dec_to_dec"}); // + print("tmp1 after op_dec_to_dec"); + for (auto& c : tmp1) c.print_size(); + t1.checkpoint(is_collected(tmp1),"random is_collected"); + double n3=inner({p1},tmp1); + t1.checkpoint(n0,n3,FunctionDefaults::get_thresh(),"random op_dec_to_dec numerics"); + t1.checkpoint(tmp1.size()<=2,"only max. two types of terms after consolidate"); + } + + return t1.end(); } -int test_apply(World& world, std::shared_ptr ncf, const Molecule& molecule, + +template +int test_apply(World& world, std::shared_ptr ncf, data& data, const CCParameters& parameter) { - int success=0; test_output t1("CCPairFunction::test_apply"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; +// t1.set_cout_to_terminal(); + + /// f12: exp(-r_1^2 - 2 r_2^2) + /// f23: exp(-r_1^2 - 2 r_2^2) + exp(-2 r_1^2 - 3 r_2^2) + /// p1: pure, corresponds to f12 + /// p2: dec, corresponds to f23 + /// p3: op_dec, corresponds to f23 + /// p4: pure, corresponds to f23 + /// p5: op_pure, corresponds to f23 + auto [p1,p2,p3,p4,p5]=data.get_ccpairfunctions(); + auto [f1,f2,f3,f4,f5,f]=data.get_functions(); + + auto f12=CCConvolutionOperatorPtr(world, OT_F12, parameter); + auto& op=*(f12->get_op()); + std::vector> vp2({p2}); + std::vector> vp2ex({p2.swap_particles()}); + + // tmp(2) = \int a(1)b(2') f(1,2) d1 + // result=inner(tmp,f1); + for (auto& p : {p1,p2,p3,p4,p5}) { + print("working on ",p.name()); + std::vector> vp({p}); + op.set_particle(1); + auto op1_p=op(vp); + double r1=inner(vp2,op1_p); + double r1ex=inner(vp2ex,op1_p); + printf("r1 %12.8f\n",r1); + printf("r1ex %12.8f\n",r1ex); + op.set_particle(2); + auto op2_p=op(vp); + double r2=inner(vp2,op2_p); + double r2ex=inner(vp2ex,op2_p); + printf("r2 %12.8f\n",r2); + printf("r2ex %12.8f\n",r2ex); + } - return (t1.get_final_success()) ? 0 : 1; + return t1.end(); } -int test_scalar_multiplication(World& world, std::shared_ptr ncf, const Molecule& molecule, - const CCParameters& parameter) { - int success=0; +template +int test_scalar_multiplication(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { CCTimer timer(world, "testing"); - test_output t1("CCPairFunction::test_scalar_multiplication"); + test_output t1("CCPairFunction::test_scalar_multiplication"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; - auto [f1,f2,f3,f4,f5,f] = data1.get_functions(); +// auto data=get_data(world,parameter); + auto [f1,f2,f3,f4,f5,f] = data.get_functions(); - std::vector a = {f1, f2}; - std::vector b = {f3, f1}; + std::vector> a = {f1, f2}; + std::vector> b = {f3, f1}; print("time in preparation",timer.reset()); t1.checkpoint(true,"prep"); - CCPairFunction p(f); - CCPairFunction p1(a,b); + CCPairFunction p(copy(f)); + CCPairFunction p1(a,b); double norm1=inner(p,p1); double pnorm=inner(p,p); double p1norm=inner(p1,p1); @@ -461,33 +1016,32 @@ int test_scalar_multiplication(World& world, std::shared_ptr::get_thresh(); + bool bsuccess=fabs(4.0*norm1-norm2)::get_thresh(); t1.checkpoint(bsuccess,"scaling"); - if (bsuccess) success++; - t1.end(); - return (t1.get_final_success()) ? 0 : 1; + return t1.end(); } -int test_swap_particles(World& world, std::shared_ptr ncf, const Molecule& molecule, +template +int test_swap_particles(World& world, std::shared_ptr ncf, data& data, const CCParameters& parameters) { - int success = 0; - test_output t1("CCPairFunction::swap_particles"); - CCTimer timer(world, "testing swap_particles"); + test_output t1("swap_particles<"+std::to_string(NDIM)+">"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; // prepare - auto one = [](const coord_3d& r) { return 1.0; }; - real_function_3d R2 = real_factory_3d(world).f(one); + auto one = [](const Vector& r) { return 1.0; }; + Function R2 = FunctionFactory(world).f(one); - auto [f1,f2,f3,f4,f5,f] = data1.get_functions(); - std::vector a = {f1, f2}; - std::vector b = {f3, f1}; +// auto data=get_data(world,parameters); + auto [f1,f2,f3,f4,f5,f] = data.get_functions(); + std::vector> a = {f1, f2}; + std::vector> b = {f3, f1}; // test decomposed { - CCPairFunction p1(a, b); - CCPairFunction p2(b, a); + CCPairFunction p1(a, b); + CCPairFunction p2(b, a); double norm1 = inner(p1, p2.swap_particles(), R2); double norm1a = inner(p1, p1, R2); @@ -496,19 +1050,19 @@ int test_swap_particles(World& world, std::shared_ptr print("norm1 ", norm1); print("norm1a", norm1a); print("norm2 ", norm2); - t1.checkpoint(std::abs(norm1 - norm2) < FunctionDefaults<3>::get_thresh(), "swap_particles a,b"); + t1.checkpoint(std::abs(norm1 - norm2) < FunctionDefaults::get_thresh(), "swap_particles a,b"); } // test pure { - CCPairFunction p(f); - CCPairFunction p_swapped=p.swap_particles(); + CCPairFunction p(copy(f)); + CCPairFunction p_swapped=p.swap_particles(); double pnorm=p.get_function().norm2(); double psnorm=p_swapped.get_function().norm2(); print("p/s norm",pnorm,psnorm); - CCPairFunction p1({f1}, {f2}); - CCPairFunction p2({f2}, {f1}); + CCPairFunction p1({f1}, {f2}); + CCPairFunction p2({f2}, {f1}); double ref1=inner(f1,f1)*inner(f2,f2); double ref2=inner(f1,f2)*inner(f2,f1); print("ref1/2",ref1,ref2); @@ -535,33 +1089,37 @@ int test_swap_particles(World& world, std::shared_ptr print("difference norms in exp(-21 - 21):",norm21_21,ref_12_12); double total_error= fabs(norm12_12-ref_12_12)+ fabs(norm12_21-ref_12_21) - + fabs(norm21_12-ref_12_21)+ fabs(norm21_21-ref_12_12); + + fabs(norm21_12-ref_12_21)+ fabs(norm21_21-ref_12_12); t1.checkpoint(total_error < FunctionDefaults<3>::get_thresh(), "swap_particles u"); }; - t1.end(); - return (t1.get_final_success()) ? 0 : 1; + return t1.end(); } -int test_projector(World& world, std::shared_ptr ncf, const Molecule& molecule, - const CCParameters& parameter) { - int success=0; - test_output t1("CCPairFunction::test_projector"); - CCTimer timer(world, "testing"); - t1.set_cout_to_logger(); - auto [f1,f2,f3,f4,f5,f] = data1.get_functions(); +template +int test_projector(World& world, std::shared_ptr ncf, data& data, + const CCParameters& parameter) { + test_output t1("test_projector<"+std::to_string(NDIM)+">"); + static_assert(NDIM%2==0, "NDIM must be even"); + constexpr std::size_t LDIM=NDIM/2; + +// t1.set_cout_to_logger(); +// t1.set_cout_to_terminal(); + const auto [f11,f22,f3,f4,f5,f] = data.get_functions(); + auto f1=copy(f11); // keep f1, f2 constant for use in other tests! + auto f2=copy(f22); double nf1=f1.norm2(); double nf2=f2.norm2(); f1.scale(1.0/nf1); f2.scale(1.0/nf1); - std::vector a = {f1+f2, f2+f3, f3+f4}; - std::vector b = {f3, f1, f2}; - std::vector o = orthonormalize_cd({f1-f3, f5}); // projects on an orthonormal basis - a = orthonormalize_cd(a); // projects on an orthonormal basis - CCConvolutionOperator f12(world, OT_F12, parameter); + std::vector> a = {f1+f2, f2+f3, f3+f4}; + std::vector> b = {f3, f1, f2}; + std::vector> o = orthonormalize_cd({f1-f3, f5}); // projects on an orthonormal basis + a = orthonormalize_cd(a); // projects on an orthonormal basis + auto f12=CCConvolutionOperatorPtr(world, OT_F12, parameter); { double n1=inner(f1,o[0]); @@ -570,19 +1128,60 @@ int test_projector(World& world, std::shared_ptr ncf, print("", ovlp); } - CCPairFunction p1(a,b); - CCPairFunction p2(&f12,a,b); - CCPairFunction p3(f); // outer (f1,f2) + CCPairFunction p1(a,b); + CCPairFunction p2(f12,a,b); + CCPairFunction p3(copy(f)); // outer (f1,f2) - std::vector vp1({p1}); - std::vector vp2({p2}); - std::vector vp3({p3}); + std::vector> vp1({p1}); + std::vector> vp2({p2}); + std::vector> vp3({p3}); - Projector O(o,o); - QProjector Q(world,o,o); - StrongOrthogonalityProjector Q12(world); + Projector O(o,o); + QProjector Q(world,o,o); + StrongOrthogonalityProjector Q12(world); Q12.set_spaces(o); + double thresh=FunctionDefaults::get_thresh(); + + // compute reference values as: ( + // compute result values as: ) + Function of1=O(f1); + Function of2=O(f2); + Function qf1=Q(f1); + Function qf2=Q(f2); + std::vector>> vp({vp1,vp2,vp3}); + for (int i=0; i<3; ++i) { + // O1 + O.set_particle(0); + { + double ref=inner({CCPairFunction({of1},{f2})},vp[i]); + double result=inner({CCPairFunction({f1},{f2})},O(vp[i])); + t1.checkpoint(result,ref,thresh,"O1 p"+std::to_string(i)); + } + + // O2 + O.set_particle(1); + { + double ref=inner({CCPairFunction({f1},{of2})},vp[i]); + double result=inner({CCPairFunction({f1},{f2})},O(vp[i])); + t1.checkpoint(result,ref,thresh,"O2 p"+std::to_string(i)); + } + // Q1 + Q.set_particle(0); + { + double ref=inner({CCPairFunction({qf1},{f2})},vp[i]); + double result=inner({CCPairFunction({f1},{f2})},Q(vp[i])); + t1.checkpoint(result,ref,thresh,"Q1 p"+std::to_string(i)); + } + // Q2 + Q.set_particle(1); + { + double ref=inner({CCPairFunction({f1},{qf2})},vp[i]); + double result=inner({CCPairFunction({f1},{f2})},Q(vp[i])); + t1.checkpoint(result,ref,thresh,"Q2 p"+std::to_string(i)); + } + } + // some hardwire test { // = @@ -590,28 +1189,29 @@ int test_projector(World& world, std::shared_ptr ncf, double ak=ak_tensor.trace(ak_tensor); auto aa=matrix_inner(world,a,a); auto bb=matrix_inner(world,b,b); - Projector O1(a,a); + Projector O1(a,a); O.set_particle(0); O1.set_particle(0); - Projector O2(a,a); - O2.set_particle(0); + Projector O2(a,a); + O2.set_particle(1); double n1=inner(vp1,O1(vp2)); double n1a=inner(O1(vp1),vp2); + t1.checkpoint(fabs(n1-n1a) types { int i=0; for (auto p : {p1,p2,p3}) { - std::string s="CCPairFunction p"+std::to_string(++i); - std::vector vp({p}); + std::string s="CCPairFunction p"+std::to_string(++i); + std::vector> vp({p}); for (int particle : {0,1}) { O.set_particle(particle); Q.set_particle(particle); @@ -621,13 +1221,13 @@ int test_projector(World& world, std::shared_ptr ncf, double n1 = inner(Op, vp3) + inner(Qp, vp3); double n2 = inner(vp, vp3); print("n1 (RI), n2", n1, n2, fabs(n1 - n2)); - t1.checkpoint(fabs(n1 - n2) < FunctionDefaults<3>::get_thresh(), "RI with particle "+std::to_string(particle)+" on "+s ,timer.reset()); + t1.checkpoint(fabs(n1 - n2) < FunctionDefaults::get_thresh(), "RI with particle "+std::to_string(particle)+" on "+s); auto Op1 = O(Op); double n3=inner(Op, vp3); double n4=inner(Op1, vp3); print("n3, n4", n3, n4); - t1.checkpoint(fabs(n3-n4) < FunctionDefaults<3>::get_thresh(), "idempotency with particle "+std::to_string(particle)+" on "+s,timer.reset()); + t1.checkpoint(fabs(n3-n4) < FunctionDefaults::get_thresh(), "idempotency with particle "+std::to_string(particle)+" on "+s); } // testing SO projector @@ -650,21 +1250,13 @@ int test_projector(World& world, std::shared_ptr ncf, n1 = inner(SOp, vp3); print("SO: n1, n2, n3", n1, n2, n3, fabs(n1 - n2)); double zero=fabs(n1-n2) + fabs(n1-n3) + fabs(n2-n3); - t1.checkpoint(zero < FunctionDefaults<3>::get_thresh(), "SO operator on "+s,timer.reset() ); + t1.checkpoint(zero < FunctionDefaults<3>::get_thresh(), "SO operator on "+s); } } - t1.end(); - - return (t1.get_final_success()) ? 0 : 1; + return t1.end(); } -int test_dirac_convolution(World& world, std::shared_ptr ncf, const Molecule& molecule, - const CCParameters& parameter) { - int success=0; - test_output t1("CCPairFunction::test_dirac_convolution"); - return (t1.get_final_success()) ? 0 : 1; -} /** functionality * @@ -687,37 +1279,90 @@ int main(int argc, char **argv) { madness::World& world = madness::initialize(argc, argv); startup(world, argc, argv); commandlineparser parser(argc, argv); + int k=5; + double thresh=1.e-4; + if (parser.key_exists("k")) k=std::stoi(parser.value("k")); + if (parser.key_exists("thresh")) thresh=std::stod(parser.value("thresh")); + FunctionDefaults<1>::set_k(k); + FunctionDefaults<2>::set_k(k); + FunctionDefaults<3>::set_k(k); + FunctionDefaults<4>::set_k(k); + FunctionDefaults<5>::set_k(k); + FunctionDefaults<6>::set_k(k); + FunctionDefaults<1>::set_thresh(thresh); + FunctionDefaults<2>::set_thresh(thresh); + FunctionDefaults<3>::set_thresh(thresh); + FunctionDefaults<4>::set_thresh(thresh); + FunctionDefaults<5>::set_thresh(thresh); + FunctionDefaults<6>::set_thresh(thresh); FunctionDefaults<6>::set_tensor_type(TT_2D); - FunctionDefaults<3>::set_thresh(1.e-5); - FunctionDefaults<3>::set_cubic_cell(-1.0,1.0); - FunctionDefaults<6>::set_cubic_cell(-1.0,1.0); + FunctionDefaults<1>::set_cubic_cell(-10.,10.); + FunctionDefaults<2>::set_cubic_cell(-10.,10.); + FunctionDefaults<3>::set_cubic_cell(-10.,10.); + FunctionDefaults<4>::set_cubic_cell(-10.,10.); + FunctionDefaults<5>::set_cubic_cell(-10.,10.); + FunctionDefaults<6>::set_cubic_cell(-10.,10.); print("numerical parameters: k, eps(3D), eps(6D)", FunctionDefaults<3>::get_k(), FunctionDefaults<3>::get_thresh(), FunctionDefaults<6>::get_thresh()); int isuccess=0; #ifdef USE_GENTENSOR + CCParameters ccparam(world, parser); try { - parser.set_keyval("geometry", "source=library,he"); + parser.set_keyval("geometry", "he"); parser.print_map(); Molecule mol(world, parser); mol.print(); - CCParameters ccparam(world, parser); - - data1=data(world,ccparam); - std::shared_ptr ncf = create_nuclear_correlation_factor(world, - mol, nullptr, std::make_pair("slater", 2.0)); - - isuccess+=test_constructor(world, ncf, mol, ccparam); - isuccess+=test_overlap(world, ncf, mol, ccparam); - isuccess+=test_swap_particles(world, ncf, mol, ccparam); - isuccess+=test_scalar_multiplication(world, ncf, mol, ccparam); - isuccess+=test_partial_inner(world, ncf, mol, ccparam); - isuccess+=test_projector(world, ncf, mol, ccparam); - data1.clear(); - } catch (...) { - + mol, nullptr, std::make_pair("slater", 2.0)); + + auto data2=data(world,ccparam); + auto data4=data(world,ccparam); + auto data6=data(world,ccparam); + + isuccess+=test_constructor(world, ncf, data2, ccparam); + isuccess+=test_load_store(world,ncf,data2,ccparam); + isuccess+=test_operator_apply(world, ncf, data2, ccparam); + isuccess+=test_transformations(world, ncf, data2, ccparam); + isuccess+=test_multiply_with_f12(world, ncf, data2, ccparam); + isuccess+=test_inner(world, ncf, data2, ccparam); + isuccess+=test_multiply(world, ncf, data2, ccparam); + isuccess+=test_swap_particles(world, ncf, data2, ccparam); + isuccess+=test_scalar_multiplication(world, ncf, data2, ccparam); + isuccess+=test_projector(world, ncf, data2, ccparam); + isuccess+=test_partial_inner_3d(world, ncf, data2, ccparam); + isuccess+=test_partial_inner_6d(world, ncf, data2, ccparam); + isuccess+=test_apply(world, ncf, data2, ccparam); + isuccess+=test_consolidate(world, ncf, data2, ccparam); + + +// isuccess+=test_constructor(world, ncf, data4, ccparam); +// isuccess+=test_load_store(world,ncf,data2,ccparam); +// isuccess+=test_operator_apply(world, ncf, data4, ccparam); +// isuccess+=test_transformations(world, ncf, data4, ccparam); +// isuccess+=test_multiply_with_f12(world, ncf, data4, ccparam); +// isuccess+=test_inner(world, ncf, data4, ccparam); +// isuccess+=test_multiply(world, ncf, data4, ccparam); +// isuccess+=test_swap_particles(world, ncf, data4, ccparam); +// isuccess+=test_scalar_multiplication(world, ncf, data4, ccparam); +// isuccess+=test_projector(world, ncf, data4, ccparam); +// isuccess+=test_partial_inner_3d(world, ncf, data4, ccparam); +// isuccess+=test_partial_inner_6d(world, ncf, data4, ccparam); +// isuccess+=test_apply(world, ncf, data, ccparam); + + +// isuccess+=test_projector(world, ncf, data6, ccparam); + + data2.clear(); + data4.clear(); + data6.clear(); + world.gop.fence(); + } catch (std::exception& e) { + madness::print("an error occured"); + madness::print(e.what()); + world.gop.fence(); } + world.gop.fence(); #else print("could not run test_ccpairfunction: U need to compile with ENABLE_GENTENSOR=1"); #endif diff --git a/src/madness/chem/test_low_rank_function.cc b/src/madness/chem/test_low_rank_function.cc new file mode 100644 index 00000000000..a0130dc4330 --- /dev/null +++ b/src/madness/chem/test_low_rank_function.cc @@ -0,0 +1,868 @@ +// +// Created by Florian Bischoff on 4/24/23. +// + + +#include +#include + + +#include +#include +#include + + +using namespace madness; + + + +int test_lowrank_function(World& world, LowRankFunctionParameters parameters) { + test_output t1("CCPairFunction::low rank function"); + t1.set_cout_to_terminal(); + madness::default_random_generator.setstate(int(cpu_time())%4149); + madness::default_random_generator.setstate(int(cpu_time())%4149); + std::string id=unique_fileid(); + + constexpr std::size_t LDIM=3; + constexpr std::size_t NDIM=2*LDIM; + print("eps, k, NDIM, id",FunctionDefaults::get_thresh(),FunctionDefaults::get_k(),NDIM,id); + + parameters.print("grid","end"); + std::string f12type=parameters.f12type(); + std::string transpose=parameters.get("transpose"); + + json j; + std::string jsonfilename="test_low_rank_function."+id+".json"; + j["radius"]=parameters.radius(); + j["f12type"]=parameters.f12type(); + j["gamma"]=parameters.gamma(); + j["volume_element"]=parameters.volume_element(); + j["tol"]=parameters.tol(); + j["transpose"]=transpose; + j["orthomethod"]=parameters.orthomethod(); + j["gridtype"]=parameters.gridtype(); + j["rhsfunctiontype"]=parameters.rhsfunctiontype(); + j["optimize"]=parameters.optimize(); + std::ofstream of(jsonfilename,std::ios::out); + of< offset; + offset.fill(0.0); + Function phi1=FunctionFactory(world).functor([](const Vector& r) + { return 1.0;}); + Function phi2=FunctionFactory(world).functor([&offset](const Vector& r) + { return exp(-1.0*(r-offset).normf());}); + if (transpose=="slater1") std::swap(phi1,phi2); + { + double n1 = phi1.norm2(); + double n2 = phi2.norm2(); + bool first_one = (fabs(phi1({1, 1, 1}) - 1.0) < 1.e-6); + if (world.rank() == 0) { + if (first_one) print("1(1) phi(2)"); + else print("phi(1) 1(2)"); + print("norms", n1, n2); + } + } + + Function one=FunctionFactory(world) + .functor([](const Vector& r) { return 1.0;}); + + + std::shared_ptr f12; + if (f12type=="slaterf12") f12.reset(SlaterF12OperatorPtr(world,parameters.gamma(),1.e-6,FunctionDefaults::get_thresh())); + else if (f12type=="slater") f12.reset(SlaterOperatorPtr(world,parameters.gamma(),1.e-6,FunctionDefaults::get_thresh())); + else { + MADNESS_EXCEPTION(std::string("unknown f12type"+f12type).c_str(),1); + } + + + auto compute_result = [&world, &one](const auto& lrf) { + real_function_3d result=real_factory_3d(world); + for (int r=0; r(world,reference,"reference."+id,PlotParameters(world).set_plane({"x1","x2"})); + double n2=reference.norm2(); + print("reference.norm2() = int f12 phi2 d2",n2); + output(0.0,0.0,0.0,0.0,0.0,0.0); + + LRFunctorF12 lrfunctor(f12,phi1,phi1); + double cpu0=cpu_time(); + auto lrf=LowRankFunctionFactory(parameters).project(lrfunctor); + lrf.do_print=true; +// plot_plane<6>(world,lrfunctor,"plot_original."+id,PlotParameters(world).set_plane({"x1","x4"})); + double cpu1=cpu_time(); + double error1=lrf.l2error(lrfunctor); + print("l2error projection",error1); +// plot_plane<6>(world,lrf,"plot_lrf_projection."+id,PlotParameters(world).set_plane({"x1","x4"})); + + // compare + // \phi(1) \bar \phi(1) = \int phi(1) \phi(2) f(1,2) d2 + // = \int \sum_r g_r(1) h_r(2) d2 + // = \sum_r g_r(1) <\phi|h_r> + real_function_3d result=compute_result(lrf); + double projection_error=compute_relative_error(reference,result,lrf); + auto diff=reference-result; +// plot_plane<3>(world,diff,"plot_diff_int_projection."+id,PlotParameters(world).set_plane({"x1","x2"})); +// plot_plane<3>(world,result,"plot_lrf_int_projection."+id,PlotParameters(world).set_plane({"x1","x2"})); + output(projection_error,lrf.rank(),cpu1-cpu0,0.0,0.0,0.0); + j["projection_error_integrated"]=projection_error; + j["projection_error_l2"]=error1; + j["projection_time"]=cpu1-cpu0; + std::ofstream of1(jsonfilename,std::ios::out); + of1<(world,diff,"plot_diff_int_optimization."+id,PlotParameters(world).set_plane({"x1","x2"})); + plot_plane<3>(world,result,"plot_lrf_int_optimization."+id,PlotParameters(world).set_plane({"x1","x2"})); + double optimization_error=compute_relative_error(reference,result,lrf); + output(projection_error,lrf.rank(),cpu1-cpu0,optimization_error,lrf.rank(),cpu3-cpu2); + bool success=(projection_error<5.e-2) and (optimization_error<1.e-2); + + j["optimization_error_integrated"]=optimization_error; + j["optimization_error_l2"]=error2; + j["optimization_time"]=cpu3-cpu2; + + std::ofstream of2(jsonfilename,std::ios::out); + of2< +int test_Kcommutator(World& world, LowRankFunctionParameters& parameters) { + test_output t1("CCPairFunction::low exchange commutator"); + t1.set_cout_to_terminal(); + madness::default_random_generator.setstate(int(cpu_time())%4149); + std::string id=unique_fileid(); + + constexpr std::size_t LDIM=3; + constexpr std::size_t NDIM=2*LDIM; + print("eps, k, NDIM, id",FunctionDefaults::get_thresh(),FunctionDefaults::get_k(),NDIM,id); + parameters.print("grid"); + + real_convolution_3d g12=(CoulombOperator(world,1.e-6,FunctionDefaults::get_thresh())); + g12.particle()=1; + std::shared_ptr f12ptr; + std::string f12type=parameters.f12type(); + if (f12type=="slaterf12") f12ptr.reset(SlaterF12OperatorPtr(world,parameters.gamma(),1.e-6,FunctionDefaults::get_thresh())); + else if (f12type=="slater") f12ptr.reset(SlaterOperatorPtr(world,parameters.gamma(),1.e-6,FunctionDefaults::get_thresh())); + else { + MADNESS_EXCEPTION(std::string("unknown f12type"+f12type).c_str(),1); + } + real_convolution_3d& f12=*f12ptr; + + real_function_3d phi=real_factory_3d(world).f([](const coord_3d& r){return exp(-r.normf());}); + double n=phi.norm2(); + phi.scale(1/n); + real_function_3d phi_k=phi; // looks silly, helps reading. + + + // reference term ( < ij | K(1) ) = = < Ki(1) j(2) | f12 | i(1) j(2) > = ",reference); + + json j; + std::string jsonfilename="test_kcommuntator."+id+".json"; + j["radius"]=parameters.radius(); + j["f12type"]=parameters.f12type(); + j["gamma"]=parameters.gamma(); + j["thresh"]=FunctionDefaults<3>::get_thresh(); + j["volume_element"]=parameters.volume_element(); + j["tol"]=parameters.tol(); + j["orthomethod"]=parameters.orthomethod(); + j["gridtype"]=parameters.gridtype(); + j["rhsfunctiontype"]=parameters.rhsfunctiontype(); + j["optimize"]=parameters.optimize(); + j["reference"]=reference; + + auto json2file= [](const json& j, const std::string& jsonfilename) { + std::ofstream of(jsonfilename, std::ios::out); + of << j; + of.close(); + }; + + json2file(j,jsonfilename); + timer t(world); + auto compute_error = [&](const std::string msg, const LowRankFunction& lrf) { + auto gk = mul(world, phi_k, g12(lrf.g * phi_k)); // function of 1 + auto hj = lrf.h * phi; // function of 2 + Tensor j_hj = inner(world, phi, hj); + Tensor i_gk = inner(world, phi, gk); + double result_right = j_hj.trace(i_gk); + print(msg,"norm ", result_right); + print(msg,"error", result_right-reference); + print(msg,"rank ", lrf.rank()); + j[msg]=result_right-reference; + j[msg+"_rank"]=lrf.rank(); + j[msg+"_compute_time"]=t.tag(msg+"_compute_time"); + json2file(j,jsonfilename); + }; + + + if (1) { + // lowrankfunction left phi: lrf(1',2) = f12(1',2) i(1') + // K f12 ij = \sum_k k(1) \int g(1,1') f12(1'2) i(1') j(2) k(1') d1' + // = \sum_kr k(1) j(2) \int g(1,1') g_r(1') h_r(2) k(1') d1' + // = \sum_r j(2) h_r(2) \sum_k k(1) \int g(1,1') g_r(1') k(1') d1' + real_function_3d one = real_factory_3d(world).f([](const coord_3d& r) { return 1.0; }); + LRFunctorF12 lrfunctor(f12ptr,phi,one); +// LowRankFunction fi_one(f12ptr, copy(phi), copy(one)); + auto fi_one=LowRankFunctionFactory(parameters).project(lrfunctor); +// fi_one.project(parameters); + double l2error=fi_one.l2error(lrfunctor); + print("left_project_l2error",l2error); + + j["left_project_time"]=t.tag("left_project_time"); + json2file(j,jsonfilename); + compute_error("left_project",fi_one); + + fi_one.optimize(lrfunctor); + l2error=fi_one.l2error(lrfunctor); + print("left_optimize_l2error",l2error); + j["left_optimize_time"]=t.tag("left_optimize_time"); + json2file(j,jsonfilename); + compute_error("left_optimize",fi_one); + +// fi_one.reorthonormalize(); +// j["left_reorthonormalize"]=t.tag("left_reorthonormalize"); +// json2file(j,jsonfilename); +// compute_error("left_reorthonormalize",fi_one); + + LowRankFunction phi0(world); + phi0.g={phi}; + phi0.h={phi}; + + timer t2(world); + // this is f12|ij> + auto f12ij=copy(fi_one); + f12ij.h=f12ij.h*phi; + double result1=inner(phi0,f12ij); + print("",result1); + t2.tag("multiply 1(1)* phi(1))"); + + // this is f12|(ki) j> + auto f12kij=copy(f12ij); + f12kij.g=f12kij.g*phi; + double result3=inner(phi0,f12kij); + print("",result3); + t2.tag("multiply 1"); + + // this is g(f12|(ki) j>); + auto gf12kij=copy(f12kij); + gf12kij.g=g12(f12kij.g); + double result2=inner(phi0,gf12kij); + print(")",result2); + t2.tag("apply g "); + + // this is kg(f12|(ki) j>); + auto kgf12kij=copy(gf12kij); + kgf12kij.g=gf12kij.g * phi; + double result4=inner(phi0,kgf12kij); + print(")",result4); + t2.tag("apply g "); + } + + + // apply exchange operator in 6d +// if (f12type=="slaterf12") { + if (1) { +// FunctionDefaults<3>::print(); +// FunctionDefaults<6>::print(); + real_function_6d phi0=CompositeFactory(world).particle1(phi).particle2(phi); + + double thresh=FunctionDefaults<3>::get_thresh(); + double dcut=1.e-6; + real_function_6d tmp=TwoElectronFactory(world).dcut(dcut).gamma(parameters.gamma()).f12().thresh(thresh); + real_function_6d f12ij=CompositeFactory(world).g12(tmp).particle1(copy(phi)).particle2(copy(phi)); + + timer t(world); + f12ij.fill_tree(); + t.tag("exchange: fill_tree"); + f12ij.print_size("f12ij"); + + auto result1=madness::inner(phi0,f12ij); + print("", result1); + double reference1=inner(phi*phi,f12(phi*phi)); + print("reference ",reference1); + + + real_function_6d kf12ij=multiply(f12ij,copy(phi_k),1); + kf12ij.print_size("kf12ij"); + t.tag("exchange: multiply 1"); + + auto result2=madness::inner(phi0,kf12ij); + print("", result2); + double reference2=inner(phi*phi,f12(phi*phi*phi)); + print("reference ",reference2); + + kf12ij.change_tree_state(reconstructed); + real_function_6d gkf12ij=g12(kf12ij).truncate(); + gkf12ij.print_size("gkf12ij"); + t.tag("exchange: apply g"); + + auto result3=madness::inner(phi0,gkf12ij); + print("", result3); + double reference3=inner(phi*phi,f12(phi*phi*g12(copy(phi)))); + print("reference ",reference3); + + + auto exf12ij=multiply(gkf12ij,copy(phi_k),1).truncate(); + exf12ij.print_size("exf12ij"); + t.tag("exchange: multiply 2"); + + auto result=madness::inner(phi0,exf12ij); + print("", result); + print("error",result-reference); + + + } + + return t1.end(); + +} + +template +int test_full_rank_functor(World& world, LowRankFunctionParameters parameters) { + + test_output t1("test_full_rank_functor"); + t1.set_cout_to_terminal(); + print_header2("entering test_full_rank_functor"); + constexpr int NDIM=2*LDIM; +// FunctionDefaults::set_thresh(1.e-6); +// FunctionDefaults::set_thresh(1.e-6); + double tol=6.e-3; + double gaussexponent=2.0; + + const particle p1=particle::particle1(); + const particle p2=particle::particle2(); + + LRFunctorPure functorpure; + Function gauss=FunctionFactory(world) + .functor([&gaussexponent](const Vector& r){ + Vector a,b; + for (int i=0; i>(GaussOperatorPtr(world,gaussexponent)); + LRFunctorF12 functorf12(gaussop,std::vector>({}),{}); +// functorf12.f12.reset(GaussOperatorPtr(world,gaussexponent)); + + auto builder= LowRankFunctionFactory(parameters).set_radius(8) + .set_volume_element(0.1).set_rank_revealing_tol(1.e-10).set_orthomethod("canonical"); + + auto lrfunction1=builder.project(functorf12); + t1.checkpoint(true,"construction f12 functor"); + auto lrfunction2=builder.project(functorpure); + t1.checkpoint(true,"construction full rank functor"); + lrfunction1.optimize(functorf12); + t1.checkpoint(true,"optimization f12 functor"); + lrfunction2.optimize(functorpure); + t1.checkpoint(true,"optimization full rank functor"); + + + try { + double error1=lrfunction1.l2error(functorf12); + t1.checkpoint(error1,tol,"f12 functor, f12 l2 error: "); + } catch (...) { + print("l2 error negative 1"); + } + try { + double error2=lrfunction2.l2error(functorpure); + t1.checkpoint(error2,tol,"full rank functor, full rank l2 error"); + } catch (...) { + print("l2 error negative 2"); + } + try { + double error3=lrfunction2.l2error(functorf12); + t1.checkpoint(error3,tol,"full rank functor, f12 l2 error:"); + } catch (...) { + print("l2 error negative 3"); + } + try { + double error4=lrfunction1.l2error(functorpure); + t1.checkpoint(error4,tol,"f12 functor, full rank l2 error: "); + } catch (...) { + print("l2 error negative 4"); + } +// print("errors",error2,error4); + + print_header2("leaving test_full_rank_functor"); + return t1.end(); +} + + +template +int test_arithmetic(World& world, LowRankFunctionParameters parameters) { + constexpr std::size_t NDIM = 2 * LDIM; + test_output t1("LowRankFunction::arithmetic in dimension " + std::to_string(NDIM)); + t1.set_cout_to_terminal(); + double thresh=FunctionDefaults::get_thresh(); + double thresh_ndim=FunctionDefaults::get_thresh(); + print("thresh ldim/ndim",thresh,thresh_ndim); + Function phi=FunctionFactory(world) + .functor([](const Vector& r){return exp(-4.0*inner(r,r));}); + + auto gauss1=std::shared_ptr>(GaussOperatorPtr(world,1.0)); + LRFunctorF12 functor1(gauss1,{phi},{}); + auto gauss2=std::shared_ptr>(GaussOperatorPtr(world,2.0)); + LRFunctorF12 functor2(gauss2,{phi},{}); + + auto p1=particle::particle1(); + auto p2=particle::particle2(); + + auto builder= LowRankFunctionFactory(parameters).set_radius(4) + .set_volume_element(0.1).set_rank_revealing_tol(1.e-10).set_orthomethod("canonical"); + auto lrf1=builder.project(functor1); + auto lrf2=builder.project(functor2); + + Vector r; + r.fill(0.2); + + // addition/subtraction + { + auto l1=lrf1+lrf1; + t1.checkpoint(fabs(l1(r)-2.0*lrf1(r)) +int test_inner(World& world, LowRankFunctionParameters parameters) { + + static_assert(LDIM==1 or LDIM==2); + constexpr std::size_t NDIM = 2 * LDIM; + test_output t1("LowRankFunction::test_inner in dimension " + std::to_string(NDIM)); +// t1.set_cout_to_terminal(); + double thresh=FunctionDefaults::get_thresh(); + double thresh_ndim=FunctionDefaults::get_thresh(); + print("thresh ldim/ndim",thresh,thresh_ndim); + Function phi=FunctionFactory(world) + .functor([](const Vector& r){return exp(-4.0*inner(r,r));}); + + auto gauss1=std::shared_ptr>(GaussOperatorPtr(world,1.0)); + LRFunctorF12 functor1(gauss1,{phi},{}); + auto gauss2=std::shared_ptr>(GaussOperatorPtr(world,2.0)); + LRFunctorF12 functor2(gauss2,{phi},{}); +// functor2.a={phi}; + + auto p1=particle::particle1(); + auto p2=particle::particle2(); + + auto builder= LowRankFunctionFactory(parameters).set_radius(4) + .set_volume_element(0.1).set_rank_revealing_tol(1.e-10).set_orthomethod("canonical"); + auto lrf1=builder.project(functor1); + auto lrf2=builder.project(functor2); + + // reference numbers: (by mathematica) + // f1(x,y) = exp(-a*x^2) * exp(-(x-y)^2) + // f2(x,y) = exp(-a*x^2) * exp(-g (x-y)^2) + // with a=4, g=2 + // int f1(x,y),f2(x,z) dx = inner(f1,f2,0,0) : norm^2 = Pi^2/(2 Sqrt[2] Sqrt[a gamma] Sqrt[1 + 2 a + gamma]) = 0.37197471167788324677 + // int f1(x,y),f2(z,x) dx = inner(f1,f2,0,1) : norm^2 = Pi^2/(2 Sqrt[a (1 + a + gamma) (a + 2 gamma)]) = 0.32972034117743393239 + // int f1(y,x),f2(x,z) dx = inner(f1,f2,1,0) : norm^2 = 0.26921553123369812300 + // int f1(y,x),f2(z,x) dx = inner(f1,f2,1,1) : norm^2 = 0.35613867236025352322 + + // inner f(1,2) f(2,3) + auto fullrank1=lrf1.reconstruct(); + auto fullrank2=lrf2.reconstruct(); + t1.checkpoint(true,"prep inner"); + { + std::vector reference={ 0.37197471167788324677, 0.32972034117743393239, 0.26921553123369812300, 0.35613867236025352322}; + int counter=0; + for (auto p11 : {p1,p2}) { + for (auto p22 : {p1,p2}) { + double ref=reference[counter]; + if (LDIM==2) ref*=ref; + + // full/full + auto lhs1=inner(fullrank1,fullrank2,p11.get_tuple(),p22.get_tuple()); + const double l1=lhs1.norm2(); + print("l1",l1,l1*l1,ref); + t1.checkpoint(l1*l1,ref,thresh,"inner(full,full,"+p11.str()+","+p22.str()+")"); + double asymmetric_ref=inner(fullrank1,lhs1); + double asymmetric1=inner(fullrank1,lhs1); + t1.checkpoint(asymmetric1,asymmetric_ref,thresh,"asymmetric(full,full,"+p11.str()+","+p22.str()+")"); + + // low rank/full + auto lhs2=inner(lrf1,fullrank2,p11,p22); + double l2=lhs2.norm2(); + t1.checkpoint(l2*l2,ref,thresh,"inner(lrf,full,"+p11.str()+","+p22.str()+")"); + double asymmetric2=inner(lrf1,lhs2); + t1.checkpoint(asymmetric2,asymmetric_ref,thresh,"asymmetric(lrf,full,"+p11.str()+","+p22.str()+")"); + + // full/low rank + auto lhs3=inner(fullrank1,lrf2,p11,p22); + double l3=lhs3.norm2(); + t1.checkpoint(l3*l3,ref,thresh,"inner(full,lrf,"+p11.str()+","+p22.str()+")"); + double asymmetric3=inner(lrf1,lhs3); + t1.checkpoint(asymmetric3,asymmetric_ref,thresh,"asymmetric(full,lrf,"+p11.str()+","+p22.str()+")"); + + + // low rank/low rank + auto lhs4=inner(lrf1,lrf2,p11,p22); + double l4=lhs4.norm2(); + t1.checkpoint(l4*l4,ref,thresh,"inner(lrf,lrf,"+p11.str()+","+p22.str()+")"); + double asymmetric4=inner(lrf1,lhs4); + t1.checkpoint(asymmetric4,asymmetric_ref,thresh,"asymmetric(lrf,lrf,"+p11.str()+","+p22.str()+")"); + + print("result norm",p11,p22,"), reference ",l1*l1,l2*l2,l3*l3,l4*l4,ref); + // l2 norm cannot distinguish between f(1,2) and f(2,1) + print("result asym",p11,p22,")",asymmetric1,asymmetric2,asymmetric3,asymmetric4); + + counter++; + } + } + } + + // inner f(1,2) g(2) + // this is surprisingly inaccurate, but algorithm is correct, the error can be systematically decreased + { + thresh=FunctionDefaults::get_thresh()*50.0; + // fresh start + lrf1=builder.project(functor1); + fullrank1=FunctionFactory(world).functor(functor1); + + std::vector> arg(3); + for (int i=0; i<3; ++i) arg[i]=FunctionFactory(world) + .functor([&i](const Vector& r) + {return exp(-(i+1)*r.normf());}); + + std::vector> lhs_full1, lhs_full2,lhs_func1,lhs_func2; + for (auto& a : arg) { + lhs_full1.push_back(inner(fullrank1,a,p1.get_tuple(),p1.get_tuple())); + lhs_full2.push_back(inner(fullrank1,a,p2.get_tuple(),p1.get_tuple())); + + lhs_func1.push_back(inner(functor1,a,p1,p1)); + lhs_func2.push_back(inner(functor1,a,p2,p1)); + } + auto lhs_lrf1=inner(lrf1,arg,p1,p1); + auto lhs_lrf2=inner(lrf1,arg,p2,p1); + + double norm_func1=norm2(world,lhs_func1); + double norm_func2=norm2(world,lhs_func2); + double norm_full1=norm2(world,lhs_full1); + double norm_full2=norm2(world,lhs_full2); + double norm_lrf1=norm2(world,lhs_lrf1); + double norm_lrf2=norm2(world,lhs_lrf2); + print("norms 1",norm_func1,norm_full1,norm_lrf1); + print("norms 2",norm_func2,norm_full2,norm_lrf2); + + double error1=norm2(world,lhs_full1-lhs_func1); + double error2=norm2(world,lhs_full2-lhs_func2); + double error3=norm2(world,lhs_lrf1 -lhs_func1); + double error4=norm2(world,lhs_lrf2 -lhs_func2); + + print("error1/2",error1,error2,error3,error4); +// t1.checkpoint(error1 +int test_construction_optimization(World& world, LowRankFunctionParameters parameters) { + constexpr std::size_t NDIM=2*LDIM; + test_output t1("LowRankFunction::construction/optimization in dimension "+std::to_string(NDIM)); + t1.set_cout_to_terminal(); + OperatorInfo info(1.0,1.e-6,FunctionDefaults::get_thresh(),OT_SLATER); + auto slater=std::shared_ptr >(new SeparatedConvolution(world,info)); + Function one=FunctionFactory(world).functor([](const Vector& r){return exp(-0.4*inner(r,r));}); + Function half=FunctionFactory(world).functor([](const Vector& r){return sqrt(0.5)*exp(-0.4*inner(r,r));}); + + LRFunctorF12 lrfunctor1(slater,one,one); + LRFunctorF12 lrfunctor2(slater,{half,half},{half,half}); + + for (auto& lrfunctor : {lrfunctor1,lrfunctor2}) { + LowRankFunctionFactory builder(parameters); + auto lrf = builder.project(lrfunctor); + t1.checkpoint(lrf.rank() > 0, "construction"); + + // with Slater tol must be relaxed + double tol = 1.e-2; + + double error = lrf.l2error(lrfunctor); + double norm = lrf.norm2(); + print("lrf.norm", norm); + print("l2 error project ", error); + t1.checkpoint(error, tol, "l2 error in projection"); + + auto lrf2(lrf); + error = lrf2.l2error(lrfunctor); + print("l2 error copy ctor ", error); + MADNESS_CHECK(lrf.rank() == lrf2.rank()); + MADNESS_CHECK(&(lrf.g[0]) != &(lrf2.g[0])); // deep copy + t1.checkpoint(error, tol, "l2 error in copy ctor"); + + lrf.optimize(lrfunctor); + error = lrf.l2error(lrfunctor); + print("l2 error optimize", error); + t1.checkpoint(error, tol, "l2 error in optimization"); + + lrf.reorthonormalize(); + error = lrf.l2error(lrfunctor); + print("l2 error reorthonormalize", error); + t1.checkpoint(error, tol, "l2 error in reorthonormalization"); + + lrf+=lrf; + lrf*=0.5; + lrf.reorthonormalize(); + error = lrf.l2error(lrfunctor); + print("l2 error reorthonormalize with lindep", error); + t1.checkpoint(error, tol, "l2 error in reorthonormalization with lindep"); + + } + return t1.end(); +} + +template +int test_molecular_grid(World& world, LowRankFunctionParameters parameters) { + constexpr std::size_t NDIM=2*LDIM; + test_output t1("LowRankFunction::molecular_grid in dimension "+std::to_string(NDIM)); + t1.set_cout_to_terminal(); + OperatorInfo info(1.0,1.e-6,FunctionDefaults::get_thresh(),OT_SLATER); + auto slater=std::shared_ptr >(new SeparatedConvolution(world,info)); + + /* + * we test the accuracy of the matrix element _12 + */ + + // a molecule of hydrogen atoms in the xy plane + std::vector> atomic_sites; + atomic_sites.push_back(Vector( 0.0)); + atomic_sites.push_back(Vector( 2.0)); + atomic_sites.push_back(Vector(-4.0)); + if (LDIM>1) { + atomic_sites.back()[0]=0.0; + atomic_sites.back()[1]=-4.0; + } + + randomgrid rgrid(parameters.volume_element(), parameters.radius()); +// randomgrid rgrid(parameters.volume_element(), 1.0); + molecular_grid mgrid(atomic_sites,rgrid); + auto grid=mgrid.get_grid(); + mgrid.visualize("grid",grid); + + // something like a density + auto dens=[&atomic_sites](const Vector& r) { + double result=0.0; + for (auto& c : atomic_sites) result+=exp(-0.4*inner(r-c,r-c)); + return result; + }; + + Function density=FunctionFactory(world).functor(dens); + Function one=FunctionFactory(world).functor([](const Vector& r){return exp(-0.4*inner(r,r));}); + Function half=FunctionFactory(world).functor([](const Vector& r){return sqrt(0.5)*exp(-0.4*inner(r,r));}); + + LRFunctorF12 lrfunctor(slater,density,density); + { + +// atomic_sites.erase(atomic_sites.begin()+1, atomic_sites.end()); + LowRankFunctionFactory builder(parameters, atomic_sites); + auto lrf = builder.project(lrfunctor); + t1.checkpoint(lrf.rank() > 0, "construction"); + + // with Slater tol must be relaxed + double tol = 1.e-2; + + double error = lrf.l2error(lrfunctor); + double norm = lrf.norm2(); + print("lrf.norm", norm); + print("l2 error project ", error); + t1.checkpoint(error, tol, "l2 error in projection"); + + auto lrf2(lrf); + error = lrf2.l2error(lrfunctor); + print("l2 error copy ctor ", error); + MADNESS_CHECK(lrf.rank() == lrf2.rank()); + MADNESS_CHECK(&(lrf.g[0]) != &(lrf2.g[0])); // deep copy + t1.checkpoint(error, tol, "l2 error in copy ctor"); + + lrf.optimize(lrfunctor); + error = lrf.l2error(lrfunctor); + print("l2 error optimize", error); + t1.checkpoint(error, tol, "l2 error in optimization"); + + lrf.reorthonormalize(); + error = lrf.l2error(lrfunctor); + print("l2 error reorthonormalize", error); + t1.checkpoint(error, tol, "l2 error in reorthonormalization"); + + lrf+=lrf; + lrf*=0.5; + lrf.reorthonormalize(); + error = lrf.l2error(lrfunctor); + print("l2 error reorthonormalize with lindep", error); + t1.checkpoint(error, tol, "l2 error in reorthonormalization with lindep"); + + } + return t1.end(); +} + + + +int main(int argc, char **argv) { + + madness::World& world = madness::initialize(argc, argv); + startup(world, argc, argv); + commandlineparser parser(argc, argv); + bool long_test = parser.key_exists("long_test"); + bool exchange_test = parser.key_exists("exchange_test"); + int k = parser.key_exists("k") ? std::atoi(parser.value("k").c_str()) : 6; + double thresh = parser.key_exists("thresh") ? std::stod(parser.value("thresh")) : 3.e-5; + FunctionDefaults<6>::set_tensor_type(TT_2D); + + + FunctionDefaults<3>::set_truncate_mode(1); + FunctionDefaults<6>::set_truncate_mode(1); + + FunctionDefaults<1>::set_thresh(thresh); + FunctionDefaults<2>::set_thresh(thresh); + FunctionDefaults<3>::set_thresh(thresh); + FunctionDefaults<4>::set_thresh(thresh); + FunctionDefaults<5>::set_thresh(thresh); + FunctionDefaults<6>::set_thresh(thresh); + FunctionDefaults<6>::set_thresh(1.e-3); + + FunctionDefaults<1>::set_k(k); + FunctionDefaults<2>::set_k(k); + FunctionDefaults<3>::set_k(k); + FunctionDefaults<4>::set_k(k); + FunctionDefaults<5>::set_k(k); + FunctionDefaults<6>::set_k(k); + + FunctionDefaults<1>::set_cubic_cell(-10.,10.); + FunctionDefaults<2>::set_cubic_cell(-10.,10.); + FunctionDefaults<3>::set_cubic_cell(-10.,10.); + FunctionDefaults<4>::set_cubic_cell(-10.,10.); + FunctionDefaults<5>::set_cubic_cell(-10.,10.); + FunctionDefaults<6>::set_cubic_cell(-10.,10.); + + FunctionDefaults<2>::set_tensor_type(TT_FULL); + print("numerical parameters: k, eps(3D), eps(6D)", FunctionDefaults<3>::get_k(), FunctionDefaults<3>::get_thresh(), + FunctionDefaults<6>::get_thresh()); + LowRankFunctionParameters parameters; + parameters.set_derived_value("f12type",std::string("slaterf12")); + parameters.read_and_set_derived_values(world,parser,"grid"); + parameters.set_user_defined_value("radius",2.5); + parameters.set_user_defined_value("volume_element",1.e-1); +// parameters.set_user_defined_value("tol",1.0e-10); + parameters.print("grid"); + int isuccess=0; +#ifdef USE_GENTENSOR + + + try { + + isuccess+=test_full_rank_functor<1>(world, parameters); + isuccess+=test_construction_optimization<1>(world,parameters); + isuccess+=test_arithmetic<1>(world,parameters); + isuccess+=test_inner<1>(world,parameters); + + if (long_test) { + isuccess+=test_construction_optimization<2>(world,parameters); + isuccess+=test_arithmetic<2>(world,parameters); + isuccess+=test_inner<2>(world,parameters); + isuccess+=test_molecular_grid<2>(world,parameters); + } + +// parameters.set_user_defined_value("volume_element",1.e-1); +// isuccess+=test_lowrank_function(world,parameters); +// isuccess+=test_Kcommutator(world,parameters); + } catch (std::exception& e) { + madness::print("an error occured"); + madness::print(e.what()); + } +#else + print("could not run test_ccpairfunction: U need to compile with ENABLE_GENTENSOR=1"); +#endif + finalize(); + + return isuccess; +} \ No newline at end of file diff --git a/src/madness/misc/CMakeLists.txt b/src/madness/misc/CMakeLists.txt index 81f5842b3d3..790a0d1e09a 100644 --- a/src/madness/misc/CMakeLists.txt +++ b/src/madness/misc/CMakeLists.txt @@ -2,7 +2,7 @@ set(MADMISC_HEADERS misc.h ran.h phandler.h interpolation_1d.h cfft.h info.h) set(MADMISC_SOURCES - checksum_file.cc position_stream.cc gprofexit.cc ran.cc cfft.cc info.cc) + checksum_file.cc position_stream.cc gprofexit.cc ran.cc cfft.cc info.cc unique_filename.cc) # retrieve git metadata include(GetGitMetadata) vgkit_cmake_git_metadata() diff --git a/src/madness/misc/misc.h b/src/madness/misc/misc.h index 313c53dcdeb..f8ace2438f1 100644 --- a/src/madness/misc/misc.h +++ b/src/madness/misc/misc.h @@ -46,6 +46,8 @@ namespace madness { const char comment='#', bool rewind=true, bool silent=false); std::string lowercase(const std::string& s); void gprofexit(int id, int nproc); + /// creates a unique filename, using PBS ID if available + std::string unique_fileid(); } #endif // MADNESS_MISC_MISC_H__INCLUDED diff --git a/src/madness/misc/unique_filename.cc b/src/madness/misc/unique_filename.cc new file mode 100644 index 00000000000..7788ef328e4 --- /dev/null +++ b/src/madness/misc/unique_filename.cc @@ -0,0 +1,25 @@ +// +// Created by Florian Bischoff/chatgpt on 9/7/23. +// + +#include +#include + +namespace madness { + +std::string unique_fileid() { + std::string uniqueFileName; + + // Check if the PBS_JOBID environment variable is set + const char* pbsId = std::getenv("PBS_JOBID"); + if (pbsId != nullptr) { + uniqueFileName =std::string(pbsId); + } else { + // If PBS_ID is not available, generate a random number + int randomNumber = RandomValue(); + uniqueFileName = std::to_string(randomNumber); + } + + return uniqueFileName; +} +} diff --git a/src/madness/mra/CMakeLists.txt b/src/madness/mra/CMakeLists.txt index e69ccad6907..9dabf031942 100644 --- a/src/madness/mra/CMakeLists.txt +++ b/src/madness/mra/CMakeLists.txt @@ -10,7 +10,7 @@ set(MADMRA_HEADERS function_interface.h gfit.h convolution1d.h simplecache.h derivative.h displacements.h functypedefs.h sdf_shape_3D.h sdf_domainmask.h vmra1.h leafop.h nonlinsol.h macrotaskq.h macrotaskpartitioner.h QCCalculationParametersBase.h - commandlineparser.h) + commandlineparser.h operatorinfo.h) set(MADMRA_SOURCES mra1.cc mra2.cc mra3.cc mra4.cc mra5.cc mra6.cc startup.cc legendre.cc twoscale.cc qmprop.cc QCCalculationParametersBase.cc) diff --git a/src/madness/mra/funcimpl.h b/src/madness/mra/funcimpl.h index 399f01d8fb1..4282d2472a7 100644 --- a/src/madness/mra/funcimpl.h +++ b/src/madness/mra/funcimpl.h @@ -524,7 +524,7 @@ namespace madness { std::vector s0(NDIM/2,s); const double tol=f->get_thresh(); - const double thresh=f->truncate_tol(tol, key); + const double thresh=f->truncate_tol(tol, key)*0.3; // custom factor to "ensure" accuracy // include the wavelets in the norm, makes it much more accurate const double fnorm=fcoeff.normf(); const double gnorm=gcoeff.normf(); @@ -942,6 +942,7 @@ template private: typedef WorldObject< FunctionImpl > woT; ///< Base class world object type public: + typedef T typeT; typedef FunctionImpl implT; ///< Type of this class (implementation) typedef std::shared_ptr< FunctionImpl > pimplT; ///< pointer to this class typedef Tensor tensorT; ///< Type of tensor for anything but to hold coeffs @@ -1150,6 +1151,15 @@ template if (fence) world.gop.fence(); } + /// merge the trees of this and other, while multiplying them with the alpha or beta, resp + + /// result and rhs do not have to have the same distribution or live in the same world + /// result+=alpha* this + /// @param[in] alpha prefactor for this + template + void accumulate_trees(FunctionImpl& result, const R alpha, const bool fence=true) const { + flo_unary_op_node_inplace(do_accumulate_trees(result,alpha),fence); + } /// perform: this= alpha*f + beta*g, invoked by result @@ -2337,6 +2347,36 @@ template }; + /// merge the coefficent boxes of this into result's tree + + /// result+= alpha*this + /// this and result don't have to have the same distribution or live in the same world + /// no comm, and the tree should be in an consistent state by virtue + template + struct do_accumulate_trees{ + typedef Range rangeT; + FunctionImpl* result=0; + T alpha=T(1.0); + do_accumulate_trees() = default; + do_accumulate_trees(FunctionImpl& result, const T alpha) + : result(&result), alpha(alpha) {} + + /// return the norm of the difference of this node and its "mirror" node + bool operator()(typename rangeT::iterator& it) const { + + const keyT& key = it->first; + const nodeT& node = it->second; + if (node.has_coeff()) result->get_coeffs().task(key, &nodeT::accumulate, + alpha*node.coeff(), result->get_coeffs(), key, result->targs); + return true; + } + + template void serialize(const Archive& ar) { + MADNESS_EXCEPTION("no serialization of do_accumulate_trees",1); + } + }; + + /// merge the coefficent boxes of this into other's tree /// no comm, and the tree should be in an consistent state by virtue @@ -3313,11 +3353,11 @@ template /// /// k=number of wavelets, so k=5 means max order is 4, so max exactly /// representable squarable polynomial is of order 2. - void tnorm(const tensorT& t, double* lo, double* hi) const; + void static tnorm(const tensorT& t, double* lo, double* hi); - void tnorm(const GenTensor& t, double* lo, double* hi) const; + void static tnorm(const GenTensor& t, double* lo, double* hi); - void tnorm(const SVDTensor& t, double* lo, double* hi, const int particle) const; + void static tnorm(const SVDTensor& t, double* lo, double* hi, const int particle); // This invoked if node has not been autorefined void do_square_inplace(const keyT& key); @@ -3821,67 +3861,52 @@ template template struct pointwise_multiplier { - const implT* impl; - const FunctionImpl* gimpl; coeffT val_lhs, coeff_lhs; + long oversampling=1; double error=0.0; double lo=0.0, hi=0.0, lo1=0.0, hi1=0.0, lo2=0.0, hi2=0.0; - pointwise_multiplier() : impl(0), gimpl(0) {} - pointwise_multiplier(const Key key, const coeffT& clhs, implT* i, const FunctionImpl* gimpl) - : impl(i), gimpl(gimpl), coeff_lhs(clhs) { - val_lhs=impl->coeffs2values(key,coeff_lhs); - error=0.0; - impl->tnorm(coeff_lhs,&lo,&hi); - gimpl->tnorm(coeff_lhs.get_svdtensor(),&lo1,&hi1,1); - gimpl->tnorm(coeff_lhs.get_svdtensor(),&lo2,&hi2,2); - } - - pointwise_multiplier(const Key key, const coeffT& clhs, implT* i) - : impl(i), gimpl(0), coeff_lhs(clhs) { - val_lhs=impl->coeffs2values(key,coeff_lhs); + pointwise_multiplier() {} + pointwise_multiplier(const Key key, const coeffT& clhs) : coeff_lhs(clhs) { + const auto& fcf=FunctionCommonFunctionality(coeff_lhs.dim(0)); + val_lhs=fcf.coeffs2values(key,coeff_lhs); error=0.0; - impl->tnorm(coeff_lhs,&lo,&hi); - } - - /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of the same dimensions - coeffT operator()(const Key key, const coeffT& coeff_rhs) { - double rlo, rhi; - impl->tnorm(coeff_rhs,&rlo,&rhi); - error = hi*rlo + rhi*lo + rhi*hi; - coeffT val_rhs=impl->coeffs2values(key, coeff_rhs); - val_rhs.emul(val_lhs); - return impl->values2coeffs(key,val_rhs); - + implT::tnorm(coeff_lhs,&lo,&hi); + if (coeff_lhs.is_svd_tensor()) { + FunctionImpl::tnorm(coeff_lhs.get_svdtensor(),&lo1,&hi1,1); + FunctionImpl::tnorm(coeff_lhs.get_svdtensor(),&lo2,&hi2,2); + } } /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of the same dimensions tensorT operator()(const Key key, const tensorT& coeff_rhs) { MADNESS_ASSERT(coeff_rhs.dim(0)==coeff_lhs.dim(0)); + auto fcf=FunctionCommonFunctionality(coeff_lhs.dim(0)); // the tnorm estimate is not tight enough to be efficient, better use oversampling bool use_tnorm=false; if (use_tnorm) { double rlo, rhi; - impl->tnorm(coeff_rhs,&rlo,&rhi); + implT::tnorm(coeff_rhs,&rlo,&rhi); error = hi*rlo + rhi*lo + rhi*hi; - tensorT val_rhs=impl->coeffs2values(key, coeff_rhs); + tensorT val_rhs=fcf.coeffs2values(key, coeff_rhs); val_rhs.emul(val_lhs.full_tensor_copy()); - return impl->values2coeffs(key,val_rhs); + return fcf.values2coeffs(key,val_rhs); } else { // use quadrature of order k+1 - auto cdata=FunctionCommonData::get(impl->get_k()+1); // npt=k+1 - FunctionCommonFunctionality fcf_hi_npt(cdata); + auto& cdata=FunctionCommonData::get(coeff_rhs.dim(0)); // npt=k+1 + auto& cdata_npt=FunctionCommonData::get(coeff_rhs.dim(0)+oversampling); // npt=k+1 + FunctionCommonFunctionality fcf_hi_npt(cdata_npt); // coeffs2values for rhs: k -> npt=k+1 - tensorT coeff1(cdata.vk); - coeff1(impl->cdata.s0)=coeff_rhs; // s0 is smaller than vk! + tensorT coeff1(cdata_npt.vk); + coeff1(cdata.s0)=coeff_rhs; // s0 is smaller than vk! tensorT val_rhs_k1=fcf_hi_npt.coeffs2values(key,coeff1); // coeffs2values for lhs: k -> npt=k+1 - tensorT coeff_lhs_k1(cdata.vk); - coeff_lhs_k1(impl->cdata.s0)=coeff_lhs.full_tensor_copy(); + tensorT coeff_lhs_k1(cdata_npt.vk); + coeff_lhs_k1(cdata.s0)=coeff_lhs.full_tensor_copy(); tensorT val_lhs_k1=fcf_hi_npt.coeffs2values(key,coeff_lhs_k1); // multiply @@ -3891,24 +3916,25 @@ template tensorT result1=fcf_hi_npt.values2coeffs(key,val_lhs_k1); // extract coeffs up to k - tensorT result=copy(result1(impl->cdata.s0)); - result1(impl->cdata.s0)=0.0; + tensorT result=copy(result1(cdata.s0)); + result1(cdata.s0)=0.0; error=result1.normf(); return result; - } - } /// multiply values of rhs and lhs, result on rhs, rhs and lhs are of differnet dimensions coeffT operator()(const Key key, const tensorT& coeff_rhs, const int particle) { Key key1, key2; key.break_apart(key1,key2); - MADNESS_ASSERT(gimpl); - FunctionCommonFunctionality fcf_lo(gimpl->cdata); - FunctionCommonFunctionality fcf_hi(impl->cdata); - FunctionCommonFunctionality fcf_lo_npt(gimpl->get_k()+1); - FunctionCommonFunctionality fcf_hi_npt(impl->get_k()+1); + const long k=coeff_rhs.dim(0); + auto& cdata=FunctionCommonData::get(k); + auto& cdata_lowdim=FunctionCommonData::get(k); + FunctionCommonFunctionality fcf_lo(cdata_lowdim); + FunctionCommonFunctionality fcf_hi(cdata); + FunctionCommonFunctionality fcf_lo_npt(k+oversampling); + FunctionCommonFunctionality fcf_hi_npt(k+oversampling); + // make hi-dim values from lo-dim coeff_rhs on npt grid points tensorT ones=tensorT(fcf_lo_npt.cdata.vk); @@ -3935,16 +3961,14 @@ template coeffT result1=fcf_hi_npt.values2coeffs(key,val_lhs_npt); // extract coeffs up to k - coeffT result=copy(result1(impl->cdata.s0)); - result1(impl->cdata.s0)=0.0; + coeffT result=copy(result1(cdata.s0)); + result1(cdata.s0)=0.0; error=result1.normf(); - result.reduce_rank(impl->get_tensor_args().thresh); return result; - } template void serialize(const Archive& ar) { - ar & error & lo & lo1 & lo2 & hi & hi1& hi2 & gimpl & impl & val_lhs & coeff_lhs; + ar & error & lo & lo1 & lo2 & hi & hi1& hi2 & val_lhs & coeff_lhs; } @@ -4165,25 +4189,19 @@ template double refine_error=compute_error_from_inaccurate_refinement(key,ceri); double error=refine_error; - // prepare the multiplication - pointwise_multiplier pm; - if (have_v1()) pm=pointwise_multiplier(key,coeff_ket,result,iav1.get_impl()); - else if (have_v2()) { - pm=pointwise_multiplier(key,coeff_ket,result,iav2.get_impl()); - } else { - pm=pointwise_multiplier(key,coeff_ket,result); - } - - // perform the multiplication, compute tnorm part of the total error - coeffT cresult(result->cdata.vk,result->get_tensor_args()); - if (have_v1()) { - cresult+=pm(key,cpot1.get_tensor(),1); - error+=pm.error; - } - if (have_v2()) { - cresult+=pm(key,cpot2.get_tensor(),2); - error+=pm.error; - } + // prepare the multiplication + pointwise_multiplier pm(key,coeff_ket); + + // perform the multiplication, compute tnorm part of the total error + coeffT cresult(result->cdata.vk,result->get_tensor_args()); + if (have_v1()) { + cresult+=pm(key,cpot1.get_tensor(),1); + error+=pm.error; + } + if (have_v2()) { + cresult+=pm(key,cpot2.get_tensor(),2); + error+=pm.error; + } if (have_eri()) { tensorT result1=cresult.full_tensor_copy(); @@ -5443,6 +5461,147 @@ template (rangeT(coeffs.begin(),coeffs.end()),do_inner_local(&g, leaves_only)); } + + /// compute the inner product of this range with other + template + struct do_inner_local_on_demand { + const FunctionImpl* ket; + const FunctionImpl* bra; + bool leaves_only=true; + typedef TENSOR_RESULT_TYPE(T,R) resultT; + + do_inner_local_on_demand(const FunctionImpl* bra, const FunctionImpl* ket, + const bool leaves_only=true) + : bra(bra), ket(ket), leaves_only(leaves_only) {} + resultT operator()(typename dcT::const_iterator& it) const { + + constexpr std::size_t LDIM=std::max(NDIM/2,std::size_t(1)); + + const keyT& key=it->first; + const nodeT& fnode = it->second; + if (not fnode.has_coeff()) return resultT(0.0); // probably internal nodes + + // assuming all boxes (esp the low-dim ones) are local, i.e. the functions are replicated + auto find_valid_parent = [](auto& key, auto& impl, auto&& find_valid_parent) { + MADNESS_CHECK(impl->get_coeffs().owner(key)==impl->world.rank()); // make sure everything is local! + if (impl->get_coeffs().probe(key)) return key; + auto parentkey=key.parent(); + return find_valid_parent(parentkey, impl, find_valid_parent); + }; + + // returns coefficients, empty if no functor present + auto get_coeff = [&find_valid_parent](const auto& key, const auto& v_impl) { + if ((v_impl.size()>0) and v_impl.front().get()) { + auto impl=v_impl.front(); + +// bool have_impl=impl.get(); +// if (have_impl) { + auto parentkey = find_valid_parent(key, impl, find_valid_parent); + MADNESS_CHECK(impl->get_coeffs().probe(parentkey)); + typename decltype(impl->coeffs)::accessor acc; + impl->get_coeffs().find(acc,parentkey); + auto parentcoeff=acc->second.coeff(); + auto coeff=impl->parent_to_child(parentcoeff, parentkey, key); + return coeff; + } else { + // get type of vector elements + typedef typename std::decay_t::value_type::element_type::typeT S; +// typedef typename std::decay_t::value_type S; + return GenTensor(); +// return GenTensor::typeT>(); + } + }; + + auto make_vector = [](auto& arg) { + return std::vector>(1,arg); + }; + + + Key key1,key2; + key.break_apart(key1,key2); + + auto func=dynamic_cast* >(ket->functor.get()); + MADNESS_ASSERT(func); + + MADNESS_CHECK_THROW(func->impl_ket_vector.size()==0 or func->impl_ket_vector.size()==1, + "only one ket function supported in inner_on_demand"); + MADNESS_CHECK_THROW(func->impl_p1_vector.size()==0 or func->impl_p1_vector.size()==1, + "only one p1 function supported in inner_on_demand"); + MADNESS_CHECK_THROW(func->impl_p2_vector.size()==0 or func->impl_p2_vector.size()==1, + "only one p2 function supported in inner_on_demand"); + auto coeff_bra=fnode.coeff(); + auto coeff_ket=get_coeff(key,func->impl_ket_vector); + auto coeff_v1=get_coeff(key1,make_vector(func->impl_m1)); + auto coeff_v2=get_coeff(key2,make_vector(func->impl_m2)); + auto coeff_p1=get_coeff(key1,func->impl_p1_vector); + auto coeff_p2=get_coeff(key2,func->impl_p2_vector); + + // construct |ket(1,2)> or |p(1)p(2)> or |p(1)p(2) ket(1,2)> + double error=0.0; + if (coeff_ket.has_data() and coeff_p1.has_data()) { + pointwise_multiplier pm(key,coeff_ket); + coeff_ket=pm(key,outer(coeff_p1,coeff_p2,TensorArgs(TT_FULL,-1.0)).full_tensor()); + error+=pm.error; + } else if (coeff_ket.has_data() or coeff_p1.has_data()) { + coeff_ket = (coeff_ket.has_data()) ? coeff_ket : outer(coeff_p1,coeff_p2); + } else { // not ket and no p1p2 + MADNESS_EXCEPTION("confused ket/p1p2 in do_inner_local_on_demand",1); + } + + // construct (v(1) + v(2)) |ket(1,2)> + coeffT v1v2ket; + if (coeff_v1.has_data()) { + pointwise_multiplier pm(key,coeff_ket); + v1v2ket = pm(key,coeff_v1.full_tensor(), 1); + error+=pm.error; + v1v2ket+= pm(key,coeff_v2.full_tensor(), 2); + error+=pm.error; + } else { + v1v2ket = coeff_ket; + } + + resultT result; + if (func->impl_eri) { // project bra*ket onto eri, avoid multiplication with eri + MADNESS_CHECK(func->impl_eri->get_functor()->provides_coeff()); + coeffT coeff_eri=func->impl_eri->get_functor()->coeff(key).full_tensor(); + pointwise_multiplier pm(key,v1v2ket); + tensorT braket=pm(key,coeff_bra.full_tensor_copy().conj()); + error+=pm.error; + if (error>1.e-3) print("error in key",key,error); + result=coeff_eri.full_tensor().trace(braket); + + } else { // no eri, project ket onto bra + result=coeff_bra.full_tensor_copy().trace_conj(v1v2ket.full_tensor_copy()); + } + return result; + } + + resultT operator()(resultT a, resultT b) const { + return (a+b); + } + + template void serialize(const Archive& ar) { + throw "NOT IMPLEMENTED"; + } + }; + + /// Returns the inner product of this with function g constructed on-the-fly + + /// the leaf boxes of this' MRA tree defines the inner product + template + TENSOR_RESULT_TYPE(T,R) inner_local_on_demand(const FunctionImpl& gimpl) const { + PROFILE_MEMBER_FUNC(FunctionImpl); + typedef TENSOR_RESULT_TYPE(T,R) resultT; + typedef FunctionImpl implR; + + MADNESS_CHECK(this->is_reconstructed()); + + typedef Range rangeT; + rangeT range(coeffs.begin(), coeffs.end()); + return world.taskq.reduce>(range, + do_inner_local_on_demand(this, &gimpl)); + } + /// Type of the entry in the map returned by make_key_vec_map typedef std::vector< std::pair > mapvecT; @@ -5697,6 +5856,8 @@ template } it = it_end; } +// print("thresh ",thresh); +// print("contraction list size",contraction_map.size()); // remove all double entries for (auto& elem: contraction_map) { @@ -5808,7 +5969,7 @@ template // print("\nkey, j_this_key", key, j_this_key); const double max_d_norm=j_other_list.find(j_this_key)->second; - const bool sd_norm_product_large = node.get_snorm() * max_d_norm > thresh; + const bool sd_norm_product_large = node.get_snorm() * max_d_norm > truncate_tol(thresh,key); // print("sd_product_norm",node.get_snorm() * max_d_norm, thresh); // end recursion if we have reached the final scale n @@ -6004,9 +6165,9 @@ template int gparticle= v1[0]==0 ? 0 : 1; // which particle to integrate over int hparticle= v2[0]==0 ? 0 : 1; // which particle to integrate over // merge multiple contraction dimensions into one - Tensor gtensor = gcoeff1.get_svdtensor().make_vector_with_weights(gparticle); + Tensor gtensor = gcoeff1.get_svdtensor().flat_vector_with_weights(gparticle); Tensor gtensor_other = gcoeff1.get_svdtensor().flat_vector((gparticle+1)%2); - Tensor htensor = hcoeff1.get_svdtensor().make_vector_with_weights(hparticle); + Tensor htensor = hcoeff1.get_svdtensor().flat_vector_with_weights(hparticle); Tensor htensor_other = hcoeff1.get_svdtensor().flat_vector((hparticle+1)%2); Tensor tmp1=inner(gtensor,htensor,1,1); // tmp1(r,r') = sum_j b(r,j) a(r',j) Tensor tmp2=inner(tmp1,gtensor_other,0,0); // tmp2(r',i) = sum_r tmp1(r,r') a(r,i) @@ -6018,9 +6179,9 @@ template if (key.level() > 0) { GenTensor gcoeff2 = copy(gcoeff1(g->get_cdata().s0)); GenTensor hcoeff2 = copy(hcoeff1(h->get_cdata().s0)); - Tensor gtensor = gcoeff2.get_svdtensor().make_vector_with_weights(gparticle); + Tensor gtensor = gcoeff2.get_svdtensor().flat_vector_with_weights(gparticle); Tensor gtensor_other = gcoeff2.get_svdtensor().flat_vector((gparticle+1)%2); - Tensor htensor = hcoeff2.get_svdtensor().make_vector_with_weights(hparticle); + Tensor htensor = hcoeff2.get_svdtensor().flat_vector_with_weights(hparticle); Tensor htensor_other = hcoeff2.get_svdtensor().flat_vector((hparticle+1)%2); Tensor tmp1=inner(gtensor,htensor,1,1); // tmp1(r,r') = sum_j b(r,j) a(r',j) Tensor tmp2=inner(tmp1,gtensor_other,0,0); // tmp2(r',i) = sum_r tmp1(r,r') a(r,i) @@ -6480,27 +6641,57 @@ template if (fcoeff.has_no_data() or gcoeff.has_no_data()) return Future (argT(fnode.is_leaf(),coeffT()));; - // let's specialize for the time being on SVD tensors for f and full tensors of half dim for g - MADNESS_ASSERT(gcoeff.is_full_tensor()); - MADNESS_ASSERT(fcoeff.is_svd_tensor()); - const tensorT gtensor=gcoeff.full_tensor(); + MADNESS_CHECK(gcoeff.is_full_tensor()); tensorT final(result->cdata.vk); - - const int otherdim=(dim+1)%2; const int k=fcoeff.dim(0); - std::vector s(fcoeff.get_svdtensor().dim_per_vector(dim)+1,_); - std::vector other_s(fcoeff.get_svdtensor().dim_per_vector(otherdim)+1,_); - - // do the actual contraction - std::vector shape(LDIM,k); - for (int r=0; r shape(LDIM, k); + + if (fcoeff.is_full_tensor()) { + // result_i = \sum_j g_j f_ji + const tensorT gtensor = gcoeff.full_tensor().reshape(k_ldim); + const tensorT ftensor = fcoeff.full_tensor().reshape(k_ldim,k_ldim); + final=inner(gtensor,ftensor,0,dim).reshape(shape); + + } else if (fcoeff.is_svd_tensor()) { + if (fcoeff.rank()>0) { + + // result_i = \sum_jr g_j a_rj w_r b_ri + const int otherdim = (dim + 1) % 2; + const tensorT gtensor = gcoeff.full_tensor().flat(); + const tensorT atensor = fcoeff.get_svdtensor().flat_vector(dim); // a_rj + const tensorT btensor = fcoeff.get_svdtensor().flat_vector(otherdim); + const tensorT gatensor = inner(gtensor, atensor, 0, 1); // ga_r + tensorT weights = copy(fcoeff.get_svdtensor().weights_); + weights.emul(gatensor); // ga_r * w_r + // sum over all ranks of b, include new weights: + // result_i = \sum_r ga_r * w_r * b_ri + for (int r = 0; r < fcoeff.rank(); ++r) final += weights(r) * btensor(r, _); + final = final.reshape(shape); + } + + } else { + MADNESS_EXCEPTION("unsupported tensor type in project_out_op",1); + } + if (0) { + const tensorT gtensor = gcoeff.full_tensor(); + + const int otherdim = (dim + 1) % 2; + const int k = fcoeff.dim(0); + std::vector s(fcoeff.get_svdtensor().dim_per_vector(dim) + 1, _); + std::vector other_s(fcoeff.get_svdtensor().dim_per_vector(otherdim) + 1, _); + + // do the actual contraction + for (int r = 0; r < fcoeff.rank(); ++r) { + s[0] = Slice(r, r); + other_s[0] = Slice(r, r); + const tensorT contracted_tensor = fcoeff.get_svdtensor().ref_vector(dim)(s).reshape(shape); + const tensorT other_tensor = fcoeff.get_svdtensor().ref_vector(otherdim)(other_s).reshape( + shape); + const double ovlp = gtensor.trace_conj(contracted_tensor); + const double fac = ovlp * fcoeff.get_svdtensor().weights(r); + final += fac * other_tensor; + } } // accumulate the result diff --git a/src/madness/mra/funcplot.h b/src/madness/mra/funcplot.h index f73a41f1428..995d9e9b692 100644 --- a/src/madness/mra/funcplot.h +++ b/src/madness/mra/funcplot.h @@ -55,7 +55,7 @@ namespace madness { // initialize with: key, value, comment (optional), allowed values (optional) initialize("zoom",2,"zoom into the simulation cell"); initialize("npoints",151,"number of plot points per dimension"); - initialize>("origin",{0.0,0.0,0.0},"origin of the plot"); + initialize>("origin",{},"origin of the plot"); initialize>("plane",{"x1","x2"},"plot plane: x1, x2, .., x6"); } @@ -83,6 +83,9 @@ namespace madness { template Vector origin() const { auto origin_vec=get>("origin"); + // fill in zeros if the default origin has fewer dimensions than the actual origin + int missing=NDIM-origin_vec.size(); + for (auto i=0; i o(origin_vec); return o; } @@ -1021,9 +1024,9 @@ void plot_plane(World& world, const std::vector >& vfuncti World& world=vf.front().world(); for(size_t i=0;i(world,vf[i],namei); - plot_cubefile(world,vf[i],namei+".cube",header); +// plot_cubefile(world,vf[i],namei+".cube",header); } } diff --git a/src/madness/mra/function_factory.h b/src/madness/mra/function_factory.h index d0abd4a7662..61c16b8d72a 100644 --- a/src/madness/mra/function_factory.h +++ b/src/madness/mra/function_factory.h @@ -50,6 +50,7 @@ #include #include #include +#include namespace madness { @@ -67,6 +68,7 @@ Tensor fcube(const Key&, T (*f)(const Vector&), const Ten template Tensor fcube(const Key& key, const FunctionFunctorInterface& f, const Tensor& qx); + /// FunctionFactory implements the named-parameter idiom for Function /// C++ does not provide named arguments (as does, e.g., Python). @@ -323,7 +325,7 @@ class CompositeFactory : public FunctionFactory { // std::vector _particle1; // std::vector _particle2; std::vector _ket; -// std::shared_ptr > _ket; ///< supposedly a 6D pair function ket +// std::shared_ptr > _ket; ///< supposedly a 6D pair function ket std::shared_ptr > _g12; ///< supposedly a interaction potential std::shared_ptr > _v1; ///< supposedly a potential for particle 1 std::shared_ptr > _v2; ///< supposedly a potential for particle 2 @@ -430,54 +432,66 @@ class CompositeFactory : public FunctionFactory { }; /// factory for generating TwoElectronInterfaces -class TwoElectronFactory : public FunctionFactory { +template +class TwoElectronFactory : public FunctionFactory { protected: - typedef std::shared_ptr > InterfacePtr; + typedef std::shared_ptr > InterfacePtr; public: TwoElectronFactory(World& world) - : FunctionFactory(world), type_(coulomb_), interface_(), - dcut_(FunctionDefaults<3>::get_thresh()), gamma_(-1.0), bc_(FunctionDefaults<6>::get_bc()) { - _tree_state = on_demand; - this->_thresh = (FunctionDefaults<3>::get_thresh()); - this->_k = (FunctionDefaults<3>::get_k()); + : FunctionFactory(world), interface_(), bc_(FunctionDefaults::get_bc()) { + this->_tree_state = on_demand; + info.mu=-1.0; + info.type=OT_G12; + constexpr std::size_t LDIM=NDIM/2; + static_assert(NDIM==2*LDIM, "NDIM must be even"); + info.thresh=FunctionDefaults::get_thresh(); + this->_thresh = (FunctionDefaults::get_thresh()); + this->_k = (FunctionDefaults::get_k()); } /// the smallest length scale to be represented (aka lo) TwoElectronFactory& dcut(double dcut) { - dcut_ = dcut; + info.lo = dcut; return self(); } /// the requested precision TwoElectronFactory& thresh(double thresh) { - _thresh = thresh; + this->_thresh=thresh; + info.thresh = thresh; return self(); } /// the exponent of a slater function TwoElectronFactory& gamma(double g) { - gamma_ = g; + info.mu = g; return self(); } /// return the operator (1 - exp(-gamma x) / (2 gamma) TwoElectronFactory& f12() { - type_ = f12_; + info.type=OT_F12; return self(); } /// return the operator (1 - exp(-gamma x) / (2 gamma) TwoElectronFactory& slater() { - type_ = slater_; + info.type=OT_SLATER; return self(); } /// return the BSH operator TwoElectronFactory& BSH() { - type_ = bsh_; + info.type=OT_BSH; + return self(); + } + + /// return the BSH operator + TwoElectronFactory& set_info(const OperatorInfo info1) { + info=info1; return self(); } @@ -487,31 +501,25 @@ class TwoElectronFactory : public FunctionFactory { // return if we already have a valid interface if (this->interface_) return this->interface_; - // construction of the functor is const in spirit, but non-const in sad reality.. - if (type_ == coulomb_) { - const_cast(this->interface_) = - InterfacePtr(new ElectronRepulsionInterface( - dcut_, _thresh, bc_, _k)); - } else if (type_ == f12_) { - // make sure gamma is set - MADNESS_ASSERT(gamma_ > 0); - const_cast(this->interface_) = - InterfacePtr(new SlaterF12Interface( - gamma_, dcut_, _thresh, bc_, _k)); - } else if (type_ == slater_) { - // make sure gamma is set - MADNESS_ASSERT(gamma_ > 0); - const_cast(this->interface_) = - InterfacePtr(new SlaterFunctionInterface( - gamma_, dcut_, _thresh, bc_, _k)); - } else if (type_ == bsh_) { - const_cast(this->interface_) = - InterfacePtr(new BSHFunctionInterface( - gamma_, dcut_, _thresh, bc_, _k)); - - } else { - MADNESS_EXCEPTION("unimplemented integral kernel", 1); - } + const_cast(this->interface_) = + InterfacePtr(new GeneralTwoElectronInterface(info)); + +// // construction of the functor is const in spirit, but non-const in sad reality.. +// if (info.type==OT_G12) { +// const_cast(this->interface_) = +// InterfacePtr(new ElectronRepulsionInterface(info.lo, _thresh, bc_, _k)); +// } else if (info.type == OT_F12) { +// const_cast(this->interface_) = +// InterfacePtr(new SlaterF12Interface(info.mu, info.lo, _thresh, bc_, _k)); +// } else if (info.type == OT_SLATER) { +// const_cast(this->interface_) = +// InterfacePtr(new SlaterFunctionInterface(info.mu,info.lo, _thresh, bc_, _k)); +// } else if (info.type==OT_BSH) { +// const_cast(this->interface_) = +// InterfacePtr(new BSHFunctionInterface(info.mu, info.lo, _thresh, bc_, _k)); +// } else { +// MADNESS_EXCEPTION("unimplemented integral kernel", 1); +// } return this->interface_; } @@ -519,20 +527,21 @@ class TwoElectronFactory : public FunctionFactory { protected: - enum operatortype { - coulomb_, slater_, f12_, bsh_ - }; +// enum operatortype { +// coulomb_, slater_, f12_, bsh_ +// }; - operatortype type_; + OperatorInfo info; +// operatortype type_; /// the interface providing the actual coefficients InterfacePtr interface_; - double dcut_; ///< cutoff radius for 1/r12, aka regularization +// double dcut_; ///< cutoff radius for 1/r12, aka regularization - double gamma_; +// double gamma_; - BoundaryConditions<6> bc_; + BoundaryConditions bc_; }; diff --git a/src/madness/mra/function_interface.h b/src/madness/mra/function_interface.h index 916db278e27..6edc2234da8 100644 --- a/src/madness/mra/function_interface.h +++ b/src/madness/mra/function_interface.h @@ -40,18 +40,27 @@ #include // needed for the TwoElectronInterface +#include #include #include #include + namespace madness { // forward declaration needed for CompositeFunctorInterface template class FunctionImpl; + template + class Function; + template Tensor fcube(const Key&, T (*f)(const Vector&), const Tensor&); +// template +// const std::vector>& change_tree_state(const std::vector>& v, +// const TreeState finalstate, +// const bool fence); /// Abstract base class interface required for functors used as input to Functions @@ -190,6 +199,16 @@ namespace madness { } + /// replicate low-dimensional functions over all ranks of this world + void replicate_low_dim_functions(const bool fence) { + if (impl_m1 and (not impl_m1->is_on_demand())) impl_m1->replicate(false); + if (impl_m2 and (not impl_m2->is_on_demand())) impl_m2->replicate(false); + + for (auto& p1 : impl_p1_vector) if (p1 and (not p1->is_on_demand())) p1->replicate(false); + for (auto& p2 : impl_p2_vector) if (p2 and (not p2->is_on_demand())) p2->replicate(false); + if (fence) world.gop.fence(); + } + void make_redundant(const bool fence) { // prepare base functions that make this function for (auto& k : impl_ket_vector) if (k and (not k->is_on_demand())) k->change_tree_state(redundant,false); @@ -199,10 +218,12 @@ namespace madness { if (impl_m1 and (not impl_m1->is_on_demand())) impl_m1->change_tree_state(redundant,false); if (impl_m2 and (not impl_m2->is_on_demand())) impl_m2->change_tree_state(redundant,false); - for (auto& k : impl_p1_vector) if (k and (not k->is_on_demand())) k->change_tree_state(redundant,false); - for (auto& k : impl_p2_vector) if (k and (not k->is_on_demand())) k->change_tree_state(redundant,false); -// if (impl_p1 and (not impl_p1->is_on_demand())) impl_p1->make_redundant(false); -// if (impl_p2 and (not impl_p2->is_on_demand())) impl_p2->make_redundant(false); + change_tree_state(impl2function(impl_p1_vector),redundant,false); + change_tree_state(impl2function(impl_p2_vector),redundant,false); +// for (auto& k : impl_p1_vector) if (k and (not k->is_on_demand())) k->change_tree_state(redundant,false); +// for (auto& k : impl_p2_vector) if (k and (not k->is_on_demand())) k->change_tree_state(redundant,false); +// if (impl_p1 and (not impl_p1->is_on_demand())) impl_p1->change_tree_state(redundant,false); +// if (impl_p2 and (not impl_p2->is_on_demand())) impl_p2->change_tree_state(redundant,false); if (fence) world.gop.fence(); } @@ -303,6 +324,8 @@ namespace madness { /// functions is imprecise and slow. template class TwoElectronInterface : public FunctionFunctorInterface { + protected: + static constexpr std::size_t LDIM=NDIM/2; public: typedef GenTensor coeffT; @@ -312,14 +335,14 @@ namespace madness { /// @param[in] lo the smallest length scale to be resolved /// @param[in] eps the accuracy threshold TwoElectronInterface(double lo, double eps, - const BoundaryConditions<6>& bc=FunctionDefaults<6>::get_bc(), - int kk=FunctionDefaults<6>::get_k()) + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int kk=FunctionDefaults::get_k()) :rank(), k(kk), lo(lo), hi(1.0) { // Presently we must have periodic or non-periodic in all dimensions. - for (std::size_t d=1; d<6; ++d) {MADNESS_ASSERT(bc(d,0)==bc(0,0));} + for (std::size_t d=1; d& width = FunctionDefaults<6>::get_cell_width(); + const Tensor& width = FunctionDefaults::get_cell_width(); hi = width.normf(); // Diagonal width of cell if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation @@ -332,7 +355,7 @@ namespace madness { /// return the coefficients of the function in 6D (x1,y1,z1, x2,y2,z2) coeffT coeff(const Key& key) const { Tensor c=make_coeff(key); - return coeffT(map_coeff(c),FunctionDefaults<6>::get_thresh(),TT_FULL); + return coeffT(map_coeff(c),FunctionDefaults::get_thresh(),TT_FULL); } T operator()(const Vector& x) const { @@ -344,74 +367,113 @@ namespace madness { protected: /// make the coefficients from the 1d convolution - Tensor make_coeff(const Key<6>& key) const { + Tensor make_coeff(const Key& key) const { const Level n=key.level(); - const Vector l=key.translation(); + const Vector l=key.translation(); // get the displacements for all 3 dimensions: x12, y12, z12 - const Translation l0=(l[0]-l[3]); - const Translation l1=(l[1]-l[4]); - const Translation l2=(l[2]-l[5]); - - Tensor scr1(rank,k*k), scr2(rank,k*k,k*k); + Translation l0, l1, l2; + if (NDIM==2) { + l0=l[0]-l[1]; + } else if (NDIM==4) { + l0=(l[0]-l[2]); + l1=(l[1]-l[3]); + } else if (NDIM==6) { + l0=(l[0]-l[3]); + l1=(l[1]-l[4]); + l2=(l[2]-l[5]); + } else { + MADNESS_EXCEPTION("TwoElectronInterface: NDIM must be 2, 4, or 6",1); + } + + Tensor scr1(rank,k*k), scr2(rank,k*k,k*k), scr3(rank,k*k); // lump all the terms together for (long mu=0; mu r0=(ops[mu].getop(0)->rnlij(n,l0)).reshape(k*k); - const Tensor r1=(ops[mu].getop(1)->rnlij(n,l1)).reshape(k*k); - const Tensor r2=(ops[mu].getop(2)->rnlij(n,l2)).reshape(k*k); + Tensor r0, r1, r2; + if (NDIM>=2) r0=(ops[mu].getop(0)->rnlij(n,l0)).reshape(k*k); + if (NDIM>=4) r1=(ops[mu].getop(1)->rnlij(n,l1)).reshape(k*k); + if (NDIM>=6) r2=(ops[mu].getop(2)->rnlij(n,l2)).reshape(k*k); // include weights in first vector scr1(mu,Slice(_))=r0*ops[mu].getfac(); - // merge second and third vector to scr(r,k1,k2) - scr2(mu,Slice(_),Slice(_))=outer(r1,r2); + if (NDIM==2) { + ; + } else if (NDIM==4) { + scr3(mu,Slice(_))=r1; + } else if (NDIM==6) { + // merge second and third vector to scr(r,k1,k2) + scr2(mu,Slice(_),Slice(_))=outer(r1,r2); + } else { + MADNESS_EXCEPTION("TwoElectronInterface: NDIM must be 2, 4, or 6",1); + } } - Tensor c=inner(scr1,scr2,0,0); - return c; + if (NDIM==2) { + // perform sum over the rank + Tensor result(scr1.dim(1)); + for (long mu=0; mu(); + } } /// the dimensions are a bit confused (x1,x2, y1,y2, z1,z2) -> (x1,y1,z1, x2,y2,z2) Tensor map_coeff(const Tensor& c) const { - std::vector map(6); - map[0]=0; map[1]=3; map[2]=1; - map[3]=4; map[4]=2; map[5]=5; - return copy(c.reshape(k,k,k,k,k,k).mapdim(map)); + std::vector map(NDIM); + if (NDIM==2) { + map[0]=0; map[1]=1; + return copy(c.reshape(k,k)); + } else if (NDIM==4) { + map[0]=0; map[1]=2; + map[2]=1; map[3]=3; + return copy(c.reshape(k,k,k,k).mapdim(map)); + } else if (NDIM==6) { + map[0]=0; map[1]=3; map[2]=1; + map[3]=4; map[4]=2; map[5]=5; + return copy(c.reshape(k,k,k,k,k,k).mapdim(map)); + } + return Tensor(); } /// initialize the Gaussian fit; uses the virtual function fit() to fit void initialize(const double eps) { - GFit fit=this->fit(eps); + GFit fit=this->fit(eps); Tensor coeff=fit.coeffs(); Tensor expnt=fit.exponents(); // set some parameters rank=coeff.dim(0); ops.resize(rank); - const Tensor& width = FunctionDefaults<6>::get_cell_width(); + const Tensor& width = FunctionDefaults::get_cell_width(); // construct all the terms for (int mu=0; mu(NDIM)); // Normalization coeff - double c = std::pow(sqrt(expnt(mu)/constants::pi),3); // Normalization coeff + double c = std::pow(sqrt(expnt(mu)/constants::pi),LDIM); // Normalization coeff // We cache the normalized operator so the factor is the value we must multiply // by to recover the coeff we want. ops[mu].setfac(coeff(mu)/c); // only 3 dimensions here! - for (std::size_t d=0; d<3; ++d) { + for (std::size_t d=0; d::get(k, expnt(mu)*width[d]*width[d], 0, false)); } } } /// derived classes must implement this -- cf GFit.h - virtual GFit fit(const double eps) const = 0; + virtual GFit fit(const double eps) const = 0; /// storing the coefficients - mutable std::vector< ConvolutionND > ops; + mutable std::vector< ConvolutionND > ops; /// the number of terms in the Gaussian quadrature int rank; @@ -427,8 +489,42 @@ namespace madness { }; + + /// a function like f(x)=1/x + template + class GeneralTwoElectronInterface : public TwoElectronInterface { + public: + + /// constructor: cf the Coulomb kernel + + /// @param[in] lo the smallest length scale to be resolved + /// @param[in] eps the accuracy threshold + GeneralTwoElectronInterface(OperatorInfo info, + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int kk=FunctionDefaults::get_k()) + : TwoElectronInterface(info.lo,info.thresh,bc,kk), info(info) { + + if (info.hi<0) { + double hi=FunctionDefaults::get_cell_width().normf(); + if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation + this->info.hi=hi; + } + this->initialize(info.thresh); + } + + private: + OperatorInfo info; + static constexpr std::size_t LDIM=NDIM/2; + + GFit fit(const double eps) const { + return GFit(info); + } + }; + /// a function like f(x)=1/x - class ElectronRepulsionInterface : public TwoElectronInterface { + template + class ElectronRepulsionInterface : public TwoElectronInterface { + public: /// constructor: cf the Coulomb kernel @@ -436,17 +532,18 @@ namespace madness { /// @param[in] lo the smallest length scale to be resolved /// @param[in] eps the accuracy threshold ElectronRepulsionInterface(double lo,double eps, - const BoundaryConditions<6>& bc=FunctionDefaults<6>::get_bc(), - int kk=FunctionDefaults<6>::get_k()) - : TwoElectronInterface(lo,eps,bc,kk) { + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int kk=FunctionDefaults::get_k()) + : TwoElectronInterface(lo,eps,bc,kk) { - initialize(eps); + this->initialize(eps); } private: + static constexpr std::size_t LDIM=NDIM/2; - GFit fit(const double eps) const { - return GFit::CoulombFit(lo,hi,eps,false); + GFit fit(const double eps) const { + return GFit::CoulombFit(this->lo,this->hi,eps,false); } }; @@ -518,28 +615,28 @@ namespace madness { initialize(eps); } - /// overload the function of the base class - coeffT coeff(const Key<6>& key) const { - - Tensor c=make_coeff(key); - - // subtract 1 from the (0,0,..,0) element of the tensor, - // which is the 0th order polynomial coefficient - double one_coeff1=1.0*sqrt(FunctionDefaults<6>::get_cell_volume()) - *pow(0.5,0.5*6*key.level()); - std::vector v0(6,0L); - c(v0)-=one_coeff1; - - c.scale(-0.5/mu); - return coeffT(map_coeff(c),FunctionDefaults<6>::get_thresh(),TT_FULL); - } +// /// overload the function of the base class +// coeffT coeff(const Key<6>& key) const { +// +// Tensor c=make_coeff(key); +// +// // subtract 1 from the (0,0,..,0) element of the tensor, +// // which is the 0th order polynomial coefficient +// double one_coeff1=1.0*sqrt(FunctionDefaults<6>::get_cell_volume()) +// *pow(0.5,0.5*6*key.level()); +// std::vector v0(6,0L); +// c(v0)-=one_coeff1; +// +// c.scale(-0.5/mu); +// return coeffT(map_coeff(c),FunctionDefaults<6>::get_thresh(),TT_FULL); +// } private: double mu; GFit fit(const double eps) const { - return GFit::SlaterFit(mu,lo,hi,eps,false); + return GFit::F12Fit(mu,lo,hi,eps,false); } }; diff --git a/src/madness/mra/gfit.h b/src/madness/mra/gfit.h index bb13810e930..c457a4af4f7 100644 --- a/src/madness/mra/gfit.h +++ b/src/madness/mra/gfit.h @@ -48,6 +48,8 @@ #include "../tensor/tensor_lapack.h" #include "../world/madness_exception.h" #include "../world/print.h" +#include + namespace madness { @@ -57,7 +59,42 @@ class GFit { public: /// default ctor does nothing - GFit() {} + GFit() = default; + + GFit(OperatorInfo info) { + double mu = info.mu; + double lo = info.lo; + double hi = info.hi; + MADNESS_CHECK_THROW(hi>0,"hi must be positive in gfit: U need to set it manually in operator.h"); + double eps = info.thresh; + bool debug=info.debug; + OpType type = info.type; + + + if (type==OT_G12) {*this=CoulombFit(lo,hi,eps,debug); + } else if (type==OT_SLATER) {*this=SlaterFit(mu,lo,hi,eps,debug); + } else if (type==OT_GAUSS) {*this=GaussFit(mu,lo,hi,eps,debug); + } else if (type==OT_F12) {*this=F12Fit(mu,lo,hi,eps,debug); + } else if (type==OT_FG12) {*this=FGFit(mu,lo,hi,eps,debug); + } else if (type==OT_F212) {*this=F12sqFit(mu,lo,hi,eps,debug); + } else if (type==OT_F2G12) {*this=F2GFit(mu,lo,hi,eps,debug); + } else if (type==OT_BSH) {*this=BSHFit(mu,lo,hi,eps,debug); + } else { + print("Operator type not implemented: ",type); + MADNESS_EXCEPTION("Operator type not implemented: ",1); + } + + } + + /// copy constructor + GFit(const GFit& other) = default; + + /// assignment operator + GFit& operator=(const GFit& other) { + coeffs_ = other.coeffs_; + exponents_ = other.exponents_; + return *this; + } /// return a fit for the Coulomb function @@ -79,8 +116,15 @@ class GFit { /// @parma[in] prnt print level static GFit BSHFit(double mu, double lo, double hi, double eps, bool prnt=false) { GFit fit; - if (NDIM==3) bsh_fit(mu,lo,hi,eps,fit.coeffs_,fit.exponents_,prnt); + bool fix_interval=false; + if (NDIM==3) bsh_fit(mu,lo,hi,eps,fit.coeffs_,fit.exponents_,prnt,fix_interval); else bsh_fit_ndim(NDIM,mu,lo,hi,eps,fit.coeffs_,fit.exponents_,prnt); + + if (prnt) { + print("bsh fit"); + auto exact = [&mu](const double r) -> double { return 1.0/(4.0 * constants::pi) * exp(-mu * r)/r; }; + fit.print_accuracy(exact, lo, hi); + } return fit; } @@ -95,10 +139,161 @@ class GFit { /// @parma[in] prnt print level static GFit SlaterFit(double gamma, double lo, double hi, double eps, bool prnt=false) { GFit fit; - slater_fit(gamma,lo,hi,eps,fit.coeffs_,fit.exponents_,prnt); + slater_fit(gamma,lo,hi,eps,fit.coeffs_,fit.exponents_,false); + if (prnt) { + print("Slater fit"); + auto exact = [&gamma](const double r) -> double { return exp(-gamma * r); }; + fit.print_accuracy(exact, lo, hi); + } return fit; } + /// return a (trivial) fit for a single Gauss function + + /// the Gauss function is defined by + /// f(r) = exp(-\gamma r^2) + /// @param[in] gamma the exponent of the Gauss function + /// @param[in] lo the smallest length scale that needs to be precisely represented + /// @param[in] hi the largest length scale that needs to be precisely represented + /// @param[in] eps the precision threshold + /// @parma[in] prnt print level + static GFit GaussFit(double gamma, double lo, double hi, double eps, bool prnt=false) { + GFit fit; + fit.coeffs_=Tensor(1); + fit.exponents_=Tensor(1); + fit.coeffs_=1.0; + fit.exponents_=gamma; + if (prnt) { + print("Gauss fit"); + auto exact = [&gamma](const double r) -> double { return exp(-gamma * r*r); }; + fit.print_accuracy(exact, lo, hi); + } + return fit; + } + + /// return a fit for the F12 correlation factor + + /// the Slater function is defined by + /// f(r) = 1/(2 gamma) * (1 - exp(-\gamma r)) + /// @param[in] gamma the exponent of the Slater function + /// @param[in] lo the smallest length scale that needs to be precisely represented + /// @param[in] hi the largest length scale that needs to be precisely represented + /// @param[in] eps the precision threshold + /// @parma[in] prnt print level + static GFit F12Fit(double gamma, double lo, double hi, double eps, bool prnt=false) { + GFit fit; + f12_fit(gamma,lo*0.1,hi,eps*0.01,fit.coeffs_,fit.exponents_,false); + fit.coeffs_*=(0.5/gamma); + if (prnt) { + print("f12 fit"); + auto exact=[&gamma](const double r) -> double {return 0.5/gamma*(1.0-exp(-gamma*r));}; + fit.print_accuracy(exact,lo,hi); + } + return fit; + } + + /// return a fit for the F12^2 correlation factor + + /// the Slater function square is defined by + /// f(r) = [ 1/(2 gamma) * (1 - exp(-\gamma r)) ] ^2 + /// @param[in] gamma the exponent of the Slater function + /// @param[in] lo the smallest length scale that needs to be precisely represented + /// @param[in] hi the largest length scale that needs to be precisely represented + /// @param[in] eps the precision threshold + /// @parma[in] prnt print level + static GFit F12sqFit(double gamma, double lo, double hi, double eps, bool prnt=false) { + GFit fit; + f12sq_fit(gamma,lo*0.1,hi,eps*0.01,fit.coeffs_,fit.exponents_,false); + fit.coeffs_*=(0.25/(gamma*gamma)); + if (prnt) { + print("f12sq fit"); + auto exact=[&gamma](const double r) -> double {return std::pow(0.5/gamma*(1.0-exp(-gamma*r)),2.0);}; + fit.print_accuracy(exact,lo,hi); + } + return fit; + } + + + /// return a fit for the FG function + + /// fg = 1/(2 mu) * (1 - exp(-gamma r12)) / r12 + /// = 1/(2 mu) *( 1/r12 - exp(-gamma r12)/r12) + /// = 1/(2 mu) * (coulomb - bsh) + /// @param[in] gamma the exponent of the Slater function + /// @param[in] lo the smallest length scale that needs to be precisely represented + /// @param[in] hi the largest length scale that needs to be precisely represented + /// @param[in] eps the precision threshold + /// @parma[in] prnt print level + static GFit FGFit(double gamma, double lo, double hi, double eps, bool prnt=false) { + GFit bshfit,coulombfit; + eps*=0.1; + lo*=0.1; +// bool restrict_interval=false; + bool fix_interval=true; + bsh_fit(gamma,lo,hi,eps,bshfit.coeffs_,bshfit.exponents_,false,fix_interval); + bsh_fit(0.0,lo,hi,eps,coulombfit.coeffs_,coulombfit.exponents_,false,fix_interval); + // check the exponents are identical + auto diffexponents=(coulombfit.exponents() - bshfit.exponents()); + MADNESS_CHECK(diffexponents.normf()/coulombfit.exponents().size()<1.e-12); + auto diffcoefficients=(coulombfit.coeffs() - bshfit.coeffs()); + GFit fgfit; + fgfit.exponents_=bshfit.exponents_; + fgfit.coeffs_=4.0*constants::pi*0.5/gamma*diffcoefficients; + GFit::prune_small_coefficients(eps,lo,hi,fgfit.coeffs_,fgfit.exponents_); + + if (prnt) { + print("fg fit"); + auto exact=[&gamma](const double r) -> double {return 0.5/gamma*(1.0-exp(-gamma*r))/r;}; + fgfit.print_accuracy(exact,lo,hi); + } + return fgfit; + } + + /// return a fit for the F2G function + + /// f2g = (1/(2 mu) * (1 - exp(-gamma r12)))^2 / r12 + /// = 1/(4 mu^2) * [ 1/r12 - 2 exp(-gamma r12)/r12) + exp(-2 gamma r12)/r12 ] + /// @param[in] gamma the exponent of the Slater function + /// @param[in] lo the smallest length scale that needs to be precisely represented + /// @param[in] hi the largest length scale that needs to be precisely represented + /// @param[in] eps the precision threshold + /// @parma[in] prnt print level + static GFit F2GFit(double gamma, double lo, double hi, double eps, bool prnt=false) { + GFit bshfit,coulombfit,bsh2fit; + eps*=0.1; + lo*=0.1; +// bool restrict_interval=false; + bool fix_interval=true; + bsh_fit(gamma,lo,hi,eps,bshfit.coeffs_,bshfit.exponents_,false,fix_interval); + bsh_fit(2.0*gamma,lo,hi,eps,bsh2fit.coeffs_,bsh2fit.exponents_,false,fix_interval); + bsh_fit(0.0,lo,hi,eps,coulombfit.coeffs_,coulombfit.exponents_,false,fix_interval); + + // check the exponents are identical + auto diffexponents=(coulombfit.exponents() - bshfit.exponents()); + MADNESS_CHECK(diffexponents.normf()/coulombfit.exponents().size()<1.e-12); + auto diffexponents1=(coulombfit.exponents() - bsh2fit.exponents()); + MADNESS_CHECK(diffexponents1.normf()/coulombfit.exponents().size()<1.e-12); + + auto coefficients=(coulombfit.coeffs() - 2.0* bshfit.coeffs() + bsh2fit.coeffs()); + GFit f2gfit; + f2gfit.exponents_=bshfit.exponents_; + // additional factor 4 pi due to implementation of bsh_fit + double fourpi=4.0*constants::pi; + double fourmu2=4.0*gamma*gamma; + f2gfit.coeffs_=fourpi/fourmu2*coefficients; + GFit::prune_small_coefficients(eps,lo,hi,f2gfit.coeffs_,f2gfit.exponents_); + + if (prnt) { + print("fg fit"); + auto exact=[&gamma](const double r) -> double { + return 0.25/(gamma*gamma)*(1.0-2.0*exp(-gamma*r)+exp(-2.0*gamma*r))/r; + }; + f2gfit.print_accuracy(exact,lo,hi); + } + return f2gfit; + } + + /// return a fit for a general isotropic function /// note that the error is controlled over a uniform grid, the boundaries @@ -114,7 +309,26 @@ class GFit { /// return the exponents of the fit Tensor exponents() const {return exponents_;} - void truncate_periodic_expansion(Tensor& c, Tensor& e, + void static prune_small_coefficients(const double eps, const double lo, const double hi, + Tensor& coeff, Tensor& expnt) { + double mid = lo + (hi-lo)*0.5; + long npt=coeff.size(); + long i; + for (i=npt-1; i>0; --i) { + double cnew = coeff[i]*exp(-(expnt[i]-expnt[i-1])*mid*mid); + double errlo = coeff[i]*exp(-expnt[i]*lo*lo) - + cnew*exp(-expnt[i-1]*lo*lo); + double errhi = coeff[i]*exp(-expnt[i]*hi*hi) - + cnew*exp(-expnt[i-1]*hi*hi); + if (std::max(std::abs(errlo),std::abs(errhi)) > 0.03*eps) break; + npt--; + coeff[i-1] = coeff[i-1] + cnew; + } + coeff = coeff(Slice(0,npt-1)); + expnt = expnt(Slice(0,npt-1)); + } + + void truncate_periodic_expansion(Tensor& c, Tensor& e, double L, bool discardG0) const { double tcut = 0.25/L/L; @@ -157,6 +371,35 @@ class GFit { } + /// print coefficients and exponents, and values and errors + + /// @param[in] op the exact function, e.g. 1/r, exp(-mu r), etc + /// @param[in] lo lower bound for the range r + /// @param[in] hi higher bound for the range r + template + void print_accuracy(opT op, const double lo, const double hi) const { + + std::cout << "weights and roots" << std::endl; + for (int i=0; i coeffs_; @@ -173,11 +416,14 @@ class GFit { /// Multiresolution Quantum Chemistry in Multiwavelet Bases, /// Lecture Notes in Computer Science, vol. 2660, p. 707, 2003. static void bsh_fit(double mu, double lo, double hi, double eps, - Tensor& pcoeff, Tensor& pexpnt, bool prnt) { + Tensor& pcoeff, Tensor& pexpnt, bool prnt, bool fix_interval) { - if (mu < 0.0) throw "cannot handle negative mu in bsh_fit"; + if (mu < 0.0) throw "cannot handle negative mu in bsh_fit"; +// bool restrict_interval=(mu>0) and use_mu_for_restricting_interval; - if (mu > 0) { + + if ((mu > 0) and (not fix_interval)) { +// if (restrict_interval) { // Restrict hi according to the exponential decay double r = -log(4*constants::pi*0.01*eps); r = -log(r * 4*constants::pi*0.01*eps); @@ -195,7 +441,8 @@ class GFit { else if (eps >= 1e-12) TT = 26; else TT = 30; - if (mu > 0) { + if ((mu > 0) and (not fix_interval)) { +// if (restrict_interval) { slo = -0.5*log(4.0*TT/(mu*mu)); } else { @@ -253,21 +500,9 @@ class GFit { // end points ... if this error is less than the desired // precision, can discard the diffuse gaussian. - if (mu == 0.0) { - double mid = lo + (hi-lo)*0.5; - long i; - for (i=npt-1; i>0; --i) { - double cnew = coeff[i]*exp(-(expnt[i]-expnt[i-1])*mid*mid); - double errlo = coeff[i]*exp(-expnt[i]*lo*lo) - - cnew*exp(-expnt[i-1]*lo*lo); - double errhi = coeff[i]*exp(-expnt[i]*hi*hi) - - cnew*exp(-expnt[i-1]*hi*hi); - if (std::max(std::abs(errlo),std::abs(errhi)) > 0.03*eps) break; - npt--; - coeff[i-1] = coeff[i-1] + cnew; - } - coeff = coeff(Slice(0,npt-1)); - expnt = expnt(Slice(0,npt-1)); + if ((mu == 0.0) and (not fix_interval)) { +// if (restrict_interval) { + GFit::prune_small_coefficients(eps,lo,hi,coeff,expnt); } // Modify the coeffs of the largest exponents to satisfy the moment conditions @@ -328,27 +563,6 @@ class GFit { coeff(Slice(0,nmom-1)) = ncoeff; } - if (prnt) { - for (int i=0; i& pcoeff, Tensor& pexpnt, bool prnt) { + MADNESS_CHECK(gamma >0.0); // empirical number TT for the upper integration limit double TT; if (eps >= 1e-2) TT = 5; @@ -373,6 +588,7 @@ class GFit { else TT = 30; // integration limits for quadrature over s: slo and shi + // slo and shi must not depend on gamma!!! double slo=0.5 * log(eps) - 1.0; double shi=log(TT/(lo*lo))*0.5; @@ -401,32 +617,6 @@ class GFit { expnt[i] = 0.25*exp(-2.0*s); } - // Prune large exponents from the fit ... never necessary due to construction - - // Prune small exponents from Coulomb fit. Evaluate a gaussian at - // the range midpoint, and replace it there with the next most - // diffuse gaussian. Then examine the resulting error at the two - // end points ... if this error is less than the desired - // precision, can discard the diffuse gaussian. - - if (gamma == 0.0) { - double mid = lo + (hi-lo)*0.5; - long i; - for (i=npt-1; i>0; --i) { - double cnew = coeff[i]*exp(-(expnt[i]-expnt[i-1])*mid*mid); - double errlo = coeff[i]*exp(-expnt[i]*lo*lo) - - cnew*exp(-expnt[i-1]*lo*lo); - double errhi = coeff[i]*exp(-expnt[i]*hi*hi) - - cnew*exp(-expnt[i-1]*hi*hi); - if (std::max(std::abs(errlo),std::abs(errhi)) > 0.03*eps) break; - npt--; - coeff[i-1] = coeff[i-1] + cnew; - } - coeff = coeff(Slice(0,npt-1)); - expnt = expnt(Slice(0,npt-1)); - } - - if (prnt) { std::cout << "weights and roots for a Slater function with gamma=" << gamma << std::endl; for (int i=0; i& pcoeff, Tensor& pexpnt, bool prnt) { + Tensor coeff,expnt; + slater_fit(gamma, lo, hi, eps, coeff, expnt, prnt); + + pcoeff=Tensor(coeff.size()+1); + pcoeff(Slice(1,-1,1))=-coeff(_); + pexpnt=Tensor(expnt.size()+1); + pexpnt(Slice(1,-1,1))=expnt(_); + + pcoeff(0l)=1.0; + pexpnt(0l)=1.e-10; + } + + + /// fit a correlation factor f12^2 = (1- exp(-mu r))^2 = 1 - 2 exp(-mu r) + exp(-2 mu r) + + /// no factor 1/(2 mu) or square of it included! + /// use the Slater fit with an additional term: 1*exp(-0 r^2) + static void f12sq_fit(double gamma, double lo, double hi, double eps, + Tensor& pcoeff, Tensor& pexpnt, bool prnt) { + Tensor coeff1,expnt1, coeff2, expnt2; + slater_fit(gamma, lo, hi, eps, coeff1, expnt1, prnt); + slater_fit(2.0*gamma, lo, hi, eps, coeff2, expnt2, prnt); + + // check exponents are the same + MADNESS_CHECK((expnt1-expnt2).normf()/expnt1.size()<1.e-12); + + // add exponential terms + auto coeff=coeff2-2.0*coeff1; + pcoeff=Tensor(coeff1.size()+1); + pcoeff(Slice(1,-1,1))=coeff(_); + pexpnt=Tensor(expnt1.size()+1); + pexpnt(Slice(1,-1,1))=expnt1(_); + + // add constant term + pcoeff(0l)=1.0; + pexpnt(0l)=1.e-10; + } + + + void static bsh_fit_ndim(int ndim, double mu, double lo, double hi, double eps, Tensor& pcoeff, Tensor& pexpnt, bool prnt) { if (mu > 0) { diff --git a/src/madness/mra/macrotaskq.h b/src/madness/mra/macrotaskq.h index ea523b6febd..751c7cfe6e3 100644 --- a/src/madness/mra/macrotaskq.h +++ b/src/madness/mra/macrotaskq.h @@ -35,6 +35,145 @@ namespace madness { +/// helper class for returning the result of a task, which is not a madness Function, but a simple scalar + +/// the result value is accumulated via gaxpy in universe rank=0, after completion of the taskq the final +/// value can be obtained via get(), which includes a broadcast of the final value to all processes +template +class ScalarResult : public WorldObject> { +public: + typedef T value_type; + ScalarResult(World &world) : WorldObject>(world) { + this->process_pending(); + } + + /// Disable the default copy constructor + ScalarResult(const ScalarResult& other) = delete; + ScalarResult(ScalarResult&& ) = default; + ScalarResult& operator=(ScalarResult&& ) = default; + + /// disable assignment operator + ScalarResult& operator=(const ScalarResult& other) = delete; + + ~ScalarResult() { +// print("calling destructor of ScalarResult",this->id()); +// std::cout << std::flush; + } + + /// simple assignment of the scalar value + ScalarResult& operator=(const T& x) { + value = x; + return *this; + } + + ScalarResult& operator+= (const T& x) { + gaxpy(1.0, x, 1.0,true); + return *this; + } + + /// accumulate, optional fence + void gaxpy(const double a, const T& right, double b, const bool fence=true) { + if (this->get_world().rank()==0) { + value =a*value + b * right; + } + else this->send(0, &ScalarResult::gaxpy, a, right, b, fence); + } + + template + void serialize(Archive &ar) { + ar & value; + } + + /// after completion of the taskq get the final value + T get() { + this->get_world().gop.broadcast_serializable(*this, 0); + return value; + } + + /// get the local value of this rank, which might differ for different ranks + /// for the final value use get() + T get_local() const { + return value; + } + +private: + /// the scalar value + T value=T(); +}; + +/// helper function to create a vector of ScalarResult, circumventing problems with the constructors +template +std::vector>> scalar_result_shared_ptr_vector(World& world, std::size_t n) { + auto v=std::vector>>(); + for (std::size_t i=0; i>(world)); +// for (int i=0; iid()); + std::cout << std::flush; + auto ptr_opt = world.ptr_from_id< WorldObject< ScalarResult > >(v[0]->id()); + if (!ptr_opt) + MADNESS_EXCEPTION("ScalarResult: remote operation attempting to use a locally uninitialized object",0); + auto ptr = static_cast< ScalarResult*>(*ptr_opt); + if (!ptr) + MADNESS_EXCEPTION("ScalarResult operation attempting to use an unregistered object",0); + return v; +} + + +// type traits to check if a template parameter is a WorldContainer +template +struct is_scalar_result_ptr : std::false_type {}; + +template +struct is_scalar_result_ptr>> : std::true_type {}; + +template +struct is_scalar_result_ptr_vector : std::false_type { +}; + +template +struct is_scalar_result_ptr_vector>>> : std::true_type { +}; + + + +/// the result type of a macrotask must implement gaxpy +template +void gaxpy(const double a, ScalarResult& left, const double b, const T& right, const bool fence=true) { + left.gaxpy(a, right, b, fence); +} + +template +struct madness::archive::ArchiveStoreImpl>> { + static void store(const Archive& ar, const std::shared_ptr>& ptr) { + bool exists=(ptr) ? true : false; + ar & exists; + if (exists) ar & ptr->id(); + } +}; + + +template +struct madness::archive::ArchiveLoadImpl>> { + static void load(const Archive& ar, std::shared_ptr>& ptr) { + bool exists=false; + ar & exists; + if (exists) { + uniqueidT id; + ar & id; + World* world = World::world_from_id(id.get_world_id()); + MADNESS_ASSERT(world); + auto ptr_opt = (world->ptr_from_id< ScalarResult >(id)); + if (!ptr_opt) + MADNESS_EXCEPTION("ScalarResult: remote operation attempting to use a locally uninitialized object",0); + ptr.reset(ptr_opt.value(), [] (ScalarResult *p_) -> void {}); // disable destruction + if (!ptr) + MADNESS_EXCEPTION("ScalarResult operation attempting to use an unregistered object",0); + } else { + ptr=nullptr; + } + } +}; + + /// base class class MacroTaskBase { public: @@ -316,9 +455,11 @@ class MacroTask { typedef typename taskT::argtupleT argtupleT; typedef Cloud::recordlistT recordlistT; taskT task; + bool debug=false; public: + /// constructor takes the actual task MacroTask(World &world, taskT &task, std::shared_ptr taskq_ptr = 0) : task(task), world(world), taskq_ptr(taskq_ptr) { if (taskq_ptr) { @@ -328,6 +469,10 @@ class MacroTask { } } + void set_debug(const bool value) { + debug=value; + } + /// this mimicks the original call to the task functor, called from the universe /// store all input to the cloud, create output Function in the universe, @@ -337,6 +482,7 @@ class MacroTask { const bool immediate_execution = (not taskq_ptr); if (not taskq_ptr) taskq_ptr.reset(new MacroTaskQ(world, world.size())); + if (debug) taskq_ptr->set_printlevel(20); auto argtuple = std::tie(args...); static_assert(std::is_same::value, "type or number of arguments incorrect"); @@ -349,7 +495,8 @@ class MacroTask { // store input and output: output being a pointer to a universe function (vector) recordlistT inputrecords = taskq_ptr->cloud.store(world, argtuple); - auto[outputrecords, result] =prepare_output(taskq_ptr->cloud, argtuple); + resultT result = task.allocator(world, argtuple); + auto outputrecords =prepare_output_records(taskq_ptr->cloud, result); // create tasks and add them to the taskq MacroTaskBase::taskqT vtask; @@ -359,9 +506,9 @@ class MacroTask { } taskq_ptr->add_tasks(vtask); - if (immediate_execution) taskq_ptr->run_all(vtask); + if (immediate_execution) taskq_ptr->run_all(); - return result; + return std::move(result); } private: @@ -369,21 +516,29 @@ class MacroTask { World &world; std::shared_ptr taskq_ptr; - /// prepare the output of the macrotask: Function must be created in the universe - std::pair prepare_output(Cloud &cloud, const argtupleT &argtuple) { - static_assert(is_madness_function::value || is_madness_function_vector::value); - resultT result = task.allocator(world, argtuple); + /// store the result WorldObject in the cloud and return the recordlist + recordlistT prepare_output_records(Cloud &cloud, resultT& result) { + static_assert(is_madness_function::value + || is_madness_function_vector::value + || is_scalar_result_ptr::value + || is_scalar_result_ptr_vector::value, + "unknown result type in prepare_output_records"); recordlistT outputrecords; if constexpr (is_madness_function::value) { outputrecords += cloud.store(world, result.get_impl().get()); // store pointer to FunctionImpl - } else if constexpr (is_vector::value) { + } else if constexpr (is_madness_function_vector::value) { outputrecords += cloud.store(world, get_impl(result)); + } else if constexpr (is_scalar_result_ptr::value) { + outputrecords += cloud.store(world, result); // store pointer to ScalarResult + } else if constexpr (is_vector::value && is_scalar_result_ptr::value) { + outputrecords+=cloud.store(world,result); } else { MADNESS_EXCEPTION("\n\n unknown result type in prepare_input ", 1); } - return std::make_pair(outputrecords, result); + return outputrecords; } + class MacroTaskInternal : public MacroTaskIntermediate { typedef decay_tuple argtupleT; // removes const, &, etc @@ -396,7 +551,11 @@ class MacroTask { MacroTaskInternal(const taskT &task, const std::pair &batch_prio, const recordlistT &inputrecords, const recordlistT &outputrecords) : inputrecords(inputrecords), outputrecords(outputrecords), task(task) { - static_assert(is_madness_function::value || is_madness_function_vector::value); + static_assert(is_madness_function::value + || is_madness_function_vector::value + || is_scalar_result_ptr::value + || is_scalar_result_ptr_vector::value, + "unknown result type in MacroTaskInternal constructor"); this->task.batch=batch_prio.first; this->priority=batch_prio.second; } @@ -435,7 +594,6 @@ class MacroTask { if constexpr (is_madness_function::value) { result_tmp.compress(); gaxpy(1.0,result,1.0, result_tmp); - result += result_tmp; } else if constexpr(is_madness_function_vector::value) { compress(subworld, result_tmp); resultT tmp1=task.allocator(subworld,argtuple); @@ -443,6 +601,16 @@ class MacroTask { gaxpy(1.0,result,1.0,tmp1,false); // was using operator+=, but this requires a fence, which is not allowed here.. // result += tmp1; + } else if constexpr (is_scalar_result_ptr::value) { + gaxpy(1.0, *result, 1.0, result_tmp->get_local(), false); + } else if constexpr (is_scalar_result_ptr_vector::value) { + resultT tmp1=task.allocator(subworld,argtuple); + tmp1=task.batch.template insert_result_batch(tmp1,result_tmp); + + std::size_t sz=result.size(); + for (int i=0; iget_local(), false); + } } else { MADNESS_EXCEPTION("failing result",1); } @@ -463,6 +631,13 @@ class MacroTask { std::vector rimpl = cloud.load>(subworld, outputrecords); result.resize(rimpl.size()); set_impl(result, rimpl); + } else if constexpr (is_scalar_result_ptr::value) { + result = cloud.load(subworld, outputrecords); + } else if constexpr (is_scalar_result_ptr_vector::value) { + typedef typename resultT::value_type::element_type::value_type T; + typedef typename resultT::value_type::element_type ScalarResultT; + result=cloud.load>>(subworld, outputrecords); + } else { MADNESS_EXCEPTION("unknown result type in get_output", 1); } diff --git a/src/madness/mra/mra.h b/src/madness/mra/mra.h index 4c63b69a235..a34d2edd3a5 100644 --- a/src/madness/mra/mra.h +++ b/src/madness/mra/mra.h @@ -716,15 +716,7 @@ namespace madness { /// Since reconstruction/compression do not discard information we define them /// as const ... "logical constness" not "bitwise constness". const Function& compress(bool fence = true) const { - PROFILE_MEMBER_FUNC(Function); - if (!impl || is_compressed()) return *this; - if (VERIFY_TREE) verify_tree(); - if (impl->is_nonstandard() or impl->is_nonstandard_with_leaves()) { - impl->standard(fence); - } else { - const_cast*>(this)->impl->compress(TreeState::compressed, fence); - } - return *this; + return change_tree_state(compressed,fence); } @@ -737,21 +729,12 @@ namespace madness { /// /// Noop if already compressed or if not initialized. void make_nonstandard(bool keepleaves, bool fence=true) const { - PROFILE_MEMBER_FUNC(Function); - verify(); - if (impl->is_nonstandard()) return; - if (impl->is_nonstandard_with_leaves()) return; - - if (VERIFY_TREE) verify_tree(); - if (!is_reconstructed()) reconstruct(); TreeState newstate=TreeState::nonstandard; if (keepleaves) newstate=nonstandard_with_leaves; -// impl->compress(true, keepleaves, false, fence); - const_cast*>(this)->impl->compress(newstate, fence); - + change_tree_state(newstate,fence); } - /// Converts the function from nonstandard form to standard form. Possible non-blocking comm. + /// Converts the function standard compressed form. Possible non-blocking comm. /// By default fence=true meaning that this operation completes before returning, /// otherwise if fence=false it returns without fencing and the user must invoke @@ -760,11 +743,7 @@ namespace madness { /// /// Must be already compressed. void standard(bool fence = true) { - PROFILE_MEMBER_FUNC(Function); - verify(); - MADNESS_ASSERT(impl->is_nonstandard() or impl->is_nonstandard_with_leaves()); - impl->standard(fence); - if (fence && VERIFY_TREE) verify_tree(); + change_tree_state(compressed,fence); } /// Converts the function to redundant form, i.e. sum coefficients on all levels @@ -776,10 +755,7 @@ namespace madness { /// /// Must be already compressed. void make_redundant(bool fence = true) { - PROFILE_MEMBER_FUNC(Function); - verify(); change_tree_state(redundant, fence); - if (fence && VERIFY_TREE) verify_tree(); } /// Reconstructs the function, transforming into scaling function basis. Possible non-blocking comm. @@ -794,11 +770,7 @@ namespace madness { /// Since reconstruction/compression do not discard information we define them /// as const ... "logical constness" not "bitwise constness". const Function& reconstruct(bool fence = true) const { - PROFILE_MEMBER_FUNC(Function); - if (!impl || impl->is_reconstructed()) return *this; - change_tree_state(reconstructed, fence); - if (fence && VERIFY_TREE) verify_tree(); // Must be after in case nonstandard - return *this; + return change_tree_state(reconstructed, fence); } /// changes tree state to given state @@ -810,22 +782,23 @@ namespace madness { const Function& change_tree_state(const TreeState finalstate, bool fence = true) const { PROFILE_MEMBER_FUNC(Function); if (not impl) return *this; - TreeState current_state=impl->get_tree_state(); - if (finalstate==current_state) return *this; - MADNESS_CHECK_THROW(current_state!=TreeState::unknown,"unknown tree state"); + TreeState current_state = impl->get_tree_state(); + if (finalstate == current_state) return *this; + MADNESS_CHECK_THROW(current_state != TreeState::unknown, "unknown tree state"); impl->change_tree_state(finalstate, fence); if (fence && VERIFY_TREE) verify_tree(); return *this; } - /// Sums scaling coeffs down tree restoring state with coeffs only at leaves. Optional fence. Possible non-blocking comm. void sum_down(bool fence = true) const { PROFILE_MEMBER_FUNC(Function); verify(); - MADNESS_ASSERT(is_reconstructed()); + MADNESS_CHECK_THROW(impl->get_tree_state()==redundant_after_merge, "sum_down requires a redundant_after_merge state"); const_cast*>(this)->impl->sum_down(fence); + const_cast*>(this)->impl->set_tree_state(reconstructed); + if (fence && VERIFY_TREE) verify_tree(); // Must be after in case nonstandard } @@ -1290,6 +1263,8 @@ namespace madness { // if this and g are the same, use norm2() if (this->get_impl()==g.get_impl()) { + TreeState state=this->get_impl()->get_tree_state(); + if (not (state==reconstructed or state==compressed)) change_tree_state(reconstructed); double norm=this->norm2(); return norm*norm; } @@ -1302,18 +1277,18 @@ namespace madness { if (VERIFY_TREE) g.verify_tree(); // compression is more efficient for 3D - if (NDIM==3) { - if (!is_compressed()) compress(false); - if (!g.is_compressed()) g.compress(false); + TreeState state=this->get_impl()->get_tree_state(); + TreeState gstate=g.get_impl()->get_tree_state(); + if (NDIM<=3) { + change_tree_state(compressed,false); + g.change_tree_state(compressed,false); impl->world.gop.fence(); } if (this->is_compressed() and g.is_compressed()) { } else { - change_tree_state(redundant); - g.change_tree_state(redundant); -// if (not this->get_impl()->is_redundant()) this->get_impl()->make_redundant(false); -// if (not g.get_impl()->is_redundant()) g.get_impl()->make_redundant(false); + change_tree_state(redundant,false); + g.change_tree_state(redundant,false); impl->world.gop.fence(); } @@ -1322,8 +1297,9 @@ namespace madness { impl->world.gop.sum(local); impl->world.gop.fence(); - if (this->get_impl()->is_redundant()) this->get_impl()->undo_redundant(false); - if (g.get_impl()->is_redundant()) g.get_impl()->undo_redundant(false); + // restore state + change_tree_state(state,false); + g.change_tree_state(gstate,false); impl->world.gop.fence(); return local; @@ -1340,9 +1316,8 @@ namespace madness { T inner_ext_local(const std::shared_ptr< FunctionFunctorInterface > f, const bool leaf_refine=true, const bool keep_redundant=false) const { PROFILE_MEMBER_FUNC(Function); change_tree_state(redundant); -// if (not impl->is_redundant()) impl->make_redundant(true); T local = impl->inner_ext_local(f, leaf_refine); - if (not keep_redundant) impl->undo_redundant(true); + if (not keep_redundant) change_tree_state(reconstructed); return local; } @@ -1357,11 +1332,10 @@ namespace madness { T inner_ext(const std::shared_ptr< FunctionFunctorInterface > f, const bool leaf_refine=true, const bool keep_redundant=false) const { PROFILE_MEMBER_FUNC(Function); change_tree_state(redundant); -// if (not impl->is_redundant()) impl->make_redundant(true); T local = impl->inner_ext_local(f, leaf_refine); impl->world.gop.sum(local); impl->world.gop.fence(); - if (not keep_redundant) impl->undo_redundant(true); + if (not keep_redundant) change_tree_state(reconstructed); return local; } @@ -1401,29 +1375,24 @@ namespace madness { /// g is constructed with an implicit multiplication, e.g. /// result = , with g = 1/r12 | gg> /// @param[in] g on-demand function - template - TENSOR_RESULT_TYPE(T,R) inner_on_demand(const Function& g) const { - MADNESS_ASSERT(g.is_on_demand() and (not this->is_on_demand())); - - this->reconstruct(); + template + TENSOR_RESULT_TYPE(T, R) inner_on_demand(const Function& g) const { + MADNESS_ASSERT(g.is_on_demand() and (not this->is_on_demand())); - // save for later, will be removed by make_Vphi - std::shared_ptr< FunctionFunctorInterface > func=g.get_impl()->get_functor(); - //leaf_op fnode_is_leaf(this->get_impl().get()); - Leaf_op_other fnode_is_leaf(this->get_impl().get()); - g.get_impl()->make_Vphi(fnode_is_leaf,true); // fence here + constexpr std::size_t LDIM=std::max(NDIM/2,std::size_t(1)); + auto func=dynamic_cast* >(g.get_impl()->get_functor().get()); + MADNESS_ASSERT(func); + func->make_redundant(true); + func->replicate_low_dim_functions(true); + this->reconstruct(); // if this == &g we don't need g to be redundant - if (VERIFY_TREE) verify_tree(); - TENSOR_RESULT_TYPE(T,R) local = impl->inner_local(*g.get_impl()); - impl->world.gop.sum(local); - impl->world.gop.fence(); + if (VERIFY_TREE) verify_tree(); - // restore original state - g.get_impl()->set_functor(func); - g.get_impl()->get_coeffs().clear(); - g.get_impl()->set_tree_state(on_demand); + TENSOR_RESULT_TYPE(T, R) local = impl->inner_local_on_demand(*g.get_impl()); + impl->world.gop.sum(local); + impl->world.gop.fence(); - return local; + return local; } /// project this on the low-dim function g: h(x) = @@ -1434,7 +1403,7 @@ namespace madness { template Function project_out(const Function& g, const int dim) const { if (NDIM<=LDIM) MADNESS_EXCEPTION("confused dimensions in project_out?",1); - MADNESS_ASSERT(dim==0 or dim==1); + MADNESS_CHECK_THROW(dim==0 or dim==1,"dim must be 0 or 1 in project_out"); verify(); typedef TENSOR_RESULT_TYPE(T,R) resultT; static const size_t KDIM=NDIM-LDIM; @@ -1456,8 +1425,9 @@ namespace madness { return result; } - template - Function dirac_convolution(const bool fence=true) const { + Function dirac_convolution(const bool fence=true) const { + constexpr std::size_t LDIM=NDIM/2; + MADNESS_CHECK_THROW(NDIM==2*LDIM,"NDIM must be even"); // // this will be the result function FunctionFactory factory=FunctionFactory(world()).k(this->k()); Function f = factory; @@ -1694,14 +1664,13 @@ namespace madness { double check_symmetry() const { change_tree_state(redundant); -// impl->make_redundant(true); if (VERIFY_TREE) verify_tree(); double local = impl->check_symmetry_local(); impl->world.gop.sum(local); impl->world.gop.fence(); double asy=sqrt(local); if (this->world().rank()==0) print("asymmetry wrt particle",asy); - impl->undo_redundant(true); + change_tree_state(reconstructed); return asy; } @@ -1717,9 +1686,8 @@ namespace madness { Function& chop_at_level(const int n, const bool fence=true) { verify(); change_tree_state(redundant); -// impl->make_redundant(true); impl->chop_at_level(n,true); - impl->undo_redundant(true); + change_tree_state(reconstructed); return *this; } }; @@ -2156,8 +2124,11 @@ namespace madness { apply_only(const opT& op, const Function& f, bool fence=true) { Function result; + constexpr std::size_t OPDIM=opT::opdim; + constexpr bool low_dim=(OPDIM*2==NDIM); // apply on some dimensions only + // specialized version for 3D - if (NDIM <= 3) { + if (NDIM <= 3 and (not low_dim)) { result.set_impl(f, false); result.get_impl()->apply(op, *f.get_impl(), fence); @@ -2177,16 +2148,6 @@ namespace madness { //result.get_impl()->recursive_apply(op, f.get_impl().get(), // r1.get_impl().get(),true); // will fence here -// result.world().gop.fence(); -// result.print_size("result before finalization"); -// double time=result.get_impl()->finalize_apply(fence); // need fence before reconstruction -// result.world().gop.fence(); -// if (print_timings) { -// result.get_impl()->print_timer(); -// op.print_timer(); -// if (result.world().rank()==0) print("time in finlize_apply", time); -// } - } return result; @@ -2197,6 +2158,13 @@ namespace madness { /// Returns a new function with the same distribution /// /// !!! For the moment does NOT respect fence option ... always fences + /// if the operator acts on one particle only the result will be sorted as + /// g.particle=1: g(f) = \int g(x,x') f(x',y) dx' = result(x,y) + /// g.particle=2: g(f) = \int g(y,y') f(x,y') dy' = result(x,y) + /// for the second case it will notably *not* be as it is implemented in the partial inner product! + /// g.particle=2 g(f) = result(x,y) + /// inner(g(y,y'),f(x,y'),1,1) = result(y,x) + /// also note the confusion with the counting of the particles/integration variables template Function apply(const opT& op, const Function& f, bool fence=true) { @@ -2214,7 +2182,6 @@ namespace madness { if (op.modified()) { - MADNESS_ASSERT(not op.is_slaterf12); ff.change_tree_state(redundant); // ff.get_impl()->make_redundant(true); result = apply_only(op, ff, fence); @@ -2223,14 +2190,6 @@ namespace madness { } else { - // the slaterf12 function is - // 1/(2 mu) \int d1 (1 - exp(- mu r12)) f(1) - // = 1/(2 mu) (f.trace() - \int d1 exp(-mu r12) f(1) ) - // f.trace() is just a number - R ftrace=0.0; - if (op.is_slaterf12) ftrace=f.trace(); -// print("ftrace",ftrace); - // saves the standard() step, which is very expensive in 6D // Function fff=copy(ff); Function fff=(ff); @@ -2241,6 +2200,7 @@ namespace madness { fff.get_impl()->timer_compress_svd.print("compress_svd"); } result = apply_only(op, fff, fence); + result.get_impl()->set_tree_state(nonstandard_after_apply); ff.world().gop.fence(); if (print_timings) result.print_size("result after apply_only"); @@ -2263,7 +2223,6 @@ namespace madness { } else { ff.standard(); } - if (op.is_slaterf12) result=(result-ftrace).scale(-0.5/op.mu()); } if (print_timings) result.print_size("result after reconstruction"); @@ -2283,6 +2242,7 @@ namespace madness { result.set_impl(ff, false); result.get_impl()->apply_1d_realspace_push(op, ff.get_impl().get(), axis, fence); + result.get_impl()->set_tree_state(redundant_after_merge); return result; } @@ -2341,17 +2301,23 @@ namespace madness { /// param[in] f a function of 2 particles f(1,2) /// return the input function with particles swapped g(1,2) = f(2,1) - template - Function - swap_particles(const Function & f){ + template + typename std::enable_if_t> + swap_particles(const Function & f){ // this could be done more efficiently for SVD, but it works decently - std::vector map(6); - map[0]=3; - map[1]=4; - map[2]=5; // 2 -> 1 - map[3]=0; - map[4]=1; - map[5]=2; // 1 -> 2 + std::vector map(NDIM); + constexpr std::size_t LDIM=NDIM/2; + static_assert(LDIM*2==NDIM); + for (auto d=0; d 1 +// map[3]=0; +// map[4]=1; +// map[5]=2; // 1 -> 2 return mapdim(f,map); } @@ -2418,18 +2384,16 @@ namespace madness { // Function& ff = const_cast< Function& >(f); // Function& gg = const_cast< Function& >(g); + f.change_tree_state(redundant,false); + g.change_tree_state(redundant); FunctionImpl* fimpl=f.get_impl().get(); FunctionImpl* gimpl=g.get_impl().get(); - f.change_tree_state(redundant,false); - g.change_tree_state(redundant,false); - result.world().gop.fence(); result.get_impl()->multiply(fimpl,gimpl,particle); result.world().gop.fence(); - fimpl->undo_redundant(false); - gimpl->undo_redundant(false); - result.world().gop.fence(); + f.change_tree_state(reconstructed,false); + g.change_tree_state(reconstructed); return result; } @@ -2466,8 +2430,8 @@ namespace madness { /// @param[in] task 0: everything, 1; prepare only (fence), 2: work only (no fence), 3: finalize only (fence) template - Function - innerXX(const Function& f, const Function& g, const std::array v1, + std::vector> + innerXX(const Function& f, const std::vector>& vg, const std::array v1, const std::array v2, int task=0) { bool prepare = ((task==0) or (task==1)); bool work = ((task==0) or (task==2)); @@ -2484,8 +2448,8 @@ namespace madness { MADNESS_CHECK((v2[0]==0) or (v2[CDIM-1]==KDIM-1)); MADNESS_CHECK(f.is_initialized()); - MADNESS_CHECK(g.is_initialized()); - MADNESS_CHECK(f.world().id() == g.world().id()); + MADNESS_CHECK(vg[0].is_initialized()); + MADNESS_CHECK(f.world().id() == vg[0].world().id()); // this needs to be run in a single world, so that all coefficients are local. // Use macrotasks if run on multiple processes. World& world=f.world(); @@ -2493,32 +2457,33 @@ namespace madness { if (prepare) { f.change_tree_state(nonstandard); - g.change_tree_state(nonstandard); + change_tree_state(vg,nonstandard); world.gop.fence(); f.get_impl()->compute_snorm_and_dnorm(false); - g.get_impl()->compute_snorm_and_dnorm(false); + for (auto& g : vg) g.get_impl()->compute_snorm_and_dnorm(false); world.gop.fence(); } typedef TENSOR_RESULT_TYPE(T, R) resultT; - Function result; + std::vector> result(vg.size()); if (work) { world.gop.set_forbid_fence(true); - result=FunctionFactory(world) - .k(f.k()).thresh(f.thresh()).empty().nofence(); - result.get_impl()->partial_inner(*f.get_impl(),*g.get_impl(),v1,v2); - result.get_impl()->set_tree_state(nonstandard_after_apply); + for (int i=0; i(world) + .k(f.k()).thresh(f.thresh()).empty().nofence(); + result[i].get_impl()->partial_inner(*f.get_impl(),*(vg[i]).get_impl(),v1,v2); + result[i].get_impl()->set_tree_state(nonstandard_after_apply); + } world.gop.set_forbid_fence(false); } if (finish) { world.gop.fence(); - result.get_impl()->reconstruct(true); - result.reconstruct(); - FunctionImpl& f_nc=const_cast&>(*f.get_impl()); - FunctionImpl& g_nc=const_cast&>(*g.get_impl()); +// result.get_impl()->reconstruct(true); + change_tree_state(result,reconstructed); +// result.reconstruct(); // restore initial state of g and h auto erase_list = [] (const auto& funcimpl) { typedef typename std::decay_t::keyT keyTT; @@ -2531,10 +2496,14 @@ namespace madness { return to_be_erased; }; + FunctionImpl& f_nc=const_cast&>(*f.get_impl()); for (auto& key : erase_list(f_nc)) f_nc.get_coeffs().erase(key); - for (auto& key : erase_list(g_nc)) g_nc.get_coeffs().erase(key); + for (auto& g : vg) { + FunctionImpl& g_nc=const_cast&>(*g.get_impl()); + for (auto& key : erase_list(g_nc)) g_nc.get_coeffs().erase(key); + } world.gop.fence(); - g_nc.reconstruct(false); + change_tree_state(vg,reconstructed); f_nc.reconstruct(false); world.gop.fence(); @@ -2543,6 +2512,20 @@ namespace madness { return result; } + + /// Computes the partial scalar/inner product between two functions, returns a low-dim function + + /// syntax similar to the inner product in tensor.h + /// e.g result=inner<3>(f,g),{0},{1}) : r(x,y) = int f(x1,x) g(y,x1) dx1 + /// @param[in] task 0: everything, 1; prepare only (fence), 2: work only (no fence), 3: finalize only (fence) + template + Function + innerXX(const Function& f, const Function& g, const std::array v1, + const std::array v2, int task=0) { + return innerXX(f,std::vector>({g}),v1,v2,task)[0]; + } + /// Computes the partial scalar/inner product between two functions, returns a low-dim function /// syntax similar to the inner product in tensor.h diff --git a/src/madness/mra/mraimpl.h b/src/madness/mra/mraimpl.h index 15b6057a7b4..54dea9c027a 100644 --- a/src/madness/mra/mraimpl.h +++ b/src/madness/mra/mraimpl.h @@ -2103,8 +2103,12 @@ namespace madness { } if (this->world.rank()==0) { - printf("%40s at time %.1fs: norm/tree/#coeff/size: %7.5f %zu, %6.3f m, %6.3f GByte\n", + + std::size_t bufsize=128; + char buf[bufsize]; + snprintf(buf, bufsize, "%40s at time %.1fs: norm/tree/#coeff/size: %7.5f %zu, %6.3f m, %6.3f GByte", (name.c_str()), wall, norm, tsize,double(ncoeff)*1.e-6,double(ncoeff)/fac*d); + print(std::string(buf)); } } @@ -3166,13 +3170,9 @@ namespace madness { template - void FunctionImpl::tnorm(const tensorT& t, double* lo, double* hi) const { + void FunctionImpl::tnorm(const tensorT& t, double* lo, double* hi) { //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling - // Chosen approach looks stupid but it is more accurate - // than the simple approach of summing everything and - // subtracting off the low-order stuff to get the high - // order (assuming the high-order stuff is small relative - // to the low-order) + auto& cdata=FunctionCommonData::get(t.dim(0)); tensorT work = copy(t); tensorT tlo = work(cdata.sh); *lo = tlo.normf(); @@ -3181,7 +3181,8 @@ namespace madness { } template - void FunctionImpl::tnorm(const GenTensor& t, double* lo, double* hi) const { + void FunctionImpl::tnorm(const GenTensor& t, double* lo, double* hi) { + auto& cdata=FunctionCommonData::get(t.dim(0)); coeffT shalf=t(cdata.sh); *lo=shalf.normf(); coeffT sfull=copy(t); @@ -3191,9 +3192,10 @@ namespace madness { template void FunctionImpl::tnorm(const SVDTensor& t, double* lo, double* hi, - const int particle) const { + const int particle) { *lo=0.0; *hi=0.0; + auto& cdata=FunctionCommonData::get(t.dim(0)); if (t.rank()==0) return; const tensorT vec=t.flat_vector(particle-1); for (long i=0; i #include #include +#include namespace madness { + template + class SeparatedConvolution; + + template + class CCPairFunction; + + template + std::vector< Function > + apply(const SeparatedConvolution& op, const std::vector< Function > f); + + template + std::vector> apply(const SeparatedConvolution& op, const std::vector>& argument); + + template + std::vector> apply(const SeparatedConvolution& op, const std::vector>& argument); + + template + CCPairFunction apply(const SeparatedConvolution& op, const CCPairFunction& argument); + + template + CCPairFunction apply(const SeparatedConvolution& op, const CCPairFunction& argument); + /// SeparatedConvolutionInternal keeps data for 1 term and all dimensions and 1 displacement /// Why is this here?? Why don't you just use ConvolutionND in SeparatedConvolutionData?? template @@ -109,18 +132,21 @@ namespace madness { */ - - template class SeparatedConvolution : public WorldObject< SeparatedConvolution > { public: + typedef Q opT; ///< The apply function uses this to infer resultT=opT*inputT + + OperatorInfo info; + bool doleaves; ///< If should be applied to leaf coefficients ... false by default bool isperiodicsum;///< If true the operator 1D kernels have been summed over lattice translations ///< and may be non-zero at both ends of the unit cell - bool modified_; ///< use modified NS form - int particle_; - bool destructive_; ///< destroy the argument or restore it (expensive for 6d functions) + bool modified_=false; ///< use modified NS form + int particle_=1; ///< must only be 1 or 2 + bool destructive_=false; ///< destroy the argument or restore it (expensive for 6d functions) + bool print_timings=false; typedef Key keyT; const static size_t opdim=NDIM; @@ -129,11 +155,6 @@ namespace madness { Timer timer_low_accumulate; Timer timer_stats_accumulate; - // if this is a Slater-type convolution kernel: 1-exp(-mu r12)/(2 mu) - bool is_slaterf12; - bool print_timings=false; - double mu_; - private: @@ -141,7 +162,7 @@ namespace madness { const BoundaryConditions bc; const int k; const FunctionCommonData& cdata; - const int rank; + int rank; const std::vector vk; const std::vector v2k; const std::vector s0; @@ -157,12 +178,17 @@ namespace madness { int& particle() {return particle_;} const int& particle() const {return particle_;} + SeparatedConvolution& set_particle(const int p) { + if (p!=1 and p!=2) throw std::runtime_error("particle must be 1 or 2"); + particle_=p; + return *this; + } bool& destructive() {return destructive_;} const bool& destructive() const {return destructive_;} - const double& gamma() const {return mu_;} - const double& mu() const {return mu_;} + const double& gamma() const {return info.mu;} + const double& mu() const {return info.mu;} private: @@ -181,6 +207,51 @@ namespace madness { const Q* VT; }; + static inline std::pair,Tensor> + make_coeff_for_operator(World& world, double mu, double lo, double eps, OpType type, + const BoundaryConditions& bc=FunctionDefaults::get_bc()) { + + OperatorInfo info(mu,lo,eps,type); + return make_coeff_for_operator(world, info, bc); +// const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); +// double hi = cell_width.normf(); // Diagonal width of cell +// if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation +// +// OperatorInfo info(mu,lo,eps,type); +// info.hi=hi; +// GFit fit(info); +// +// Tensor coeff=fit.coeffs(); +// Tensor expnt=fit.exponents(); +// +// if (bc(0,0) == BC_PERIODIC) { +// fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); +// } +// +// return std::make_pair(coeff,expnt); + } + + static inline std::pair,Tensor> + make_coeff_for_operator(World& world, OperatorInfo info, + const BoundaryConditions& bc=FunctionDefaults::get_bc()) { + + const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); + double hi = cell_width.normf(); // Diagonal width of cell + if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation + + info.hi=hi; + GFit fit(info); + + Tensor coeff=fit.coeffs(); + Tensor expnt=fit.exponents(); + + if (bc(0,0) == BC_PERIODIC) { + fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); + } + + return std::make_pair(coeff,expnt); + } + // /// return the right block of the upsampled operator (modified NS only) // // /// unlike the operator matrices on the natural level the upsampled operator @@ -882,8 +953,7 @@ namespace madness { , modified_(false) , particle_(1) , destructive_(false) - , is_slaterf12(false) - , mu_(0.0) + , info() , bc(bc) , k(k) , cdata(FunctionCommonData::get(k)) @@ -917,8 +987,7 @@ namespace madness { , modified_(false) , particle_(1) , destructive_(false) - , is_slaterf12(false) - , mu_(0.0) + , info() , ops(argops) , bc(bc) , k(k) @@ -935,9 +1004,23 @@ namespace madness { this->process_pending(); } + /// Constructor for Gaussian Convolutions (mostly for backward compatability) + SeparatedConvolution(World& world, const OperatorInfo info1, + const BoundaryConditions& bc = FunctionDefaults::get_bc(), + int k=FunctionDefaults::get_k(), + bool doleaves = false) + : SeparatedConvolution(world,Tensor(0l),Tensor(0l),info1.lo,info1.thresh,bc,k,doleaves,info1.mu) { + info.type=info1.type; + auto [coeff, expnt] =make_coeff_for_operator(world, info1, bc); + rank=coeff.dim(0); + ops.resize(rank); + initialize(coeff,expnt); + } + /// Constructor for Gaussian Convolutions (mostly for backward compatability) SeparatedConvolution(World& world, const Tensor& coeff, const Tensor& expnt, + double lo, double thresh, const BoundaryConditions& bc = FunctionDefaults::get_bc(), int k=FunctionDefaults::get_k(), bool doleaves = false, @@ -945,11 +1028,7 @@ namespace madness { : WorldObject< SeparatedConvolution >(world) , doleaves(doleaves) , isperiodicsum(bc(0,0)==BC_PERIODIC) - , modified_(false) - , particle_(1) - , destructive_(false) - , is_slaterf12(mu>0.0) - , mu_(mu) + , info(mu,lo,thresh,OT_UNDEFINED) , ops(coeff.dim(0)) , bc(bc) , k(k) @@ -957,8 +1036,11 @@ namespace madness { , rank(coeff.dim(0)) , vk(NDIM,k) , v2k(NDIM,2*k) - , s0(std::max(2,NDIM),Slice(0,k-1)) - { + , s0(std::max(2,NDIM),Slice(0,k-1)) { + initialize(coeff,expnt); + } + + void initialize(const Tensor& coeff, const Tensor& expnt) { // Presently we must have periodic or non-periodic in all dimensions. for (std::size_t d=1; d + std::vector> operator()(const std::vector>& f) const { + return madness::apply(*this, f); + } + /// apply this operator on a separable function f(1,2) = f(1) f(2) /// @param[in] f1 a function of dim LDIM @@ -1105,7 +1192,6 @@ namespace madness { template Function operator()(const Function& f1, const Function& f2) const { - MADNESS_ASSERT(not is_slaterf12); return madness::apply(*this, std::vector>({f1}), std::vector>({f2})); } @@ -1118,10 +1204,17 @@ namespace madness { template Function operator()(const std::vector>& f1, const std::vector>& f2) const { - MADNESS_ASSERT(not is_slaterf12); return madness::apply(*this, f1, f2); } + /// apply this onto another suitable argument, returning the same type + + /// argT must implement argT::apply(const SeparatedConvolution& op, const argT& arg) + template + argT operator()(const argT& argument) const { + return madness::apply(*this,argument); + } + /// apply this operator on coefficients in full rank form @@ -1256,8 +1349,8 @@ namespace madness { // get the appropriate singular vector (left or right depends on particle) // and apply the full tensor muopxv_fast on it, term by term s[0]=Slice(r,r); - const Tensor chunk=svdcoeff.ref_vector(particle()-1)(s).reshape(2*k,2*k,2*k); - const Tensor chunk0=f0.get_svdtensor().ref_vector(particle()-1)(s).reshape(k,k,k); + const Tensor chunk=svdcoeff.ref_vector(particle()-1)(s).reshape(v2k); + const Tensor chunk0=f0.get_svdtensor().ref_vector(particle()-1)(s).reshape(vk); // const double weight=std::abs(coeff.config().weights(r)); // accumulate all terms of the operator for a specific term of the function @@ -1551,6 +1644,73 @@ namespace madness { return tt; } + + static bool can_combine(const SeparatedConvolution& left, const SeparatedConvolution& right) { + return (combine_OT(left,right).type!=OT_UNDEFINED); + } + + /// return operator type and other info of the combined operator (e.g. fg = f(1,2)* g(1,2) + static OperatorInfo combine_OT(const SeparatedConvolution& left, const SeparatedConvolution& right) { + OperatorInfo info=left.info; + if ((left.info.type==OT_F12) and (right.info.type==OT_G12)) { + info.type=OT_FG12; + } else if ((left.info.type==OT_GAUSS) and (right.info.type==OT_GAUSS)) { + info=right.info; + info.type=OT_GAUSS; + info.mu=2.0*right.info.mu; + } else if ((left.info.type==OT_SLATER) and (right.info.type==OT_SLATER)) { + info=right.info; + info.type=OT_SLATER; + info.mu=2.0*right.info.mu; + } else if ((left.info.type==OT_G12) and (right.info.type==OT_F12)) { + info=right.info; + info.type=OT_FG12; + } else if ((left.info.type==OT_G12) and (right.info.type==OT_F212)) { + info=right.info; + info.type=OT_F2G12; + } else if (((left.info.type==OT_F212) and (right.info.type==OT_G12)) or + ((left.info.type==OT_F12) and (right.info.type==OT_FG12)) or + ((left.info.type==OT_FG12) and (right.info.type==OT_F12))) { + info=right.info; + info.type=OT_F2G12; + if (right.info.type!=OT_G12) MADNESS_CHECK(right.info.mu == left.info.mu); + } else if ((left.info.type==OT_F12) and (right.info.type==OT_F12)) { + info.type=OT_F212; + // keep the original gamma + // (f12)^2 = (1- slater12)^2 = 1/(4 gamma) (1 - 2 exp(-gamma) + exp(-2 gamma)) + MADNESS_CHECK(right.info.mu == left.info.mu); + } else { + MADNESS_EXCEPTION("unknown combination of SeparatedConvolutions: feel free to extend in operator.h",1); + } + return info; + } + + + /// combine 2 convolution operators to one + static SeparatedConvolution combine(const SeparatedConvolution& left, + const SeparatedConvolution& right) { + MADNESS_CHECK(can_combine(left,right)); + MADNESS_CHECK(left.get_world().id()==right.get_world().id()); + + auto info=combine_OT(left,right); + return SeparatedConvolution(left.get_world(),info,left.bc,left.k); + } + + /// combine 2 convolution operators to one + friend SeparatedConvolution combine(const std::shared_ptr> left, + const std::shared_ptr> right) { + SeparatedConvolution result; + if (left and right) { + return combine(*left, *right); + } else if (left) { + return *left; + } else if (right) { + return *right; + } else { + MADNESS_EXCEPTION("can't combine empty SeparatedConvolutions",1); + } + return result; + } }; @@ -1592,19 +1752,7 @@ namespace madness { int k=FunctionDefaults<3>::get_k()) { - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - - GFit fit=GFit::CoulombFit(lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); - - - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), true); - } - return SeparatedConvolution(world, coeff, expnt, bc, k); + return SeparatedConvolution(world,OperatorInfo(0.0,lo,eps,OT_G12),bc,k); } @@ -1617,104 +1765,45 @@ namespace madness { const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), int k=FunctionDefaults<3>::get_k()) { - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - GFit fit=GFit::CoulombFit(lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); - - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), true); - } - return new SeparatedConvolution(world, coeff, expnt, bc, k); + return new SeparatedConvolution(world,OperatorInfo(0.0,lo,eps,OT_G12),bc,k); } /// Factory function generating separated kernel for convolution with BSH kernel in general NDIM template - static - inline - SeparatedConvolution BSHOperator(World& world, - double mu, - double lo, - double eps, - const BoundaryConditions& bc=FunctionDefaults::get_bc(), - int k=FunctionDefaults::get_k()) - { + static inline + SeparatedConvolution + BSHOperator(World& world, double mu, double lo, double eps, + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int k=FunctionDefaults::get_k()) { if (eps>1.e-4) { if (world.rank()==0) print("the accuracy in BSHOperator is too small, tighten the threshold",eps); MADNESS_EXCEPTION("0",1); } - const Tensor& cell_width = FunctionDefaults::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - - GFit fit=GFit::BSHFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); - - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); - } - - return SeparatedConvolution(world, coeff, expnt, bc, k); + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_BSH),bc,k); } /// Factory function generating separated kernel for convolution with BSH kernel in general NDIM template - static - inline - SeparatedConvolution* BSHOperatorPtr(World& world, - double mu, - double lo, - double eps, - const BoundaryConditions& bc=FunctionDefaults::get_bc(), - int k=FunctionDefaults::get_k()) - { - if (eps>1.e-4) { - if (world.rank()==0) print("the accuracy in BSHOperator is too small, tighten the threshold",eps); - MADNESS_EXCEPTION("0",1); - } - const Tensor& cell_width = FunctionDefaults::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - - GFit fit=GFit::BSHFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); - - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); + static inline + SeparatedConvolution* + BSHOperatorPtr(World& world, double mu, double lo, double eps, + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int k=FunctionDefaults::get_k()) { + if (eps>1.e-4) { + if (world.rank()==0) print("the accuracy in BSHOperator is too small, tighten the threshold",eps); + MADNESS_EXCEPTION("0",1); } - - return new SeparatedConvolution(world, coeff, expnt, bc, k); + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_BSH),bc,k); } /// Factory function generating separated kernel for convolution with exp(-mu*r)/(4*pi*r) in 3D - static - inline - SeparatedConvolution BSHOperator3D(World& world, - double mu, - double lo, - double eps, - const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), - int k=FunctionDefaults<3>::get_k()) - - { - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - - GFit fit=GFit::BSHFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); - - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); - } - return SeparatedConvolution(world, coeff, expnt, bc, k); + static inline SeparatedConvolution + BSHOperator3D(World& world, double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_BSH),bc,k); } /// Factory function generating separated kernel for convolution with exp(-mu*r)/(4*pi*r) in 3D @@ -1744,8 +1833,7 @@ namespace madness { } /// Factory function generating separated kernel for convolution with exp(-mu*r)/(4*pi*r) in 3D - static - inline + static inline SeparatedConvolution* PeriodicBSHOperatorPtr3D(World& world, Vector args, double mu, @@ -1769,49 +1857,67 @@ namespace madness { return new SeparatedConvolution(world, args, coeff, expnt, bc, k); } - /// Factory function generating separated kernel for convolution with (1 - exp(-mu*r))/(2 mu) in 3D - - /// note that the 1/2mu factor is not included here, nor is the term 1/(2mu) - static inline SeparatedConvolution SlaterF12Operator(World& world, - double mu, double lo, double eps, - const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), - int k=FunctionDefaults<3>::get_k()) { - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation + static inline SeparatedConvolution + SlaterF12Operator(World& world, double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), int k=FunctionDefaults<3>::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_F12),bc,k); + } - GFit fit=GFit::SlaterFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); + static inline SeparatedConvolution SlaterF12sqOperator(World& world, + double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_F212),bc,k); + } - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); - } - return SeparatedConvolution(world, coeff, expnt, bc, k, false, mu); + static inline SeparatedConvolution* SlaterF12sqOperatorPtr(World& world, + double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_F212),bc,k); } /// Factory function generating separated kernel for convolution with exp(-mu*r) in 3D - /// Note that the 1/(2mu) factor is not included, this is just the exponential function - static inline SeparatedConvolution SlaterOperator(World& world, + template + static inline SeparatedConvolution SlaterOperator(World& world, double mu, double lo, double eps, - const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), - int k=FunctionDefaults<3>::get_k()) { + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int k=FunctionDefaults::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_SLATER),bc,k); + } - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation + /// Factory function generating separated kernel for convolution with exp(-mu*r*r) - GFit fit=GFit::SlaterFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); + /// lo and eps are not used here + template + static inline SeparatedConvolution GaussOperator(World& world, + double mu, double lo=0.0, double eps=0.0, + const BoundaryConditions& bc=FunctionDefaults::get_bc(), + int k=FunctionDefaults::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_GAUSS),bc,k); + } - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); - } - SeparatedConvolution tmp(world, coeff, expnt, bc, k, false, mu); - tmp.is_slaterf12 = false; - return tmp; + /// Factory function generating separated kernel for convolution with exp(-mu*r*r) in 3D + + /// lo and eps are not used here + template + static inline SeparatedConvolution* GaussOperatorPtr(World& world, + double mu, double lo=0.0, double eps=0.0, + const BoundaryConditions& bc = FunctionDefaults::get_bc(), + int k = FunctionDefaults::get_k()) { + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_GAUSS),bc,k); + } + + + /// Factory function generating separated kernel for convolution with exp(-mu*r) in 3D + /// Note that the 1/(2mu) factor of SlaterF12Operator is not included, this is just the exponential function + template + static inline SeparatedConvolution* SlaterOperatorPtr_ND(World& world, + double mu, double lo, double eps, + const BoundaryConditions& bc = FunctionDefaults::get_bc(), + int k = FunctionDefaults::get_k()) { + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_SLATER),bc,k); } /// Factory function generating separated kernel for convolution with exp(-mu*r) in 3D @@ -1820,41 +1926,64 @@ namespace madness { double mu, double lo, double eps, const BoundaryConditions<3>& bc = FunctionDefaults<3>::get_bc(), int k = FunctionDefaults<3>::get_k()) { - - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - - GFit fit=GFit::SlaterFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); - - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); - } - SeparatedConvolution* tmp=new SeparatedConvolution(world, coeff, expnt, bc, k, false, mu); - tmp->is_slaterf12 = false; - return tmp; + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_SLATER),bc,k); } /// Factory function generating separated kernel for convolution with (1 - exp(-mu*r))/(2 mu) in 3D + + /// includes the factor 1/(2 mu) static inline SeparatedConvolution* SlaterF12OperatorPtr(World& world, double mu, double lo, double eps, const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), int k=FunctionDefaults<3>::get_k()) { + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_F12),bc,k); + } - const Tensor& cell_width = FunctionDefaults<3>::get_cell_width(); - double hi = cell_width.normf(); // Diagonal width of cell - if (bc(0,0) == BC_PERIODIC) hi *= 100; // Extend range for periodic summation - GFit fit=GFit::SlaterFit(mu,lo,hi,eps,false); - Tensor coeff=fit.coeffs(); - Tensor expnt=fit.exponents(); + /// Factory function generating separated kernel for convolution with 1/(2 mu)*(1 - exp(-mu*r))/r in 3D - if (bc(0,0) == BC_PERIODIC) { - fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); - } - return new SeparatedConvolution(world, coeff, expnt, bc, k, false, mu); + /// fg = (1 - exp(-gamma r12)) / r12 = 1/r12 - exp(-gamma r12)/r12 = coulomb - bsh + /// includes the factor 1/(2 mu) + static inline SeparatedConvolution + FGOperator(World& world, double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_FG12),bc,k); + } + + /// Factory function generating separated kernel for convolution with 1/(2 mu)*(1 - exp(-mu*r))/r in 3D + + /// fg = (1 - exp(-gamma r12)) / r12 = 1/r12 - exp(-gamma r12)/r12 = coulomb - bsh + /// includes the factor 1/(2 mu) + static inline SeparatedConvolution* + FGOperatorPtr(World& world, double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_FG12),bc,k); + } + + /// Factory function generating separated kernel for convolution with (1/(2 mu)*(1 - exp(-mu*r)))^2/r in 3D + + /// f2g = (1/(2 gamma) (1 - exp(-gamma r12)))^2 / r12 + /// = 1/(4 gamma) * [ 1/r12 - 2 exp(-gamma r12)/r12 + exp(-2 gamma r12)/r12 ] + /// includes the factor 1/(2 mu)^2 + static inline SeparatedConvolution* + F2GOperatorPtr(World& world, double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return new SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_F2G12),bc,k); + } + + /// Factory function generating separated kernel for convolution with (1/(2 mu)*(1 - exp(-mu*r)))^2/r in 3D + + /// f2g = (1/(2 gamma) (1 - exp(-gamma r12)))^2 / r12 + /// = 1/(4 gamma) * [ 1/r12 - 2 exp(-gamma r12)/r12 + exp(-2 gamma r12)/r12 ] + /// includes the factor 1/(2 mu)^2 + static inline SeparatedConvolution + F2GOperator(World& world, double mu, double lo, double eps, + const BoundaryConditions<3>& bc=FunctionDefaults<3>::get_bc(), + int k=FunctionDefaults<3>::get_k()) { + return SeparatedConvolution(world,OperatorInfo(mu,lo,eps,OT_F2G12),bc,k); } @@ -1869,7 +1998,7 @@ namespace madness { Tensor coeffs(1), exponents(1); exponents(0L) = exponent; coeffs(0L)=pow(exponent/M_PI,0.5*3.0); // norm of the gaussian - return SeparatedConvolution(world, coeffs, exponents); + return SeparatedConvolution(world, coeffs, exponents, 1.e-8, eps); } @@ -1885,7 +2014,7 @@ namespace madness { Tensor coeffs(1), exponents(1); exponents(0L) = exponent; coeffs(0L)=pow(exponent/M_PI,0.5*NDIM); // norm of the gaussian - return SeparatedConvolution(world, coeffs, exponents); + return SeparatedConvolution(world, coeffs, exponents, 1.e-8, eps); } /// Factory function generating separated kernel for convolution with exp(-mu*r)/(4*pi*r) in 3D @@ -1909,7 +2038,7 @@ namespace madness { if (bc(0,0) == BC_PERIODIC) { fit.truncate_periodic_expansion(coeff, expnt, cell_width.max(), false); } - return new SeparatedConvolution(world, coeff, expnt, bc, k); + return new SeparatedConvolution(world, coeff, expnt, lo, eps, bc, k); } @@ -2021,6 +2150,7 @@ namespace madness { } + namespace archive { template struct ArchiveLoadImpl*> { diff --git a/src/madness/mra/operatorinfo.h b/src/madness/mra/operatorinfo.h new file mode 100644 index 00000000000..7b57acc0d31 --- /dev/null +++ b/src/madness/mra/operatorinfo.h @@ -0,0 +1,72 @@ +// +// Created by Florian Bischoff on 11/2/23. +// + +#ifndef MADNESS_OPERATORINFO_H +#define MADNESS_OPERATORINFO_H + +namespace madness { + +/// operator types +enum OpType { + OT_UNDEFINED, + OT_ONE, /// indicates the identity + OT_G12, /// 1/r + OT_SLATER, /// exp(-r) + OT_GAUSS, /// exp(-r2) + OT_F12, /// 1-exp(-r) + OT_FG12, /// (1-exp(-r))/r + OT_F212, /// (1-exp(-r))^2 + OT_F2G12, /// (1-exp(-r))^2/r = 1/r + exp(-2r)/r - 2 exp(-r)/r + OT_BSH, /// exp(-r)/r + OT_SIZE /// for ending loops +}; + +/// operator type to string +template // dummy template argument to avoid ambiguity with the other operator<< +std::ostream& operator<<(std::ostream& os, const OpType type) { + auto name = [](OpType type) { + switch (type) { + case OpType::OT_UNDEFINED: + return "undefined"; + case OpType::OT_ONE: + return "identity"; + case OpType::OT_G12: + return "g12"; + case OpType::OT_SLATER: + return "slater"; + case OpType::OT_GAUSS: + return "gauss"; + case OpType::OT_F12: + return "f12"; + case OpType::OT_FG12: + return "fg12"; + case OpType::OT_F212: + return "f12^2"; + case OpType::OT_F2G12: + return "f12^2g"; + case OpType::OT_BSH: + return "bsh"; + default: + return "undefined"; + } + }; + os << name(type); + return os; +} + +struct OperatorInfo { + OperatorInfo() = default; + OperatorInfo(double mu, double lo, double thresh, OpType type) : mu(mu), thresh(thresh), lo(lo), type(type) { } + OpType type=OT_UNDEFINED; ///< introspection + double mu=0.0; ///< some introspection + double thresh=1.e-4; + double lo=1.e-5; + double hi=-1.0; + bool debug=false; +}; + + + +} +#endif //MADNESS_OPERATORINFO_H diff --git a/src/madness/mra/test6.cc b/src/madness/mra/test6.cc index c459c2cfb64..07337c69633 100644 --- a/src/madness/mra/test6.cc +++ b/src/madness/mra/test6.cc @@ -36,8 +36,7 @@ /// \brief test various functionality for 6d functions #include - -#include "test_utilities.h" +#include using namespace madness; @@ -747,8 +746,9 @@ int test_vector_composite(World& world, const long& k, const double thresh) { int main(int argc, char**argv) { - initialize(argc,argv); - World world(SafeMPI::COMM_WORLD); +// initialize(argc,argv); +// World world(SafeMPI::COMM_WORLD); + World& world= initialize(argc,argv); srand(time(nullptr)); startup(world,argc,argv); @@ -823,9 +823,8 @@ int main(int argc, char**argv) { error+=test_inner(world,k,thresh); error+=test_replicate(world,k,thresh); - print(ok(error==0),error,"finished test suite\n"); - - world.gop.fence(); + print(ok(error==0),error,"finished test suite\n"); + world.gop.fence(); finalize(); return error; diff --git a/src/madness/mra/test_cloud.cc b/src/madness/mra/test_cloud.cc index 12a33416a84..b421e8b1efa 100644 --- a/src/madness/mra/test_cloud.cc +++ b/src/madness/mra/test_cloud.cc @@ -126,6 +126,40 @@ int chunk_example(World &universe) { } } + +template using is_world_constructible = std::is_constructible; + + +/// test storing and loading a custom WorldObject, used e.g. for the scalar output of a macrotask +int test_custom_worldobject(World& universe, World& subworld, Cloud& cloud) { + test_output t1("testing custom worldobject"); + t1.set_cout_to_terminal(); + cloud.set_debug(false); + auto o1 =std::shared_ptr>(new ScalarResult(universe)); + auto o5 =std::shared_ptr>(new ScalarResult(universe)); + *o1=1.2; + if (universe.rank() == 0) gaxpy(1.0,*o1,2.0,2.8); + + auto adrecords = cloud.store(universe, o1); + MacroTaskQ::set_pmap(subworld); + print("world constructible",is_world_constructible>::value); + + cloud.set_force_load_from_cache(false); + auto o2 = cloud.load>>(subworld, adrecords); + cloud.set_force_load_from_cache(true); + auto o3 = cloud.load>>(subworld, adrecords); + double d1=o1->get_local(); + double d2=o2->get_local(); + double d3=o3->get_local(); + std::cout << "pointer " << o1->id() << " " << o2->id() << " " << o3->id() << " other: " << o5->id() << std::endl; + std::cout << "numerics (plain)" << d1 << " " << d2 << " " << d3 << std::endl; + std::cout << "numerics (get) " << o1->get() << " " << o2->get() << " " << o3->get() << std::endl; + double error=d1-d2; + cloud.set_force_load_from_cache(false); + return t1.end(error < 1.e-10 ); + +} + int main(int argc, char **argv) { madness::World &universe = madness::initialize(argc, argv); @@ -139,7 +173,10 @@ int main(int argc, char **argv) { // cloud.set_debug(true); auto subworld_ptr = MacroTaskQ::create_worlds(universe, universe.size()); - World &subworld = *subworld_ptr; + World& subworld = *subworld_ptr; + + // test storing custom WorldObject + success += test_custom_worldobject(universe, subworld, cloud); if (universe.rank() == 0) print("entering test_cloud"); print("my world: universe_rank, subworld_id", universe.rank(), subworld.id()); diff --git a/src/madness/mra/test_vectormacrotask.cc b/src/madness/mra/test_vectormacrotask.cc index 19a055e52bc..4ce5b87f2bf 100644 --- a/src/madness/mra/test_vectormacrotask.cc +++ b/src/madness/mra/test_vectormacrotask.cc @@ -138,6 +138,55 @@ class MicroTask2 : public MacroTaskOperationBase{ } }; +class VectorOfScalarTask : public MacroTaskOperationBase{ +public: + // you need to define the result type + // resultT must implement gaxpy(alpha, result, beta, contribution) + // with resultT result, contribution; + typedef std::vector>> resultT; + + // you need to define the exact argument(s) of operator() as tuple + typedef std::tuple &, const double &, + const std::vector &> argtupleT; + + resultT allocator(World &world, const argtupleT &argtuple) const { + std::size_t n = std::get<0>(argtuple).size(); + return scalar_result_shared_ptr_vector(world,n); + } + + resultT operator()(const std::vector& f1, const double &arg2, + const std::vector& f2) const { + World &world = f1[0].world(); + auto result=scalar_result_shared_ptr_vector(world,f1.size()); + for (int i=0; i> resultT; + + // you need to define the exact argument(s) of operator() as tuple + typedef std::tuple &> argtupleT; + + resultT allocator(World &world, const argtupleT &argtuple) const { + return resultT(new ScalarResult(world)); + } + + resultT operator()(const std::vector& f1) const { + World &world = f1[0].world(); + resultT result(new ScalarResult(world)); + *result=double(f1.size()); + return result; + } +}; + + + int check_vector(World& universe, const std::vector &ref, const std::vector &test, const std::string msg) { double norm_ref = norm2(universe, ref); @@ -213,11 +262,44 @@ int test_task1(World& universe, const std::vector& v3) { MicroTask1 t1; real_function_3d ref_t1 = t1(v3[0], 2.0, v3); MacroTask task1(universe, t1); + task1.set_debug(true); real_function_3d ref_t2 = task1(v3[0], 2.0, v3); int success = check(universe,ref_t1, ref_t2, "task1 immediate"); return success; } +/// each task accumulates into the same result +int test_scalar_task(World& universe, const std::vector& v3) { + if (universe.rank()==0) print("\nstarting ScalarTask\n"); + ScalarTask t1; + std::shared_ptr> result = t1(v3); + print("result",result->get()); + + + MacroTask task1(universe, t1); + auto result2= task1(v3); + print("result",result2->get()); + +// int success = check(universe,ref_t1, ref_t2, "task1 immediate"); + int success=0; + return success; +} +int test_vector_of_scalar_task(World& universe, const std::vector& v3) { + if (universe.rank()==0) print("\nstarting VectorOfScalarTask\n"); + VectorOfScalarTask t1; + std::vector>> result = t1(v3, 2.0, v3); + for (auto& r : result) print("result",r->get()); + + + MacroTask task1(universe, t1); + auto result2= task1(v3, 2.0, v3); + for (auto& r : result2) print("result",r->get()); + +// int success = check(universe,ref_t1, ref_t2, "task1 immediate"); + int success=0; + return success; +} + int test_2d_partitioning(World& universe, const std::vector& v3) { if (universe.rank() == 0) print("\nstarting 2d partitioning"); auto taskq = std::shared_ptr(new MacroTaskQ(universe, universe.size())); @@ -265,6 +347,12 @@ int main(int argc, char **argv) { success+=test_immediate(universe,v3,ref); timer1.tag("immediate taskq execution"); + success+=test_vector_of_scalar_task(universe,v3); + timer1.tag("vector of scalar task execution"); + + success+=test_scalar_task(universe,v3); + timer1.tag("scalar task execution"); + success+=test_deferred(universe,v3,ref); timer1.tag("deferred taskq execution"); @@ -284,7 +372,7 @@ int main(int argc, char **argv) { } madness::finalize(); - return 0; + return success; } template<> volatile std::list WorldObject::pending = std::list(); diff --git a/src/madness/mra/testinnerext.cc b/src/madness/mra/testinnerext.cc index f92d60a8ac5..72cc728a4b4 100644 --- a/src/madness/mra/testinnerext.cc +++ b/src/madness/mra/testinnerext.cc @@ -82,12 +82,41 @@ bool test_loose1(std::string msg, double a, double b, double tol=thresh) { } +int test_tight_diffuse(World& world) { + FunctionDefaults<4>::set_thresh(1.e-5); + double a=1.e2; + double b=1.e-2; + for (int i=0; i<4; ++i) { + a=std::pow(10.0,double(i)); + b=std::pow(0.1,double(i)); + print("a,b",a,b); + + real_function_2d aa=real_factory_2d(world).functor([&](const coord_2d& r){return exp(-a*inner(r,r));}); + real_function_2d bb=real_factory_2d(world).functor([&](const coord_2d& r){return exp(-b*inner(r,r));}); + real_function_4d cc=real_factory_4d(world).functor([&](const coord_4d& r){return exp(-b*inner(r,r));}); + real_function_4d dd=CompositeFactory(world).particle1(aa).particle2(copy(aa)); + aa.print_size("exp(-1000 r^2"); + bb.print_size("exp(-0.001 r^2"); + double result=inner(cc,dd); + double refresult=std::pow(constants::pi/(a+b),2.0); + print("result,refresult,error",result,refresult,result-refresult); + MADNESS_CHECK(test(" inner(exp(-a r^2 , exp(-b r^2)) ", result,refresult)); + } + + + + + return 0; + + +} + int test_partial_inner(World& world) { - print("\ntesting partial inner\n"); bool do_low_rank=false; #if HAVE_GENTENSOR do_low_rank=true; #endif + print("\ntesting partial inner; low rank: ",do_low_rank,"\n"); real_function_1d one_1d=real_factory_1d(world).functor([](const coord_1d& r){return 1.0;}); real_function_2d one_2d=real_factory_2d(world).functor([](const coord_2d& r){return 1.0;}); @@ -270,6 +299,7 @@ int main(int argc, char** argv) { initialize<6>(world); test_partial_inner(world); + test_tight_diffuse(world); if (!smalltest) { real_function_3d alpha1 = real_factory_3d(world).f(alpha_func); diff --git a/src/madness/mra/testopdir.cc b/src/madness/mra/testopdir.cc index 909e9eff04f..829a2aea5e8 100644 --- a/src/madness/mra/testopdir.cc +++ b/src/madness/mra/testopdir.cc @@ -39,6 +39,7 @@ #include #include #include +#include using namespace madness; @@ -294,9 +295,82 @@ int testgradG(World& world) { } + +/// test the various operators following op_type +template +int test_combined_operators(World& world) { + FunctionDefaults::set_cubic_cell(-20,20); + FunctionDefaults::set_k(8); + FunctionDefaults::set_thresh(1.e-5); + + test_output t("testing combined operators"); +// t.set_cout_to_terminal(); + + // parameters for the convolutions + double mu=1.0; + double lo=1.e-6; + double thresh=FunctionDefaults::get_thresh(); + + // we assume these three are actually working and correct + auto slater=SeparatedConvolution(world,OperatorInfo(mu,lo,thresh,OT_SLATER)); + auto slater2=SeparatedConvolution(world,OperatorInfo(2.0*mu,lo,thresh,OT_SLATER)); + auto bsh=SeparatedConvolution(world,OperatorInfo(mu,lo,thresh,OT_BSH)); + auto bsh2=SeparatedConvolution(world,OperatorInfo(2.0*mu,lo,thresh,OT_BSH)); + auto coulomb=SeparatedConvolution(world,OperatorInfo(mu,lo,thresh,OT_G12)); + + for (int itype=int(OT_ONE); itype::get_thresh(); + OperatorInfo info(mu,lo,thresh,type); + SeparatedConvolution op(world,info); + + // argument is a Gaussian function exp(-r^2) in any dimension + Function arg=FunctionFactory(world) + .functor([](const Vector& r){return exp(-inner(r,r));}); + + // test application + Function result=op(arg); + Function ref; + print("result norm",result.norm2()); + double fourpi=4.0*constants::pi; + + // numerical checks + if (type==OT_F12) { // (1 - Slater)/(2 mu) + ref=0.5*(arg.trace() - slater(arg)); + } else if (type==OT_F212) { // ((1 - Slater)/(2 mu) )^2 = 1/(4 mu^2) (1 - 2 Slater + Slater2 ) + ref=0.25*(arg.trace() - 2*slater(arg) + slater2(arg)); + } else if (type==OT_F2G12) { // ((1 - Slater)/(2 mu))^2 1/g = 1/(4 mu^2) (g12 - 2 bsh + bsh2 ) + ref=0.25*(coulomb(arg)- 2*fourpi*bsh(arg) + fourpi*bsh2(arg)); + } else if (type==OT_FG12) { // (1 - Slater)/(2 mu) 1/g = 1/(2 mu) (g12 - bsh ) + ref=0.5*(coulomb(arg)- fourpi*bsh(arg)); + } + double error=(ref-result).norm2()/ref.norm2(); + print("refnorm",ref.norm2()); + print("diff norm ",(ref-result).norm2()); + + t.checkpoint(error,thresh,type_str); + } + + return t.end(); + +} + + int main(int argc, char**argv) { - initialize(argc,argv); - World world(SafeMPI::COMM_WORLD); + World& world=initialize(argc,argv); int success=0; try { @@ -307,6 +381,7 @@ int main(int argc, char**argv) { std::cout << "small test : " << smalltest << std::endl; success+=test_opdir(world); + success+=test_combined_operators<3>(world); if (!smalltest) success+=testgradG(world); } catch (const SafeMPI::Exception& e) { @@ -325,10 +400,10 @@ int main(int argc, char**argv) { print(s); error("caught a c-string exception"); } - catch (const char* s) { - print(s); - error("caught a c-string exception"); - } +// catch (const char* s) { +// print(s); +// error("caught a c-string exception"); +// } catch (const std::string& s) { print(s); error("caught a string (class) exception"); diff --git a/src/madness/mra/testper.cc b/src/madness/mra/testper.cc index 61ba93b6c96..a044756375e 100644 --- a/src/madness/mra/testper.cc +++ b/src/madness/mra/testper.cc @@ -65,7 +65,8 @@ int test_per(World& world) { expnt[0] = 10000.0; coeff[0] = sqrt(expnt[0]/constants::pi); print(coeff,expnt); - SeparatedConvolution op(world, coeff, expnt); + double lo=1.e-6; thresh=1.e-4; // dummy values + SeparatedConvolution op(world, coeff, expnt, lo, thresh); Function f = FunctionFactory(world).f(constant).initial_level(3).norefine(); diff --git a/src/madness/mra/testsuite.cc b/src/madness/mra/testsuite.cc index faf62fb3e45..83251edd879 100644 --- a/src/madness/mra/testsuite.cc +++ b/src/madness/mra/testsuite.cc @@ -743,7 +743,8 @@ int test_op(World& world) { Tensor coeffs(1), exponents(1); exponents(0L) = 10.0; coeffs(0L) = pow(exponents(0L)/PI, 0.5*NDIM); - SeparatedConvolution op(world, coeffs, exponents); + double lo=1.e-6, thresh1=1.e-4; + SeparatedConvolution op(world, coeffs, exponents, lo, thresh1); START_TIMER; Function r = madness::apply(op,f); END_TIMER("apply"); diff --git a/src/madness/mra/testvmra.cc b/src/madness/mra/testvmra.cc index 8bd05e0e4c5..96fe01e1f29 100644 --- a/src/madness/mra/testvmra.cc +++ b/src/madness/mra/testvmra.cc @@ -2,6 +2,7 @@ #include #include #include +#include const double PI = 3.1415926535897932384; @@ -190,6 +191,65 @@ void test_inner(World& world) { print("error norm",(rold-rnew).normf(),"\n"); } +template +int test_transform(World& world) { + test_output to("testing transform"); + to.set_cout_to_terminal(); + typedef std::shared_ptr< FunctionFunctorInterface > ffunctorT; + + + const double thresh=1.e-7; + Tensor cell(NDIM,2); + for (std::size_t i=0; i::set_cell(cell); + FunctionDefaults::set_k(8); + FunctionDefaults::set_thresh(thresh); + FunctionDefaults::set_refine(true); + FunctionDefaults::set_initial_level(3); + FunctionDefaults::set_truncate_mode(1); + + + const int nleft=RandomValue()%10; + const int nright=RandomValue()%10; + print("nleft, nright",nleft,nright); + + START_TIMER; + std::vector< Function > left(nleft); + for (int i=0; i(FunctionDefaults::get_cell(),0.5)); + left[i] = FunctionFactory(world).functor(f); + } + END_TIMER("project"); + to.checkpoint(true,"initial projection"); + + Tensor c(nleft,nright); + auto result1=transform(world,left,c); + to.checkpoint(true,"reference transform"); + + change_tree_state(left,reconstructed); + auto result2=transform_reconstructed(world,left,c,false); + world.gop.fence(); + for (auto& r : result2) MADNESS_CHECK(r.get_impl()->get_tree_state()==redundant_after_merge); + change_tree_state(result2,reconstructed); + double err1=norm2(world, result1-result2); + to.checkpoint(err1,thresh,"reference transform"); + + change_tree_state(left,compressed); + auto result3=transform_reconstructed(world,left,c); + double err2=norm2(world, result1-result3); + to.checkpoint(err2,thresh,"reference transform"); + + + return to.end(); +} + + + + + template void test_cross(World& world) { typedef std::shared_ptr< FunctionFunctorInterface > ffunctorT; @@ -408,13 +468,15 @@ int main(int argc, char**argv) { for (int iarg=1; iarg(world); - test_add,3 >(world); + // test_add(world); + // test_add,3 >(world); test_inner(world); test_inner(world); @@ -425,6 +487,11 @@ int main(int argc, char**argv) { test_cross,double,2>(world); test_cross,std::complex,2>(world); + test_transform(world); + test_transform(world); + test_transform(world); + test_transform(world); + test_rot(world); test_rot,3>(world); @@ -457,10 +524,6 @@ int main(int argc, char**argv) { print(s); error("caught a c-string exception"); } - catch (const char* s) { - print(s); - error("caught a c-string exception"); - } catch (const std::string& s) { print(s); error("caught a string (class) exception"); diff --git a/src/madness/mra/vmra.h b/src/madness/mra/vmra.h index 704baec902b..037f0b2cf25 100644 --- a/src/madness/mra/vmra.h +++ b/src/madness/mra/vmra.h @@ -288,9 +288,42 @@ namespace madness { if (not dummy.is_initialized()) return v; World& world=dummy.world(); -// if (not fence) world.gop.set_forbid_fence(true); // make sure fence is respected + + // if a tree state cannot directly be changed to finalstate, we need to go via intermediate + auto change_initial_to_intermediate =[](const std::vector>& v, + const TreeState initialstate, + const TreeState intermediatestate) { + int must_fence=0; + for (auto& f : v) { + if (f.is_initialized() and f.get_impl()->get_tree_state()==initialstate) { + f.change_tree_state(intermediatestate,false); + must_fence=1; + } + } + return must_fence; + }; + + int do_fence=0; + if (finalstate==compressed) { + do_fence+=change_initial_to_intermediate(v,redundant,TreeState::reconstructed); + } + if (finalstate==nonstandard) { + do_fence+=change_initial_to_intermediate(v,compressed,TreeState::reconstructed); + do_fence+=change_initial_to_intermediate(v,redundant,TreeState::reconstructed); + } + if (finalstate==nonstandard_with_leaves) { + do_fence+=change_initial_to_intermediate(v,compressed,TreeState::reconstructed); + do_fence+=change_initial_to_intermediate(v,nonstandard,TreeState::reconstructed); + do_fence+=change_initial_to_intermediate(v,redundant,TreeState::reconstructed); + } + if (finalstate==redundant) { + do_fence+=change_initial_to_intermediate(v,compressed,TreeState::reconstructed); + do_fence+=change_initial_to_intermediate(v,nonstandard,TreeState::reconstructed); + do_fence+=change_initial_to_intermediate(v,nonstandard_with_leaves,TreeState::reconstructed); + } + if (do_fence>0) world.gop.fence(); + for (unsigned int i=0; i + std::vector> impl2function(const std::vector>> vimpl) { + std::vector> v(vimpl.size()); + for (std::size_t i=0; i + std::vector< Function > + transform_reconstructed(World& world, + const std::vector< Function >& v, + const Tensor& c, + bool fence=true) { + + PROFILE_BLOCK(Vtransformsp); + typedef TENSOR_RESULT_TYPE(T,R) resultT; + int n = v.size(); // n is the old dimension + int m = c.dim(1); // m is the new dimension + MADNESS_CHECK(n==c.dim(0)); + + // if we fence set the right tree state here, otherwise it has to be correct from the start. + if (fence) change_tree_state(v,reconstructed); + for (const auto& vv : v) MADNESS_CHECK_THROW( + vv.get_impl()->get_tree_state()==reconstructed,"trees have to be reconstructed in transform_reconstructed"); + + std::vector< Function > result = zero_functions(world, m); + + for (int i=0; iset_tree_state(redundant_after_merge); + for (int j=0; jaccumulate_trees(*(result[i].get_impl()),resultT(c(j,i)),true); + } + } + + // if we fence we can as well finish the job here. Otherwise no harm done, as the tree state is well-defined. + if (fence) { + world.gop.fence(); + // for (auto& r : vc) r.sum_down(false); + for (auto& r : result) r.get_impl()->finalize_sum(); + world.gop.fence(); + } + return result; + } + /// this version of transform uses Function::vtransform and screens /// using both elements of `c` and `v` template @@ -864,7 +944,9 @@ namespace madness { { world.gop.fence(); compress(world, f); - if ((void*)(&f) != (void*)(&g)) compress(world, g); +// if ((void*)(&f) != (void*)(&g)) compress(world, g); + compress(world, g); + std::vector*> left(f.size()); std::vector*> right(g.size()); @@ -1094,7 +1176,46 @@ namespace madness { } - /// Computes the square of a vector of functions --- q[i] = v[i]**2 + /// multiply a high-dimensional function with a low-dimensional function + + /// @param[in] f NDIM function of NDIM dimensions + /// @param[in] g LDIM function of LDIM + /// @param[in] v dimension indices of f to multiply + /// @return h[i](0,1,2,3) = f(0,1,2,3) * g[i](1,2,3) for v={1,2,3} + template + std::vector > partial_mul(const Function f, const std::vector > g, + const int particle) { + + World& world=f.world(); + std::vector > result(g.size()); + for (auto& r : result) r.set_impl(f, false); + + FunctionImpl* fimpl=f.get_impl().get(); +// fimpl->make_redundant(false); + fimpl->change_tree_state(redundant,false); + make_redundant(world,g,false); + world.gop.fence(); + + for (std::size_t i=0; i* gimpl=g[i].get_impl().get(); + result[i].get_impl()->multiply(fimpl,gimpl,particle); // stupid naming inconsistency + } + world.gop.fence(); + + fimpl->undo_redundant(false); + for (auto& ig : g) ig.get_impl()->undo_redundant(false); + world.gop.fence(); + return result; + } + + template + std::vector > multiply(const Function f, const std::vector > g, + const std::tuple v) { + return partial_mul(f,g,std::array({std::get<0>(v),std::get<1>(v),std::get<2>(v)})); + } + + +/// Computes the square of a vector of functions --- q[i] = v[i]**2 template std::vector< Function > square(World& world, @@ -1178,6 +1299,17 @@ namespace madness { return r; } + + /// Returns a deep copy of a vector of functions + template + std::vector< Function > + copy(const std::vector< Function >& v, bool fence=true) { + PROFILE_BLOCK(Vcopy); + std::vector< Function > r(v.size()); + if (v.size()>0) r=copy(v.front().world(),v,fence); + return r; + } + /// Returns a vector of `n` deep copies of a function template std::vector< Function > @@ -1408,7 +1540,6 @@ namespace madness { std::vector< Function > result(f.size()); for (unsigned int i=0; iis_slaterf12); result[i] = apply_only(*op[i], f[i], false); result[i].get_impl()->set_tree_state(nonstandard_after_apply); } @@ -1423,6 +1554,15 @@ namespace madness { } + /// Applies an operator to a vector of functions --- q[i] = apply(op,f[i]) + template + std::vector< Function > + apply(const SeparatedConvolution& op, + const std::vector< Function > f) { + return apply(op.get_world(),op,f); + } + + /// Applies an operator to a vector of functions --- q[i] = apply(op,f[i]) template std::vector< Function > @@ -1432,10 +1572,10 @@ namespace madness { PROFILE_BLOCK(Vapply); std::vector< Function >& ncf = *const_cast< std::vector< Function >* >(&f); - bool print_timings=(NDIM==6) and (world.rank()==0); + bool print_timings=(NDIM==6) and (world.rank()==0) and op.print_timings; double wall0=wall_time(); - reconstruct(world, f); +// reconstruct(world, f); make_nonstandard(world, ncf); double wall1=wall_time(); if (print_timings) printf("timer: %20.20s %8.2fs\n", "make_nonstandard", wall1-wall0); @@ -1448,7 +1588,12 @@ namespace madness { world.gop.fence(); // restores promise of logical constness - if (not op.destructive()) standard(world, ncf, false); + if (op.destructive()) { + for (auto& ff : ncf) ff.clear(false); + world.gop.fence(); + } else { + reconstruct(world,f); + } // svd-tensor requires some cleanup after apply if (result[0].get_impl()->get_tensor_type()==TT_2D) { @@ -1461,15 +1606,6 @@ namespace madness { } reconstruct(world, result); - if (op.is_slaterf12) { - MADNESS_ASSERT(not op.destructive()); - if (typeid(T)!=typeid(R)) MADNESS_EXCEPTION("think again!",1); - for (unsigned int i=0; i full_tensor_copy() const {return copy(*this);} + Tensor full_tensor_copy() {return copy(*this);} bool is_assigned() const {return this->size()>0;}; bool has_data() const {return this->size()>0;}; diff --git a/src/madness/tensor/lowranktensor.h b/src/madness/tensor/lowranktensor.h index 1618038332d..d56be254275 100644 --- a/src/madness/tensor/lowranktensor.h +++ b/src/madness/tensor/lowranktensor.h @@ -695,9 +695,6 @@ class GenTensor { friend GenTensor transform_dir( const GenTensor& t, const Tensor& c, const int axis); - template - friend GenTensor outer( - const GenTensor& t1, const GenTensor& t2); std::string what_am_i() const { TensorType tt; @@ -815,7 +812,7 @@ void change_tensor_type(GenTensor& t, const TensorArgs& targs) { /// all other combinations are currently invalid. template GenTensor outer(const GenTensor& t1, - const GenTensor& t2, const TensorArgs final_tensor_args) { + const GenTensor& t2, const TensorArgs final_tensor_args=TensorArgs(-1.0,TT_2D)) { typedef TENSOR_RESULT_TYPE(T,Q) resultT; diff --git a/src/madness/tensor/srconf.h b/src/madness/tensor/srconf.h index 480b0e7f2c5..9ecb4858b48 100644 --- a/src/madness/tensor/srconf.h +++ b/src/madness/tensor/srconf.h @@ -693,10 +693,17 @@ namespace madness { Tensor make_vector_with_weights(const int dim) const { Tensor v=copy(vector_[dim].reshape(rank(),vector_[dim].size()/rank())); for (unsigned int r=0; r flat_vector_with_weights(const int dim) const { + return make_vector_with_weights(dim).reshape(rank(),vector_[dim].size()/rank()); + } + protected: /// return the number of coefficients unsigned int nCoeff() const { diff --git a/src/madness/tensor/tensor.h b/src/madness/tensor/tensor.h index c438f2dcc07..bdb0a77dfad 100644 --- a/src/madness/tensor/tensor.h +++ b/src/madness/tensor/tensor.h @@ -260,6 +260,9 @@ namespace madness { template T mynorm(T t) { return t*t; } + template double mynorm(int t) { + return double(t)*double(t); + } template T mynorm(std::complex t) { return std::norm(t); diff --git a/src/madness/tensor/tensor_lapack.h b/src/madness/tensor/tensor_lapack.h index f446236a5b0..1215aebbb9a 100644 --- a/src/madness/tensor/tensor_lapack.h +++ b/src/madness/tensor/tensor_lapack.h @@ -60,6 +60,20 @@ namespace madness { void svd_result(Tensor& a, Tensor& U, Tensor< typename Tensor::scalar_type >& s, Tensor& VT, Tensor& work); + /// SVD - MATLAB syntax + + /// call as + /// auto [U,s,VT] = svd(A); + /// with A=U*S*VT + template + std::tuple, Tensor< typename Tensor::scalar_type >, Tensor> + svd(const Tensor& A) { + Tensor U,VT; + Tensor< typename Tensor::scalar_type > s; + svd(A,U,s,VT); + return std::make_tuple(U,s,VT); + } + /// Solves linear equations /// \ingroup linalg diff --git a/src/madness/world/cloud.h b/src/madness/world/cloud.h index 2e0d87af5ba..aba5f36ad38 100644 --- a/src/madness/world/cloud.h +++ b/src/madness/world/cloud.h @@ -60,12 +60,26 @@ struct Recordlist { return list.size(); } + // if type provides id() member function (i.e. WorldObject) use that for hashing, otherwise use hash_value() for + // fundamental types (see worldhash.h) + template + using member_id_t = decltype(std::declval().id()); + + template + using has_member_id = madness::meta::is_detected; + template static keyT compute_record(const Function& arg) {return hash_value(arg.get_impl()->id());} template static keyT compute_record(const FunctionImpl* arg) {return hash_value(arg->id());} + template + static keyT compute_record(const WorldContainer& arg) {return hash_value(arg.id());} + + template + static keyT compute_record(const std::shared_ptr>& arg) {return hash_value(arg->id());} + template static keyT compute_record(const std::shared_ptr>& arg) {return hash_value(arg->id());} @@ -76,7 +90,18 @@ struct Recordlist { static keyT compute_record(const Tensor& arg) {return hash_value(arg.normf());} template - static keyT compute_record(const T& arg) {return hash_value(arg);} + static keyT compute_record(const std::shared_ptr& arg) {return compute_record(*arg);} + + template + static keyT compute_record(const T& arg) { + if constexpr (has_member_id::value) { + return hash_value(arg.id()); + } else if constexpr (std::is_pointer_v && has_member_id>::value) { + return hash_value(arg->id()); + } else { + return hash_value(arg); + } + } friend std::ostream &operator<<(std::ostream &os, const Recordlist &arg) { @@ -221,7 +246,7 @@ class Cloud { if constexpr (is_tuple::value) { return load_tuple(world, rlist); } else { - return load_internal(world, rlist); + return load_other(world, rlist); } } @@ -349,22 +374,8 @@ class Cloud { // if (auto obj = std::get_if(&cached_objects.find(record)->second)) return *obj; if (auto obj = std::any_cast(&cached_objects.find(record)->second)) return *obj; MADNESS_EXCEPTION("failed to load from cloud-cache", 1); - return T(); - } - - template - T load_internal(madness::World &world, recordlistT &recordlist) const { - T result; - if constexpr (is_vector::value) { - if constexpr( is_parallel_serializable_object::value) { - result = load_vector_of_parallel_serializable_objects(world, recordlist); - } else { - result = load_other(world, recordlist); - } - } else { - result = load_other(world, recordlist); - } - return result; + T target = allocator(world); + return target; } bool is_cached(const keyT &key) const { @@ -383,8 +394,11 @@ class Cloud { template T allocator(World &world) const { - if constexpr (is_world_constructible::value) return T(world); - return T(); + if constexpr (is_world_constructible::value) { + return T(world); + } else { + return T(); + } } template @@ -393,6 +407,10 @@ class Cloud { bool is_already_present= is_in_container(record); if (debug) { if (is_already_present) std::cout << "skipping "; + std::string msg; + if constexpr (Recordlist::has_member_id::value) { + std::cout << "storing world object of " << typeid(T).name() << "id " << source.id() << " to record " << record << std::endl; + } std::cout << "storing object of " << typeid(T).name() << " to record " << record << std::endl; } @@ -410,7 +428,19 @@ class Cloud { } template - T load_other(World &world, recordlistT &recordlist) const { + typename std::enable_if::value, T>::type + load_other(World &world, recordlistT &recordlist) const { + std::size_t sz = load_other(world, recordlist); + T target(sz); + for (std::size_t i = 0; i < sz; ++i) { + target[i] = load_other(world, recordlist); + } + return target; + } + + template + typename std::enable_if::value, T>::type + load_other(World &world, recordlistT &recordlist) const { keyT record = recordlist.pop_front_and_return(); if (force_load_from_cache) MADNESS_CHECK(is_cached(record)); @@ -426,39 +456,16 @@ class Cloud { // overloaded template - std::enable_if_t::value, recordlistT> - store_other(madness::World& world, const std::vector& source) { + recordlistT store_other(madness::World& world, const std::vector& source) { if (debug) std::cout << "storing " << typeid(source).name() << " of size " << source.size() << std::endl; recordlistT l = store_other(world, source.size()); - for (auto s : source) l += store_other(world, s); + for (const auto& s : source) l += store_other(world, s); if (dofence) world.gop.fence(); if (debug) std::cout << "done with vector storing; container size " << container.size() << std::endl; return l; } -// // overloaded -// template -// recordlistT store_other(madness::World &world, const std::vector> &source) { -// if (debug) -// std::cout << "storing " << typeid(source).name() << " of size " << source.size() << std::endl; -// recordlistT l = store_other(world, source.size()); -// for (auto s : source) l += store_other(world, s); -// if (dofence) world.gop.fence(); -// if (debug) std::cout << "done with vector storing; container size " << container.size() << std::endl; -// return l; -// } - - template - T load_vector_of_parallel_serializable_objects(World &world, recordlistT &recordlist) const { - std::size_t sz = load_other(world, recordlist); - T target(sz); - for (std::size_t i = 0; i < sz; ++i) { - target[i] = load_other(world, recordlist); - } - return target; - } - /// store a tuple in multiple records template recordlistT store_tuple(World &world, const std::tuple &input) { @@ -478,7 +485,7 @@ class Cloud { if (debug) std::cout << "loading tuple of type " << typeid(T).name() << " to world " << world.id() << std::endl; T target; std::apply([&](auto &&... args) { - ((args = load_internal::type>(world, recordlist)), ...); + ((args = load_other::type>(world, recordlist)), ...); }, target); return target; } diff --git a/src/madness/world/test_utilities.h b/src/madness/world/test_utilities.h index 1dc4a10f02f..f98db1eaea5 100644 --- a/src/madness/world/test_utilities.h +++ b/src/madness/world/test_utilities.h @@ -16,6 +16,8 @@ struct test_output { test_output(std::string line) { std::cout << ltrim_to_length(line,70); logger << std::scientific << std::setprecision(8) ; + time_begin=cpu_time(); + time_last_checkpoint=time_begin; set_cout_to_logger(); } @@ -26,7 +28,7 @@ struct test_output { } ~test_output() { - set_cout_to_terminal(); + set_cout_to_terminal(false); } void print_and_clear_log() { @@ -35,21 +37,60 @@ struct test_output { logger.clear(); } + void checkpoint(double error, double tol, + std::string message, double time=-1.0) { + bool use_logger=cout_set_to_logger; + set_cout_to_terminal(false); + bool success=error=0.0) os << " error " << error; os << std::endl; } int end(bool success=true) { - set_cout_to_terminal(); + set_cout_to_terminal(false); if (have_checkpoints) std::cout << ltrim_to_length("--> final result -->",70); success = success and final_success; - print_success_fail(std::cout,success); + double time_end=cpu_time(); + print_success_fail(std::cout,success,time_end-time_begin,-1.0); if (not success) print_and_clear_log(); return (success) ? 0 : 1; } @@ -78,12 +121,14 @@ struct test_output { std::cout.rdbuf(stream_buffer_file); } - void set_cout_to_terminal() { + /// newline for use by user, not for internal use (e.g. checkpoint()) + void set_cout_to_terminal(bool newline=true) { if (not cout_set_to_logger) return; if (cout_set_to_logger) { std::cout.rdbuf(stream_buffer_cout); } cout_set_to_logger=false; + if (newline) std::cout << std::endl; } std::stringstream logger; @@ -94,6 +139,8 @@ struct test_output { bool cout_set_to_logger=false; // do not change this directly! bool have_checkpoints=false; std::streambuf* stream_buffer_cout; + double time_begin=0.0; + double time_last_checkpoint=0.0; }; diff --git a/src/madness/world/timing_utilities.h b/src/madness/world/timing_utilities.h index 8b2a80266fd..db47c7e36db 100644 --- a/src/madness/world/timing_utilities.h +++ b/src/madness/world/timing_utilities.h @@ -8,31 +8,53 @@ namespace madness { struct timer { World &world; - double ttt, sss; + double ttt=0.0, sss=0.0; // duration bool do_print = true; + bool is_running=false; timer(World &world, bool do_print = true) : world(world), do_print(do_print) { world.gop.fence(); - ttt = wall_time(); - sss = cpu_time(); + resume(); } - void tag(const std::string msg) { + void resume() { + if (is_running) print("timer was already running!"); world.gop.fence(); - double tt1 = wall_time() - ttt; - double ss1 = cpu_time() - sss; + ttt-=wall_time(); + sss-=cpu_time(); + is_running=true; + } + + double interrupt() { + world.gop.fence(); + ttt+=wall_time(); + sss+=cpu_time(); + is_running=false; + return sss; + } + + void print(const std::string msg) const { if (world.rank() == 0 and do_print) { std::stringstream ss; ss << "timer:" << std::setw(30) << msg << std::setw(8) << std::setprecision(2) - << std::fixed << ss1 << "s " << tt1 <<"s"; + << std::fixed << sss << "s " << ttt <<"s"; std::cout << ss.str() << std::endl; } - ttt = wall_time(); - sss = cpu_time(); } - void end(const std::string msg) { - tag(msg); + double tag(const std::string msg) { + world.gop.fence(); + interrupt(); + print(msg); + double cpu=sss; + ttt=0.0; + sss=0.0; + resume(); + return cpu; + } + + double end(const std::string msg) { + return tag(msg); // world.gop.fence(); // double tt1 = wall_time() - ttt; // double ss1 = cpu_time() - sss;