Closed chengcli closed 5 months ago
This isn't a typo -- we're aware that MultiRate
makes copies of the ReactionRate
objects. We've discussed this previously in #1211, but without coming to a resolution. This is, essentially, the reason that we require the use of the Kinetics.modifyReaction
function to handle dynamic rate modifications.
I think the change suggested here is insufficient. For instance, it is possible to replace the Reaction.m_rate
object outright, which would leave the reference in MultiRate.m_rxn_rates
pointing to now-freed memory rather than the new ReactionRate
object.
Thanks for pointing out #1211 and speth@b8f6132. It could be a potential memory problem if Reaction.m_rate
is changed. However, upon inspecting the code, it seems to me that the only way a user can update Reaction.m_rate
is through creating a new Reaction
object. If a user can only change the Reaction
object inside Kinetics
via the modifyReaction
method, the method itself can ensure that MutiRate.m_rxn_rates
are updated correctly as has been done in BulkKinetics
:
void BulkKinetics::modifyReaction(size_t i, shared_ptr<Reaction> rNew)
{
Kinetics::modifyReaction(i, rNew);
...
size_t index = m_bulk_types[rtype];
rate->setRateIndex(i);
rate->setContext(*rNew, *this);
m_bulk_rates[index]->replace(i, *rate);
}
So I think the memory is fine if RateType
is changed to RateType&
.
Context
I came across this problem when I was augmenting Cantera for photochemical reactions. I'm trying to combine all photolysis yields of a species into a "net photolysis reaction" for performance. Since the quantum yields depend on the wavelength of the photon and the temperature, the stoichiometric coefficients of the "net reaction" are variable. MutiRate
objects are capable of calculating the net stoichiometric coefficients after getting the reaction rate. However, when passing the net stoichiometric coefficients calculated by the MultiRate
object back to the Kinetics
object, I encountered the inconsistency between the rate in Reaction
and the rate in MultiRate
. I tried to access the ReactionRate
in Reaction
but found that it was not updated because it was the MultiRate
object that carried out the calculation. The trick of changing RateType
to RateType&
works for my purpose, but I'm unsure about other side effects.
Within the context of #1211, the original motivation for using copies were performance considerations. Keeping things synchronized is a little bit of a pain (an understatement), but as @speth mentioned the current layout is indeed intentional and thus not a bug or issue. While I am closing this issue, starting a discussion in Cantera/enhancements about alternatives (with associated benchmarks) would certainly be appropriate.
Problem description
In the template class
MultiRate
, its memberm_rxn_rates
has the following type:It causes an inconsistency between the
ReactionRate
object stored inReaction
and theReactionRate
object stored inMultiRate
. Unless it is intentionally designed to be the case, the typeRateType
should be changed toRateType&
.Behavior
ReactionRate
objects are stored in two places, one in the classReaction
and the other in the template classMultiRate
. They should reflect the same object rather than different objects. In the current version, theReactionRate
object is first instantiated when aReaction
object is instantiated. If theReaction
object is instantiated by a derived class ofKinetics
that has a MultiRate evaluator, such as theBulkKinetics
class, the sameReactionRate
object is passed to a MultiRate object. For example, the following code snippet inBulkKinetics.cpp
shows the passing of theReactionRate
object to aMultiRate
evaluator:The function signature and behavior of the
add
function inMultiRate
is:So, it accepts a reference to
ReactionRate
and casts it the correct derived class ofReactionRate
. However, since the type of the second member ofm_rxn_rates
isRateType
rather thanRateType&
, the copy constructor is called in theemplace_back
call. As a result, theReactionRate
object stored inm_rxn_rates
is a copy of theReactionRate
object stored in classReaction
. This causes an inconsistency between theReactionRate
object inMultiRate
and the object inReaction
.How to fix
Simply changing the type of
RateType
toRateType&
in the declaration ofm_rxn_rates
will fix the problem. Changing the concrete type to a reference type will avoid copying the object. So the twoReactionRate
objects inReaction
and inMultiRate
are exactly the same. All tests shall pass without any issues.