votca 2024.1-dev
Loading...
Searching...
No Matches
kmcmultiple.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// Standard includes
19#include <chrono>
20
21// Third party includes
22#include <boost/format.hpp>
23#include <boost/numeric/conversion/cast.hpp>
24
25// VOTCA includes
28
29// Local VOTCA includes
30#include "votca/xtp/gnode.h"
31#include "votca/xtp/qmstate.h"
32#include "votca/xtp/topology.h"
33
34// Local private VOTCA includes
35#include "kmcmultiple.h"
36
37namespace votca {
38namespace xtp {
40
41 runtime_ = options.get(".runtime").as<double>();
42 field_ = options.get(".field").as<Eigen::Vector3d>();
43 double mtobohr = 1E9 * tools::conv::nm2bohr;
44 field_ *=
45 (tools::conv::ev2hrt / mtobohr); // Converting from V/m to Hartree/bohr
46
47 outputtime_ = options.get(".outputtime").as<double>();
48 timefile_ = options.ifExistsReturnElseReturnDefault<std::string>(".timefile",
49 timefile_);
50
51 std::string carriertype = options.get(".carriertype").as<std::string>();
52 carriertype_ = QMStateType(carriertype);
53 if (!carriertype_.isKMCState()) {
54 throw std::runtime_error("KMC cannot be run for state:" +
56 }
57
59 log_.setCommonPreface("\n ...");
60}
61
62void KMCMultiple::PrintDiffandMu(const Eigen::Matrix3d& avgdiffusiontensor,
63 double simtime, unsigned long step) {
64 double absolute_field = field_.norm();
65
66 if (absolute_field == 0) {
67 unsigned long diffusionsteps = step / diffusionresolution_;
68 Eigen::Matrix3d result =
69 avgdiffusiontensor /
70 (double(diffusionsteps) * 2.0 * simtime * double(numberofcarriers_));
72 << "\nStep: " << step
73 << " Diffusion tensor averaged over all carriers (nm^2/s):\n"
74 << result * tools::conv::bohr2nm * tools::conv::bohr2nm << std::flush;
75 } else {
76 double average_mobility = 0;
77 double bohr2Hrts_to_nm2Vs =
79 XTP_LOG(Log::error, log_) << "\nMobilities (nm^2/Vs): " << std::flush;
80 for (Index i = 0; i < numberofcarriers_; i++) {
81 Eigen::Vector3d velocity = carriers_[i].get_dRtravelled() / simtime;
82 double mobility =
83 velocity.dot(field_) / (absolute_field * absolute_field);
85 << std::scientific << " carrier " << i + 1
86 << ": mu=" << mobility * bohr2Hrts_to_nm2Vs << std::flush;
87 average_mobility +=
88 velocity.dot(field_) / (absolute_field * absolute_field);
89 }
90 average_mobility /= double(numberofcarriers_);
92 << std::scientific
93 << " Overall average mobility in field direction <mu>="
94 << average_mobility * bohr2Hrts_to_nm2Vs << " nm^2/Vs " << std::flush;
95 }
96}
97
98void KMCMultiple::WriteToTrajectory(std::fstream& traj,
99 std::vector<Eigen::Vector3d>& startposition,
100 double simtime, unsigned long step) const {
101 traj << simtime << "\t";
102 traj << step << "\t";
103 for (Index i = 0; i < numberofcarriers_; i++) {
104 Eigen::Vector3d pos = startposition[i] + carriers_[i].get_dRtravelled();
105 traj << pos.x() * tools::conv::bohr2nm << "\t";
106 traj << pos.y() * tools::conv::bohr2nm << "\t";
107 traj << pos.z() * tools::conv::bohr2nm;
108 if (i < numberofcarriers_ - 1) {
109 traj << "\t";
110 } else {
111 traj << std::endl;
112 }
113 }
114}
115
116void KMCMultiple::WriteToEnergyFile(std::fstream& tfile, double simtime,
117 unsigned long step) const {
118 double absolute_field = field_.norm();
119 double currentenergy = 0;
120 double currentmobility = 0;
121 Eigen::Vector3d dr_travelled_current = Eigen::Vector3d::Zero();
122 double dr_travelled_field = 0.0;
123 Eigen::Vector3d avgvelocity_current = Eigen::Vector3d::Zero();
124 if (absolute_field != 0) {
125 for (const auto& carrier : carriers_) {
126 dr_travelled_current += carrier.get_dRtravelled();
127 currentenergy += carrier.getCurrentEnergy();
128 }
129 dr_travelled_current /= double(numberofcarriers_);
130 currentenergy /= double(numberofcarriers_);
131 avgvelocity_current = dr_travelled_current / simtime;
132 currentmobility =
133 avgvelocity_current.dot(field_) / (absolute_field * absolute_field);
134 dr_travelled_field = dr_travelled_current.dot(field_) / absolute_field;
135 }
136 double bohr2Hrts_to_nm2Vs =
138 tfile << simtime << "\t" << step << "\t"
139 << currentenergy * tools::conv::hrt2ev << "\t"
140 << currentmobility * bohr2Hrts_to_nm2Vs << "\t"
141 << dr_travelled_field * tools::conv::bohr2nm << "\t"
142 << dr_travelled_current.norm() * tools::conv::bohr2nm << "\t"
143 << std::endl;
144}
145
146void KMCMultiple::PrintDiagDandMu(const Eigen::Matrix3d& avgdiffusiontensor,
147 double simtime, unsigned long step) {
148 unsigned long diffusionsteps = step / diffusionresolution_;
149 Eigen::Matrix3d result =
150 avgdiffusiontensor /
151 (double(diffusionsteps) * 2.0 * simtime * double(numberofcarriers_));
152
153 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> es;
154 es.computeDirect(result);
155 double bohr2_nm2 = tools::conv::bohr2nm * tools::conv::bohr2nm;
156 XTP_LOG(Log::error, log_) << "\nEigenvalues:\n " << std::flush;
157 for (Index i = 0; i < 3; i++) {
159 << "Eigenvalue: " << es.eigenvalues()(i) * bohr2_nm2 << std::flush
160 << "Eigenvector: " << es.eigenvectors().col(i).x() << " "
161 << es.eigenvectors().col(i).y() << " " << es.eigenvectors().col(i).z()
162 << "\n"
163 << std::flush;
164 }
165 double bohr2Hrts_to_nm2Vs =
167 // calculate average mobility from the Einstein relation
168 if (field_.norm() == 0) {
170 << "The following value is calculated using the Einstein relation "
171 "and assuming an isotropic medium"
172 << std::flush;
173 double avgD = 1. / 3. * es.eigenvalues().sum();
174 double average_mobility = std::abs(avgD / temperature_);
176 << std::scientific << " Overall average mobility <mu>="
177 << average_mobility * bohr2Hrts_to_nm2Vs << " nm^2/Vs " << std::flush;
178 }
179}
180
182 Eigen::Vector3d avg_dr_travelled = Eigen::Vector3d::Zero();
183 for (Index i = 0; i < numberofcarriers_; i++) {
185 << std::scientific << " carrier " << i + 1 << ": "
186 << carriers_[i].get_dRtravelled().transpose() / simtime *
188 << std::flush;
189 avg_dr_travelled += carriers_[i].get_dRtravelled();
190 }
191 avg_dr_travelled /= double(numberofcarriers_);
192
193 Eigen::Vector3d avgvelocity = avg_dr_travelled / simtime;
195 << std::scientific << " Overall average velocity (nm/s): "
196 << avgvelocity.transpose() * tools::conv::bohr2nm << std::flush;
197}
198
200
201 std::chrono::time_point<std::chrono::system_clock> realtime_start =
202 std::chrono::system_clock::now();
204 << "\nAlgorithm: VSSM for Multiple Charges" << std::flush;
206 << "number of carriers: " << numberofcarriers_ << std::flush;
208 << "number of nodes: " << nodes_.size() << std::flush;
209
210 bool checkifoutput = (outputtime_ != 0);
211 double nexttrajoutput = 0;
212 unsigned long maxsteps = boost::numeric_cast<unsigned long>(runtime_);
213 unsigned long outputstep = boost::numeric_cast<unsigned long>(outputtime_);
214 bool stopontime = false;
215
216 if (runtime_ > 100) {
218 << "stop condition: " << maxsteps << " steps." << std::flush;
219
220 if (checkifoutput) {
221 XTP_LOG(Log::error, log_) << "output frequency: ";
223 << "every " << outputstep << " steps." << std::flush;
224 }
225 } else {
226 stopontime = true;
228 << "stop condition: " << runtime_ << " seconds runtime." << std::flush;
229
230 if (checkifoutput) {
231 XTP_LOG(Log::error, log_) << "output frequency:\n "
232 "every "
233 << outputtime_ << " seconds." << std::flush;
234 }
235 }
237 << "(If you specify runtimes larger than 100 kmcmultiple assumes that "
238 "you are specifying the number of steps for both runtime and "
239 "outputtime.)"
240 << std::flush;
241
242 if (!stopontime && outputtime_ != 0 && floor(outputtime_) != outputtime_) {
243 throw std::runtime_error(
244 "ERROR in kmcmultiple: runtime was specified in steps (>100) and "
245 "outputtime in seconds (not an integer). Please use the same units for "
246 "both input parameters.");
247 }
248
249 if (numberofcarriers_ > Index(nodes_.size())) {
250 throw std::runtime_error(
251 "ERROR in kmcmultiple: specified number of carriers is greater than "
252 "the "
253 "number of nodes. This conflicts with single occupation.");
254 }
255
256 std::fstream traj;
257 std::fstream tfile;
258
259 if (checkifoutput) {
260
262 << "Writing trajectory to " << trajectoryfile_ << "." << std::flush;
263 traj.open(trajectoryfile_, std::fstream::out);
264
265 traj << "time[s]\tsteps\t";
266 for (Index i = 0; i < numberofcarriers_; i++) {
267 traj << "carrier" << i + 1 << " x_\t";
268 traj << "carrier" << i + 1 << " y_\t";
269 traj << "carrier" << i + 1 << " z_";
270 if (i < numberofcarriers_ - 1) {
271 traj << "'\t";
272 }
273 }
274 traj << std::endl;
275 if (!timefile_.empty()) {
277 << "Writing time dependence of energy and mobility to " << timefile_
278 << "." << std::flush;
279 tfile.open(timefile_, std::fstream::out);
280 tfile << "time[s]\t "
281 "steps\tenergy_per_carrier[eV]\tmobility[nm**2/"
282 "Vs]\tdistance_fielddirection[nm]\tdistance_absolute[nm]"
283 << std::endl;
284 }
285 }
287 std::vector<Eigen::Vector3d> startposition(numberofcarriers_,
288 Eigen::Vector3d::Zero());
289 for (Index i = 0; i < numberofcarriers_; i++) {
290 startposition[i] = carriers_[i].getCurrentPosition();
291 }
292
293 traj << 0 << "\t";
294 traj << 0 << "\t";
295 for (Index i = 0; i < numberofcarriers_; i++) {
296 traj << startposition[i].x() * tools::conv::bohr2nm << "\t";
297 traj << startposition[i].y() * tools::conv::bohr2nm << "\t";
298 traj << startposition[i].z() * tools::conv::bohr2nm;
299 if (i < numberofcarriers_ - 1) {
300 traj << "\t";
301 } else {
302 traj << std::endl;
303 }
304 }
305
306 std::vector<GNode*> forbiddennodes;
307 std::vector<GNode*> forbiddendests;
308
309 Eigen::Matrix3d avgdiffusiontensor = Eigen::Matrix3d::Zero();
310
311 double simtime = 0.0;
312 unsigned long step = 0;
313
314 while (((stopontime && simtime < runtime_) ||
315 (!stopontime && step < maxsteps))) {
316
317 std::chrono::duration<double> elapsed_time =
318 std::chrono::system_clock::now() - realtime_start;
319 if (elapsed_time.count() > (maxrealtime_ * 60. * 60.)) {
321 << "\nReal time limit of " << maxrealtime_ << " hours ("
322 << Index(maxrealtime_ * 60 * 60 + 0.5)
323 << " seconds) has been reached. Stopping here.\n"
324 << std::flush;
325 break;
326 }
327
328 double cumulated_rate = 0;
329 for (const auto& carrier : carriers_) {
330 cumulated_rate += carrier.getCurrentEscapeRate();
331 }
332 if (cumulated_rate <= 0) { // this should not happen: no possible jumps
333 // defined for a node
334 throw std::runtime_error(
335 "ERROR in kmcmultiple: Incorrect rates. All "
336 "the escape rates for the current setting are 0.");
337 }
338
339 double dt = Promotetime(cumulated_rate);
340
341 simtime += dt;
342 step++;
343
344 for (auto& carrier : carriers_) {
345 carrier.updateOccupationtime(dt);
346 }
347
348 ResetForbiddenlist(forbiddennodes);
349 bool level1step = true;
350 while (level1step) {
351
352 // determine which electron will escape
353 GNode* newnode = nullptr;
354 Chargecarrier* affectedcarrier = ChooseAffectedCarrier(cumulated_rate);
355
356 if (CheckForbidden(affectedcarrier->getCurrentNode(), forbiddennodes)) {
357 continue;
358 }
359 ResetForbiddenlist(forbiddendests);
360 while (true) {
361 // LEVEL 2
362
363 const GLink& event =
364 ChooseHoppingDest(affectedcarrier->getCurrentNode());
365 newnode = event.getDestination();
366
367 if (newnode == nullptr) {
368 AddtoForbiddenlist(affectedcarrier->getCurrentNode(), forbiddennodes);
369 break; // select new escape node (ends level 2 but without setting
370 // level1step to 1)
371 }
372
373 // check after the event if this was allowed
374 if (CheckForbidden(*newnode, forbiddendests)) {
375 continue;
376 }
377
378 // if the new segment is unoccupied: jump; if not: add to forbidden
379 // list and choose new hopping destination
380 if (newnode->isOccupied()) {
381 if (CheckSurrounded(affectedcarrier->getCurrentNode(),
382 forbiddendests)) {
383 AddtoForbiddenlist(affectedcarrier->getCurrentNode(),
384 forbiddennodes);
385 break; // select new escape node (ends level 2 but without
386 // setting level1step to 1)
387 }
388 AddtoForbiddenlist(*newnode, forbiddendests);
389 continue; // select new destination
390 } else {
391 affectedcarrier->jumpAccordingEvent(event);
392 level1step = false;
393 break; // this ends LEVEL 2 , so that the time is updated and the
394 // next MC step started
395 }
396 // END LEVEL 2
397 }
398 // END LEVEL 1
399 }
400
401 if (step % diffusionresolution_ == 0) {
402 for (const auto& carrier : carriers_) {
403 avgdiffusiontensor += (carrier.get_dRtravelled()) *
404 (carrier.get_dRtravelled()).transpose();
405 }
406 }
407
408 if (step != 0 && step % intermediateoutput_frequency_ == 0) {
409 PrintDiffandMu(avgdiffusiontensor, simtime, step);
410 }
411
412 if (checkifoutput) {
413 bool outputsteps = (!stopontime && step % outputstep == 0);
414 bool outputtime = (stopontime && simtime > nexttrajoutput);
415 if (outputsteps || outputtime) {
416 // write to trajectory file
417 nexttrajoutput = simtime + outputtime_;
418 WriteToTrajectory(traj, startposition, simtime, step);
419 if (!timefile_.empty()) {
420 WriteToEnergyFile(tfile, simtime, step);
421 }
422 }
423 }
424 } // KMC
425
426 if (checkifoutput) {
427 traj.close();
428 if (!timefile_.empty()) {
429 tfile.close();
430 }
431 }
432
434
435 XTP_LOG(Log::error, log_) << "\nfinished KMC simulation after " << step
436 << " steps.\n"
437 "simulated time "
438 << simtime << " seconds.\n"
439 << std::flush;
440
441 PrintChargeVelocity(simtime);
442
443 XTP_LOG(Log::error, log_) << "\nDistances travelled (nm): " << std::flush;
444 for (Index i = 0; i < numberofcarriers_; i++) {
446 << std::scientific << " carrier " << i + 1 << ": "
447 << carriers_[i].get_dRtravelled().transpose() * tools::conv::bohr2nm
448 << std::flush;
449 }
450
451 PrintDiffandMu(avgdiffusiontensor, simtime, step);
452 PrintDiagDandMu(avgdiffusiontensor, simtime, step);
453
454 return;
455}
456
458
459 XTP_LOG(Log::error, log_) << "\n-----------------------------------"
460 "\n KMC FOR MULTIPLE CHARGES"
461 "\n-----------------------------------\n"
462 << std::flush;
463
464 XTP_LOG(Log::info, log_) << "\nInitialising random number generator"
465 << std::flush;
467
468 LoadGraph(top);
469 RunVSSM();
470 std::cout << log_;
471 return true;
472}
473
474} // namespace xtp
475} // namespace votca
class to manage program options with xml serialization functionality
Definition property.h:55
Property & get(const std::string &key)
get existing property
Definition property.cc:79
T as() const
return value as type
Definition property.h:283
T ifExistsReturnElseReturnDefault(const std::string &key, T defaultvalue) const
Definition property.h:321
void init(Index seed)
Definition random.h:32
GNode & getCurrentNode() const
void jumpAccordingEvent(const GLink &event)
bool isOccupied() const
Definition gnode.h:39
void ResetForbiddenlist(std::vector< GNode * > &forbiddenid) const
void LoadGraph(Topology &top)
std::vector< Chargecarrier > carriers_
double Promotetime(double cumulated_rate)
Chargecarrier * ChooseAffectedCarrier(double cumulated_rate)
const GLink & ChooseHoppingDest(const GNode &node)
void AddtoForbiddenlist(GNode &node, std::vector< GNode * > &forbiddenid) const
bool CheckSurrounded(const GNode &node, const std::vector< GNode * > &forbiddendests) const
tools::Random RandomVariable_
std::vector< GNode > nodes_
void WriteOccupationtoFile(double simtime, std::string filename)
bool CheckForbidden(const GNode &node, const std::vector< GNode * > &forbiddenlist) const
bool Evaluate(Topology &top)
unsigned long diffusionresolution_
Definition kmcmultiple.h:63
void PrintChargeVelocity(double simtime)
void WriteToTrajectory(std::fstream &traj, std::vector< Eigen::Vector3d > &startposition, double simtime, unsigned long step) const
void WriteToEnergyFile(std::fstream &tfile, double simtime, unsigned long step) const
void PrintDiffandMu(const Eigen::Matrix3d &avgdiffusiontensor, double simtime, unsigned long step)
void PrintDiagDandMu(const Eigen::Matrix3d &avgdiffusiontensor, double simtime, unsigned long step)
void ParseSpecificOptions(const tools::Property &user_options)
void setReportLevel(Log::Level ReportLevel)
Definition logger.h:185
void setCommonPreface(const std::string &preface)
Definition logger.h:198
std::string ToLongString() const
Definition qmstate.cc:69
bool isKMCState() const
Definition qmstate.h:75
Container for segments and box and atoms.
Definition topology.h:41
#define XTP_LOG(level, log)
Definition logger.h:40
const double ev2hrt
Definition constants.h:54
const double bohr2nm
Definition constants.h:46
const double hrt2ev
Definition constants.h:53
const double nm2bohr
Definition constants.h:47
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
static Level current_level
Definition globals.h:30