votca 2024.1-dev
Loading...
Searching...
No Matches
qmmm.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 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// Standard includes
21#include <algorithm>
22#include <chrono>
23#include <filesystem>
24#include <sstream>
25
26// Third party includes
27#include <numeric>
28#include <stdexcept>
29
30// Local VOTCA includes
35#include "votca/xtp/qmregion.h"
36
37// Local private VOTCA includes
38#include "qmmm.h"
39#include "votca/xtp/qmstate.h"
40
41namespace votca {
42namespace xtp {
43
45
46 print_regions_pdb_ = options.get(".print_regions_pdb").as<bool>();
47 max_iterations_ = options.get(".max_iterations").as<Index>();
48 regions_def_.second = options.get(".regions");
49 regions_def_.first = mapfile_;
50 use_gs_for_ex_ = options.get("io_jobfile.use_gs_for_ex").as<bool>();
51
52 states_ = options.get("io_jobfile.states").as<std::vector<QMState>>();
53 which_segments_ = options.get("io_jobfile.segments").as<std::string>();
54
55 bool groundstate_found = std::any_of(
56 states_.begin(), states_.end(),
57 [](const QMState& state) { return state.Type() == QMStateType::Gstate; });
58 if (!groundstate_found) {
59 states_.push_back(QMState("n"));
60 }
61}
62
64 std::chrono::time_point<std::chrono::system_clock> start =
65 std::chrono::system_clock::now();
66
67 std::string qmmm_work_dir = "QMMM";
68 if (!this->hasQMRegion()) {
69 qmmm_work_dir = "MMMM";
70 }
71 std::string frame_dir =
72 "frame_" + boost::lexical_cast<std::string>(top.getStep());
73 std::string job_dir =
74 "job_" + std::to_string(job.getId()) + "_" + job.getTag();
75 std::filesystem::path arg_path;
76 std::string workdir =
77 (arg_path / qmmm_work_dir / frame_dir / job_dir).generic_string();
78 std::filesystem::create_directories(workdir);
80 Logger& pLog = Thread.getLogger();
81
82 JobTopology jobtop = JobTopology(job, pLog, workdir);
83 if (job.getInput().exists("restart")) {
84 std::string checkptfile = job.getInput().get("restart").as<std::string>();
85 XTP_LOG(Log::error, pLog)
86 << TimeStamp() << " Restart job from " << checkptfile << std::flush;
87 jobtop.ReadFromHdf5(checkptfile);
88 } else {
89 jobtop.BuildRegions(top, regions_def_);
90 }
91
93 std::string pdb_filename = "regions.pdb";
94 XTP_LOG(Log::error, pLog) << TimeStamp() << " Writing jobtopology to "
95 << (workdir + "/" + pdb_filename) << std::flush;
96 jobtop.WriteToPdb(workdir + "/" + pdb_filename);
97 }
98
99 Index no_static_regions = std::accumulate(
100 jobtop.begin(), jobtop.end(), 0, [](Index count, const auto& region) {
101 return count += Index(region->Converged());
102 });
103
104 bool no_top_scf = false;
105 if (jobtop.size() - no_static_regions < 2) {
106 XTP_LOG(Log::error, pLog)
107 << TimeStamp() << " Only " << jobtop.size() - no_static_regions
108 << " scf region is used. The remaining regions are static. So no "
109 "inter regions scf is required. "
110 << std::flush;
111 no_top_scf = true;
112 max_iterations_ = 1;
113 }
114 Index iteration = 0;
115 for (; iteration < max_iterations_; iteration++) {
116
117 XTP_LOG(Log::error, pLog)
118 << TimeStamp() << " --Inter Region SCF Iteration " << iteration + 1
119 << " of " << max_iterations_ << std::flush;
120
121 for (std::unique_ptr<Region>& region : jobtop) {
122 XTP_LOG(Log::error, pLog)
123 << TimeStamp() << " Evaluating " << region->identify() << " "
124 << region->getId() << std::flush;
125 region->Reset();
126 region->Evaluate(jobtop.Regions());
127 if (!region->Successful()) {
129 jres.setError(region->ErrorMsg());
130 return jres;
131 }
132 }
133
134 std::string checkpointfilename =
135 "checkpoint_iter_" + std::to_string(iteration + 1) + ".hdf5";
136 XTP_LOG(Log::error, pLog) << TimeStamp() << " Writing checkpoint to "
137 << checkpointfilename << std::flush;
138 jobtop.WriteToHdf5(workdir + "/" + checkpointfilename);
139
140 if (!no_top_scf) {
141 std::vector<bool> converged_regions;
142 for (std::unique_ptr<Region>& region : jobtop) {
143 converged_regions.push_back(region->Converged());
144 }
145
146 double etot = std::accumulate(
147 jobtop.begin(), jobtop.end(), 0.0,
148 [](double e, const auto& region) { return region->Etotal() + e; });
149
150 XTP_LOG(Log::error, pLog) << TimeStamp() << " --Total Energy all regions "
151 << etot << std::flush;
152
153 bool all_regions_converged =
154 std::all_of(converged_regions.begin(), converged_regions.end(),
155 [](bool i) { return i; });
156
157 if (all_regions_converged) {
158 XTP_LOG(Log::error, pLog)
159 << TimeStamp() << " Job converged after " << iteration + 1
160 << " iterations." << std::flush;
162 break;
163 }
164 if (iteration == max_iterations_ - 1) {
165 XTP_LOG(Log::error, pLog)
166 << TimeStamp() << " Job did not converge after " << iteration + 1
167 << " iterations.\n Writing results to jobfile." << std::flush;
169 jres.setError("Inter Region SCF did not converge in " +
170 std::to_string(max_iterations_) + " iterations.");
171 }
172 } else {
174 }
175 }
176
177 tools::Property results;
178 tools::Property& jobresult = results.add("output", "");
179 tools::Property& regionsresults = jobresult.add("regions", "");
180 double etot = 0.0;
181 double charge = 0.0;
182 for (const std::unique_ptr<Region>& reg : jobtop) {
183 reg->AddResults(regionsresults);
184 etot += reg->Etotal();
185 charge += reg->charge();
186 }
187 std::chrono::time_point<std::chrono::system_clock> end =
188 std::chrono::system_clock::now();
189 std::chrono::duration<double> elapsed_time = end - start;
190 jobresult.add("E_tot", std::to_string(etot * tools::conv::hrt2ev));
191 jobresult.add("Compute_Time", std::to_string(Index(elapsed_time.count())));
192 jobresult.add("Total_Charge", std::to_string(charge));
193 if (!no_top_scf) {
194 jobresult.add("Iterations", std::to_string(iteration + 1));
195 }
196 jres.setOutput(results);
197 return jres;
198}
199
200bool QMMM::hasQMRegion() const {
201 Logger log;
202 QMRegion QMdummy(0, log, "");
203 return std::any_of(regions_def_.second.begin(), regions_def_.second.end(),
204 [&](const tools::Property& reg) {
205 return reg.name() == QMdummy.identify();
206 });
207}
208
209std::string QMMM::getFirstRegionName() const {
210 for (const auto& reg : regions_def_.second) {
211 if (reg.get("id").as<Index>() == 0) {
212 return reg.name();
213 }
214 }
215 throw std::runtime_error("region ids do not start at 0");
216 return "";
217}
218
219void QMMM::WriteJobFile(const Topology& top) {
220
221 std::cout << std::endl
222 << "... ... Writing job file " << jobfile_ << std::flush;
223
224 std::ofstream ofs;
225 ofs.open(jobfile_, std::ofstream::out);
226 if (!ofs.is_open()) {
227 throw std::runtime_error("\nERROR: bad file handle: " + jobfile_);
228 }
229
230 std::vector<Index> segments_to_write;
231 if (which_segments_ == "all") {
232 for (Index i = 0; i < Index(top.Segments().size()); ++i) {
233 segments_to_write.push_back(i);
234 }
235 } else {
236 segments_to_write = IndexParser().CreateIndexVector(which_segments_);
237 }
238
239 ofs << "<jobs>" << std::endl;
240 Index jobid = 0;
241 for (Index segID : segments_to_write) {
242 const Segment& seg = top.Segments()[segID];
243 for (const QMState& state : states_) {
244 Job job = createJob(seg, state, jobid);
245 job.ToStream(ofs);
246 jobid++;
247 }
248 }
249
250 ofs << "</jobs>" << std::endl;
251 ofs.close();
252 std::cout << std::endl
253 << "... ... In total " << jobid << " jobs" << std::flush;
254 return;
255}
256
257Job QMMM::createJob(const Segment& seg, const QMState& state,
258 Index jobid) const {
259 std::string marker = std::to_string(seg.getId()) + ":" + state.ToString();
260 std::string tag = seg.getType() + "_" + marker;
261
262 tools::Property Input;
263 tools::Property& pInput = Input.add("input", "");
264 pInput.add("site_energies", marker);
265 tools::Property& regions = pInput.add("regions", "");
266 tools::Property& region = regions.add(getFirstRegionName(), "");
267 region.add("id", "0");
268 if (hasQMRegion()) {
269 region.add("state", state.ToString());
270 }
271 if (use_gs_for_ex_ && (state.Type() == QMStateType::Singlet ||
272 state.Type() == QMStateType::Triplet)) {
273 region.add("segments", std::to_string(seg.getId()) + ":n");
274 } else {
275 region.add("segments", marker);
276 }
277 Job job(jobid, tag, Input, Job::AVAILABLE);
278 return job;
279}
280
282
283 Index incomplete_jobs = 0;
284
285 Eigen::Matrix<double, Eigen::Dynamic, 5> energies =
286 Eigen::Matrix<double, Eigen::Dynamic, 5>::Zero(top.Segments().size(), 5);
287 Eigen::Matrix<bool, Eigen::Dynamic, 5> found =
288 Eigen::Matrix<bool, Eigen::Dynamic, 5>::Zero(top.Segments().size(), 5);
289
290 tools::Property xml;
292 for (tools::Property* job : xml.Select("jobs.job")) {
293
294 Index jobid = job->get("id").as<Index>();
295 if (!job->exists("status")) {
296 throw std::runtime_error(
297 "Jobfile is malformed. <status> tag missing for job " +
298 std::to_string(jobid));
299 }
300 if (job->get("status").as<std::string>() != "COMPLETE" ||
301 !job->exists("output")) {
302 incomplete_jobs++;
303 continue;
304 }
305
306 std::vector<std::string> split =
307 tools::Tokenizer(job->get("input.site_energies").as<std::string>(), ":")
308 .ToVector();
309
310 Index segid = std::stoi(split[0]);
311 if (segid < 0 || segid >= Index(top.Segments().size())) {
312 throw std::runtime_error("JobSegment id" + std::to_string(segid) +
313 " is not in topology for job " +
314 std::to_string(jobid));
315 }
316 QMState state;
317 try {
318 state.FromString(split[1]);
319 } catch (std::runtime_error& e) {
320 std::stringstream message;
321 message << e.what() << " for job " << jobid;
322 throw std::runtime_error(message.str());
323 }
324 double energy = job->get("output.E_tot").as<double>() * tools::conv::ev2hrt;
325 if (found(segid, state.Type().Type()) != 0) {
326 throw std::runtime_error("There are two entries in jobfile for segment " +
327 std::to_string(segid) +
328 " state:" + state.ToString());
329 }
330
331 energies(segid, state.Type().Type()) = energy;
332 found(segid, state.Type().Type()) = true;
333 }
334
335 Eigen::Matrix<Index, 1, 5> found_states = found.colwise().count();
336 std::cout << std::endl;
337 for (Index i = 0; i < 5; i++) {
338 if (found_states(i) > 0) {
339 QMStateType type(static_cast<QMStateType::statetype>(i));
340 std::cout << "Found " << found_states(i) << " states of type "
341 << type.ToString() << std::endl;
342 }
343 }
344 if (incomplete_jobs > 0) {
345 std::cout << incomplete_jobs << " incomplete jobs found." << std::endl;
346 }
347
348 for (Segment& seg : top.Segments()) {
349 Index segid = seg.getId();
350 for (Index i = 0; i < 4; i++) {
351 QMStateType type(static_cast<QMStateType::statetype>(i));
352 if (found(segid, i) && found(segid, 4)) {
353 double energy = energies(segid, i) - energies(segid, 4);
354 seg.setEMpoles(type, energy);
355 }
356 }
357 }
358}
359
360} // namespace xtp
361} // namespace votca
class to manage program options with xml serialization functionality
Definition property.h:55
Property & add(const std::string &key, const std::string &value)
add a new property to structure
Definition property.cc:108
Property & get(const std::string &key)
get existing property
Definition property.cc:79
bool exists(const std::string &key) const
check whether property exists
Definition property.cc:122
T as() const
return value as type
Definition property.h:283
std::vector< Property * > Select(const std::string &filter)
select property based on a filter
Definition property.cc:185
void LoadFromXML(std::string filename)
Definition property.cc:238
Framework for threaded execution.
Definition thread.h:35
break string into words
Definition tokenizer.h:72
std::vector< T > ToVector()
store all words in a vector of type T, does type conversion.
Definition tokenizer.h:109
const std::string & getType() const
std::vector< Index > CreateIndexVector(const std::string &Ids) const
std::vector< std::unique_ptr< Region > >::iterator end()
Definition jobtopology.h:63
void ReadFromHdf5(std::string filename)
const std::vector< std::unique_ptr< Region > > & Regions() const
Definition jobtopology.h:67
void WriteToPdb(std::string filename) const
void BuildRegions(const Topology &top, std::pair< std::string, tools::Property > options)
void WriteToHdf5(std::string filename) const
std::vector< std::unique_ptr< Region > >::iterator begin()
Definition jobtopology.h:60
void setError(std::string error)
Definition job.h:67
void setOutput(std::string output)
Definition job.h:50
void setStatus(JobStatus stat)
Definition job.h:49
tools::Property & getInput()
Definition job.h:87
void ToStream(std::ofstream &ofs) const
Definition job.cc:121
std::string getTag() const
Definition job.h:86
Index getId() const
Definition job.h:85
Logger is used for thread-safe output of messages.
Definition logger.h:164
std::string which_segments_
Definition qmmm.h:59
Index max_iterations_
Definition qmmm.h:55
bool print_regions_pdb_
Definition qmmm.h:56
void WriteJobFile(const Topology &top)
Definition qmmm.cc:219
std::string getFirstRegionName() const
Definition qmmm.cc:209
bool hasQMRegion() const
Definition qmmm.cc:200
std::vector< QMState > states_
Definition qmmm.h:58
Job::JobResult EvalJob(const Topology &top, Job &job, QMThread &Thread)
Definition qmmm.cc:63
void ParseSpecificOptions(const tools::Property &user_options)
Definition qmmm.cc:44
std::pair< std::string, tools::Property > regions_def_
Definition qmmm.h:53
void ReadJobFile(Topology &top)
Definition qmmm.cc:281
bool use_gs_for_ex_
Definition qmmm.h:57
Job createJob(const Segment &seg, const QMState &state, Index jobid) const
Definition qmmm.cc:257
statetype Type() const
Definition qmstate.h:51
std::string ToString() const
Definition qmstate.cc:35
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
Definition qmstate.h:132
void FromString(const std::string &statestring)
Definition qmstate.cc:203
std::string ToString() const
Definition qmstate.cc:146
const QMStateType & Type() const
Definition qmstate.h:151
Timestamp returns the current time as a string Example: cout << TimeStamp()
Definition logger.h:224
Container for segments and box and atoms.
Definition topology.h:41
Index getStep() const
Definition topology.h:75
std::vector< Segment > & Segments()
Definition topology.h:58
#define XTP_LOG(level, log)
Definition logger.h:40
const double ev2hrt
Definition constants.h:54
const double hrt2ev
Definition constants.h:53
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26