52bool MatchesPattern(
const std::string& pattern,
const std::string& name) {
56std::vector<std::string> SplitTypes(
const std::string& list) {
57 return tools::Tokenizer(list,
", \t\n").ToVector();
62 const std::string& ignore_types) {
67 opt.
source_pattern = (injection_pattern ==
"*") ?
"" : injection_pattern;
75 std::string axis = tof.
get(
"axis").
as<std::string>();
78 }
else if (axis ==
"b") {
80 }
else if (axis ==
"c") {
83 throw std::runtime_error(
"ToFGraph: axis must be a, b or c, not " + axis);
97 double temperature,
const Eigen::Vector3d& field)
100 throw std::runtime_error(
"ToFGraph: " + carrier_.ToLongString() +
101 " is not a hopping state");
104 throw std::runtime_error(
"ToFGraph: the temperature must be positive");
106 if (
opt_.axis < 0 ||
opt_.axis > 2) {
107 throw std::runtime_error(
"ToFGraph: axis must be 0, 1 or 2");
109 bool source_criterion =
110 opt_.source_thickness > 0.0 || !
opt_.source_pattern.empty();
111 bool sink_criterion =
opt_.sink_thickness > 0.0 || !
opt_.sink_pattern.empty();
113 if (opt_.source_thickness > 0.0 || sink_criterion) {
114 throw std::runtime_error(
115 "ToFGraph: a bulk graph has no slabs and no sinks");
117 }
else if (!source_criterion || !sink_criterion) {
118 throw std::runtime_error(
119 "ToFGraph: source and sink each need a slab thickness and/or a "
122 if (top.Segments().empty()) {
123 throw std::runtime_error(
"ToFGraph: the topology contains no segments");
135 const Eigen::Matrix3d& box = top.
getBox();
136 double volume = std::abs(box.determinant());
137 if (!(volume > 0.0)) {
138 throw std::runtime_error(
139 "ToFGraph: the box is degenerate, a periodic box is required");
142 const Eigen::Vector3d u = box.col((
opt_.axis + 1) % 3);
143 const Eigen::Vector3d v = box.col((
opt_.axis + 2) % 3);
144 normal_ = u.cross(v).normalized();
151 throw std::runtime_error(
152 "ToFGraph: source and sink slabs together are thicker than the box");
155 const Eigen::Matrix3d inv = box.inverse();
156 const double charge = CarrierCharge(
carrier_);
157 const std::vector<Segment>& segs = top.
Segments();
158 sites_.resize(segs.size());
159 for (
Index i = 0; i <
Index(segs.size()); ++i) {
161 if (seg.
getId() != i) {
162 throw std::runtime_error(
163 "ToFGraph: segment ids must be consecutive and start at 0");
167 Eigen::Vector3d frac = inv * seg.
getPos();
168 frac = frac.array() - frac.array().floor();
170 for (
Index d = 0; d < 3; ++d) {
171 if (frac[d] >= 1.0) {
175 site.
pos = box * frac;
180 std::any_of(
opt_.ignore_types.begin(),
opt_.ignore_types.end(),
181 [&seg](
const std::string& p) {
182 return MatchesPattern(p, seg.getType());
190 const bool source_slab =
191 !(
opt_.source_thickness > 0.0) || site.
x <
opt_.source_thickness;
192 const bool source_type =
opt_.source_pattern.empty() ||
193 MatchesPattern(
opt_.source_pattern, seg.
getType());
194 const bool sink_slab = !(
opt_.sink_thickness > 0.0) ||
196 const bool sink_type =
opt_.sink_pattern.empty() ||
198 const bool source = source_slab && source_type;
199 const bool sink = sink_slab && sink_type && !
opt_.bulk;
200 if (source && sink) {
201 throw std::runtime_error(
"ToFGraph: segment " + std::to_string(i) +
202 " is selected as source and as sink");
212 throw std::runtime_error(
"ToFGraph: no sink sites selected");
217 const Eigen::Matrix3d& box = top.
getBox();
218 const Eigen::Matrix3d inv = box.inverse();
226 std::vector<RawEdge> raw;
227 raw.reserve(2 * nblist.
size());
229 for (
const QMPair* pair : nblist) {
230 Index i = pair->Seg1()->getId();
231 Index j = pair->Seg2()->getId();
232 const Eigen::Vector3d& R = pair->R();
234 Eigen::Vector3d shift = inv * (R - (
sites_[j].pos -
sites_[i].pos));
235 Eigen::Vector3d rounded = shift.array().round();
236 if ((shift - rounded).cwiseAbs().maxCoeff() > 1
e-6) {
237 throw std::runtime_error(
238 "ToFGraph: the vector of pair " + std::to_string(pair->getId()) +
239 " does not connect its segments or a periodic image of them");
244 if (!
opt_.bulk && rounded[
opt_.axis] != 0.0) {
249 if (!std::isfinite(rates.
rate12) || !std::isfinite(rates.
rate21) ||
251 throw std::runtime_error(
"ToFGraph: invalid rate for pair " +
252 std::to_string(pair->getId()));
261 raw.push_back({i,
Edge{j, rates.
rate12, R, pair->getId()}});
262 raw.push_back({j,
Edge{i, rates.
rate21, -R, pair->getId()}});
268 for (
const RawEdge&
e : raw) {
271 for (
Index i = 0; i < n; ++i) {
274 edges_.resize(raw.size());
276 for (
const RawEdge&
e : raw) {
280 for (
Index i = 0; i < n; ++i) {
290 std::vector<std::vector<Index>> incoming(n);
291 for (
Index i = 0; i < n; ++i) {
294 incoming[
e.to].push_back(i);
298 std::queue<Index> queue;
300 sites_[s].reaches_sink =
true;
303 while (!queue.empty()) {
304 Index j = queue.front();
306 for (
Index i : incoming[j]) {
307 if (!
sites_[i].reaches_sink) {
308 sites_[i].reaches_sink =
true;
315 for (
Index i = 0; i < n; ++i) {
316 if (!
sites_[i].reaches_sink) {
320 if (
sites_[i].reaches_sink) {
329 throw std::runtime_error(
330 "ToFGraph: no source site from which a sink can be reached (" +
332 " source sites without connection to a sink)");
335 double min_sink_x = std::accumulate(
336 sinks_.begin(),
sinks_.end(), std::numeric_limits<double>::max(),
337 [
this](
double m,
Index s) { return std::min(m, sites_[s].x); });
340 throw std::runtime_error(
341 "ToFGraph: the sink sites are not further along the transport axis "
342 "than the source sites");
353 throw std::runtime_error(
354 "ToFGraph: no injection site matches the pattern '" +
355 opt_.source_pattern +
"'");
363 const char* axes[] = {
"a",
"b",
"c"};
365 s <<
"Bulk hopping graph (periodic in a, b and c, no sinks) for "
366 <<
carrier_.ToLongString() <<
"s\n";
369 s <<
" injection sites: " <<
sources_.size() <<
"\n";
370 s <<
" detailed balance: " << (
reversible_ ?
"yes" :
"no")
371 <<
" (max deviation of ln(w_ij/w_ji) " <<
db_deviation_ <<
")";
374 s <<
"ToF graph for " <<
carrier_.ToLongString() <<
"s\n";
375 s <<
" periodicity cut across box vector " << axes[
opt_.axis] <<
", normal ("
377 <<
"), thickness " <<
thickness_ * nm <<
" nm\n";
380 <<
", pairs cut at the periodic boundary: " <<
cut_pairs_ <<
"\n";
381 s <<
" sources: " <<
sources_.size() <<
" connected, "
383 s <<
" sinks: " <<
sinks_.size()
385 s <<
" transport length (mean source to first sink): " <<
length_ * nm
387 s <<
" detailed balance: " << (
reversible_ ?
"yes" :
"no")
388 <<
" (max deviation of ln(w_ij/w_ji) " <<
db_deviation_ <<
")";
const std::string & getType() const
const Eigen::Vector3d & getPos() const
PairRates Rate(const QMPair &pair, QMStateType carriertype) const
double getSiteEnergy(QMStateType state) const
void SetupSites(const Topology &top)
std::vector< double > escape_rates_
std::vector< Index > disconnected_sources_
std::vector< Site > sites_
Index NumberOfSites() const
std::vector< Index > offsets_
std::vector< Index > sources_
std::vector< Edge > edges_
ToFGraph(const Topology &top, const Options &opt, QMStateType carrier, double temperature, const Eigen::Vector3d &field)
temperature as kT in Hartree, field in Hartree/bohr (as Rate_Engine)
static Options BulkOptions(const std::string &injection_pattern, const std::string &ignore_types)
Index NumberOfEdges() const
EdgeRange OutEdges(Index i) const
Index sites_without_sink_
std::vector< Index > sinks_
static Options OptionsFromProperty(const tools::Property &tof)
Options from the "tof" subpackage (subpackages/tof.xml), lengths in nm.
void SetupEdges(const Topology &top)
std::string Summary() const
human readable summary
Container for segments and box and atoms.
std::vector< Segment > & Segments()
const Eigen::Matrix3d & getBox() const
Charge transport classes.
Provides a means for comparing floating point numbers.
std::string source_pattern
std::vector< std::string > ignore_types
segment type wildcards of segments without edges
Index axis
box vector across which the periodicity is cut (0: a, 1: b, 2: c)
double potential
energy + field term, U = E - q F.r: hops release U_i - U_j
double energy
site energy of the carrier
bool ignored
segment type in Options::ignore_types, no edges
double x
coordinate along the transport normal, in [0, thickness)
Eigen::Vector3d pos
position with the fractional coordinates wrapped into [0,1)