47 throw std::runtime_error(
"kmcmultiple cannot be run for state: " +
56 std::string method = options.
get(
".injectionmethod").
as<std::string>();
57 if (method !=
"random") {
58 throw std::runtime_error(
"kmcmultiple: injectionmethod must be random");
65 settings_.equilibration = options.
get(
".equilibration").
as<
double>();
66 settings_.limits.max_wall_time = options.
get(
".maxrealtime").
as<
double>();
73 throw std::runtime_error(
74 "kmcmultiple: runtime is a number of events (> 100), so outputtime "
75 "must be an integer number of hops too");
81 throw std::runtime_error(
"kmcmultiple: runtime must be positive");
99 log_.setMultithreading(
true);
100 log_.setCommonPreface(
"\n ...");
114 "\n KMC FOR MULTIPLE CHARGES"
115 "\n-----------------------------------"
126 double volume = top.
BoxVolume() * nm * nm * nm;
129 <<
"(s), density " << double(
settings_.carriers) / volume <<
" nm^-3, "
131 <<
" thread(s)" << std::flush;
134 <<
"Each run stops after " <<
settings_.limits.max_events
135 <<
" KMC events (hops and attempts into occupied sites)" << std::flush;
138 <<
" s simulated time" << std::flush;
142 std::unique_ptr<WaitingTimeStrategy> strategy =
146 <<
"Waiting times: " << strategy->Name() << std::flush;
147 auto start = std::chrono::steady_clock::now();
150 std::chrono::duration<double>(std::chrono::steady_clock::now() - start)
155 << std::setprecision(6) << result.
hops <<
" hops, " << result.
null_events
156 <<
" attempts into occupied sites ("
157 << 100.0 * double(result.
null_events) / std::max(attempts, 1.0) <<
" %), "
158 <<
seconds <<
" s" << std::flush;
161 <<
" run(s) stopped by maxrealtime" << std::flush;
165 <<
"Observation window of run 0: " << run0.
start <<
" s to " << run0.
end
169 std::string error_kind =
settings_.runs > 1
170 ?
"standard error over runs"
171 :
"standard error from the time blocks";
174 <<
"Drift velocity (nm/s): " << (result.
velocity * nm).transpose()
176 << error_kind <<
")" << std::flush;
181 const double to_m = nm * 1
e-9;
182 const bool driven =
F > 0.0 &&
Charge() != 0.0;
183 const Eigen::Vector3d dir =
184 F > 0.0 ? Eigen::Vector3d(
field_Vm_ /
F) : Eigen::Vector3d::Zero();
185 auto mobility = [&](
const Eigen::Vector3d& v) {
186 return Charge() * v.dot(dir) * to_m /
F;
193 std::sqrt((result.
velocity_error.cwiseProduct(dir)).squaredNorm()) *
196 <<
"Mobility along the field: " << mu * 1e4 <<
" +- " << mu_err * 1e4
197 <<
" cm^2/Vs (" << mu * 1e18 <<
" nm^2/Vs)" << std::flush;
200 Eigen::Matrix3d
D = result.
diffusion * nm * nm;
207 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> es(
D);
208 for (
Index i = 0; i < 3; ++i) {
210 <<
" eigenvalue " << es.eigenvalues()(i) <<
" nm^2/s, direction "
211 << es.eigenvectors().col(i).transpose() << std::flush;
214 auto einstein = [kT](
const Eigen::Matrix3d& Dnm) {
215 return Dnm.trace() / 3.0 * 1
e-18 / kT;
219 <<
"Einstein mobility D/kT from the mean eigenvalue (isotropic "
220 "medium, nondegenerate carriers): "
221 << einstein(
D) * 1e4 <<
" cm^2/Vs (" << einstein(
D) * 1e18
222 <<
" nm^2/Vs)" << std::flush;
230 <<
"Exact single-carrier values not available: " << exact.
message
233 Eigen::Matrix3d Dx = exact.
diffusion * nm * nm;
235 <<
"Exact for one carrier (master equation, " << exact.
solver <<
", "
237 <<
"):\n drift velocity (nm/s): "
238 << (exact.
velocity * nm).transpose() <<
"\n diffusion tensor "
243 <<
" mobility along the field: " << mobility(exact.
velocity) * 1e4
244 <<
" cm^2/Vs" << std::flush;
245 }
else if (
F == 0.0) {
247 <<
" Einstein mobility: " << einstein(Dx) * 1e4 <<
" cm^2/Vs"
252 <<
" Warning: the linear solver did not converge" << std::flush;
257 <<
" carriers the KMC includes site exclusion, the exact values "
258 "are for an isolated carrier)"
260 }
else if (driven && mu_err > 0.0) {
262 << (mu - mobility(exact.
velocity)) / mu_err
263 <<
" standard errors" << std::flush;
269 ResultFile out(
"kmcmultiple: bulk kinetic Monte Carlo");
274 out.
Add(
"window_start", run0.
start,
"s");
275 out.
Add(
"window_end", run0.
end,
"s");
276 const char* axes =
"xyz";
277 for (
Index a = 0; a < 3; ++a) {
278 out.
Add(std::string(
"velocity_") + axes[a], result.
velocity[a] * to_m,
280 out.
Add(std::string(
"velocity_error_") + axes[a],
284 out.
Add(
"mobility", mu * 1e4,
"cm^2/Vs");
285 out.
Add(
"mobility_error", mu_err * 1e4,
"cm^2/Vs");
287 auto tensor = [&out, axes](
const std::string& name,
288 const Eigen::Matrix3d& T) {
289 for (
Index a = 0; a < 3; ++a) {
290 for (
Index b = a; b < 3; ++b) {
291 out.
Add(name +
"_" + axes[a] + axes[b], T(a, b) * 1
e-18,
"m^2/s");
300 out.
Add(
"einstein_mobility", einstein(
D) * 1e4,
"cm^2/Vs");
304 for (
Index a = 0; a < 3; ++a) {
305 out.
Add(std::string(
"exact_velocity_") + axes[a],
309 out.
Add(
"exact_mobility", mobility(exact.
velocity) * 1e4,
"cm^2/Vs");
311 Eigen::Matrix3d Dx = exact.
diffusion * nm * nm;
312 tensor(
"exact_D", Dx);
314 out.
Add(
"exact_einstein_mobility", einstein(Dx) * 1e4,
"cm^2/Vs");
335 out <<
"#PairID,SiteID1,SiteID2, ,rate12[1/s],rate21[1/s] at "
341 out << pair->getId() <<
" " << pair->Seg1()->getId() <<
" "
342 << pair->Seg2()->getId() <<
" " << rates.
rate12 <<
" " << rates.
rate21
351 out <<
"#SiteID, Occupation prob at "
355 out << i <<
"\t" << result.
occupation[i] <<
"\n";
358 <<
"Occupation probabilities written to " <<
occfile_ << std::flush;
363 out <<
"time[s]\tsteps";
365 out <<
"\tcarrier" << c + 1 <<
" x\tcarrier" << c + 1 <<
" y\tcarrier"
371 out << s.time <<
"\t" << s.hops;
372 for (
const Eigen::Vector3d& r : s.positions) {
373 out <<
"\t" << r.x() * nm <<
"\t" << r.y() * nm <<
"\t" << r.z() * nm;
383 out <<
"time[s]\tsteps\tenergy_per_carrier[eV]\tmobility[nm**2/Vs]\t"
384 "distance_fielddirection[nm]\tdistance_absolute[nm]\n";
390 const std::vector<Eigen::Vector3d>& r0 = result.
trajectory.front().positions;
393 Eigen::Vector3d dr = Eigen::Vector3d::Zero();
394 for (
Index c = 0; c <
Index(s.positions.size()); ++c) {
395 dr += s.positions[c] - r0[c];
397 dr *= nm / double(s.positions.size());
398 double along = (
F > 0.0) ? dr.dot(
field_Vm_) /
F : 0.0;
399 double mobility = 0.0;
400 if (
F > 0.0 && s.time > t0) {
402 mobility =
Charge() * along / (s.time - t0) / (
F * 1
e-9);
405 <<
"\t" << mobility <<
"\t" << along <<
"\t" << dr.norm() <<
"\n";
Kinetic Monte Carlo of a fixed number of carriers with site exclusion in a periodic box (bulk transpo...
static ExactResult Exact(const ToFGraph &graph, Index direct_limit=20000, double tolerance=1e-12)
direct sparse LU up to direct_limit sites, BiCGSTAB with ILUT above
void WriteOccupation(const ToFGraph &graph, const BulkSimulation::Result &result)
void WriteRates(const Topology &top)
BulkSimulation::Settings settings_
std::string trajectoryfile_
std::string ignore_types_
Eigen::Vector3d field_Vm_
std::string injection_pattern_
void ParseOptions(const tools::Property &options) override
void WriteTrajectory(const BulkSimulation::Result &result)
void WriteTimeFile(const BulkSimulation::Result &result)
bool Evaluate(Topology &top) override
double Charge() const
sign of the carrier charge (0 for neutral excitations)
PairRates Rate(const QMPair &pair, QMStateType carriertype) const
Small machine-readable result file of a calculator: one "key value unit" line per quantity (unit "-" ...
void Write(const std::string &filename) const
void Add(const std::string &key, double value, const std::string &unit)
the rates stored in the ToFGraph, constant in time
Hopping graph for time-of-flight (ToF) transport.
Index NumberOfSites() const
static Options BulkOptions(const std::string &injection_pattern, const std::string &ignore_types)
std::string Summary() const
human readable summary
Container for segments and box and atoms.
#define XTP_LOG(level, log)
Charge transport classes.
std::unique_ptr< WaitingTimeStrategy > MakeWaitingTimeStrategy(const std::string &name)
strategy by name: exponential, thinning, inversion
Provides a means for comparing floating point numbers.
static Level current_level
Eigen::Matrix3d diffusion
std::vector< Sample > trajectory
Eigen::Matrix3d diffusion_error
standard error over runs, zero for one run
Eigen::VectorXd occupation
occupation probability per site, averaged over runs
Eigen::Matrix3d diffusion
std::vector< RunData > runs
Eigen::Vector3d velocity_error
double start
observation window [start, end]
state of run 0 at a sampling time