Clone Solution objects used by Reactor / ReactorSurface#1921
Clone Solution objects used by Reactor / ReactorSurface#1921speth merged 23 commits intoCantera:mainfrom
Conversation
76fc4be to
2038ba6
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1921 +/- ##
==========================================
- Coverage 75.58% 75.58% -0.01%
==========================================
Files 451 451
Lines 56362 56550 +188
Branches 9299 9331 +32
==========================================
+ Hits 42602 42744 +142
- Misses 10616 10648 +32
- Partials 3144 3158 +14 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
9244690 to
f410da9
Compare
|
With this implementation now largely working, I have a couple of observations in relation to the discussion in Cantera/enhancements#201:
As such, I think the next steps for this PR are:
|
|
@speth ... thanks for your work on this, as well as the thoughts for the roadmap, which looks 👍. I was somewhat surprised that Beyond, it appears that #1923 fixes other sample tests that are currently failing. |
87263f0 to
b30373c
Compare
I didn't anticipate this either, but it's pretty clear in retrospect. Indeed, the issue is that such lambdas cannot be serialized.
This affects all Likewise, The "next steps" I mentioned before are now completed, but I'm currently struggling with how to get this to play nice with the generated CLib. I added a new method, |
|
Thanks, @speth for further context.
It should be pretty straight-forward to come up with
Hmm. This makes sense - it wasn't straightforward to get the initial accessors to work properly (most relevant example: At the same time, the existing logic may be sufficient to handle this situation using your new clib-constructor: |-
## CLib constructor template: instantiates new object
// constructor: {{ cxx_wraps }}
try {
{% for line in lines %}
## add lines used for CLib/C++ variable crosswalk
{{ line }}
{% endfor %}
{% if shared %}
## instantiated object manages associated objects (see: sol3_newSolution)
## storage of associated objects requires id (handle) of new object
auto obj = {{ cxx_name }}({{ ', '.join(cxx_args) }});
int id = {{ base }}Cabinet::add(obj);
{% for typ, getter in shared %}
## add associated objects (see: sol3_newSolution, sol3_adjacent)
if (obj->{{ getter }}()) {
{{ typ }}Cabinet::add(obj->{{ getter }}(), id);
}
{% endfor %}
return id;
{% else %}{# not shared #}
## instantiated object has no associated objects; no id is needed
return {{ base }}Cabinet::add({{ cxx_name }}({{ ', '.join(cxx_args) }}));
{% endif %}{# shared #}
} catch (...) {
return handleAllExceptions({{ error[0] }}, {{ error[1] }});
}Specifically, you'd need to get the As the cloned As an aside, I noticed that there are some stray PS: In a nutshell, you may need to update - name: new
wraps: newReactorBase
uses: [solution]
[...]
- name: del
uses: [solution]I haven't tested it, though. |
| void setSolution(shared_ptr<Solution> sol); | ||
|
|
||
| //! Access the Solution object used to represent the contents of this reactor. | ||
| shared_ptr<Solution> solution() { return m_solution; } |
There was a problem hiding this comment.
In other contexts (specifically Python), we use contents. I don't have strong opinions, though.
Fwiw, we're not consistent in Python, either:
property thermo:
"""
The `ThermoPhase` object representing the reactor's contents.
"""
def __get__(self):
self.rbase.restoreState()
return self._contents
The docstring here isn't correct any longer as it has become the Solution object (it refers to the same object as the new C++ method). The situation is the also same for an existing kinetics property. It still all works due to inheritance, but is overall misleading.
Obviously, this is a pre-existing condition. Introducing the C++ method does, however, provide an opportunity to make things consistent across all APIs.
There was a problem hiding this comment.
Unfortunately, ReactorBase::contents already exists, returning a ThermoPhase&.
I think getting to a consistent state with contents as the name will take a couple of iterations (versions) -- we can deprecate the existing ReactorBase::contents now, along with the Python ReactorBase.thermo and ReactorSurface.kinetics properties, but at least for now we need a distinct name for this method.
There was a problem hiding this comment.
Fair point. I'd say use ReactorBase::contents4 as the intermediate name so it's clear that it's temporary? Having consistent naming is one of my pet peeves ...
There was a problem hiding this comment.
I don't really understand what the "4" means in this context, and would rather stick with "solution" as a reasonably interpretable name for now, with a "TODO" to rename it later. Do you want me to introduce the other changes as part of this PR or as a separate follow-on?
There was a problem hiding this comment.
The 4 is a placeholder similar to the 3 I have used extensivly during transitions. Right now, we have a Reactor4 ... my idea is that this is a feature that goes closer to a future Cantera, rather than the current major version. I'm 👍 with solution but would like to deprecate ReactorBase::contents regardless. This PR or another one - up to you as long as it goes in for 3.2.
Thanks for pointing me to the
I think I don't fully understand what the whole "parent" logic within |
|
@ischoegl, thanks for the help sorting out the new The complaint from Codecov is at least partly incorrect, due to it miscounting coverage of the lambda functions in |
ischoegl
left a comment
There was a problem hiding this comment.
@speth ... thanks for taking this on: this is a pretty important feature.
I have minor comments and one larger issue: I believe that long-term, we'd like to keep treating a ReactorSurface just like any other reactor, and instead implement a new Connector to object to create the linkage? I personally believe that this would a cleaner solution, but would require severing the internal stacking of the work vector. This would likely happen in a future release, but would also reverse some of the current deprecation decisions.
| void setSolution(shared_ptr<Solution> sol); | ||
|
|
||
| //! Access the Solution object used to represent the contents of this reactor. | ||
| shared_ptr<Solution> solution() { return m_solution; } |
There was a problem hiding this comment.
Fair point. I'd say use ReactorBase::contents4 as the intermediate name so it's clear that it's temporary? Having consistent naming is one of my pet peeves ...
| //! @deprecated To be removed after Cantera 3.2. Replaced by constructor where | ||
| //! contents and adjacent reactors are specified |
There was a problem hiding this comment.
I'm not sure we should go there yet. Python has a 'pending deprecation' ... I'd prefer to use a comment like this here.
There was a problem hiding this comment.
It is impossible to clone the Interface object without access to the Solution objects which define the adjacent phases.
While I suppose making these links later is possible in the case where the Interface object isn't being cloned, I think requiring these Solution objects to be provided at construction time is consistent with the changes that you've introduced to require Solution objects when constructing Reactors and requiring Reactor objects to be provided when constructing Connectors.
There was a problem hiding this comment.
This goes back to me viewing ReactorNet as a bipartite graph, where ReactorBase specializations are connected by Connector specializations. Ultimately, I envision using a BiLayer (or similar) object to establish the connection. From that perspective, this isn't an extrapolation of what I've done before ... I'm 👍 with this for the time being, but the deprecation may be temporary.
It is true that cloning is impossible without access, but this is due to the solution vector being stacked in a way that I don't think is ideal.
There was a problem hiding this comment.
I think the coupling between phases involved in an interface kinetic mechanism is intrinsically a tighter relationship than that, but we can continue to work on how this is represented after Cantera 3.2. In any case, this PR provides a necessary first step: the ability to simultaneously set all of the Solution and Interface objects involved in a ReactorNet to match the state of the network, and to then evaluate properties and rates as needed.
There was a problem hiding this comment.
FWIW, referencing #1874. Definitely beyond the scope of this PR.
|
P.S.: Thinking about this some more, I'm curious about the rationale for not using |
Yes, I believe there are cases where not cloning the
What existing (as of Cantera 3.1) use case doesn't provide a warning or notification about this change?
|
530a32d to
4e95cc5
Compare
ischoegl
left a comment
There was a problem hiding this comment.
@speth ... thanks for your detailed comments: your explanations address most of my concerns. The only sticking points are the partial cloning option, where I'd rather see a clean break, and the deprecation of ReactorBase::contents, which can be addressed in a follow-on PR. Regarding the pending deprecations, I explained that I envision an alternative way of attaching a Reactor to a ReactorSurface with a future Connector object at some point in the future. If we need to reactivate what's deprecated now is necessary, so be it.
Regarding your feedback on my 'PS' - yes, with the C++ deprecation warnings in place, this is good by me.
| void setSolution(shared_ptr<Solution> sol); | ||
|
|
||
| //! Access the Solution object used to represent the contents of this reactor. | ||
| shared_ptr<Solution> solution() { return m_solution; } |
There was a problem hiding this comment.
The 4 is a placeholder similar to the 3 I have used extensivly during transitions. Right now, we have a Reactor4 ... my idea is that this is a feature that goes closer to a future Cantera, rather than the current major version. I'm 👍 with solution but would like to deprecate ReactorBase::contents regardless. This PR or another one - up to you as long as it goes in for 3.2.
| //! @deprecated To be removed after Cantera 3.2. Replaced by constructor where | ||
| //! contents and adjacent reactors are specified |
There was a problem hiding this comment.
This goes back to me viewing ReactorNet as a bipartite graph, where ReactorBase specializations are connected by Connector specializations. Ultimately, I envision using a BiLayer (or similar) object to establish the connection. From that perspective, this isn't an extrapolation of what I've done before ... I'm 👍 with this for the time being, but the deprecation may be temporary.
It is true that cloning is impossible without access, but this is due to the solution vector being stacked in a way that I don't think is ideal.
Fixes some cases not handled correctly in Cantera#1923.
- Check for incorrectly linked surface-bulk phases - Check that adjacent reactors list for reactor surfaces is correct Fixes Cantera#1880
Actually, scratch that. I started in on migrating |
|
@speth ... in order to handle this consistently going forward, do you believe it to be necessary to extend cloning to |
Changes proposed in this pull request
clonemethod forSolution,ThermoPhase,Kinetics, andTransport, based onAnyMapserializationReactorobjects now take an optionalcloneargument, which determines whether theSolutionobject used should be cloned. The default is currentlyfalse, which will raise a warning. After Cantera 3.2, this default will be changed totrue, with no warning (so, for the duration of Cantera 3.2, this argument is not optional if you don't want a warning).ReactorNet::initializechecks allSolutionandInterfaceobjects used byReactorandReactorSurfaceobjects in the network to make sure that they are all unique. If not, a warning is issued. This warning will be an error after Cantera 3.2.Interfaceobjects requires having access to the (probably cloned)Solutionobjects for the adjacent phases at the time theReactorSurfaceis created, which necessitates the introduction of a new constructor forReactorSurface. In doing so, we also resolve the issue of linking a singleReactorSurfaceto multiple adjacentReactors.If applicable, fill in the issue number this pull request is fixing
After Cantera 3.2, when we can guarantee that the
ReactorNetcontains no re-usedSolutionobjects, there is additional refactoring to the logic for howReactorNet::evalworks that can be implemented to complete these two enhancements.Checklist
scons build&scons test) and unit tests address code coverage