13#ifndef CD_KMCSOLVERIMPLEM_H
14#define CD_KMCSOLVERIMPLEM_H
18#include <unordered_set>
29#include <CD_NamespaceHeader.H>
31template <
typename R,
typename State,
typename T>
34 this->setSolverParameters(0, 0, 100, std::numeric_limits<Real>::max(), 0.0, 1.E-6);
37template <
typename R,
typename State,
typename T>
40 this->define(a_reactions);
43template <
typename R,
typename State,
typename T>
47template <
typename R,
typename State,
typename T>
51 m_reactions = a_reactions;
54 this->setSolverParameters(0, 0, 100, std::numeric_limits<Real>::max(), 0.0, 1.E-6);
57template <
typename R,
typename State,
typename T>
64 const Real a_exitTol)
noexcept
68 m_maxIter = a_maxIter;
71 m_exitTol = a_exitTol;
74template <
typename R,
typename State,
typename T>
75inline std::vector<std::vector<T>>
78 std::vector<std::vector<T>> ret(a_reactions.size());
80 for (
int j = 0; j < a_reactions.size(); j++) {
81 std::vector<T>& nu = ret[j];
84 State state = a_state;
86 std::vector<T> preState = state.linearOut();
87 for (
auto& x : preState) {
88 x =
static_cast<T
>(0);
90 state.linearIn(preState);
93 a_reactions[j]->advanceState(state,
static_cast<T
>(1));
95 std::vector<T> postState = state.linearOut();
98 nu.resize(preState.size());
99 for (
int i = 0; i < preState.size(); i++) {
100 nu[i] = postState[i] - preState[i];
107template <
typename R,
typename State,
typename T>
108inline std::vector<Real>
111 return this->propensities(a_state, m_reactions);
114template <
typename R,
typename State,
typename T>
115inline std::vector<Real>
118 std::vector<Real> A(a_reactions.size());
120 const size_t numReactions = a_reactions.size();
122 for (
size_t i = 0; i < numReactions; i++) {
123 A[i] = a_reactions[i]->propensity(a_state);
129template <
typename R,
typename State,
typename T>
133 return this->totalPropensity(a_state, m_reactions);
136template <
typename R,
typename State,
typename T>
142 const size_t numReactions = a_reactions.size();
144 for (
size_t i = 0; i < numReactions; i++) {
145 A += a_reactions[i]->propensity(a_state);
151template <
typename R,
typename State,
typename T>
155 return this->partitionReactions(a_state, m_reactions);
158template <
typename R,
typename State,
typename T>
165 const size_t numReactions = a_reactions.size();
169 criticalReactions.reserve(numReactions);
170 nonCriticalReactions.reserve(numReactions);
172 for (
size_t i = 0; i < numReactions; i++) {
173 const T Lj = a_reactions[i]->computeCriticalNumberOfReactions(a_state);
176 criticalReactions.emplace_back(a_reactions[i]);
179 nonCriticalReactions.emplace_back(a_reactions[i]);
185 return std::make_pair(std::move(criticalReactions), std::move(nonCriticalReactions));
188template <
typename R,
typename State,
typename T>
193 return this->getCriticalTimeStep(a_state, m_reactions);
196template <
typename R,
typename State,
typename T>
203 Real dt = std::numeric_limits<Real>::max();
205 if (a_criticalReactions.size() > 0) {
207 const Real A = std::numeric_limits<Real>::min() + this->totalPropensity(a_state, a_criticalReactions);
209 dt = this->getCriticalTimeStep(A);
215template <
typename R,
typename State,
typename T>
220 Real A = std::numeric_limits<Real>::min();
222 for (
const auto& p : a_propensities) {
226 return this->getCriticalTimeStep(A);
229template <
typename R,
typename State,
typename T>
235 return log(1.0 / u) / a_totalPropensity;
238template <
typename R,
typename State,
typename T>
242 const auto& partitionedReactions = this->partitionReactions(a_state, m_reactions);
244 return this->getNonCriticalTimeStep(a_state, partitionedReactions.second);
247template <
typename R,
typename State,
typename T>
251 const std::vector<Real> propensities = this->propensities(a_state, a_reactions);
253 return this->getNonCriticalTimeStep(a_state, a_reactions, propensities);
256template <
typename R,
typename State,
typename T>
262 size_t numSpecies = 0;
264 for (
const auto& reaction : a_reactions) {
265 for (
const size_t reactant : reaction->getReactants()) {
266 numSpecies = std::max(numSpecies, reactant + 1);
270 if (m_seenScratch.size() < numSpecies) {
271 m_seenScratch.resize(numSpecies, 0);
272 m_muScratch.resize(numSpecies, 0.0);
273 m_sigmaScratch.resize(numSpecies, 0.0);
278 m_reactantScratch.clear();
280 for (
const auto& reaction : a_reactions) {
281 for (
const size_t reactant : reaction->getReactants()) {
282 if (!m_seenScratch[reactant]) {
283 m_seenScratch[reactant] = 1;
285 m_reactantScratch.push_back(reactant);
291 for (
const size_t reactant : m_reactantScratch) {
292 m_seenScratch[reactant] = 0;
296template <
typename R,
typename State,
typename T>
300 const std::vector<Real>& a_nonCriticalPropensities)
const noexcept
302 CH_assert(a_nonCriticalReactions.size() == a_nonCriticalPropensities.size());
304 constexpr Real one = 1.0;
306 Real dt = std::numeric_limits<Real>::max();
308 const size_t numReactions = a_nonCriticalReactions.size();
310 if (numReactions > 0) {
313 this->gatherDistinctReactants(a_nonCriticalReactions);
315 for (
const size_t reactant : m_reactantScratch) {
316 m_muScratch[reactant] = 0.0;
317 m_sigmaScratch[reactant] = 0.0;
323 for (
size_t i = 0; i < numReactions; i++) {
324 const Real& p = a_nonCriticalPropensities[i];
326 for (
const size_t reactant : m_reactantScratch) {
327 const auto muIJ = a_nonCriticalReactions[i]->getStateChange(reactant);
329 m_muScratch[reactant] += muIJ * p;
330 m_sigmaScratch[reactant] += muIJ * muIJ * p;
335 for (
const size_t reactant : m_reactantScratch) {
342 const T Xi = a_nonCriticalReactions[0]->population(reactant, a_state);
348 constexpr Real gi = 1.0;
350 Real dt1 = std::numeric_limits<Real>::max();
351 Real dt2 = std::numeric_limits<Real>::max();
353 const Real f = std::max(m_eps * Xi / gi, one);
355 const Real mu = std::abs(m_muScratch[reactant]);
356 const Real sigma2 = std::abs(m_sigmaScratch[reactant]);
358 if (mu > std::numeric_limits<Real>::min()) {
361 if (sigma2 > std::numeric_limits<Real>::min()) {
362 dt2 = (f * f) / sigma2;
365 dt = std::min(dt, std::min(dt1, dt2));
373template <
typename R,
typename State,
typename T>
377 const std::vector<Real>& a_propensities,
378 const Real a_epsilon)
const noexcept
380 CH_assert(a_reactions.size() == a_propensities.size());
382 constexpr Real one = 1.0;
384 Real dt = std::numeric_limits<Real>::max();
386 const size_t numReactions = a_reactions.size();
388 if (numReactions > 0) {
391 this->gatherDistinctReactants(a_reactions);
393 for (
const size_t reactant : m_reactantScratch) {
394 m_muScratch[reactant] = 0.0;
400 for (
size_t i = 0; i < numReactions; i++) {
401 const Real& p = a_propensities[i];
403 for (
const size_t reactant : m_reactantScratch) {
404 const auto muIJ = a_reactions[i]->getStateChange(reactant);
406 m_muScratch[reactant] += muIJ * p;
411 for (
const size_t reactant : m_reactantScratch) {
418 const T Xi = R::population(reactant, a_state);
424 constexpr Real gi = 1.0;
426 const Real f = std::max(a_epsilon * Xi / gi, one);
427 const Real mu = std::abs(m_muScratch[reactant]);
429 if (mu > std::numeric_limits<Real>::min()) {
430 dt = std::min(dt, f / mu);
439template <
typename R,
typename State,
typename T>
443 this->stepSSA(a_state, m_reactions);
446template <
typename R,
typename State,
typename T>
450 if (a_reactions.size() > 0) {
453 const std::vector<Real> propensities = this->propensities(a_state, a_reactions);
455 this->stepSSA(a_state, a_reactions, propensities);
459template <
typename R,
typename State,
typename T>
463 const std::vector<Real>& a_propensities)
const noexcept
465 CH_assert(a_reactions.size() == a_propensities.size());
467 const size_t numReactions = a_reactions.size();
469 if (numReactions > 0) {
470 constexpr T one = (T)1;
474 for (
size_t i = 0; i < numReactions; i++) {
475 A += a_propensities[i];
480 size_t r = numReactions - 1;
483 for (
size_t i = 0; i + 1 < numReactions; i++) {
484 sumProp += a_propensities[i];
486 if (sumProp >= u * A) {
493 CH_assert(r < a_reactions.size());
496 a_reactions[r]->advanceState(a_state, one);
500template <
typename R,
typename State,
typename T>
504 this->advanceSSA(a_state, m_reactions, a_dt);
507template <
typename R,
typename State,
typename T>
511 const size_t numReactions = a_reactions.size();
513 if (numReactions > 0) {
518 while (curDt <= a_dt) {
521 const std::vector<Real> propensities = this->propensities(a_state, a_reactions);
523 const Real nextDt = this->getCriticalTimeStep(propensities);
526 if (curDt + nextDt <= a_dt) {
527 this->stepSSA(a_state, a_reactions, propensities);
535template <
typename R,
typename State,
typename T>
539 this->stepExplicitEuler(a_state, m_reactions, a_dt);
542template <
typename R,
typename State,
typename T>
546 const Real a_dt)
const noexcept
548 CH_assert(a_dt > 0.0);
550 if (a_reactions.size() > 0) {
551 const std::vector<Real> propensities = this->propensities(a_state, a_reactions);
553 for (
size_t i = 0; i < a_reactions.size(); i++) {
557 const T numReactions = (T)Random::getPoisson<long long>(propensities[i] * a_dt);
559 a_reactions[i]->advanceState(a_state, numReactions);
564template <
typename R,
typename State,
typename T>
568 this->stepMidpoint(a_state, m_reactions, a_dt);
571template <
typename R,
typename State,
typename T>
575 const int numReactions = a_reactions.size();
577 if (numReactions > 0) {
579 std::vector<Real> propensities = this->propensities(a_state, a_reactions);
581 State Xdagger = a_state;
583 for (
size_t i = 0; i < numReactions; i++) {
586 a_reactions[i]->advanceState(Xdagger, (T)std::round(0.5 * propensities[i] * a_dt));
589 propensities = this->propensities(Xdagger, a_reactions);
591 for (
size_t i = 0; i < numReactions; i++) {
592 const T curReactions = (T)Random::getPoisson<long long>(propensities[i] * a_dt);
594 a_reactions[i]->advanceState(a_state, curReactions);
599template <
typename R,
typename State,
typename T>
603 this->stepPRC(a_state, m_reactions, a_dt);
606template <
typename R,
typename State,
typename T>
610 const int numReactions = a_reactions.size();
612 if (numReactions > 0) {
614 std::vector<Real> aj = this->propensities(a_state, a_reactions);
616 const std::vector<Real> ak = aj;
618 for (
int j = 0; j < numReactions; j++) {
619 for (
int k = 0; k < numReactions; k++) {
622 a_reactions[k]->advanceState(x, (T)1);
624 const Real etajk = a_reactions[j]->propensity(x) - ak[j];
626 aj[j] += 0.5 * a_dt * ak[k] * etajk;
630 for (
size_t i = 0; i < numReactions; i++) {
631 const T nr = (T)Random::getPoisson<long long>(aj[i] * a_dt);
633 a_reactions[i]->advanceState(a_state, nr);
638template <
typename R,
typename State,
typename T>
642 this->stepImplicitEuler(a_state, m_reactions, a_dt);
645template <
typename R,
typename State,
typename T>
649 const Real a_dt)
const noexcept
661 const std::vector<T> inputState = a_state.linearOut();
662 const std::vector<std::vector<T>> nu = this->getNu(a_state, a_reactions);
663 const std::vector<Real> ajX = this->propensities(a_state, a_reactions);
666 const int N = inputState.size();
667 const int M = a_reactions.size();
670 auto compConstantTerm = [&](
double* C,
double* X,
const State& state,
const Real a_dt) ->
void {
674 State explicitEulerState = state;
678 const std::vector<T> eulerOut = explicitEulerState.linearOut();
681 for (
int i = 0; i < N; i++) {
682 C[i] = 1.0 * eulerOut[i];
683 X[i] = 1.0 * eulerOut[i];
687 for (
int j = 0; j < M; j++) {
688 const std::vector<T>& nuj = nu[j];
690 for (
int i = 0; i < N; i++) {
691 C[i] -= nuj[i] * ajX[j] * a_dt;
697 auto computeF = [&](
double* F,
const double* Xit,
const double* C) ->
void {
699 State stateXit = a_state;
701 std::vector<T> linState(N);
702 for (
int i = 0; i < N; i++) {
703 linState[i] =
static_cast<T
>(llround(Xit[i]));
706 stateXit.linearIn(linState);
708 const std::vector<Real> ajXit = this->propensities(stateXit);
711 for (
int i = 0; i < N; i++) {
712 F[i] = Xit[i] - C[i];
715 for (
int j = 0; j < M; j++) {
716 const std::vector<T>& nuj = nu[j];
718 for (
int i = 0; i < N; i++) {
719 F[i] -= a_dt * nuj[i] * ajXit[j];
725 auto computeNorm = [&](
double* F,
const double* X,
const double* C) -> Real {
730 for (
int i = 0; i < N; i++) {
731 norm = std::max(norm, std::abs(F[i]));
739 std::vector<double> J(
static_cast<size_t>(N * N));
740 std::vector<double> X(
static_cast<size_t>(N));
741 std::vector<double> F(
static_cast<size_t>(N));
742 std::vector<double> C(
static_cast<size_t>(N));
745 std::vector<double> X1(
static_cast<size_t>(N));
746 std::vector<double> X2(
static_cast<size_t>(N));
747 std::vector<double> F2(
static_cast<size_t>(N));
752 std::vector<int> IPIV(
static_cast<size_t>(N));
755 compConstantTerm(C.data(), X.data(), a_state, a_dt);
758 for (
int i = 0; i < N; i++) {
762 const Real initNorm = computeNorm(F.data(), X1.data(), C.data());
764 bool converged =
true;
766 for (
int k = 0; k < m_maxIter; k++) {
771 computeF(F.data(), X.data(), C.data());
775 for (
int j = 0; j < N; j++) {
777 for (
int s = 0; s < N; s++) {
781 X2[j] += std::max(0.01 * X[j], 1.0);
783 computeF(F2.data(), X2.data(), C.data());
785 for (
int i = 0; i < N; i++) {
786 J[i + j * N] = (F2[i] - F[i]) / (X2[j] - X[j]);
792 dgesv_((
int*)&N, &NRHS, J.data(), (
int*)&N, IPIV.data(), F.data(), (
int*)&N, &INFO);
796 const std::string err =
"KMCSolver<R, State, T>::stepImplicitEuler -- could not solve A*x = b";
798 pout() << err << endl;
806 for (
int i = 0; i < N; i++) {
812 const Real norm = computeNorm(F.data(), X.data(), C.data());
814 if (norm / initNorm < m_exitTol) {
820 std::vector<T> outputState(N);
823 for (
int i = 0; i < N; i++) {
824 outputState[i] =
static_cast<T
>(llround(X[i]));
829 for (
int i = 0; i < N; i++) {
830 outputState[i] =
static_cast<T
>(-1);
834 a_state.linearIn(outputState);
837template <
typename R,
typename State,
typename T>
843 this->advanceTau(a_state, m_reactions, a_dt, a_leapPropagator);
846template <
typename R,
typename State,
typename T>
853 if (a_reactions.size() > 0) {
856 while (curTime < a_dt) {
861 const std::vector<Real> propensities = this->propensities(a_state, a_reactions);
863 const Real dtLeap = this->getNonCriticalTimeStep(a_state, a_reactions, propensities);
865 Real curDt = std::min(a_dt - curTime, dtLeap);
872 State state = a_state;
875 switch (a_leapPropagator) {
877 this->stepExplicitEuler(state, a_reactions, curDt);
882 this->stepMidpoint(state, a_reactions, curDt);
887 this->stepPRC(state, a_reactions, curDt);
892 this->stepImplicitEuler(state, a_reactions, curDt);
902 valid = state.isValidState();
917template <
typename R,
typename State,
typename T>
923 this->advanceHybrid(a_state, m_reactions, a_dt, a_leapPropagator);
926template <
typename R,
typename State,
typename T>
933 switch (a_leapPropagator) {
935 this->advanceHybrid(a_state, a_reactions, a_dt, [
this](State& s,
const ReactionList& r,
const Real dt) {
936 this->stepExplicitEuler(s, r, dt);
942 this->advanceHybrid(a_state, a_reactions, a_dt, [
this](State& s,
const ReactionList& r,
const Real dt) {
943 this->stepMidpoint(s, r, dt);
949 this->advanceHybrid(a_state, a_reactions, a_dt, [
this](State& s,
const ReactionList& r,
const Real dt) {
950 this->stepPRC(s, r, dt);
956 this->advanceHybrid(a_state, a_reactions, a_dt, [
this](State& s,
const ReactionList& r,
const Real dt) {
957 this->stepImplicitEuler(s, r, dt);
963 MayDay::Error(
"KMCSolver::advanceHybrid - unknown leap propagator requested");
968template <
typename R,
typename State,
typename T>
974 const std::function<
void(State&,
const ReactionList& a_reactions,
const Real a_dt)>& a_propagator)
const noexcept
976 constexpr T one = (T)1;
982 while (curTime < a_dt) {
986 const std::pair<ReactionList, ReactionList> partitionedReactions = this->partitionReactions(a_state, a_reactions);
988 const ReactionList& criticalReactions = partitionedReactions.first;
989 const ReactionList& nonCriticalReactions = partitionedReactions.second;
991 const std::vector<Real> propensitiesCrit = this->propensities(a_state, criticalReactions);
992 const std::vector<Real> propensitiesNonCrit = this->propensities(a_state, nonCriticalReactions);
994 Real dtCrit = this->getCriticalTimeStep(propensitiesCrit);
995 Real dtNonCrit = this->getNonCriticalTimeStep(a_state, nonCriticalReactions, propensitiesNonCrit);
999 bool validStep =
false;
1001 while (!validStep) {
1006 State state = a_state;
1009 const Real curDt = std::min(a_dt - curTime, std::min(dtCrit, dtNonCrit));
1012 const bool nonCriticalOnly = (dtNonCrit < dtCrit) || (criticalReactions.size() == 0) ||
1013 (dtCrit > (a_dt - curTime));
1020 const Real A = this->totalPropensity(state, a_reactions);
1021 const bool useSSA = (m_numSSA >= one) && (A * curDt < m_SSAlim);
1031 while (dtSSA < curDt && numSSA < m_numSSA) {
1034 const std::vector<Real> propensities = this->propensities(a_state, a_reactions);
1037 for (
size_t i = 0; i < propensities.size(); i++) {
1038 Asum += propensities[i];
1042 const Real dtReact = this->getCriticalTimeStep(Asum);
1044 if (dtSSA + dtReact < curDt) {
1045 this->stepSSA(a_state, a_reactions, propensities);
1060 else if (nonCriticalOnly) {
1062 a_propagator(state, nonCriticalReactions, curDt);
1065 validStep = state.isValidState();
1084 a_propagator(state, nonCriticalReactions, curDt);
1086 this->stepSSA(state, criticalReactions, propensitiesCrit);
1089 validStep = state.isValidState();
1104#include <CD_NamespaceFooter.H>
Class for running Kinetic Monte Carlo functionality.
KMCLeapPropagator
Supported propagators for hybrid tau leaping.
Definition CD_KMCSolver.H:35
@ ImplicitEuler
Implicit Euler tau leaping.
@ Midpoint
Gillespie's midpoint method.
@ ExplicitEuler
Regular tau leaping.
@ PRC
Hu and Li's Poisson random correction method.
Interface to some LaPack routines.
File containing some useful static methods related to random number generation.
std::vector< std::shared_ptr< const R > > ReactionList
Alias for the list of reactions.
Definition CD_KMCSolver.H:66
void setSolverParameters(T a_numCrit, T a_numSSA, T a_maxIter, Real a_eps, Real a_SSAlim, Real a_exitTol) noexcept
Set solver parameters.
Definition CD_KMCSolverImplem.H:59
Real computeDt(const State &a_state, const ReactionList &a_reactions, const std::vector< Real > &a_propensities, Real a_epsilon) const noexcept
Compute a time step using the leap condition on the mean value.
Definition CD_KMCSolverImplem.H:375
virtual ~KMCSolver() noexcept
Destructor.
Definition CD_KMCSolverImplem.H:44
void define(const ReactionList &a_reactions) noexcept
Define function. Sets the reactions.
Definition CD_KMCSolverImplem.H:49
KMCSolver() noexcept
Default constructor. Must subsequently call define.
Definition CD_KMCSolverImplem.H:32
std::vector< std::vector< T > > getNu(const State &a_state, const ReactionList &a_reactions) const noexcept
Compute the state vector changes for all reactions.
Definition CD_KMCSolverImplem.H:76
Real getCriticalTimeStep(const State &a_state) const noexcept
Get the time to the next critical reaction.
Definition CD_KMCSolverImplem.H:190
void advanceSSA(State &a_state, Real a_dt) const noexcept
Advance with the SSA over the input time. This can end up using substepping.
Definition CD_KMCSolverImplem.H:502
void gatherDistinctReactants(const ReactionList &a_reactions) const noexcept
Fill m_reactantScratch with the distinct reactants of the input reactions.
Definition CD_KMCSolverImplem.H:258
void stepImplicitEuler(State &a_state, Real a_dt) const noexcept
Perform one implicit Euler tau-leaping step using ALL reactions.
Definition CD_KMCSolverImplem.H:640
void advanceHybrid(State &a_state, Real a_dt, const KMCLeapPropagator &a_leapPropagator=KMCLeapPropagator::ExplicitEuler) const noexcept
Advance using Cao et. al. hybrid algorithm over the input time. This can end up using substepping.
Definition CD_KMCSolverImplem.H:919
void stepPRC(State &a_state, Real a_dt) const noexcept
Perform one leaping step using the PRC method for ALL reactions.
Definition CD_KMCSolverImplem.H:601
void stepMidpoint(State &a_state, Real a_dt) const noexcept
Perform one leaping step using the midpoint method for ALL reactions.
Definition CD_KMCSolverImplem.H:566
std::pair< ReactionList, ReactionList > partitionReactions(const State &a_state) const noexcept
Partition reactions into critical and non-critical reactions.
Definition CD_KMCSolverImplem.H:153
void stepSSA(State &a_state) const noexcept
Perform a single SSA step.
Definition CD_KMCSolverImplem.H:441
void advanceTau(State &a_state, const Real &a_dt, const KMCLeapPropagator &a_leapPropagator=KMCLeapPropagator::ExplicitEuler) const noexcept
Advance using a specified tau-leaping algorithm.
Definition CD_KMCSolverImplem.H:839
Real getNonCriticalTimeStep(const State &a_state) const noexcept
Get the non-critical time step.
Definition CD_KMCSolverImplem.H:240
Real totalPropensity(const State &a_state) const noexcept
Compute the total propensity for ALL reactions.
Definition CD_KMCSolverImplem.H:131
void stepExplicitEuler(State &a_state, Real a_dt) const noexcept
Perform one plain tau-leaping step using ALL reactions.
Definition CD_KMCSolverImplem.H:537
std::vector< Real > propensities(const State &a_state) const noexcept
Compute propensities for ALL reactions.
Definition CD_KMCSolverImplem.H:109
static Real getUniformReal01()
Get a uniform real number on the interval [0,1].
Definition CD_RandomImplem.H:156