81 std::vector<double> times;
82 for (
const Pulse& p : pulses) {
83 if (p.carriers < 0 || p.start < 0.0 || p.width < 0.0) {
84 throw std::runtime_error(
85 "KMCEngine: pulses need start >= 0, width >= 0 and carriers >= 0");
87 for (
Index c = 0; c < p.carriers; ++c) {
91 t = p.start + p.width * random.
Normal();
94 t = p.start + p.width * random.
Uniform();
99 std::sort(times.begin(), times.end());
122 const Limits& limits)
const {
123 if (!std::is_sorted(injection_times.begin(), injection_times.end())) {
124 throw std::runtime_error(
"KMCEngine: injection times must be sorted");
126 const double inf = std::numeric_limits<double>::infinity();
128 const Index ncarriers =
Index(injection_times.size());
129 const std::vector<Index>& sources =
graph_.SourceSites();
130 std::vector<Index> occupant(n, -1);
132 std::vector<Index> site_of(ncarriers, -1);
133 std::vector<char> decays_next(ncarriers, 0);
134 const std::vector<double>* decay =
decay_.rates;
137 using Pending = std::pair<double, Index>;
138 std::priority_queue<Pending, std::vector<Pending>, std::greater<Pending>>
142 Index next_carrier = 0;
146 const auto wall_start = std::chrono::steady_clock::now();
147 auto wall_exceeded = [&]() {
151 std::chrono::duration<double> elapsed =
152 std::chrono::steady_clock::now() - wall_start;
156 auto schedule = [&](
Index carrier) {
157 double tc =
strategy_.NextTime(site_of[carrier], t, random);
161 throw std::runtime_error(
162 "KMCEngine: the waiting-time strategy returned a time before the "
165 decays_next[carrier] = 0;
166 if (decay !=
nullptr) {
168 double k = (*decay)[site_of[carrier]];
173 decays_next[carrier] = 1;
178 pending.emplace(tc, carrier);
182 auto inject = [&]() {
183 Index carrier = next_carrier++;
184 if (carrier >=
Index(site_of.size())) {
185 site_of.resize(carrier + 1, -1);
186 decays_next.resize(carrier + 1, 0);
188 std::vector<Index> free;
189 free.reserve(sources.size());
190 std::copy_if(sources.begin(), sources.end(), std::back_inserter(free),
191 [&occupant](
Index s) { return occupant[s] < 0; });
197 occupant[s] = carrier;
198 site_of[carrier] = s;
201 observer.
Inject(carrier, s, t);
205 while (alive > 0 || next < ncarriers) {
206 double t_inject = (next < ncarriers) ? injection_times[next] : inf;
207 double t_event = pending.empty() ? inf : pending.top().first;
208 if (t_inject <= t_event) {
209 if (t_inject == inf || t_inject > limits.
max_time) {
224 if (wall_exceeded()) {
229 Index carrier = pending.top().second;
233 Index s = site_of[carrier];
234 if (decays_next[carrier]) {
236 site_of[carrier] = -1;
239 observer.
Decay(carrier, s, t);
256 throw std::runtime_error(
257 "KMCEngine: the waiting-time strategy chose an edge of another "
262 if (!sink && occupant[edge.
to] >= 0) {
269 observer.
Hop(carrier, s, edge.
to, edge.
dR, t);
271 site_of[carrier] = -1;
274 observer.
Absorb(carrier, edge.
to, t);
276 occupant[edge.
to] = carrier;
277 site_of[carrier] = edge.
to;