-
Notifications
You must be signed in to change notification settings - Fork 240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for isothermal conditions in control volumes. #1558
Conversation
Co-authored-by: Adam Atia <[email protected]>
return ( | ||
b.properties[t, b.length_domain.prev(x)].temperature | ||
== b.properties[t, x].temperature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the constraint would be skipped at the outlet when using forward scheme, but what does this mean for temperature at the outlet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, most of this is not needed as there is no partial derivative. The way this is written, it has to skip the first point no matter what.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be misunderstanding, but as written, it appears that for backwards transformation, temperature at the inlet (x=0) would be set equal to the temperature of the next element, and this equality would be propagated across adjacent pairs for the whole length domain. So the temperature for each element would be covered.
However, for the forward transformation case, it seems that all elements except the last element would be covered by the equality constraint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, now I think the issue is fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also rename enthalpy_balances
to isothermal_constraint
or something here?
m.fs = Flowsheet(dynamic=False) | ||
m.fs.pp = PhysicalParameterTestBlock() | ||
m.fs.rp = ReactionParameterTestBlock(property_package=m.fs.pp) | ||
m.fs.pp.del_component(m.fs.pp.phase_equilibrium_idx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary?
m.fs.pp.del_component(m.fs.pp.phase_equilibrium_idx) | |
m.fs.pp.del_component(m.fs.pp.phase_equilibrium_idx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't - just left over from copying an old test (where it was likely not needed either).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaned up
== m.fs.cv.properties_out[t].temperature | ||
) | ||
|
||
assert_units_consistent(m.fs.cv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting- I thought I remember the units check failing for dynamic cases, or is that only when has_holdup=True as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will fail for anything with a partial derivative. Here we get aways with it because we don't have a derivative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily. Generally for spatial domains we normalize the ContinuousSet
to run from 0 to 1, adding in the flow length later. Those should pass. Any ContinuousSet
with dimensions, however, will cause it to fail.
ConfigurationError, | ||
match="fs.cv: isothermal energy balance option requires that has_heat_transfer is False. " | ||
"If you are trying to solve for heat duty to achieve isothermal operation, please use " | ||
"a full energy balance as add a constraint to equate inlet and outlet temperatures.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error message needs some revision: "a full energy balance as add a constraint to equate inlet and outlet temperatures."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: "and add"
ConfigurationError, | ||
match="fs.cv: isothermal energy balance option requires that has_work_transfer is False. " | ||
"If you are trying to solve for work under isothermal operation, please use " | ||
"a full energy balance as add a constraint to equate inlet and outlet temperatures.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue- just need to revise this segment of the error message and propagate through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above.
m.fs.cv.add_isothermal_constraint() | ||
|
||
assert isinstance(m.fs.cv.enthalpy_balances, Constraint) | ||
assert len(m.fs.cv.enthalpy_balances) == 1 # x==0 is skipped |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the length of enthalpy_balances would be fine_elements - 1. Maybe I am missing something (or the test is failing).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The model has not been discretised, so there are only 2 points.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! I see now. Didn't catch that--didn't apply transformation.
m.fs.cv.add_isothermal_constraint() | ||
|
||
assert isinstance(m.fs.cv.enthalpy_balances, Constraint) | ||
assert len(m.fs.cv.enthalpy_balances) == 4 # x==0 is skipped |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, would expect this to be of length equal to (finite_elements - 1) * len(time_set)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, it is just (2-1)*4 = 4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
finite elements=10 not 2 here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, just saw your previous response. Any reason not to apply transformation and check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reduce test overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be good just to do a transformation with, say, 3 finite elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I debated it, but decided that was as much a test of Pyomo functionality as of IDAES (and thus I was going to trust Pyomo).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realised there is a reason to test this, but not for the obvious reason - when using the domain.prev()
method it works on the domain as it appears at the time of the call, meaning that it will always create T[0, 0] == T[0, 1]
and then fill in intermediate points. In this case, it should not matter, but it needs to be tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - I don't think it would hurt too much to add a test with transformation applied to 1d CV (with 2 - 3 finite elements), but I'll leave that to other reviewers and @andrewlee94
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor changes, otherwise it looks good.
has_heat_of_reaction=False, | ||
has_heat_transfer=False, | ||
has_work_transfer=False, | ||
has_enthalpy_transfer=False, | ||
custom_term=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add Type Hints here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might as well - if we don't do it now it probably won't get done.
@self.Constraint(self.flowsheet().time, doc="Energy balances") | ||
def enthalpy_balances(b, t): | ||
return b.properties_in[t].temperature == b.properties_out[t].temperature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this constraint to something that better describe what it does? Otherwise you might, for example, get enthalpy scaling on a constraint that should have temperature scaling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I debated whether the change the name - my reason for keeping it the same was for consistency of naming between different configurations (so that the "energy balance" would always have the same name no matter what option you chose).
However, I did not think about scaling, and having a descriptive name would be far more useful there for people doing custom scaling. I'll change it.
return ( | ||
b.properties[t, b.length_domain.prev(x)].temperature | ||
== b.properties[t, x].temperature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also rename enthalpy_balances
to isothermal_constraint
or something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more documentation quibbles.
`enthalpy_balances(t)`: | ||
|
||
.. math:: T_{in, t} == T_{out, t} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now outdated
`enthalpy_balances(t)`: | ||
|
||
.. math:: T_{t, x-1} == T_{t, x} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outdated.
Method should accept time and phase list as arguments. | ||
|
||
Returns: | ||
Constraint object representing enthalpy balances |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to update this to reflect isothermal constraint.
Method should accept time and phase list as arguments. | ||
|
||
Returns: | ||
Constraint object representing enthalpy balances |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to update this to reflect isothermal constraint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Fixes None
Summary/Motivation:
WateTAP has a number of cases where they would like to use isothermal conditions in place of an energy balance. Currently, they use a mix-in to support this, but this adds an addition argument that is effectively redundant. Instead, it would be better to have explicit support for isothermal conditions as part of the core ControlVolume classes.
However, isothermal conditions do not make sense for all unit models, and there are a few cases where unit models have effectively implemented their own isothermal conditions (notable PressureChanger). Thus, to avoid potential edge cases in unit models until they can be updated, a new ExtendedControlVolume0D and ExtenededControlVolume1D class have been created as an interim solution. It is envisioned that unit models will gradually be updated to be more explicit about what balance types make sense, at which point the new and old control volumes can be merged.
Changes proposed in this PR:
isothermal
option toEnergyBalanceType
Enum
.BalanceTypeNotSupported
exceptions toControlVolume0D
andControlVolume1D
ifisothermal
option is selected, with pointer to the new control volume classes.ExtendedControlVolume0D
andExtendedControlVolume1D
classes with support for isothermal balances.Legal Acknowledgement
By contributing to this software project, I agree to the following terms and conditions for my contribution: