1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: dimer.h
4 // Copyright (C) 2012 Toru Shiozaki
5 //
6 // Author: Shane Parker <shane.parker@u.northwestern.edu>
7 // Maintainer: NU theory
8 //
9 // This file is part of the BAGEL package.
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //
24
25 #ifndef __SRC_DIMER_DIMER_H
26 #define __SRC_DIMER_DIMER_H
27
28 #include <src/ci/fci/knowles.h>
29 #include <src/ci/fci/harrison.h>
30 #include <src/ci/ras/rasci.h>
31 #include <src/asd/dimer/dimer_cispace.h>
32 #include <src/wfn/get_energy.h>
33 #include <src/util/muffle.h>
34
35 namespace bagel {
36
37 /// Contains geometries and references for isolated and joined dimers.
38 /// Used to prepare an ASD calculation and to start the requisite CI calculations
39
40 class Dimer : public std::enable_shared_from_this<Dimer> {
41 private:
42 template <class T>
43 using Ref = std::shared_ptr<const T>;
44
45 protected:
46 std::shared_ptr<const PTree> input_;
47
48 std::pair<Ref<Geometry>,Ref<Geometry>> geoms_; ///< Geometry objects of isolated monomers
49 std::pair<Ref<Reference>, Ref<Reference>> isolated_refs_; ///< Reference objects of the isolated monomers BEFORE active spaces have been chosen
50 std::pair<Ref<Reference>, Ref<Reference>> embedded_refs_; ///< Reference objects for monomers with the other monomer included as an embedding
51 std::pair<Ref<Reference>, Ref<Reference>> active_refs_; ///< Reference objects of the isolated monomers AFTER the active spaces have been chosen
52
53 std::pair<int, int> nvirt_; ///< number of virtual orbitals in sref_ for each unit (needs to be updated when sref_ is
54
55 std::shared_ptr<const Geometry> sgeom_; ///< Supergeometry, i.e., Geometry of dimer
56 /// Superreference, i.e., Reference of dimer
57 std::shared_ptr<const Reference> sref_;
58
59 double active_thresh_; ///< overlap threshold for inclusion in the active space
60 bool print_orbital_;
61
62 public:
63 // Constructors
64 Dimer(std::shared_ptr<const PTree> input, Ref<Geometry> a, Ref<Geometry> b); ///< Conjoins the provided Geometry objects
65 Dimer(std::shared_ptr<const PTree> input, Ref<Geometry> a); ///< Duplicates provided Geometry according to translation vector specified in input
66 Dimer(std::shared_ptr<const PTree> input, Ref<Reference> A, Ref<Reference> B); ///< Conjoins the provided Reference objects
67 Dimer(std::shared_ptr<const PTree> input, Ref<Reference> a, const bool linked = false); ///< Duplicates provided Reference according to translation vector specified in input (false) / linked dimer (true)
68
69 // Return functions
geoms()70 std::pair<Ref<Geometry>, Ref<Geometry>> geoms() const { return geoms_; };
isolated_refs()71 std::pair<Ref<Reference>, Ref<Reference>> isolated_refs() const { return isolated_refs_; }
embedded_refs()72 std::pair<Ref<Reference>, Ref<Reference>> embedded_refs() const { return embedded_refs_; }
active_refs()73 std::pair<Ref<Reference>, Ref<Reference>> active_refs() const { return active_refs_; }
74
sgeom()75 std::shared_ptr<const Geometry> sgeom() const { return sgeom_; };
sref()76 std::shared_ptr<const Reference> sref() const { return sref_; };
77
78 // Utility functions
79 /// Sets active space of sref_ using overlaps with isolated_ref_ active spaces
80 void set_active(std::shared_ptr<const PTree> idata, const bool localize_first);
81 /// Localizes active space and uses the given Fock matrix to diagonalize the subspaces
82 void localize(std::shared_ptr<const PTree> idata, std::shared_ptr<const Matrix> fock, const bool localize_first);
83
84 // Linked dimers
85 void set_active(std::shared_ptr<const PTree> idata);
86 void reduce_active(std::shared_ptr<const PTree> idata);
87 std::shared_ptr<Matrix> form_reference_active_coeff() const;
88 std::shared_ptr<Matrix> form_semi_canonical_coeff(std::shared_ptr<const PTree> idata) const;
89 std::shared_ptr<Matrix> overlap_selection(std::shared_ptr<const Matrix> control, std::shared_ptr<const Matrix> treatment) const;
90
91 // Calculations
92 void scf(std::shared_ptr<const PTree> idata); ///< Driver for preparation of dimer for MultiExcitonHamiltonian or CI calculation
93
94 /// Driver to run monomer CAS calculations used in MEH
95 template <class VecType>
96 std::shared_ptr<DimerCISpace_base<VecType>> compute_cispace(std::shared_ptr<const PTree> idata);
97 /// Driver to run monomer RAS calculations used in MEH
98 template <class VecType>
99 std::shared_ptr<DimerCISpace_base<VecType>> compute_rcispace(std::shared_ptr<const PTree> idata);
100
101 /// Creates a Reference object for a MEH or ASD calculation
102 std::shared_ptr<Reference> build_reference(const int site, const std::vector<bool> meanfield) const;
103
104 // Update
105 void update_coeff(std::shared_ptr<const Matrix> coeff);
106
107 private:
108 void construct_geometry(const bool linked = false); ///< Forms super geometry (sgeom_) and optionally projects isolated geometries and supergeometry to a specified basis
109 void embed_refs(); ///< Forms two references to be used in CI calculations where the inactive monomer is included as "embedding"
110 /// Reads information on monomer subspaces from input
111 void get_spaces(std::shared_ptr<const PTree> idata, std::vector<std::vector<int>>& spaces_A, std::vector<std::vector<int>>& spaces_B);
112
113 /// Takes monomer references, projections them onto the supergeom basis, and arranges the
114 /// to follow (closedA, closedB, activeA, activeB, virtA, virtB) and returns the result
115 std::shared_ptr<const Matrix> form_projected_coeffs();
116
117 /// Lowdin orthogonalizes the result of form_projected_coeffs
118 std::shared_ptr<const Matrix> construct_coeff(const bool linked = false);
119
120 /// Runs a single set of monomer CAS/RAS calculations as specified
121 template <class CIMethod, class VecType>
122 std::shared_ptr<const VecType> embedded_ci(std::shared_ptr<const PTree> idata, std::shared_ptr<const Reference> ref,
123 const int charge, const int nspin, const int nstate, std::string label);
124 };
125
126 template<class CIMethod, class VecType>
embedded_ci(std::shared_ptr<const PTree> idata,std::shared_ptr<const Reference> ref,const int charge,const int nspin,const int nstate,std::string label)127 std::shared_ptr<const VecType> Dimer::embedded_ci(std::shared_ptr<const PTree> idata, std::shared_ptr<const Reference> ref,
128 const int charge, const int nspin, const int nstate, std::string label)
129 {
130 auto input = std::make_shared<PTree>(*idata);
131
132 input->erase("charge"); input->put("charge", lexical_cast<std::string>(charge));
133 input->erase("nspin"); input->put("nspin", lexical_cast<std::string>(nspin));
134 input->erase("ncore"); input->put("ncore", lexical_cast<std::string>(ref->nclosed()));
135 input->erase("nstate"); input->put("nstate", lexical_cast<std::string>(nstate));
136
137 // Hiding cout
138 std::stringstream outfilename;
139 outfilename << "asd_ci_" << label << "_c" << charge << "_s" << nspin;
140 Muffle hide(outfilename.str());
141
142 auto ci = std::make_shared<CIMethod>(input, ref->geom(), ref);
143 ci->compute();
144
145 return ci->civectors();
146 }
147
148 template <class VecType>
compute_cispace(const std::shared_ptr<const PTree> idata)149 std::shared_ptr<DimerCISpace_base<VecType>> Dimer::compute_cispace(const std::shared_ptr<const PTree> idata) {
150 embed_refs();
151 std::pair<int,int> nelea {isolated_refs_.first->nclosed() - active_refs_.first->nclosed(), isolated_refs_.second->nclosed() - active_refs_.second->nclosed()};
152 std::pair<int,int> neleb = nelea;
153
154 auto d1 = std::make_shared<Determinants>(active_refs_.first->nact(), nelea.first, neleb.first, /*compress*/false, /*mute*/true);
155 auto d2 = std::make_shared<Determinants>(active_refs_.second->nact(), nelea.second, neleb.second, /*compress*/false, /*mute*/true);
156 auto out = std::make_shared<DimerCISpace_base<VecType>>(std::make_pair(d1, d2), nelea, neleb);
157
158 std::vector<std::vector<int>> spaces_A, spaces_B;
159 get_spaces(idata, spaces_A, spaces_B);
160
161 Timer castime;
162
163 std::shared_ptr<const PTree> fcidata = idata->get_child_optional("fci");
164 if (!fcidata) fcidata = std::make_shared<const PTree>();
165
166 auto run_calculations = [this, &fcidata, &castime]
167 (std::vector<std::vector<int>> spaces, std::shared_ptr<const Reference> eref, std::shared_ptr<const Reference> aref, std::string label) {
168 std::cout << " Starting embedded CAS-CI calculations on monomer " << label << std::endl;
169 std::vector<std::shared_ptr<const VecType>> results;
170 for (auto& ispace : spaces) {
171 if (ispace.size() != 3) throw std::runtime_error("Spaces should specify \"charge\", \"spin\", and \"nstate\"");
172
173 std::shared_ptr<PTree> input_copy = std::make_shared<PTree>(*fcidata);
174 input_copy->erase("norb"); input_copy->put("norb", lexical_cast<std::string>(aref->nact()));
175
176 std::string method = input_copy->get<std::string>("algorithm", "hz");
177 std::set<std::string> kh_options = {"kh", "knowles", "handy"};
178 std::set<std::string> hz_options = {"hz", "harrison", "zarrabian"};
179 std::set<std::string> dist_options = {"dist", "parallel"};
180
181 if (std::find(kh_options.begin(), kh_options.end(), method) != kh_options.end()) {
182 using CiType = typename VecType::Ci;
183 std::vector<std::shared_ptr<CiType>> tmp;
184 std::shared_ptr<const Dvec> vecs = embedded_ci<KnowlesHandy, Dvec>(input_copy, eref, ispace.at(0), ispace.at(1), ispace.at(2), label);
185 for (auto& i : vecs->dvec())
186 tmp.push_back(std::make_shared<CiType>(*i));
187 results.push_back(std::make_shared<VecType>(tmp));
188 }
189 else if (std::find(hz_options.begin(), hz_options.end(), method) != hz_options.end()) {
190 using CiType = typename VecType::Ci;
191 std::vector<std::shared_ptr<CiType>> tmp;
192 std::shared_ptr<const Dvec> vecs = embedded_ci<HarrisonZarrabian, Dvec>(input_copy, eref, ispace.at(0), ispace.at(1), ispace.at(2), label);
193 for (auto& i : vecs->dvec())
194 tmp.push_back(std::make_shared<CiType>(*i));
195 results.push_back(std::make_shared<VecType>(tmp));
196 }
197 else
198 throw std::runtime_error("Unrecognized FCI type algorithm");
199
200 std::cout << " - charge: " << ispace.at(0) << ", spin: " << ispace.at(1) << ", nstates: " << ispace.at(2)
201 << std::fixed << std::setw(10) << std::setprecision(2) << castime.tick() << std::endl;
202 }
203 return results;
204 };
205
206 for (auto& i : run_calculations(spaces_A, embedded_refs_.first, active_refs_.first, "A"))
207 out->template insert<0>(i);
208
209 for (auto& i : run_calculations(spaces_B, embedded_refs_.second, active_refs_.second, "B"))
210 out->template insert<1>(i);
211
212 return out;
213 }
214
215 template <class VecType>
compute_rcispace(const std::shared_ptr<const PTree> idata)216 std::shared_ptr<DimerCISpace_base<VecType>> Dimer::compute_rcispace(const std::shared_ptr<const PTree> idata) {
217 embed_refs();
218 std::pair<int,int> nelea {isolated_refs_.first->nclosed() - active_refs_.first->nclosed(), isolated_refs_.second->nclosed() - active_refs_.second->nclosed()};
219 std::pair<int,int> neleb = nelea;
220
221 // { {nras1, nras2, nras3}, max holes, max particles }
222 std::pair<std::tuple<std::array<int, 3>, int, int>, std::tuple<std::array<int, 3>, int, int>> ras_desc;
223
224 // Sample:
225 // "restricted" : [ { "orbitals" : [1, 2, 3], "max_holes" : 0, "max_particles" : 2 } ],
226 // puts 1 orbital in RAS1 with no holes allowed, 2 orbital in RAS2, and 3 orbitals in RAS3 with up to 2 particles
227 auto restrictions = idata->get_child("restricted");
228
229 auto get_restricted_data = [] (std::shared_ptr<const PTree> i) {
230 return std::make_tuple(i->get_array<int, 3>("orbitals"), i->get<int>("max_holes"), i->get<int>("max_particles"));
231 };
232
233 if (restrictions->size() == 1) {
234 ras_desc = { get_restricted_data(*restrictions->begin()), get_restricted_data(*restrictions->begin()) };
235 } else if (restrictions->size() == 2) {
236 auto iter = restrictions->begin();
237 auto tmp1 = get_restricted_data(*iter++);
238 auto tmp2 = get_restricted_data(*iter);
239 ras_desc = {tmp1, tmp2};
240 } else {
241 throw std::logic_error("One or two sets of restrictions must be provided.");
242 }
243
244 // This is less than ideal. It'd be better to have some sort of generator object that can be passed around.
245 auto d1 = std::make_shared<RASDeterminants>(std::get<0>(ras_desc.first), nelea.first, neleb.first, std::get<1>(ras_desc.first), std::get<2>(ras_desc.first), true);
246 auto d2 = std::make_shared<RASDeterminants>(std::get<0>(ras_desc.second), nelea.second, neleb.second, std::get<1>(ras_desc.second), std::get<2>(ras_desc.second), true);
247
248 auto out = std::make_shared<DimerCISpace_base<VecType>>(std::make_pair(d1, d2), nelea, neleb);
249
250 std::vector<std::vector<int>> spaces_A, spaces_B;
251 get_spaces(idata, spaces_A, spaces_B);
252
253 Timer rastime;
254
255 std::shared_ptr<const PTree> rasdata = idata->get_child_optional("ras");
256 if (!rasdata) rasdata = std::make_shared<const PTree>();
257
258 // Embedded RAS-CI calculations
259 auto run_calculations = [this, &rastime, &rasdata] (std::vector<std::vector<int>>& spaces, std::shared_ptr<const Reference> eref,
260 std::tuple<std::array<int, 3>, int, int> ras_desc, std::string label) {
261 std::cout << " Starting embedded RAS-CI calculations on monomer " << label << std::endl;
262 std::vector<std::shared_ptr<const VecType>> results;
263
264 for (auto& ispace : spaces) {
265 if (ispace.size() != 3)
266 throw std::runtime_error("Spaces should specify \"charge\", \"spin\", and \"nstate\"");
267
268 auto input_copy = std::make_shared<PTree>(*rasdata);
269 input_copy->erase("max_holes"); input_copy->put("max_holes", lexical_cast<std::string>(std::get<1>(ras_desc)));
270 input_copy->erase("max_particles"); input_copy->put("max_particles", lexical_cast<std::string>(std::get<2>(ras_desc)));
271
272 input_copy->erase("active");
273 int current = eref->nclosed();
274 auto parent = std::make_shared<PTree>();
275 for (int i = 0; i < 3; ++i) {
276 auto tmp = std::make_shared<PTree>();
277 const int nras = std::get<0>(ras_desc)[i];
278 for (int i = 0; i < nras; ++i, ++current)
279 tmp->push_back(current+1);
280 parent->push_back(tmp);
281 }
282 input_copy->add_child("active", parent);
283
284
285 std::string method = input_copy->get<std::string>("method", "local");
286 std::set<std::string> serial_options = {"local"};
287 std::set<std::string> dist_options = {"dist", "parallel"};
288
289 if (std::find(serial_options.begin(), serial_options.end(), method) != serial_options.end())
290 results.push_back(std::make_shared<VecType>(embedded_ci<RASCI, RASDvec>(input_copy, eref, ispace.at(0), ispace.at(1), ispace.at(2), label)));
291 else
292 throw std::logic_error("Unrecognized RAS type algorithm");
293
294 std::cout << " - charge: " << ispace.at(0) << ", spin: " << ispace.at(1) << ", nstates: " << ispace.at(2)
295 << std::fixed << std::setw(10) << std::setprecision(2) << rastime.tick() << std::endl;
296 }
297
298 return results;
299 };
300
301 for (auto& i : run_calculations(spaces_A, embedded_refs_.first, ras_desc.first, "A"))
302 out->template insert<0>(i);
303
304 for (auto& i : run_calculations(spaces_B, embedded_refs_.second, ras_desc.second, "B"))
305 out->template insert<1>(i);
306
307 return out;
308 }
309
310 }
311
312 #endif
313