Skip to content

Commit

Permalink
Improve performance of ArrheniusRate evaluation
Browse files Browse the repository at this point in the history
- Use Eigen to vectorize rate evaluations
- Only do calculations for rates that are actually temperature-dependent
  • Loading branch information
speth committed Mar 14, 2022
1 parent 78b2985 commit acebd5c
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 2 deletions.
55 changes: 54 additions & 1 deletion include/cantera/kinetics/Arrhenius.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "cantera/kinetics/ReactionData.h"
#include "ReactionRate.h"
#include "MultiRate.h"
#include "cantera/numerics/eigen_dense.h"
#include "cantera/base/global.h"

namespace Cantera
{
Expand Down Expand Up @@ -450,10 +452,61 @@ class BulkRate : public RateType
}
};

typedef BulkRate<Arrhenius3, ArrheniusData> ArrheniusRate;
typedef BulkRate<TwoTempPlasma, TwoTempPlasmaData> TwoTempPlasmaRate;
typedef BulkRate<BlowersMasel, BlowersMaselData> BlowersMaselRate;

class ArrheniusRate : public BulkRate<Arrhenius3, ArrheniusData>
{
using BulkRate::BulkRate;
unique_ptr<MultiRateBase> newMultiRate() const override;
};

//! A specialized rate evaluator for ArrheniusRate that avoids unnecessary rate
//! calculations for reactions where the rate is simply a constant
class MultiArrheniusRate : public MultiRate<ArrheniusRate, ArrheniusData>
{
public:
virtual void resize(size_t n_species, size_t n_reactions) override;
virtual void replace(const size_t rxn_index, ReactionRate& rate) override;
virtual void getRateConstants(double* kf) override;

protected:
//! Partition rates based on whether they are temperature dependent or not
void partitionRates();

//! Pre-exponential factors for reactions where the rate is temperature dependent.
//! The corresponding element of #m_variable gives the reaction index.
Eigen::ArrayXd m_A;

//! Temperature exponents for reactions where the rate is temperature dependent
//! The corresponding element of #m_variable gives the reaction index.
Eigen::ArrayXd m_b;

//! Activation energies for reactions where the rate is temperature dependent
//! The corresponding element of #m_variable gives the reaction index.
Eigen::ArrayXd m_Ea_R;

//! Forward rate constants for reactions where the rate is temperature dependent
//! The corresponding element of #m_variable gives the reaction index.
Eigen::ArrayXd m_kf_var;

//! Forward rate constants for reactions where the rate is not temperature dependent
//! The corresponding element of #m_const gives the reaction index.
Eigen::ArrayXd m_kf_const;

//! Reaction indices for reactions where the rate is not temperature dependent
std::vector<size_t> m_const;

//! Reaction indices for reactions where the rate is temperature dependent
std::vector<size_t> m_variable;

//! Map from reaction index to index in #m_const
std::map<size_t, size_t> m_const_indices;

//! Map from reaction index to index in #m_variable
std::map<size_t, size_t> m_variable_indices;
};

}

#endif
2 changes: 1 addition & 1 deletion include/cantera/kinetics/MultiRate.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Cantera

//! A class template handling ReactionRate specializations.
template <class RateType, class DataType>
class MultiRate final : public MultiRateBase
class MultiRate : public MultiRateBase
{
CT_DEFINE_HAS_MEMBER(has_update, updateFromStruct)
CT_DEFINE_HAS_MEMBER(has_ddT, ddTScaledFromStruct)
Expand Down
79 changes: 79 additions & 0 deletions src/kinetics/Arrhenius.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,83 @@ void BlowersMasel::setContext(const Reaction& rxn, const Kinetics& kin)
}
}

unique_ptr<MultiRateBase> ArrheniusRate::newMultiRate() const
{
return unique_ptr<MultiRateBase>(new MultiArrheniusRate());
}

void MultiArrheniusRate::resize(size_t n_species, size_t n_reactions)
{
MultiRate::resize(n_species, n_reactions);

if (m_kf_var.size() + m_kf_const.size() != m_rxn_rates.size()) {
partitionRates();
}
}

