50 throw std::runtime_error(
52 " must always be region 0. Currently only one qm region is possible.");
65 if (prop.
exists(
"statetracker")) {
72 throw std::runtime_error(
73 "No statetracker options for excited states found");
79 if (prop.
exists(
"statetracker")) {
86 throw std::runtime_error(
"No statetracker options for KS states found");
91 prop.
get(
"grid_for_potential").
as<std::string>();
99 if (prop.
get(
"dftpackage.xtpdft.dft_in_dft.activeatoms")
106 if (prop.
exists(
"environment_screening")) {
118 throw std::runtime_error(
119 "environment_screening with state " +
initstate_.ToString() +
120 ": the charged DFT state would be polarized in the QM/MM loop and "
121 "screened again in GW. Run the neutral molecule and ask for the "
122 "quasiparticle level instead: state pqpN (or dqpN) with N the "
123 "HOMO or LUMO index.");
126 throw std::runtime_error(
127 "environment_screening is implemented for closed-shell GW-BSE "
128 "only; the unrestricted exciton state " +
129 initstate_.ToString() +
" is not supported.");
132 throw std::runtime_error(
133 "environment_screening needs a GW-BSE state (an exciton or a "
134 "quasiparticle level); " +
135 initstate_.ToString() +
" has no GW-BSE calculation to screen.");
144 throw std::runtime_error(
145 "environment_screening: site_width must be >= 0 (0 for point "
151 std::replace(shells.begin(), shells.end(),
',',
' ');
152 std::istringstream in(shells);
158 <<
" Environment screening on: ground-state QM/MM loop, then one "
159 "screened GW-BSE run. K_reac "
171 if (
orb_.isOpenShell()) {
172 throw std::runtime_error(
"GWBSE not implemented for open-shell systems");
175 Index active_electrons =
orb_.getNumOfActiveElectrons();
177 if (active_electrons % 2 != 0) {
178 throw std::runtime_error(
179 "DFT-in-DFT embedded GWBSE currently supports only closed-shell "
180 "active spaces with an even number of electrons");
185 const Index active_occ = active_electrons / 2;
188 orb_.setNumberOfOccupiedLevels(active_occ);
189 orb_.setNumberOfOccupiedLevelsBeta(active_occ);
190 orb_.setNumberOfAlphaElectrons(active_occ);
191 orb_.setNumberOfBetaElectrons(active_occ);
192 orb_.setChargeAndSpin(
orb_.getCharge(), 1);
198 return orb_.getExcitedStateEnergy(state);
203 return orb_.getExcitedStateEnergy(state);
205 return -
orb_.getExcitedStateEnergy(state);
209 const std::vector<std::unique_ptr<Region> >& regions)
const {
214 bool have_damp =
false;
216 for (
const std::unique_ptr<Region>& reg : regions) {
217 if (reg->identify() != Polardummy.
identify()) {
227 throw std::runtime_error(
228 "environment_screening: shell region " +
229 std::to_string(polar.
getId()) +
230 " has charge-response segments; a shell responds with "
231 "alpha/epsilon per site, which has no induced charges. Use "
232 "Thole segments there.");
239 throw std::runtime_error(
240 "environment_screening: the explicit polar regions respond as one "
241 "coupled Thole system and must share exp_damp; found " +
242 std::to_string(env.
exp_damp) +
" and " +
243 std::to_string(polar.
ExpDamp()) +
".");
251 const bool is_polar = std::any_of(
252 regions.begin(), regions.end(),
253 [&](
const std::unique_ptr<Region>& reg) {
254 return reg->getId() == id && reg->identify() == Polardummy.identify();
257 throw std::runtime_error(
"environment_screening: shell region " +
259 " is not a polar region of this job.");
265 <<
" WARNING: environment_screening is on but there is no polar "
266 "region; GW-BSE runs unscreened."
276 !
dftoptions_.get(
"auxbasisset").as<std::string>().empty()) {
277 return dftoptions_.get(
"auxbasisset").as<std::string>();
280 !
gwbseoptions_.get(
"auxbasisset").as<std::string>().empty()) {
283 return "aux-" +
dftoptions_.get(
"basisset").as<std::string>();
287 const std::vector<std::unique_ptr<Region> >& regions)
const {
306 <<
TimeStamp() <<
" Checking the screening environment (" << auxname
307 <<
", " << auxbasis.
AOBasisSize() <<
" functions)" << std::flush;
311 throw std::runtime_error(
312 "environment_screening: 1 + R is not positive definite (lowest "
314 std::to_string(check.
lowest) +
315 "): the environment would screen some charge fluctuation of the QM "
316 "region by more than the fluctuation itself. Diagnosis:\n" +
320 <<
TimeStamp() <<
" Screening environment is stable: lowest eigenvalue "
321 <<
"of R " << check.
lowest <<
" (must be > -1)" << std::flush;
326 std::vector<std::unique_ptr<Region> >& regions) {
330 if (
orb_.getCalculationType() ==
"Truncated") {
331 throw std::runtime_error(
332 "environment_screening is not implemented for truncated active "
339 <<
TimeStamp() <<
" Running environment-screened GW-BSE" << std::flush;
340 const double e_dft =
orb_.getDFTTotalEnergy();
352 <<
" eV, region energy " << energy <<
" Hartree" << std::flush;
370 auto qp = [&](
Index level) {
371 const std::string kind =
orb_.hasQPdiag() ?
"dqp" :
"pqp";
372 return orb_.getExcitedStateEnergy(
QMState(kind + std::to_string(level)));
390 std::vector<const Eigen::Vector3d*> src =
ewaldgrid_.getGridpoints();
391 std::vector<Eigen::Vector3d> dst(src.size());
392 std::transform(src.begin(), src.end(), dst.begin(),
393 [](
const Eigen::Vector3d* v) { return *v; });
413 if (
orb_.QMAtoms().size() == 0) {
414 throw std::runtime_error(
415 "QMRegion::PrepareEwaldPotentialGrid: the region holds no atoms. The "
416 "grid is built over the QM molecule, so this must run after the "
417 "region has been filled.");
419 std::string dftbasis_name =
dftoptions_.get(
"basisset").as<std::string>();
420 std::string grid_name =
421 dftoptions_.get(
"xtpdft.integration_grid").as<std::string>();
424 bs.
Load(dftbasis_name);
426 dftbasis.
Fill(bs, mol);
427 ewaldgrid_.GridSetup(grid_name, mol, dftbasis);
429 <<
TimeStamp() <<
" Constructed Grid for Integration of Ewald Potential"
441 const double Echange =
E_hist_.getDiff();
442 const Eigen::MatrixXd diff_ao =
Dmat_hist_.getDiff();
443 const double n = double(diff_ao.cols());
449 const bool orthonormal =
overlap_sqrt_.rows() == diff_ao.rows();
450 const Eigen::MatrixXd diff =
453 const double Dchange = diff.norm() / n;
454 const double Dmax = diff.cwiseAbs().maxCoeff();
455 std::string info =
"not converged";
456 bool converged =
false;
461 std::ostringstream measure;
463 measure <<
" (orthonormal basis; AO basis: RMS " << diff_ao.norm() / n
464 <<
" Max " << diff_ao.cwiseAbs().maxCoeff() <<
")";
466 measure <<
" (AO basis)";
469 <<
" Region:" << this->
identify() <<
" " << this->
getId() <<
" is "
470 << info <<
" deltaE=" << Echange <<
" RMS Dmat=" << Dchange
471 <<
" MaxDmat=" << Dmax << measure.str() << std::flush;
479 std::accumulate(interact_energies.begin(), interact_energies.end(), 0.0);
482 <<
" Calculated interaction potentials with other regions. E[hrt]= "
483 << e_ext << std::flush;
504 throw std::runtime_error(
"QMEwald can only run with XTP as qmpackage.");
512 const bool warm_start =
E_hist_.filled() &&
orb_.hasMOs();
544 if (!Logfile_parse) {
546 errormsg_ =
"Parsing DFT logfile failed.";
550 if (!Orbfile_parse) {
552 errormsg_ =
"Parsing DFT orbfile failed.";
556 if (
orb_.isOpenShell()) {
557 throw std::runtime_error(
558 "MO localization not implemented for open-shell systems");
565 if (
orb_.isOpenShell()) {
566 throw std::runtime_error(
567 "DFT-in-DFT not implemented for open-shell systems");
570 std::unique_ptr<QMPackage> xtpdft =
572 xtpdft->setLog(&
log_);
581 xtpdft->setCharge(
charge);
583 std::vector<double> energies = std::vector<double>(regions.size(), 0.0);
584 for (std::unique_ptr<Region>& reg : regions) {
585 Index id = reg->getId();
586 if (
id == this->
getId()) {
593 <<
TimeStamp() <<
" Evaluating interaction between "
595 << reg->identify() <<
" " << reg->getId() << std::flush;
596 if (reg->identify() == Staticdummy.
identify()) {
598 xtpdft->AddRegion(*staticregion);
599 }
else if (reg->identify() == Polardummy.
identify()) {
601 xtpdft->AddRegion(*polarregion);
603 throw std::runtime_error(
604 "Interaction of regions with types:" + this->
identify() +
" and " +
605 reg->identify() +
" not implemented");
609 e_ext = std::accumulate(interact_energies.begin(), interact_energies.end(),
613 <<
" Calculated interaction potentials with other regions. E[hrt]= "
614 << e_ext << std::flush;
616 xtpdft->WriteInputFile(
orb_);
617 bool active_run = xtpdft->RunActiveRegion();
619 throw std::runtime_error(
"\n DFT in DFT embedding failed. Stopping!");
621 Logfile_parse = xtpdft->ParseLogFile(
orb_);
622 if (!Logfile_parse) {
623 throw std::runtime_error(
"\n Parsing DFT logfile failed. Stopping!");
628 double energy =
orb_.getDFTTotalEnergy();
634 energy +=
orb_.getExcitedStateEnergy(state);
637 energy -=
orb_.getExcitedStateEnergy(state);
654 if (
orb_.getCalculationType() ==
"Truncated") {
655 orb_.MOs().eigenvectors() =
orb_.getTruncMOsFullBasis();
656 orb_.QMAtoms().clearAtoms();
657 orb_.QMAtoms() = originalmol;
658 orb_.SetupDftBasis(
orb_.getDftBasis().Name());
659 orb_.SetupAuxBasis(
orb_.getAuxBasis().Name());
674 orb_.getDFTbasisName().empty()) {
680 }
catch (
const std::runtime_error&) {
686 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(overlap.
Matrix());
688 es.eigenvalues().cwiseMax(0.0).cwiseSqrt().asDiagonal() *
689 es.eigenvectors().transpose();
693 if (
orb_.QMAtoms().size() == 0) {
694 orb_.QMAtoms() = mol;
696 orb_.QMAtoms().AddContainer(mol);
706 nuccharge += a.getNuccharge();
709 Index electrons =
orb_.getNumberOfAlphaElectrons();
710 if (
orb_.isOpenShell()) {
711 electrons +=
orb_.getNumberOfBetaElectrons();
716 charge = double(nuccharge - electrons);
743 std::string dft_package_name =
dftoptions_.get(
"name").as<std::string>();
758 throw std::runtime_error(
759 "QMRegion-QMRegion interaction is not implemented yet.");
769 throw std::runtime_error(
770 "polar region " + std::to_string(region.
getId()) +
771 " has charge-response segments, which only the xtp DFT package "
772 "supports (dftpackage name " +
773 qmpackage_->getPackageName() +
"). Use xtp or Thole segments.");
790#pragma omp parallel for
791 for (
Index i = 0; i <
Index(segments.size()); ++i) {
792 if (segments[i].hasChargeResponse()) {
800 std::vector<PolarSegment>& segments)
const {
814 Eigen::MatrixXd dmat =
orb_.DensityMatrixFull(state);
818 double N_comp = dmat.cwiseProduct(overlap.
Matrix()).sum();
819 if (std::abs(Ngrid - N_comp) > 0.001) {
822 <<
"WARNING: Calculated Densities at Numerical Grid, Number of "
824 << Ngrid <<
" is far away from the the real value " << N_comp
825 <<
", you should increase the accuracy of the integration grid."
828#pragma omp parallel for
829 for (
Index i = 0; i <
Index(segments.size()); ++i) {
843 std::vector<PolarSite*> cr_sites;
845 if (seg.hasChargeResponse()) {
847 cr_sites.push_back(&site);
851 if (!cr_sites.empty()) {
852 std::vector<Eigen::Vector3d> points;
853 std::vector<double> widths;
855 points.push_back(site->getPos());
856 widths.push_back(site->CRWidth());
859 const Eigen::MatrixXd electrons =
861 for (
Index k = 0; k <
Index(cr_sites.size()); ++k) {
862 PolarSite& site = *cr_sites[std::size_t(k)];
863 site.
Phi_noE() -= electrons(k, 0);
864 site.
V_noE() -= electrons.block<1, 3>(k, 1).transpose();
866 const Eigen::Vector3d r = site.
getPos() - atom.getPos();
868 const double Z = double(atom.getNuccharge());
870 site.
V_noE() -= Z * kern.
B1 * r;
891 orb_.WriteBasisSetsToCpt(v);
909 r(state,
"initial_state");
913 orb_.ReadFromCpt(rr);
914 orb_.ReadBasisSetsFromCpt(rr);
959 points.push_back(atom.getPos());
961 const Eigen::VectorXd phi_all = region.
PotentialAt(points);
962 const Eigen::VectorXd phi = phi_all.head(n_grid);
963 const Eigen::VectorXd phi_nuclei = phi_all.tail(phi_all.size() - n_grid);
974 values.resize(std::size_t(n_box));
975 for (
Index p = 0; p < n_box; ++p) {
976 values[std::size_t(p)] = phi[offset + p];
980 if (offset != phi.size()) {
981 throw std::runtime_error(
982 "QMRegion::InteractwithEwaldRegion: the grid holds " +
983 std::to_string(offset) +
" points across its boxes but " +
984 std::to_string(phi.size()) +
985 " potential values were evaluated. getGridpoints() and the box "
986 "walk above disagree about the point ordering.");
void WriteContainer(T &container)
Container to hold Basisfunctions for all atoms.
Index AOBasisSize() const
void Fill(const BasisSet &bs, const QMMolecule &atoms)
static Eigen::MatrixXd PotentialAndGradientAt(const AOBasis &basis, const Eigen::MatrixXd &D, const std::vector< Eigen::Vector3d > &points, const std::vector< double > &widths)
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
void push_back(const T &atom)
void Load(const std::string &name)
CheckpointReader openChild(const std::string &childName) const
CheckpointWriter openChild(const std::string &childName) const
bool hasChargeResponse() const
Eigen::Vector3d IntegrateField(const Eigen::Vector3d &rvector) const
double IntegrateDensity(const Eigen::MatrixXd &density_matrix)
static Eigen::MatrixXd Metric(const AOBasis &auxbasis)
T for an auxiliary basis on its own: the metric TCMatrix_gwbse::Fill folds into the three-centre inte...
static ScreeningCheck Check(const AOBasis &auxbasis, const QMMolecule &atoms, const ScreeningEnvironment &env, const Eigen::MatrixXd &T, Index n_modes=3)
Builds R for env and reports whether 1 + R is positive definite, and why not.
The periodic Ewald background, as a Region.
Eigen::VectorXd PotentialAt(const std::vector< Eigen::Vector3d > &points) const
Electronic excitations from GW-BSE.
void setScreeningEnvironment(const ScreeningEnvironment &env)
void Initialize(tools::Property &options)
void setLogger(Logger *pLog)
const std::vector< Eigen::Vector3d > & getGridPoints() const
std::vector< double > & getPotentialValues()
std::vector< T >::iterator begin()
std::vector< T >::iterator end()
void computePML(Orbitals &orbitals)
bool hasChargeResponse() const
std::string identify() const override
Class to represent Atom/Site in electrostatic+polarization.
const Eigen::Vector3d & V_noE() const
void AppendResult(tools::Property &prop) const override
StateTracker statetracker_
hist< Eigen::MatrixXd > Dmat_hist_
void PrepareEwaldPotentialGrid()
std::string grid_accuracy_for_ext_interaction_
std::string identify() const override
void AddNucleiFields(std::vector< PolarSegment > &segments, const StaticSegment &seg) const
std::shared_ptr< DFTSetupCache > setup_cache_
std::unique_ptr< QMPackage > qmpackage_
bool screening_include_kreac_
void push_back(const QMMolecule &mol)
void WriteToCpt(CheckpointWriter &w) const override
double InteractwithStaticRegion(const StaticRegion ®ion) override
void EvaluateScreenedGWBSE(std::vector< std::unique_ptr< Region > > ®ions)
void AppendScreenedSiteEnergies(tools::Property &prop) const
std::string ScreeningAuxBasisName() const
double ewald_nuclear_energy_
double InteractwithPolarRegion(const PolarRegion ®ion) override
QMRegion(Index id, Logger &log, std::string workdir)
double StateEnergy(const QMState &state) const
void WritePDB(csg::PDBWriter &writer) const override
double InteractwithEwaldRegion(const EwaldRegion ®ion) override
void ReadFromCpt(CheckpointReader &r) override
ScreeningEnvironment BuildScreeningEnvironment(const std::vector< std::unique_ptr< Region > > ®ions) const
bool Converged() const override
void Evaluate(std::vector< std::unique_ptr< Region > > ®ions) override
tools::Property localize_options_
std::vector< Eigen::Vector3d > copyEwaldGrid()
void PrepareOrbitalsForGWBSE()
double InteractwithQMRegion(const QMRegion ®ion) override
void ApplyQMFieldToPolarSegments(std::vector< PolarSegment > &segments) const
bool ewald_potential_evaluated_
Eigen::MatrixXd overlap_sqrt_
double screening_shell_dielectric_
void Initialize(const tools::Property &prop) override
std::vector< Index > screening_shell_regions_
double charge() const override
double screening_site_width_
void CheckScreeningEnvironment(const std::vector< std::unique_ptr< Region > > ®ions) const
tools::Property dftoptions_
tools::Property gwbseoptions_
bool isSingleParticleState() const
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
std::string ToString() const
const QMStateType & Type() const
std::vector< double > ApplyInfluenceOfOtherRegions(std::vector< std::unique_ptr< Region > > ®ions)
std::string identify() const override
Class to represent Atom/Site in electrostatic.
const Eigen::Vector3d & getPos() const
Timestamp returns the current time as a string Example: cout << TimeStamp().
void GridSetup(const std::string &type, const QMMolecule &atoms, const AOBasis &basis)
Mediates interaction between polar and static sites.
#define XTP_LOG(level, log)
void Cite(const std::string &key)
marks a reference as used (thread safe; unknown keys are printed as such)
Charge transport classes.
ClassicalSegment< PolarSite > PolarSegment
ClassicalSegment< StaticSite > StaticSegment
Provides a means for comparing floating point numbers.
Interaction of two normalized Gaussian charge distributions exp(-r^2/s_i^2) and exp(-r^2/s_j^2): erf(...
Outcome of EnvironmentScreening::Check: whether 1 + R is positive definite, and a human-readable acco...
The polarizable environment a GW-BSE calculation is screened by: what a QM/MM job hands to GWBSE (GWB...
std::vector< PolarSegment > explicit_segments
std::vector< PolarSegment > shell_segments