LIBINT 2.9.0
molden.h
1/*
2 * Copyright (C) 2004-2024 Edward F. Valeev
3 *
4 * This file is part of Libint library.
5 *
6 * Libint library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Libint library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Libint library. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#ifndef INCLUDE_LIBINT2_LCAO_MOLDEN_H_
22#define INCLUDE_LIBINT2_LCAO_MOLDEN_H_
23
24#include <libint2/atom.h>
25#include <libint2/basis.h>
26#include <libint2/cgshell_ordering.h>
27#include <libint2/chemistry/elements.h>
28#include <libint2/shell.h>
29#include <libint2/shgshell_ordering.h>
30
31#include <cmath>
32#include <iomanip>
33#include <ostream>
34#include <string>
35#include <vector>
36
37#pragma GCC diagnostic push
38#pragma GCC system_header
39#include <Eigen/Core>
40#pragma GCC diagnostic pop
41
42namespace libint2 {
43namespace molden {
44
47class Export {
48 public:
80 template <typename ShellSequence, typename Coeffs, typename Occs,
81 typename Energies = Eigen::VectorXd>
83 const std::vector<Atom>& atoms, const ShellSequence& basis,
84 const Coeffs& coefficients, const Occs& occupancies,
85 const Energies& energies = Energies(),
86 const std::vector<std::string>& symmetry_labels =
87 std::vector<std::string>(),
88 const std::vector<bool>& spincases = std::vector<bool>(),
89 const double bohr_to_angstrom = constants::codata_2018::bohr_to_angstrom,
90 double coefficient_epsilon = 5e-11)
91 : atoms_(atoms),
92 basis_(validate(basis)),
93 coefs_(coefficients),
94 occupancies_(occupancies),
95 energies_(energies),
96 labels_(symmetry_labels),
97 spins_(spincases),
98 bohr_to_angstrom_(bohr_to_angstrom),
99 coefficient_epsilon_(coefficient_epsilon) {
100 initialize_bf_map();
101 }
102
104 void write_prologue(std::ostream& os) const {
105 os << "[Molden Format]" << std::endl;
106 }
107
109 void write_atoms(std::ostream& os) const {
110 os << "[Atoms] AU" << std::endl;
111
112 os.fill(' ');
113 os << std::fixed << std::setprecision(8);
114 auto iatom = 0;
115 for (const auto& atom : atoms_) {
116 auto Z = atom.atomic_number;
117 os << std::setw(4)
118 << libint2::chemistry::get_element_info().at(Z - 1).symbol
119 << std::setw(6) << (iatom + 1) << std::setw(6) << Z << std::setw(14)
120 << atom.x << std::setw(14) << atom.y << std::setw(14) << atom.z
121 << std::endl;
122 ++iatom;
123 }
124 }
125
128 void write_basis(std::ostream& os) const {
129 bool f_found = false;
130 os << "[GTO]" << std::endl;
131 for (size_t iatom = 0; iatom < atoms_.size(); ++iatom) {
132 os << std::setw(4) << (iatom + 1) << std::setw(4) << 0 << std::endl;
133 for (auto ish : atom2shell_[iatom]) {
134 const Shell& sh = basis_.at(ish);
135 if (sh.contr.size() == 1) {
136 const auto& contr = sh.contr[0];
137 const auto l = contr.l;
138 assert(l <= 4); // only up to g functions are supported
139 if (l == 3) f_found = true;
140 const auto nprim = contr.coeff.size();
141 os << std::setw(4) << Shell::am_symbol(contr.l) << std::setw(6)
142 << nprim << std::setw(6) << "1.00" << std::endl;
143 for (int iprim = 0; iprim < nprim; ++iprim) {
144 os << std::scientific << std::uppercase << std::setprecision(10);
145 os << std::setw(20) << sh.alpha[iprim] << std::setw(20)
146 << sh.coeff_normalized(0, iprim) << std::endl;
147 } // end loop over primitives
148 } // end if ncontraction == 1
149 else {
150 assert(false); // Not implemented
151 }
152 } // end loop over shells on center
153 // format calls for a blank line here
154 os << std::endl;
155 } // end loop over centers
156
157 // write solid harmonic/cartesian tags
158 {
159 // Molden default is cartesians throughout
160 // dfg_is_cart_ is set to true even if there are no shells of a given type
161 if (dfg_is_cart_[0]) { // cartesian d
162 if (!dfg_is_cart_[1]) // solid harmonic f
163 os << "[7F]" << std::endl;
164 } else { // solid harmonic d
165 if (!dfg_is_cart_[1]) { // solid harmonic f
166 os << "[5D7F]" << std::endl;
167 } else if (f_found) { // cartesian f
168 os << "[5D10F]" << std::endl;
169 } else { // no f functions
170 os << "[5D]" << std::endl;
171 }
172 }
173 if (!dfg_is_cart_[2]) // solid harmonic g
174 os << "[9G]" << std::endl;
175 }
176 }
177
179 void write_lcao(std::ostream& os) const {
180 os << "[MO]" << std::endl;
181 for (int imo = 0; imo < coefs_.cols(); ++imo) {
182 os << std::fixed << std::setprecision(10);
183 os << std::setw(8) << "Sym= " << (labels_.empty() ? "A" : labels_.at(imo))
184 << std::endl
185 << std::setw(8) << "Ene= " << std::setw(16)
186 << (energies_.rows() == 0 ? 0.0 : energies_(imo)) << std::endl
187 << std::setw(8) << "Spin= "
188 << (spins_.empty() ? "Alpha" : (spins_.at(imo) ? "Alpha" : "Beta"))
189 << std::endl
190 << std::setw(8) << "Occup= " << occupancies_(imo) << std::endl;
191 os << std::scientific << std::uppercase << std::setprecision(10);
192 for (int iao = 0; iao < coefs_.rows(); ++iao) {
193 const auto C_ao_mo = coefs_(ao_map_[iao], imo);
194 if (std::abs(C_ao_mo) >= coefficient_epsilon_) {
195 os << std::setw(6) << (iao + 1) << " " << std::setw(16) << C_ao_mo
196 << std::endl;
197 }
198 } // end loop over AOs
199 } // end loop over MOs
200 }
201
203 void write(std::ostream& os) const {
204 write_prologue(os);
205 write_atoms(os);
206 write_basis(os);
207 write_lcao(os);
208 }
209
211 void write(const std::string& filename) const {
212 std::ofstream os(filename);
213 write_prologue(os);
214 write_atoms(os);
215 write_basis(os);
216 write_lcao(os);
217 }
218
219 double bohr_to_angstrom() const { return bohr_to_angstrom_; }
220
221 private:
222 const std::vector<Atom>& atoms_;
223 const std::vector<Shell>& basis_;
224 Eigen::MatrixXd coefs_;
225 Eigen::VectorXd occupancies_;
226 Eigen::VectorXd energies_;
227 std::vector<std::string> labels_;
228 std::vector<bool> spins_;
229 double bohr_to_angstrom_;
230 double coefficient_epsilon_;
231 mutable bool dfg_is_cart_[3]; // whether {d, f, g} shells are cartesian
232 // (true) or solid harmonics (false)
233 std::vector<std::vector<long>>
234 atom2shell_; // maps atom -> shell indices in basis_
235 std::vector<long>
236 ao_map_; // maps from the AOs ordered according to Molden
237 // (atoms->shells, bf in shells ordered in the Molden order)
238 // to the AOs ordered according to basis_
239
242 const std::vector<Shell>& validate(const std::vector<Shell>& shells) const {
243 bool dfg_found[] = {false, false, false};
244 for (int i = 0; i != sizeof(dfg_is_cart_) / sizeof(bool); ++i)
245 dfg_is_cart_[i] = true;
246 for (const auto& shell : shells) {
247 for (const auto& contr : shell.contr) {
248 if (contr.l > 4)
249 throw std::logic_error(
250 "molden::Export cannot handle shells with l > 4");
251
252 switch (contr.l) {
253 case 1:
254 if (contr.pure)
255 throw std::logic_error(
256 "molden::Export cannot handle solid harmonics p shells");
257 break;
258 case 2:
259 case 3:
260 case 4: {
261 if (!dfg_found[contr.l - 2]) {
262 dfg_is_cart_[contr.l - 2] = !contr.pure;
263 dfg_found[contr.l - 2] = true;
264 }
265 if (!contr.pure ^ dfg_is_cart_[contr.l - 2])
266 throw std::logic_error(
267 "molden::Export only supports all-Cartesian or "
268 "all-solid-harmonics d/f/g shells");
269 }
270
271 default: {
272 } // l = 0 is fine
273 }
274 }
275 }
276 return shells;
277 }
278
281 const std::vector<Shell>& validate(const BasisSet& bs) const {
282 return validate(bs.shells());
283 }
284
285 void initialize_bf_map() {
286 atom2shell_ = BasisSet::atom2shell(atoms_, basis_);
287
288 const auto nao = BasisSet::nbf(basis_);
289 ao_map_.resize(nao);
290 assert(nao == coefs_.rows());
291 const auto shell2ao = BasisSet::compute_shell2bf(basis_);
292 long ao_molden = 0;
293 for (size_t iatom = 0; iatom < atoms_.size(); ++iatom) {
294 for (auto ish : atom2shell_[iatom]) {
295 auto ao = shell2ao[ish]; // refers to order assumed by coefs
296 const auto& shell = basis_[ish];
297 const auto ncontr = shell.contr.size();
298 for (int c = 0; c != ncontr; ++c) {
299 const auto l = shell.contr[c].l;
300 const auto pure = shell.contr[c].pure;
301 if (pure) {
302 int m;
303 FOR_SOLIDHARM_MOLDEN(l, m)
304 const auto ao_in_shell = libint2::INT_SOLIDHARMINDEX(l, m);
305 ao_map_[ao_molden] = ao + ao_in_shell;
306 ++ao_molden;
307 END_FOR_SOLIDHARM_MOLDEN
308 ao += 2 * l + 1;
309 } else {
310 int i, j, k;
311 FOR_CART_MOLDEN(i, j, k, l)
312 const auto ao_in_shell = INT_CARTINDEX(l, i, j);
313 ao_map_[ao_molden] = ao + ao_in_shell;
314 ++ao_molden;
315 END_FOR_CART_MOLDEN
316 ao += INT_NCART(l);
317 }
318 } // contraction loop
319 }
320 }
321 }
322
323}; // Export
324
328class PBCExport : public Export {
329 public:
358 template <typename Coeffs, typename Occs, typename Energies = Eigen::VectorXd>
360 const std::vector<Atom>& atoms,
361 const std::array<Eigen::Vector3d, 3>& cell_axes,
362 const std::vector<Shell>& basis, const Coeffs& coefficients,
363 const Occs& occupancies, int space_group,
364 const Energies& energies = Energies(),
365 const std::vector<std::string>& symmetry_labels =
366 std::vector<std::string>(),
367 const std::vector<bool>& spincases = std::vector<bool>(),
368 const double bohr_to_angstrom = constants::codata_2018::bohr_to_angstrom)
369 : Export(atoms, basis, coefficients, occupancies, energies,
370 symmetry_labels, spincases, bohr_to_angstrom),
371 cell_axes_(cell_axes),
372 space_group_(space_group) {
373 // initialize_bf_map();
374 }
375
377 void write_space_group(std::ostream& os) const {
378 os << "[SpaceGroup] (Number)" << std::endl;
379 os << space_group_ << std::endl;
380 }
381
383 void write_operators(std::ostream& os) const {
384 os << "[Operators]" << std::endl;
385 os << "x, y, z" << std::endl;
386 }
387
389 void write_cell_axes(std::ostream& os) const {
390 // https://sourceforge.net/p/jmol/code/HEAD/tree/trunk/Jmol/src/org/jmol/adapter/readers/quantum/MoldenReader.java#l107
391 // suggests that [Cell] defaults to angstroms
392 os << "[Cell]" << std::endl;
393 {
394 // convert vectors to abcɑβɣ
395 const double a = cell_axes_[0].norm();
396 const double b = cell_axes_[1].norm();
397 const double c = cell_axes_[2].norm();
398 const double eps = std::sqrt(std::numeric_limits<double>::epsilon());
399 const bool nonzero_a = a >= eps;
400 const bool nonzero_b = b >= eps;
401 const bool nonzero_c = c >= eps;
402 static constexpr double right_angle = M_PI / 2;
403 const double alpha =
404 nonzero_b && nonzero_c
405 ? std::acos(cell_axes_[1].dot(cell_axes_[2]) / (b * c))
406 : right_angle;
407 const double beta =
408 nonzero_a && nonzero_c
409 ? std::acos(cell_axes_[0].dot(cell_axes_[2]) / (a * c))
410 : right_angle;
411 const double gamma =
412 nonzero_a && nonzero_b
413 ? std::acos(cell_axes_[0].dot(cell_axes_[1]) / (a * b))
414 : right_angle;
415 const double radian_to_degree = 180 / M_PI;
416 os << std::setw(12) << a * bohr_to_angstrom() << std::setw(12)
417 << b * bohr_to_angstrom() << std::setw(12) << c * bohr_to_angstrom()
418 << std::setw(12) << alpha * radian_to_degree << std::setw(12)
419 << beta * radian_to_degree << std::setw(12) << gamma * radian_to_degree
420 << std::endl;
421 }
422 }
423
424 void write(const std::string& filename) const {
425 std::ofstream os(filename);
426 write_prologue(os);
428 write_operators(os);
429 write_cell_axes(os);
430 write_atoms(os);
431 write_basis(os);
432 write_lcao(os);
433 }
434
435 private:
436 std::array<Eigen::Vector3d, 3> cell_axes_;
437 int space_group_;
438
439}; // PBCExport
440
441} // namespace molden
442} // namespace libint2
443
444#endif // INCLUDE_LIBINT2_LCAO_MOLDEN_H_
Exports LCAO coefficients in Molden format.
Definition molden.h:47
void write_basis(std::ostream &os) const
writes the "[GTO]" section, as well as optional Cartesian/solid harmonics keywords,...
Definition molden.h:128
void write_atoms(std::ostream &os) const
writes the "[Atoms]" section to ostream os
Definition molden.h:109
void write_prologue(std::ostream &os) const
writes "[Molden Format]" to ostream os
Definition molden.h:104
void write(const std::string &filename) const
same as write(ostream), but creates new file named filename
Definition molden.h:211
Export(const std::vector< Atom > &atoms, const ShellSequence &basis, const Coeffs &coefficients, const Occs &occupancies, const Energies &energies=Energies(), const std::vector< std::string > &symmetry_labels=std::vector< std::string >(), const std::vector< bool > &spincases=std::vector< bool >(), const double bohr_to_angstrom=constants::codata_2018::bohr_to_angstrom, double coefficient_epsilon=5e-11)
Definition molden.h:82
void write_lcao(std::ostream &os) const
writes the "[MO]" section to ostream os
Definition molden.h:179
void write(std::ostream &os) const
writes "prologue", atoms, basis, and LCAOs to ostream os
Definition molden.h:203
Extension of the Molden exporter to support JMOL extensions for crystal orbitals (see https://sourcef...
Definition molden.h:328
void write_space_group(std::ostream &os) const
writes the "[SpaceGroup]" section to ostream os
Definition molden.h:377
PBCExport(const std::vector< Atom > &atoms, const std::array< Eigen::Vector3d, 3 > &cell_axes, const std::vector< Shell > &basis, const Coeffs &coefficients, const Occs &occupancies, int space_group, const Energies &energies=Energies(), const std::vector< std::string > &symmetry_labels=std::vector< std::string >(), const std::vector< bool > &spincases=std::vector< bool >(), const double bohr_to_angstrom=constants::codata_2018::bohr_to_angstrom)
Definition molden.h:359
void write_cell_axes(std::ostream &os) const
writes the "[Cell]" section to ostream os
Definition molden.h:389
void write_operators(std::ostream &os) const
writes the "[Operators]" section to ostream os
Definition molden.h:383
Defaults definitions for various parameters assumed by Libint.
Definition algebra.cc:24
generally-contracted Solid-Harmonic/Cartesion Gaussian Shell
Definition shell.h:720
svector< Contraction > contr
contractions
Definition shell.h:742
static char am_symbol(size_t l)
Definition shell.h:814
real_t coeff_normalized(size_t c, size_t p) const
Definition shell.h:915
svector< real_t > alpha
exponents
Definition shell.h:741