-
Notifications
You must be signed in to change notification settings - Fork 235
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
Complementarity-based VLE formulation #977
Conversation
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 general comments to get things started:
- You need to run
black
on the code before committing, otherwise tests will not get run. I also noted that the branch is out of date with the current main. - Whilst I understand that it is easier to implement in parallel (rather than trying to update the existing code in one go), we should not be committing both versions side-by-side like this (especially not in the "core" parts of IDAES where we want to promise backwards compatibility). For now we should probably work on a branch whilst we refine everything.
- This will also make reviewing the code a lot easier - as the files are currently all "new", it is hard to tell what you have changed here, and what is just copied from the original.
We deliberately decided not to modify existing code but take it in steps. What do you suggest we do? |
@jghouse88 Working on a branch is probably best for now - the alternative would be to put it in However, we definitely should not be merging this into EDIT: Also, on a branch we can allow tests to fail in the interim period (if required), so we can work directly on the main files and use the test failure to track any issues that still need to be addressed. |
@andrewlee94 We ideally want to deprecate the older smooth VLE formulation that uses the bubble/dew point. I would rather this go to the models folder than models extra and it languishing there without getting used. What would be necessary in addition to what @viiibhav has done here to get to models? |
@viiibhav You and I will need to redo this PR to show what changes are happening from the previous version of the CEOS which means you will need to make changes directly to those files and not create a copy with |
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.
There is a lot to go through here, but here is a first round of comments.
@@ -85,6 +86,7 @@ | |||
_valid_VL_component_list as bub_dew_VL_comp_list, | |||
) | |||
from idaes.models.properties.modular_properties.phase_equil.henry import HenryType | |||
from idaes.models.properties.modular_properties.eos.ceos import Cubic |
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 do not think we should be using a cubic option as the default here - this seems like something that should be linked to the equation of state chosen. IS there a case where you would want to select this independently of the EoS, or should we just make this part of the EoS module/decision?
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 critical_properties
can probably be built with other thermodynamic models, but they're not in place yet. Right now, I can calculate them from the method in ceos.py
which is the only reason why I am having to import Cubic
into generic_property.py
. How do I avoid it, and what default value should apply to critical_properties
?
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 would introduce the critical properties as normal properties. Then, each EoS module would need to define the method to use.
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.
Does that mean it should be treated sort of the same way as phases_in_equilibrium
or bubble_dew_method
? What I have in mind is that the critical properties method is defined for now in ceos.py
. It could stay there, and the property package (eg. ASU_PR.py
, BT_PR.py
) should specify from where the properties are built.
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 quite, or at least not for now. For now, I would assume that the critical properties method is tied to the EoS chosen, so once you choose the EoS you have chosen the method for critical properties as well.
What I am suggesting is that we treat all critical properties like any other property (e.g., cp_mol_phase). That is , we add the critical properties to the metadata dict with a build-on-demand method which is defined in in the generic StateBlock. This method the calls out to the EoS class for the method to calculate the critical properties, e.g. Cubic.temperature_crit
.
Did that help clarify my suggestion?
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.
Yes that helps, thanks Andrew
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.
One little hiccup: the other properties like cp_mol_phase
are defined in equation_of_state
which is phase-specific. But mixture critical properties are not related to a phase which is why I defined it differently compared to other properties.
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.
Ahh... that issue. That complicates things a bit, as we now need to be able to specify what method to use (similar to bubble_dew_method
), but have the added complexity that that method might rely on EoS specific parameters.
So, as a first suggestion, we could have a critical properties argument which we expect to point to an EoS module - w can then do a check to make sure that EoS module is used for at least one of the phases in the system (to keep things simple for now; lets not allow a different EoS for critical properties than is used for a phase in the system).
@dallan-keylogic Any thoughts on this?
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.
My first thought is that the critical point should be calculated for a defined phase equilibrium pair. You can also have critical points for liquid-liquid equilibrium with lower and upper consolute points. I need to take a closer look at this PR to see if that's practical for what you're doing.
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.
@dallan-keylogic I am not sure that makes sense really, and either way doesn't matter until you have a VLLE system. So I wouldn't do that yet (I regret building the framework to try to support multiple liquid phases as it is).
CONFIG.declare( | ||
"critical_properties", | ||
ConfigValue( | ||
default=Cubic, |
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 noted above.
idaes/models/properties/modular_properties/base/generic_property.py
Outdated
Show resolved
Hide resolved
@@ -1133,8 +1154,10 @@ def define_metadata(cls, obj): | |||
"pressure_osm_phase": {"method": "_pressure_osm_phase"}, | |||
"pressure_sat_comp": {"method": "_pressure_sat_comp"}, | |||
"surf_tens_phase": {"method": "_surf_tens_phase"}, | |||
"temperature_bubble": {"method": "_temperature_bubble"}, | |||
"temperature_dew": {"method": "_temperature_dew"}, | |||
# "temperature_bubble": {"method": "_bubble_dew_method"}, |
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.
Commented lines - we need to decide what to do with bubble and dew points in general. If we remove the options here, then we should be removing the supporting methods as well (as they won't be used). However, that is a big enough change that it would also require a deprecation warnings and backwards compatibility code.
I suspect the existing methods/libraries might still work with this new formulation, so I would suggest we just keep there here.
idaes/models/properties/modular_properties/base/generic_property.py
Outdated
Show resolved
Hide resolved
idaes/models/properties/modular_properties/base/generic_property.py
Outdated
Show resolved
Hide resolved
idaes/models/properties/modular_properties/base/generic_property.py
Outdated
Show resolved
Hide resolved
_temperature_pressure_bubble_dew(b, "pressure_bubble") | ||
# ------------------------------------------------------------------------- | ||
# Critical Properties | ||
def _mixture_critical_properties(b): |
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.
How does this method get triggered? This looks like it should be set up as a normal build-on-demand property (and critical temperature and pressure should be separate properties).
rule=second_derivative) | ||
|
||
@staticmethod | ||
def build_critical_properties(b): |
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 am wondering if this should be set up to be called as a normal build-on-demand property (the code here probably remains the same, we would just change how it gets called).
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.
Do we presently support EoS specific build-on-demand properties?
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 don't know that we have any explicit examples, but I don't see why we can't. I think these would be a good example of the need, as calculating the critical point depends on the EoS used.
doc='Slack variable for cubic second derivative for phase p', | ||
units=pyunits.dimensionless), | ||
) | ||
gn = getattr(b, "gn" + suffix) |
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.
FYI, you can replace blocks like this with:
gn = Var(b.params.phase_list,
initialize=0.0,
bounds=(0, None),
doc='Slack variable for cubic second derivative for phase p',
units=pyunits.dimensionless)
b.add_component("gn" + suffix, gn)
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.
Thanks, I have incorporated this
def rule_cubic_root_complementarity(b, p): | ||
p1, p2 = phase_pair | ||
return b.cubic_second_derivative[p1, p2, p] == gp[p] - gn[p] | ||
b.add_component( | ||
"cubic_root_complementarity" + suffix, | ||
Constraint(b.params.phase_list, rule=rule_cubic_root_complementarity), | ||
) |
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.
If we're directly using things referencing the cubic equation of state, I think this should be made a separate phase equilibrium method. As it stands right now, we can do phase equilibria with the ideal EoS because methods for calculating the bubble and dew points are implemented either from the Antoine equation or Henry's Law (although the Henry's Law implementation is broken at the moment, see #718).
…ference_phase, but hard-coded
…uil initialization to be calculated at subcritical pressure
# --------------------------------------------------------------------- | ||
# Initialize temperature_crit_mix and pressure_crit_mix | ||
for k in blk.keys(): | ||
reference_phase = 'Liq' |
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.
We shouldn't hard-code phase names like this. It's easy to see places down the road where we have separate aqueous phases and organic phases that are named as such.
Also, it looks like this method is run regardless of whether there are coexisting phases or not. What if the liquid and vapor phases have different equations of state that have different methods of calculating critical properties? I'm still of the opinion that critical properties should be tied to coexisting phase pairs.
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.
On the first point, I agree with @dallan-keylogic ; we should not hard-code names like that. To go a step further, there is nothing stopping someone from naming their liquid phase "foo" (an we explicitly support that).
On the second point, I am not sure that a phase pair is the correct way to go either, precisely for the point @dallan-keylogic raised; what if vapour and liquid have different methods for critical properties (and potentially multiple liquid phases in future)? Rather, my initial feeling is that we need to state a specific phase for calculating the critical properties (i.e., pick vapor or liquid).
As an aside, there might be valid cases where a users wants to take a third option as well (i.e. choose a method for calculating the critical point using something other than the EoS/equivalent for the vapour or liquid phase). I don't think we want to support that for now, but identifying a specific phase would probably make it easier to add support for that in the future.
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.
what if vapour and liquid have different methods for critical properties (and potentially multiple liquid phases in future)? Rather, my initial feeling is that we need to state a specific phase for calculating the critical properties (i.e., pick vapor or liquid).
I was thinking about making the critical properties function something you specify for the phase pair:
# Defining phase equilibria
"phases_in_equilibrium": [("Vap", "Liq")],
"phase_equilibrium_state": {("Vap", "Liq"): SmoothVLE},
"critical_properties": {("Vap", "Liq"): CubicCriticalProperties},
but other configuration methods are possible.
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.
@dallan-keylogic There are many ways we could do it. AS I was thinking of a response however I realised one good reason why a phase pair is not a good choice; it would only work for multi-phase systems, whilst there might be cases where you want to calculate critical properties for a single phase system.
@viiibhav Also note you have some conflicts to fix (which you should probably do first before they get any worse). These should all be due to some changes I made in how the metadata for units are defined - before they were stored as a dict ( Also be aware of PR #995, which does a similar thing for thermophsyical properties as well (although there is still a fair bit of work left for that PR to be ready). |
idaes/models/properties/modular_properties/phase_equil/smooth_VLE.py
Outdated
Show resolved
Hide resolved
@viiibhav is this something that will be ready for the Feb release, meaning ready to merge in the next 2 weeks? |
@ksbeattie probably not, but I will try and get to it. |
@@ -381,6 +381,12 @@ def state_initialization(b): | |||
henry_comps = [] | |||
init_VLE = False | |||
for pp in _pe_pairs: | |||
# Calculate states at subcritical pressure | |||
if b.params.config.supercritical_extension == True: |
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.
Does this need to be added to the other state definitions 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.
I don't think so, the other state definitions do not have this calculation. I will test regardless, but hasn't showed up as a problem so far.
@viiibhav is this now possible for the May release? |
@ksbeattie yes it should be ready by then. |
@viiibhav any news on this? |
@ksbeattie Still working on it, was caught up with defense and graduation. |
Moving this to the Aug release, in hopes it might get done by then. |
No news as whether this is being worked on for the Aug release. |
@viiibhav let us know if there are any updates on whether or when you'll be able to resume work on this. |
@viiibhav closing this due to inactivity. |
Fixes
Summary/Motivation:
This pull request seeks to add the new complementarity-based vapor-liquid equilibrium formulation with thermodynamic models supported by cubic equations of state. Both subcritical and supercritical regimes are supported, but the default is set to subcritical formulation.
Changes proposed in this PR:
generic_property.py
,smooth_VLE.py
,ceos.py
andFTPx.py
BT_PR.py
andASU_PR.py
components.py
test_flash_PR.py
VOLUME_MOL
andVOLUME_MASS
properties toproperty_meta.py
andtest_property_meta.py
Legal Acknowledgement
By contributing to this software project, I agree to the following terms and conditions for my contribution: