data_t::DataUpdate handleEventsGillespie( CPUKernel *const kernel, scalar timeStep, bool filterEventsInAdvance, bool approximateRate, std::vector<event_t> &&events, std::vector<record_t> *maybeRecords, reaction_counts_map *maybeCounts) { using rdy_particle_t = readdy::model::Particle; const auto &box = kernel->context().boxSize().data(); const auto &pbc = kernel->context().periodicBoundaryConditions().data(); data_t::EntriesUpdate newParticles{}; std::vector<data_t::size_type> decayedEntries{}; if (!events.empty()) { const auto &ctx = kernel->context(); auto data = kernel->getCPUKernelStateModel().getParticleData(); /** * Handle gathered reaction events */ { auto shouldEval = [&](const event_t &event) { return filterEventsInAdvance || shouldPerformEvent(event.rate, timeStep, approximateRate); }; auto depending = [&](const event_t &e1, const event_t &e2) { return (e1.idx1 == e2.idx1 || (e2.nEducts == 2 && e1.idx1 == e2.idx2) || (e1.nEducts == 2 && (e1.idx2 == e2.idx1 || (e2.nEducts == 2 && e1.idx2 == e2.idx2)))); }; auto eval = [&](const event_t &event) { auto entry1 = event.idx1; if (event.nEducts == 1) { auto reaction = ctx.reactions().order1ByType(event.t1)[event.reactionIndex]; if (maybeRecords != nullptr) { record_t record; record.id = reaction->id(); performReaction(data, ctx, entry1, entry1, newParticles, decayedEntries, reaction, &record); bcs::fixPosition(record.where, box, pbc); maybeRecords->push_back(record); } else { performReaction(data, ctx, entry1, entry1, newParticles, decayedEntries, reaction, nullptr); } if (maybeCounts != nullptr) { auto &counts = *maybeCounts; counts.at(reaction->id())++; } } else { auto reaction = ctx.reactions().order2ByType(event.t1, event.t2)[event.reactionIndex]; if (maybeRecords != nullptr) { record_t record; record.id = reaction->id(); performReaction(data, ctx, entry1, event.idx2, newParticles, decayedEntries, reaction, &record); bcs::fixPosition(record.where, box, pbc); maybeRecords->push_back(record); } else { performReaction(data, ctx, entry1, event.idx2, newParticles, decayedEntries, reaction, nullptr); } if (maybeCounts != nullptr) { auto &counts = *maybeCounts; counts.at(reaction->id())++; } } }; algo::performEvents(events, shouldEval, depending, eval); } } return std::make_pair(std::move(newParticles), std::move(decayedEntries)); }
void CPUUncontrolledApproximation::perform() { const auto &ctx = kernel->context(); auto &stateModel = kernel->getCPUKernelStateModel(); auto nl = stateModel.getNeighborList(); auto &data = nl->data(); const auto &box = ctx.boxSize().data(); const auto &pbc = ctx.periodicBoundaryConditions().data(); if (ctx.recordReactionsWithPositions()) { kernel->getCPUKernelStateModel().reactionRecords().clear(); } if (ctx.recordReactionCounts()) { stateModel.resetReactionCounts(); } // gather events std::vector<std::promise<std::size_t>> n_events_promises(kernel->getNThreads()); std::vector<event_promise_t> promises(kernel->getNThreads()); { auto &pool = kernel->pool(); std::vector<std::function<void(std::size_t)>> executables; executables.reserve(kernel->getNThreads()); std::size_t grainSize = data.size() / kernel->getNThreads(); std::size_t nlGrainSize = nl->nCells() / kernel->getNThreads(); auto it = data.cbegin(); std::size_t it_nl = 0; for (auto i = 0U; i < kernel->getNThreads()-1 ; ++i) { auto itNext = std::min(it+grainSize, data.cend()); auto nlNext = std::min(it_nl + nlGrainSize, nl->nCells()); auto bounds_nl = std::make_tuple(it_nl, nlNext); pool.push(findEvents, it, itNext, bounds_nl, kernel, timeStep(), false, std::cref(*nl), std::ref(promises.at(i)), std::ref(n_events_promises.at(i))); it = itNext; it_nl = nlNext; } pool.push(findEvents, it, data.cend(), std::make_tuple(it_nl, nl->nCells()), kernel, timeStep(), false, std::cref(*nl), std::ref(promises.back()), std::ref(n_events_promises.back())); } // collect events std::vector<event_t> events; { std::size_t n_events = 0; for (auto &&f : n_events_promises) { n_events += f.get_future().get(); } events.reserve(n_events); for (auto &&f : promises) { auto eventUpdate = std::move(f.get_future().get()); auto mBegin = std::make_move_iterator(eventUpdate.begin()); auto mEnd = std::make_move_iterator(eventUpdate.end()); events.insert(events.end(), mBegin, mEnd); } } // shuffle reactions std::shuffle(events.begin(), events.end(), std::mt19937(std::random_device()())); // execute reactions { data_t::EntriesUpdate newParticles{}; std::vector<data_t::size_type> decayedEntries{}; // todo better conflict detection? for (auto it = events.begin(); it != events.end(); ++it) { auto &event = *it; if (event.cumulativeRate == 0) { auto entry1 = event.idx1; if (event.nEducts == 1) { auto reaction = ctx.reactions().order1ByType(event.t1)[event.reactionIndex]; if (ctx.recordReactionsWithPositions()) { record_t record; record.id = reaction->id(); performReaction(&data, ctx, entry1, entry1, newParticles, decayedEntries, reaction, &record); bcs::fixPosition(record.where, box, pbc); kernel->getCPUKernelStateModel().reactionRecords().push_back(record); } else { performReaction(&data, ctx, entry1, entry1, newParticles, decayedEntries, reaction, nullptr); } if (ctx.recordReactionCounts()) { auto &counts = stateModel.reactionCounts(); counts.at(reaction->id())++; } for (auto _it2 = it + 1; _it2 != events.end(); ++_it2) { if (_it2->idx1 == entry1 || _it2->idx2 == entry1) { _it2->cumulativeRate = 1; } } } else { auto reaction = ctx.reactions().order2ByType(event.t1, event.t2)[event.reactionIndex]; if (ctx.recordReactionsWithPositions()) { record_t record; record.id = reaction->id(); performReaction(&data, ctx, entry1, event.idx2, newParticles, decayedEntries, reaction, &record); bcs::fixPosition(record.where, box, pbc); kernel->getCPUKernelStateModel().reactionRecords().push_back(record); } else { performReaction(&data, ctx, entry1, event.idx2, newParticles, decayedEntries, reaction, nullptr); } if (ctx.recordReactionCounts()) { auto &counts = stateModel.reactionCounts(); counts.at(reaction->id())++; } for (auto _it2 = it + 1; _it2 != events.end(); ++_it2) { if (_it2->idx1 == entry1 || _it2->idx2 == entry1 || _it2->idx1 == event.idx2 || _it2->idx2 == event.idx2) { _it2->cumulativeRate = 1; } } } } } data.update(std::make_pair(std::move(newParticles), std::move(decayedEntries))); } }