void MultiArrheniusRate::partitionRates()
{
m_const.clear();
m_variable.clear();
m_const_indices.clear();
m_variable_indices.clear();

vector_fp A_const, A_var, b, Ea_R;
for (size_t i = 0; i < m_rxn_rates.size(); i++) {
auto& rate = m_rxn_rates[i].second;
if (rate.temperatureExponent() == 0 && rate.activationEnergy() == 0) {
m_const.push_back(m_rxn_rates[i].first);
m_const_indices[m_rxn_rates[i].first] = m_const.size() - 1;
A_const.push_back(rate.preExponentialFactor());
} else {
m_variable.push_back(m_rxn_rates[i].first);
m_variable_indices[m_rxn_rates[i].first] = m_variable.size() - 1;
A_var.push_back(rate.preExponentialFactor());
b.push_back(rate.temperatureExponent());
Ea_R.push_back(rate.activationEnergy() / GasConstant);
}
}

m_kf_const = Eigen::Map<Eigen::ArrayXd>(A_const.data(), A_const.size());
m_A = Eigen::Map<Eigen::ArrayXd>(A_var.data(), A_var.size());
m_b = Eigen::Map<Eigen::ArrayXd>(b.data(), b.size());
m_Ea_R = Eigen::Map<Eigen::ArrayXd>(Ea_R.data(), Ea_R.size());
m_kf_var.resize(A_var.size());
}

void MultiArrheniusRate::replace(const size_t rxn_index, ReactionRate& rate)
{
auto& old_rate = m_rxn_rates[m_indices.at(rxn_index)].second;
bool old_const = (old_rate.temperatureExponent() == 0 &&
old_rate.activationEnergy() == 0);
MultiRate::replace(rxn_index, rate);
auto& new_rate = m_rxn_rates[m_indices.at(rxn_index)].second;
bool new_const = (new_rate.temperatureExponent() == 0 &&
new_rate.activationEnergy() == 0);
if (old_const && new_const) {
size_t j = m_const_indices.at(rxn_index);
m_kf_const[j] = new_rate.preExponentialFactor();
} else if (!old_const && !new_const) {
size_t j = m_variable_indices.at(rxn_index);
m_A[j] = new_rate.preExponentialFactor();
m_b[j] = new_rate.temperatureExponent();
m_Ea_R[j] = new_rate.activationEnergy() / GasConstant;
} else {
partitionRates();
}
}

void MultiArrheniusRate::getRateConstants(double* kf)
{
AssertThrow(m_kf_var.size() + m_kf_const.size() == m_rxn_rates.size(),
"MultiArrheniusRate::getRateConstants");
m_kf_var = m_A * (m_b * m_shared.logT - m_Ea_R * m_shared.recipT).exp();
for (size_t i = 0; i < m_kf_var.size(); i++) {
kf[m_variable[i]] = m_kf_var[i];
}
for (size_t i = 0; i < m_kf_const.size(); i++) {
kf[m_const[i]] = m_kf_const[i];
}
}

}
3 changes: 3 additions & 0 deletions src/kinetics/BulkKinetics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ bool BulkKinetics::addReaction(shared_ptr<Reaction> r, bool resize)
// Add reaction rate to evaluator
size_t index = m_bulk_types[rate->type()];
m_bulk_rates[index]->add(nReactions() - 1, *rate);
if (resize) {
m_bulk_rates[index]->resize(nReactions(), nTotalSpecies());
}

// Add reaction to third-body evaluator
if (r->thirdBody() != nullptr) {
Expand Down
3 changes: 3 additions & 0 deletions src/kinetics/InterfaceKinetics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,9 @@ bool InterfaceKinetics::addReaction(shared_ptr<Reaction> r_base, bool resize)
// Add reaction rate to evaluator
size_t index = m_interface_types[rate->type()];
m_interface_rates[index]->add(nReactions() - 1, *rate);
if (resize) {
m_interface_rates[index]->resize(nReactions(), nTotalSpecies());
}

} else if (r_base->reaction_type == BMINTERFACE_RXN) {
throw NotImplementedError("InterfaceKinetics::addReaction");
Expand Down

0 comments on commit acebd5c

Please sign in to comment.