Esempio n. 1
0
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)));
    }
}