votca 2026-dev
Loading...
Searching...
No Matches
embeddingengine.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2023 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19/*
20 *References- (1) A Simple, Exact Density-Functional-Theory
21 *Embedding Scheme. Frederick R. Manby, Martina Stella, Jason D. Goodpaster, and
22 *Thomas F. Miller Journal of Chemical Theory and Computation 2012 8 (8),
23 *2564-2568 DOI: 10.1021/ct300544e
24 *(2) Taylor A. Barnes, Jason D. Goodpaster, Frederick R. Manby, and Thomas F.
25 *Miller III , "Accurate basis set truncation for wavefunction embedding", J.
26 *Chem. Phys. 139, 024103 (2013) https://doi.org/10.1063/1.4811112
27 *(3) Simon J. Bennie, Martina Stella, Thomas F. Miller III, and Frederick R.
28 *Manby , "Accelerating wavefunction in density-functional-theory embedding by
29 *truncating the active basis set", J. Chem. Phys. 143, 024105 (2015)
30 *https://doi.org/10.1063/1.4923367
31 *(3) Projection-Based Wavefunction-in-DFT Embedding
32 *Sebastian J. R. Lee, Matthew Welborn, Frederick R. Manby, and Thomas F. Miller
33 *III Accounts of Chemical Research 2019 52 (5), 1359-1368
34 *DOI: 10.1021/acs.accounts.8b00672
35 */
36
37// Third party includes
38#include <algorithm>
39#include <boost/format.hpp>
40
41// VOTCA includes
44
45// Local VOTCA includes
49#include "votca/xtp/aomatrix.h"
52#include "votca/xtp/dftengine.h"
54#include "votca/xtp/logger.h"
55#include "votca/xtp/mmregion.h"
56#include "votca/xtp/orbitals.h"
58
59namespace votca {
60namespace xtp {
61
63
64 // reading in the orbitals of the full DFT calculation
65 tools::EigenSystem embeddingMOs = orb.MOs();
66
67 // constructing the full electron density matrix
68 const Eigen::MatrixXd FullDensityMatrix = orb.DensityMatrixGroundState();
69 XTP_LOG(Log::error, *pLog_) << std::flush;
71 << TimeStamp() << " Starting Projection based dft-in-dft embedding"
72 << std::flush;
73
74 // reading the localized orbitals and update the occupied orbitals in MOs
76 << TimeStamp() << " Passing localized orbitals as the initial guess"
77 << std::flush;
78 Eigen::MatrixXd LMOs = orb.getLMOs();
79
80 embeddingMOs.eigenvectors().leftCols(orb.getNumberOfAlphaElectrons()) = LMOs;
81
82 // determine the active and inactive electron densities
83 std::vector<Index> activeatoms =
85
87 << "Indices of active atoms selected are: " << active_atoms_as_string_
88 << std::flush;
89 ActiveDensityMatrix DMAT_A(orb, activeatoms, active_threshold_);
90 const Eigen::MatrixXd InitialActiveDensityMatrix = DMAT_A.compute_Dmat_A()[0];
91 Eigen::MatrixXd InitialInactiveMOs = DMAT_A.compute_Dmat_A()[2];
92
94 << TimeStamp() << " Active density formation done" << std::flush;
95
96 Eigen::MatrixXd InactiveDensityMatrix =
97 FullDensityMatrix - InitialActiveDensityMatrix;
98
99 orb.setInactiveDensity(InactiveDensityMatrix);
100
101 // AOoverlap to calculate number of active/inactive electrons
102 AOBasis aobasis = orb.getDftBasis();
103 AOOverlap overlap;
104 overlap.Fill(aobasis);
105
106 Index all_electrons = static_cast<Index>(
107 std::round(FullDensityMatrix.cwiseProduct(overlap.Matrix()).sum()));
108 active_electrons_ = static_cast<Index>(std::round(
109 InitialActiveDensityMatrix.cwiseProduct(overlap.Matrix()).sum()));
110 Index inactive_electrons = static_cast<Index>(
111 std::round(InactiveDensityMatrix.cwiseProduct(overlap.Matrix()).sum()));
112
114 << TimeStamp() << " Total electrons: " << all_electrons << std::flush;
116 << TimeStamp() << " Active electrons: " << active_electrons_
117 << std::flush;
119 << TimeStamp() << " Inactive electrons: " << inactive_electrons
120 << std::flush;
121
122 // check for consistency
123 if ((active_electrons_ + inactive_electrons) != all_electrons) {
124 throw std::runtime_error(
125 " Sum of active and inactive electrons does "
126 "not match full number of electrons!");
127 return false;
128 }
129
130 // setup the DFT engine with the active electrons only
132
133 // setup one-electron part of the Hamiltonian
134 Mat_p_Energy H0 = SetupH0(orb.QMAtoms());
135
136 // energy of the one-electron part of the Hamiltonian
137 const double E0_full = FullDensityMatrix.cwiseProduct(H0.matrix()).sum();
138 const double E0_initial_active =
139 InitialActiveDensityMatrix.cwiseProduct(H0.matrix()).sum();
140 E_nuc_ = H0.energy();
141
142 // setup the Vxc integrator
143 Vxc_Potential<Vxc_Grid> vxcpotential = SetupVxc(orb.QMAtoms());
144 // get the constant XC contributions for the embedding
145 const Mat_p_Energy xc_full = vxcpotential.IntegrateVXC(FullDensityMatrix);
146 const Mat_p_Energy xc_initial_active =
147 vxcpotential.IntegrateVXC(InitialActiveDensityMatrix);
148
149 // get the constant Coulomb matrices for the embedding
150 Eigen::MatrixXd J_full =
151 Eigen::MatrixXd::Zero(FullDensityMatrix.rows(), FullDensityMatrix.cols());
152 Eigen::MatrixXd J_initial_active =
153 Eigen::MatrixXd::Zero(FullDensityMatrix.rows(), FullDensityMatrix.cols());
154 Eigen::MatrixXd K_full =
155 Eigen::MatrixXd::Zero(FullDensityMatrix.rows(), FullDensityMatrix.cols());
156 Eigen::MatrixXd K_initial_active =
157 Eigen::MatrixXd::Zero(FullDensityMatrix.rows(), FullDensityMatrix.cols());
158 if (ScaHFX_ > 0) {
159 std::array<Eigen::MatrixXd, 2> JandK_full =
160 CalcERIs_EXX(embeddingMOs.eigenvectors(), FullDensityMatrix, 1e-12);
161 std::array<Eigen::MatrixXd, 2> JandK_initial_active = CalcERIs_EXX(
162 embeddingMOs.eigenvectors(), InitialActiveDensityMatrix, 1e-12);
163 J_full = JandK_full[0];
164 K_full = 0.5 * ScaHFX_ * JandK_full[1];
165 J_initial_active = JandK_initial_active[0];
166 K_initial_active = 0.5 * ScaHFX_ * JandK_initial_active[1];
167 } else {
168 J_full = CalcERIs(FullDensityMatrix, 1e-12);
169 J_initial_active = CalcERIs(InitialActiveDensityMatrix, 1e-12);
170 }
171 // get the Hartree energies
172 const double E_Hartree_full =
173 0.5 * FullDensityMatrix.cwiseProduct(J_full + K_full).sum();
174 const double E_Hartree_initial_active =
175 0.5 * InitialActiveDensityMatrix
176 .cwiseProduct(J_initial_active + K_initial_active)
177 .sum();
178
179 // Energy of the full reference system
180 Total_E_full_ = E0_full + E_Hartree_full + xc_full.energy() + E_nuc_;
181
183 << TimeStamp()
184 << " Reference total energy full electron system: " << Total_E_full_
185 << " Ha" << std::flush;
186
187 // projection parameter, to be made an option
188 Eigen::MatrixXd ProjectionOperator =
189 overlap.Matrix() * InactiveDensityMatrix * overlap.Matrix();
190
191 // XC and Hartree contribution to the external embedding potential/energy
192 const Eigen::MatrixXd v_embedding = J_full + K_full + xc_full.matrix() -
193 J_initial_active - K_initial_active -
194 xc_initial_active.matrix();
195
196 const double constant_embedding_energy = Total_E_full_ - E0_initial_active -
197 E_Hartree_initial_active -
198 xc_initial_active.energy();
199
201 << TimeStamp() << " Constant energy embedding terms: " << std::flush;
203 << TimeStamp() << " 1) E_DFT[full]: " << Total_E_full_ << std::flush;
205 << TimeStamp() << " 2) E_0[initial active]: " << E0_initial_active
206 << std::flush;
208 << TimeStamp() << " 3) E_H[initial active]: " << E_Hartree_initial_active
209 << std::flush;
210 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " 4) E_XC[initial active]: "
211 << xc_initial_active.energy() << std::flush;
213 << TimeStamp() << " Total 1 -2 -3 -4: " << constant_embedding_energy
214 << std::flush;
215
216 if (truncate_) { // Truncation starts here
217 orb.setCalculationType("Truncated");
218 TruncateBasis(orb, activeatoms, H0, InitialActiveDensityMatrix, v_embedding,
219 InitialInactiveMOs);
220 active_and_border_atoms_ = activeatoms;
221 }
222 // SCF loop if you don't truncate active region
223 else {
224 orb.setCalculationType("Embedded_noTrunc");
225 Eigen::MatrixXd ActiveDensityMatrix = InitialActiveDensityMatrix;
226 conv_accelerator_.StartNewSCF();
227 for (Index this_iter = 0; this_iter < max_iter_; this_iter++) {
228 XTP_LOG(Log::error, *pLog_) << std::flush;
230 << TimeStamp() << " Iteration " << this_iter + 1 << " of "
231 << max_iter_ << std::flush;
232
233 // get the XC contribution for the updated active density matrix
234 Mat_p_Energy xc_active = vxcpotential.IntegrateVXC(ActiveDensityMatrix);
235 double E_xc_active = xc_active.energy();
236
237 // get the Hartree contribution for the updated active density matrix
238 Eigen::MatrixXd J_active = Eigen::MatrixXd::Zero(
240 Eigen::MatrixXd K_active = Eigen::MatrixXd::Zero(
242 if (ScaHFX_ > 0) {
243 std::array<Eigen::MatrixXd, 2> JandK_active = CalcERIs_EXX(
244 embeddingMOs.eigenvectors(), ActiveDensityMatrix, 1e-12);
245 J_active = JandK_active[0];
246 K_active = 0.5 * ScaHFX_ * JandK_active[1];
247
248 } else {
249 J_active = CalcERIs(ActiveDensityMatrix, 1e-12);
250 }
251 double E_Hartree_active =
252 0.5 * ActiveDensityMatrix.cwiseProduct(J_active + K_active).sum();
253
254 // update the active Hamiltonian
255 const Eigen::MatrixXd Level_Shift_Operator =
256 levelshift_ * ProjectionOperator;
257 Eigen::MatrixXd H_active = H0.matrix() + v_embedding +
258 Level_Shift_Operator + xc_active.matrix() +
259 J_active + K_active;
260
261 const double E0_active =
262 ActiveDensityMatrix.cwiseProduct(H0.matrix()).sum();
263 const double E_embedding =
264 (ActiveDensityMatrix - InitialActiveDensityMatrix)
265 .cwiseProduct(v_embedding)
266 .sum();
267 const double E_levelshift =
268 ActiveDensityMatrix.cwiseProduct(Level_Shift_Operator).sum();
269
270 double TotalEnergy = E0_active + E_Hartree_active + E_xc_active +
271 constant_embedding_energy + E_levelshift +
272 E_embedding;
273
275 << TimeStamp() << " Total Energy after embedding: " << TotalEnergy
276 << std::flush;
278 << TimeStamp() << " I) E_0[active]: " << E0_active << std::flush;
280 << TimeStamp() << " II) E_H[active]: " << E_Hartree_active
281 << std::flush;
283 << TimeStamp() << " III) E_xc[active]: " << E_xc_active << std::flush;
285 << TimeStamp() << " IV) E_ext_emb :" << constant_embedding_energy
286 << std::flush;
288 << TimeStamp() << " V) E_corr_emb :" << E_embedding << std::flush;
290 << TimeStamp() << " VI) E_levelshift :" << E_levelshift
291 << std::flush;
293 << TimeStamp() << " Total I + II + III + IV + V +VI :" << TotalEnergy
294 << std::flush;
295
296 // get a new density matrix in the active region
298 << std::endl
299 << "Active electrons in = "
300 << ActiveDensityMatrix.cwiseProduct(overlap.Matrix()).sum()
301 << std::flush;
303 ActiveDensityMatrix, H_active, embeddingMOs, TotalEnergy);
305 << std::endl
306 << "Active electrons out= "
307 << ActiveDensityMatrix.cwiseProduct(overlap.Matrix()).sum()
308 << std::flush;
309
310 PrintMOs(embeddingMOs.eigenvalues(), Log::info);
311
312 if (conv_accelerator_.isConverged()) {
314 << TimeStamp() << " Total embedding energy has converged to "
315 << std::setprecision(9) << conv_accelerator_.getDeltaE()
316 << "[Ha] after " << this_iter + 1
317 << " iterations. DIIS error is converged up to "
318 << conv_accelerator_.getDIIsError() << std::flush;
320 << TimeStamp() << " Final Single Point Energy of embedding "
321 << std::setprecision(12) << TotalEnergy << " Ha" << std::flush;
323 << "The difference between the reference energy and the embedding "
324 "energy is "
325 << Total_E_full_ - TotalEnergy << " Ha" << std::flush;
326
327 if (abs(Total_E_full_ - TotalEnergy) > 1e-3) {
329 << "Warning!! The difference is greater than 1e-03 Ha"
330 << std::flush;
331 }
332
333 PrintMOs(embeddingMOs.eigenvalues(), Log::error);
334 orb.setEmbeddedMOs(embeddingMOs);
336 CalcElDipole(orb);
337 break;
338 } else if (this_iter == max_iter_ - 1) {
340 << TimeStamp()
341 << " DFT calculation for active region has not converged after "
342 << max_iter_
343 << " iterations. Use more iterations or another convergence "
344 "acceleration scheme."
345 << std::flush;
346 return false;
347 }
348 }
349 }
350 return true;
351}
352
354 if (truncate_) {
355 activemol_.ReorderAtomIDs();
356 trunc_orb.QMAtoms() = activemol_;
357 Prepare(trunc_orb, active_electrons_);
358 Vxc_Potential<Vxc_Grid> vxcpotential = SetupVxc(trunc_orb.QMAtoms());
359 ConfigOrbfile(trunc_orb);
360 AOBasis aobasis = trunc_orb.getDftBasis();
361 AOOverlap overlap;
362 overlap.Fill(aobasis);
363
364 const double E0_initial_truncated =
365 InitialActiveDmat_trunc_.cwiseProduct(H0_trunc_).sum();
366 const Mat_p_Energy xc_initial_truncated =
368 Eigen::MatrixXd J_initial_truncated = Eigen::MatrixXd::Zero(
370 Eigen::MatrixXd K_initial_truncated = Eigen::MatrixXd::Zero(
372 tools::EigenSystem MOs_trunc;
373 MOs_trunc.eigenvalues() = Eigen::VectorXd::Zero(H0_trunc_.cols());
374 MOs_trunc.eigenvectors() =
375 Eigen::MatrixXd::Zero(H0_trunc_.rows(), H0_trunc_.cols());
376 if (ScaHFX_ > 0) {
377 std::array<Eigen::MatrixXd, 2> JandK_initial_truncated = CalcERIs_EXX(
378 MOs_trunc.eigenvectors(), InitialActiveDmat_trunc_, 1e-12);
379 J_initial_truncated = JandK_initial_truncated[0];
380 K_initial_truncated = 0.5 * ScaHFX_ * JandK_initial_truncated[1];
381 } else {
382 J_initial_truncated = CalcERIs(InitialActiveDmat_trunc_, 1e-12);
383 }
384 // get the Hartree energies
385 const double E_Hartree_initial_truncated =
387 .cwiseProduct(J_initial_truncated + K_initial_truncated)
388 .sum();
389 const double Initial_truncated_energy = E0_initial_truncated +
390 E_Hartree_initial_truncated +
391 xc_initial_truncated.energy();
393 << "Initial truncated energy = " << Initial_truncated_energy
394 << std::flush;
395
396 Eigen::MatrixXd PurifiedActiveDmat_trunc =
398 Eigen::MatrixXd TruncatedDensityMatrix = PurifiedActiveDmat_trunc;
399
400 conv_accelerator_.StartNewSCF();
401 for (Index this_iter = 0; this_iter < max_iter_; this_iter++) {
402 XTP_LOG(Log::error, *pLog_) << std::flush;
404 << TimeStamp() << " Iteration " << this_iter + 1 << " of "
405 << max_iter_ << std::flush;
406
407 // get the XC contribution for the updated truncated density matrix
408 Mat_p_Energy xc_truncated =
409 vxcpotential.IntegrateVXC(TruncatedDensityMatrix);
410 double E_xc_truncated = xc_truncated.energy();
411
412 // get the Hartree contribution for the updated truncated density matrix
413 Eigen::MatrixXd J_truncated = Eigen::MatrixXd::Zero(
414 TruncatedDensityMatrix.rows(), TruncatedDensityMatrix.cols());
415 Eigen::MatrixXd K_truncated = Eigen::MatrixXd::Zero(
416 TruncatedDensityMatrix.rows(), TruncatedDensityMatrix.cols());
417 if (ScaHFX_ > 0) {
418 std::array<Eigen::MatrixXd, 2> JandK_truncated = CalcERIs_EXX(
419 MOs_trunc.eigenvectors(), TruncatedDensityMatrix, 1e-12);
420 J_truncated = JandK_truncated[0];
421 K_truncated = 0.5 * ScaHFX_ * JandK_truncated[1];
422
423 } else {
424 J_truncated = CalcERIs(TruncatedDensityMatrix, 1e-12);
425 }
426 double E_Hartree_truncated =
427 0.5 *
428 TruncatedDensityMatrix.cwiseProduct(J_truncated + K_truncated).sum();
429 // update the truncated Hamiltonian
430 Eigen::MatrixXd H_truncated =
431 H0_trunc_ + xc_truncated.matrix() + J_truncated + K_truncated;
432
433 double TruncatedTotalEnergy =
434 TruncatedDensityMatrix.cwiseProduct(H0_trunc_).sum() +
435 E_Hartree_truncated + E_xc_truncated + Total_E_full_ -
436 Initial_truncated_energy +
437 ((TruncatedDensityMatrix - InitialActiveDmat_trunc_)
438 .cwiseProduct(v_embedding_trunc_)
439 .sum());
440 // get the new truncated density matrix
442 << "Electrons in = "
443 << TruncatedDensityMatrix.cwiseProduct(overlap.Matrix()).sum()
444 << std::flush;
445 TruncatedDensityMatrix = conv_accelerator_.Iterate(
446 TruncatedDensityMatrix, H_truncated, MOs_trunc, TruncatedTotalEnergy);
448 << "Electrons out = "
449 << TruncatedDensityMatrix.cwiseProduct(overlap.Matrix()).sum()
450 << std::flush;
451
453 << TimeStamp() << " E_trunc (Ha) = "
454 << TruncatedDensityMatrix.cwiseProduct(H0_trunc_).sum() +
455 E_Hartree_truncated + E_xc_truncated
456 << "\n \t \t \t" << " E_fullDFT (Ha) = " << Total_E_full_
457 << "\n \t \t \t"
458 << " E_initial_trunc(Ha) = " << Initial_truncated_energy
459 << "\n \t \t \t" << " E_embedding_correction(Ha) = "
460 << ((TruncatedDensityMatrix - InitialActiveDmat_trunc_)
461 .cwiseProduct(v_embedding_trunc_)
462 .sum())
463 << std::flush;
464
466 << " Truncated Energy of the system: E_trunc + E_fullDFT - "
467 << "\n \t \t" << " E_initial_trunc + E_embedding_correction = "
468 << TruncatedTotalEnergy << std::flush;
469
470 PrintMOs(MOs_trunc.eigenvalues(), Log::info);
471
472 if (conv_accelerator_.isConverged()) {
474 << TimeStamp() << " Total embedding energy has converged to "
475 << std::setprecision(9) << conv_accelerator_.getDeltaE()
476 << "[Ha] after " << this_iter + 1
477 << " iterations. DIIS error is converged up to "
478 << conv_accelerator_.getDIIsError() << std::flush;
480 << TimeStamp() << " Final Single Point Energy of embedding "
481 << std::setprecision(12) << TruncatedTotalEnergy << " Ha"
482 << std::flush;
483
484 PrintMOs(MOs_trunc.eigenvalues(), Log::error);
485 trunc_orb.setEmbeddedMOs(MOs_trunc);
487 break;
488 }
489 }
491 }
492 return true;
493}
494
495void DFTEngine::TruncateBasis(Orbitals& orb, std::vector<Index>& activeatoms,
496 Mat_p_Energy& H0,
497 Eigen::MatrixXd InitialActiveDensityMatrix,
498 Eigen::MatrixXd v_embedding,
499 Eigen::MatrixXd InitialInactiveMOs) {
500 AOBasis aobasis;
501 aobasis = orb.getDftBasis();
502 AOOverlap overlap;
503 overlap.Fill(aobasis);
504 // Mulliken population per basis function on every active atom
505 Eigen::VectorXd DiagonalofDmatA = InitialActiveDensityMatrix.diagonal();
506 Eigen::VectorXd DiagonalofOverlap = overlap.Matrix().diagonal();
507 Eigen::VectorXd MnP = DiagonalofDmatA.cwiseProduct(DiagonalofOverlap);
508
509 // Get a vector containing the number of basis functions per atom
510 const std::vector<Index>& numfuncpatom = aobasis.getFuncPerAtom();
511 numfuncpatom_ = numfuncpatom;
512 Index numofactivebasisfunction = 0;
513 // Store start indices. Will be used later
514 std::vector<Index> start_indices, start_indices_activemolecule;
515 Index start_idx = 0, start_idx_activemolecule = 0;
516 std::vector<Index> borderatoms;
517 for (Index atom_num = 0; atom_num < orb.QMAtoms().size(); atom_num++) {
518 start_indices.push_back(start_idx);
519 /* Condition for basis fn on an atom to be counted: either in active
520 region or MnP of any function > threshold (border atoms) */
521 bool partOfActive =
522 (std::find(activeatoms.begin(), activeatoms.end(),
523 orb.QMAtoms()[atom_num].getId()) != activeatoms.end());
524 if (partOfActive == true) {
525 activemol_.push_back(orb.QMAtoms()[atom_num]);
526 /* if active append the atom to the new molecule. Also increment active
527 basis function size and start indices */
528 start_indices_activemolecule.push_back(start_idx_activemolecule);
529 start_idx_activemolecule += numfuncpatom[atom_num];
530 numofactivebasisfunction += numfuncpatom[atom_num];
531 } else {
532 std::vector<const AOShell*> inactiveshell =
533 aobasis.getShellsofAtom(orb.QMAtoms()[atom_num].getId());
534 bool loop_break = false;
535 for (const AOShell* shell : inactiveshell) {
536 for (Index shell_fn_no = shell->getStartIndex();
537 shell_fn_no < shell->getStartIndex() + shell->getNumFunc();
538 shell_fn_no++) {
539 if (MnP[shell_fn_no] > truncation_threshold_) {
540 activeatoms.push_back(atom_num);
541 borderatoms.push_back(atom_num); // push this index to border
542 // atoms
543 activemol_.push_back(orb.QMAtoms()[atom_num]);
544 loop_break = true; // if any function in the whole atom satisfies
545 // we break loop to check next atom
546 start_indices_activemolecule.push_back(start_idx_activemolecule);
547 start_idx_activemolecule += numfuncpatom[atom_num];
548 numofactivebasisfunction += numfuncpatom[atom_num];
549 break;
550 }
551 }
552 if (loop_break) break;
553 }
554 }
555 start_idx += numfuncpatom[atom_num];
556 }
557
558 // Sort atoms before you cut the Hamiltonian
559 sort(activeatoms.begin(), activeatoms.end());
560 sort(borderatoms.begin(), borderatoms.end());
561 Eigen::MatrixXd InactiveDensityMatrix = orb.getInactiveDensity();
562 XTP_LOG(Log::error, *pLog_) << std::flush;
564 << "Active + Border Molecule Size = " << activeatoms.size() << "\n \t \t "
565 << "Border Molecule Size = " << borderatoms.size() << std::flush;
566 Eigen::MatrixXd ProjectionOperator =
567 overlap.Matrix() * InactiveDensityMatrix * overlap.Matrix();
568
569 Eigen::MatrixXd H_embedding =
570 H0.matrix() + v_embedding + levelshift_ * ProjectionOperator;
571
572 if (borderatoms.size() != 0) {
573 Eigen::MatrixXd BorderMOs;
574 for (Index lmo_index = 0; lmo_index < InitialInactiveMOs.cols();
575 lmo_index++) {
576 double mullikenpop_lmo_borderatoms = 0;
577 for (Index borderatom : borderatoms) {
578 Index start = start_indices[borderatom];
579 Index size = numfuncpatom[borderatom];
580 mullikenpop_lmo_borderatoms +=
581 (InitialInactiveMOs.col(lmo_index) *
582 InitialInactiveMOs.col(lmo_index).transpose() * overlap.Matrix())
583 .diagonal()
584 .segment(start, size)
585 .sum();
586 }
587 /*If more than half of a MO contributes on a border atom include that in
588 * the Border MOs list. Not made an user option. If you want to fix
589 * further for density leak you can reduce this number*/
590 if (mullikenpop_lmo_borderatoms > 0.25) {
591 BorderMOs.conservativeResize(InitialInactiveMOs.rows(),
592 BorderMOs.cols() + 1);
593 BorderMOs.col(BorderMOs.cols() - 1) = InitialInactiveMOs.col(lmo_index);
594 }
595 }
596
597 Eigen::MatrixXd BorderDmat = 2 * BorderMOs * BorderMOs.transpose();
598 Eigen::MatrixXd BorderProjectionOperator =
599 overlap.Matrix() * BorderDmat * overlap.Matrix();
600 Eigen::MatrixXd DistantProjectionOperator =
601 ProjectionOperator - BorderProjectionOperator;
602 // 1e+2 is the suitable projection value for border MOs: see citations
603 H_embedding +=
604 1e+2 * BorderProjectionOperator +
605 levelshift_ * (DistantProjectionOperator - ProjectionOperator);
606 }
607
608 // from here it is time to truncate Hamiltonian H0
609 H0_trunc_ =
610 Eigen::MatrixXd::Zero(numofactivebasisfunction, numofactivebasisfunction);
612 Eigen::MatrixXd::Zero(numofactivebasisfunction, numofactivebasisfunction);
614 Eigen::MatrixXd::Zero(numofactivebasisfunction, numofactivebasisfunction);
615 // Defined needed matrices with basisset_size * basisset_size
616
617 for (Index activeatom1_idx = 0; activeatom1_idx < Index(activeatoms.size());
618 activeatom1_idx++) {
619 Index activeatom1 = activeatoms[activeatom1_idx];
620 for (Index activeatom2_idx = 0; activeatom2_idx < Index(activeatoms.size());
621 activeatom2_idx++) {
622 Index activeatom2 = activeatoms[activeatom2_idx];
623 // Fill the H0, Dmat and v_embedding at the right indices
624 H0_trunc_.block(start_indices_activemolecule[activeatom1_idx],
625 start_indices_activemolecule[activeatom2_idx],
626 numfuncpatom[activeatom1], numfuncpatom[activeatom2]) =
627 H_embedding.block(
628 start_indices[activeatom1], start_indices[activeatom2],
629 numfuncpatom[activeatom1], numfuncpatom[activeatom2]);
631 start_indices_activemolecule[activeatom1_idx],
632 start_indices_activemolecule[activeatom2_idx],
633 numfuncpatom[activeatom1], numfuncpatom[activeatom2]) =
634 InitialActiveDensityMatrix.block(
635 start_indices[activeatom1], start_indices[activeatom2],
636 numfuncpatom[activeatom1], numfuncpatom[activeatom2]);
637 v_embedding_trunc_.block(start_indices_activemolecule[activeatom1_idx],
638 start_indices_activemolecule[activeatom2_idx],
639 numfuncpatom[activeatom1],
640 numfuncpatom[activeatom2]) =
641 v_embedding.block(
642 start_indices[activeatom1], start_indices[activeatom2],
643 numfuncpatom[activeatom1], numfuncpatom[activeatom2]);
644 }
645 }
646}
647
648Eigen::MatrixXd DFTEngine::McWeenyPurification(Eigen::MatrixXd& Dmat_in,
649 AOOverlap& overlap) {
650 Eigen::MatrixXd Ssqrt = overlap.Sqrt();
651 Eigen::MatrixXd InvSsqrt = overlap.Pseudo_InvSqrt(1e-8);
652 Eigen::MatrixXd ModifiedDmat = 0.5 * Ssqrt * Dmat_in * Ssqrt;
653 for (Index iter = 0; iter < 100; iter++) {
654 Eigen::MatrixXd Dmat_new = (3 * ModifiedDmat * ModifiedDmat) -
655 (2 * ModifiedDmat * ModifiedDmat * ModifiedDmat);
656 double IdempotencyError =
657 ((Dmat_new * Dmat_new - Dmat_new) * (Dmat_new * Dmat_new - Dmat_new))
658 .trace();
660 << "Idempotency Error: " << IdempotencyError << std::flush;
661
662 ModifiedDmat = Dmat_new;
663 if (IdempotencyError < 1e-20) break;
664 }
665 Eigen::MatrixXd Dmat_out = 2 * InvSsqrt * ModifiedDmat * InvSsqrt;
666 return 0.5 * (Dmat_out + Dmat_out.transpose());
667}
668
669void DFTEngine::TruncMOsFullBasis(Orbitals& orb, std::vector<Index> activeatoms,
670 std::vector<Index> numfuncpatom) {
671 Eigen::MatrixXd expandtruncorb = orb.getEmbeddedMOs().eigenvectors();
672 Index start_index = 0;
673 Index numofactualactoms = numfuncpatom.size();
674 for (Index atomindex = 0; atomindex < numofactualactoms; atomindex++) {
675 bool partOfActive = (std::find(activeatoms.begin(), activeatoms.end(),
676 atomindex) != activeatoms.end());
677 if (partOfActive == false) {
678 expandtruncorb =
679 InsertZeroRows(expandtruncorb, start_index, numfuncpatom[atomindex]);
680 }
681 start_index += numfuncpatom[atomindex];
682 }
683 orb.setTruncMOsFullBasis(expandtruncorb);
684}
685
686Eigen::MatrixXd DFTEngine::InsertZeroRows(Eigen::MatrixXd MOsMatrix,
687 Index startidx, Index numofzerorows) {
688 Eigen::MatrixXd FinalMatrix =
689 Eigen::MatrixXd::Zero(MOsMatrix.rows() + numofzerorows, MOsMatrix.cols());
690 FinalMatrix.topRows(startidx) = MOsMatrix.topRows(startidx);
691 FinalMatrix.bottomRows(MOsMatrix.rows() - startidx) =
692 MOsMatrix.bottomRows(MOsMatrix.rows() - startidx);
693 return FinalMatrix;
694}
695} // namespace xtp
696} // namespace votca
const Eigen::VectorXd & eigenvalues() const
Definition eigensystem.h:30
const Eigen::MatrixXd & eigenvectors() const
Definition eigensystem.h:33
Container to hold Basisfunctions for all atoms.
Definition aobasis.h:42
const std::vector< const AOShell * > getShellsofAtom(Index AtomId) const
Definition aobasis.cc:63
const std::vector< Index > & getFuncPerAtom() const
Definition aobasis.h:72
void Fill(const AOBasis &aobasis) final
Eigen::MatrixXd Pseudo_InvSqrt(double etol)
Definition aomatrix.cc:29
const Eigen::MatrixXd & Matrix() const
Definition aomatrix.h:52
Eigen::MatrixXd Sqrt()
Definition aomatrix.cc:45
std::array< Eigen::MatrixXd, 3 > compute_Dmat_A()
Eigen::MatrixXd CalcERIs(const Eigen::MatrixXd &Dmat, double error) const
Build the Coulomb matrix contribution from the current density matrix.
Definition dftengine.cc:886
bool EvaluateTruncatedActiveRegion(Orbitals &trunc_orb)
Eigen::MatrixXd InitialActiveDmat_trunc_
Definition dftengine.h:657
void TruncMOsFullBasis(Orbitals &orb, std::vector< Index > activeatoms, std::vector< Index > numfuncpatom)
void TruncateBasis(Orbitals &orb, std::vector< Index > &activeatoms, Mat_p_Energy &H0, Eigen::MatrixXd InitialActiveDensityMatrix, Eigen::MatrixXd v_embedding, Eigen::MatrixXd InitialInactiveMOs)
bool EvaluateActiveRegion(Orbitals &orb)
void Prepare(Orbitals &orb, Index numofelectrons=-1)
Eigen::MatrixXd InsertZeroRows(Eigen::MatrixXd MOsMatrix, Index startidx, Index numofzerorows)
Insert zero rows into an MO coefficient matrix at the requested position.
std::array< Eigen::MatrixXd, 2 > CalcERIs_EXX(const Eigen::MatrixXd &MOCoeff, const Eigen::MatrixXd &Dmat, double error) const
Definition dftengine.cc:860
Eigen::MatrixXd H0_trunc_
Definition dftengine.h:656
void CalcElDipole(const Orbitals &orb) const
Evaluate and print the electronic dipole moment from the final density.
Definition dftengine.cc:395
std::string active_atoms_as_string_
Definition dftengine.h:651
void ConfigOrbfile(Orbitals &orb)
Propagate basis-set, XC, and metadata settings into the orbital container.
QMMolecule activemol_
Definition dftengine.h:645
void PrintMOs(const Eigen::VectorXd &MOEnergies, Log::Level level)
Print a one-spin list of orbital energies and occupations to the logger.
Definition dftengine.cc:315
std::vector< Index > numfuncpatom_
Definition dftengine.h:665
std::vector< Index > active_and_border_atoms_
Definition dftengine.h:664
Mat_p_Energy SetupH0(const QMMolecule &mol) const
Assemble the one-electron core Hamiltonian for the current molecule.
Eigen::MatrixXd v_embedding_trunc_
Definition dftengine.h:658
Eigen::MatrixXd McWeenyPurification(Eigen::MatrixXd &Dmat_in, AOOverlap &overlap)
Vxc_Potential< Vxc_Grid > SetupVxc(const QMMolecule &mol)
ConvergenceAcc conv_accelerator_
Definition dftengine.h:615
std::vector< Index > CreateIndexVector(const std::string &Ids) const
Eigen::MatrixXd & matrix()
Definition eigen.h:97
double & energy()
Definition eigen.h:98
Container for molecular orbitals and derived one-particle data.
Definition orbitals.h:47
void setCalculationType(std::string CalcType)
Store the calculation-type tag used by downstream workflows.
Definition orbitals.h:240
void setInactiveDensity(Eigen::MatrixXd inactivedensity)
Store the inactive-region density matrix used in embedding workflows.
Definition orbitals.h:687
void setNumofActiveElectrons(const Index active_electrons)
Store the number of electrons assigned to the active region.
Definition orbitals.h:680
void setTruncMOsFullBasis(const Eigen::MatrixXd &expandedMOs)
Store truncated active-region orbitals expanded back to the full AO basis.
Definition orbitals.h:64
Index getNumberOfAlphaElectrons() const
Return the stored number of alpha electrons.
Definition orbitals.h:159
const Eigen::MatrixXd & getLMOs() const
Return localized molecular orbitals, if available.
Definition orbitals.h:666
const Eigen::MatrixXd & getInactiveDensity() const
Return the inactive-region density matrix used in embedding workflows.
Definition orbitals.h:685
const tools::EigenSystem & MOs() const
Return read-only access to alpha/restricted molecular orbitals.
Definition orbitals.h:192
Eigen::MatrixXd DensityMatrixGroundState() const
Build the ground-state AO density matrix from the stored orbitals.
Definition orbitals.cc:220
const QMMolecule & QMAtoms() const
Return read-only access to the molecular geometry.
Definition orbitals.h:262
void setEmbeddedMOs(tools::EigenSystem &system)
Store molecular orbitals obtained from an embedding calculation.
Definition orbitals.h:58
const tools::EigenSystem & getEmbeddedMOs() const
Return molecular orbitals obtained from an embedding calculation.
Definition orbitals.h:61
const AOBasis & getDftBasis() const
Return the DFT AO basis, throwing if it has not been initialized.
Definition orbitals.h:328
Timestamp returns the current time as a string Example: cout << TimeStamp().
Definition logger.h:224
Mat_p_Energy IntegrateVXC(const Eigen::MatrixXd &density_matrix) const
#define XTP_LOG(level, log)
Definition logger.h:40
Charge transport classes.
Definition ERIs.h:28
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26