Skip to content
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

Closed
wants to merge 35 commits into from
Closed

Conversation

viiibhav
Copy link
Collaborator

@viiibhav viiibhav commented Oct 3, 2022

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:

  • Addition of the new formulation and modifying the scripts generic_property.py, smooth_VLE.py, ceos.py and FTPx.py
  • Addition of critical volume and critical compressibility factor in the property packages BT_PR.py and ASU_PR.py
  • Incorporation of the above two properties in components.py
  • Addition of basic unit tests in test_flash_PR.py
  • Addition of VOLUME_MOL and VOLUME_MASS properties to property_meta.py and test_property_meta.py

Legal Acknowledgement

By contributing to this software project, I agree to the following terms and conditions for my contribution:

  1. I agree my contributions are submitted under the license terms described in the LICENSE.txt file at the top level of this directory.
  2. I represent I am authorized to make the contributions and grant the license. If my employer has rights to intellectual property that includes these contributions, I represent that I have received permission to make contributions and grant the required license on behalf of that employer.

@viiibhav viiibhav self-assigned this Oct 3, 2022
Copy link
Member

@andrewlee94 andrewlee94 left a 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:

  1. 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.
  2. 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.
  3. 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.

@jghouse88
Copy link
Member

A few general comments to get things started:

1. 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.

2. 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.

3. 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?

@andrewlee94
Copy link
Member

andrewlee94 commented Oct 4, 2022

@jghouse88 Working on a branch is probably best for now - the alternative would be to put it in models_extra but I am not sure that would be any better. As I noted, a branch will also make it clearer what changes are being made to the files as we can easily see the diff.

However, we definitely should not be merging this into models in the current state.

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.

@ksbeattie ksbeattie added the Priority:Normal Normal Priority Issue or PR label Oct 6, 2022
@jghouse88
Copy link
Member

@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?

@jghouse88
Copy link
Member

@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 _ncp tag.

Copy link
Member

@andrewlee94 andrewlee94 left a 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
Copy link
Member

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?

Copy link
Collaborator Author

@viiibhav viiibhav Oct 13, 2022

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?

Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Member

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?

Copy link
Collaborator Author

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

Copy link
Collaborator Author

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.

Copy link
Member

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?

Copy link
Contributor

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.

Copy link
Member

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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted above.

@@ -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"},
Copy link
Member

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.

_temperature_pressure_bubble_dew(b, "pressure_bubble")
# -------------------------------------------------------------------------
# Critical Properties
def _mixture_critical_properties(b):
Copy link
Member

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):
Copy link
Member

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).

Copy link
Contributor

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?

Copy link
Member

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)
Copy link
Contributor

@dallan-keylogic dallan-keylogic Oct 17, 2022

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)

Copy link
Collaborator Author

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

Comment on lines 121 to 127
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),
)
Copy link
Contributor

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).

# ---------------------------------------------------------------------
# Initialize temperature_crit_mix and pressure_crit_mix
for k in blk.keys():
reference_phase = 'Liq'
Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member

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.

@andrewlee94
Copy link
Member

@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 (derived_units["temperature"]) but now they exist as @properties (derived_units.TEMPERATURE, which supports auto-completion). note that the old approach still works for backward compatibility, but the new approach is preferred.

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).

@ksbeattie ksbeattie marked this pull request as draft January 12, 2023 19:29
@ksbeattie
Copy link
Member

@viiibhav is this something that will be ready for the Feb release, meaning ready to merge in the next 2 weeks?

@viiibhav
Copy link
Collaborator Author

viiibhav commented Feb 2, 2023

@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:
Copy link
Member

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?

Copy link
Collaborator Author

@viiibhav viiibhav Feb 7, 2023

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.

@ksbeattie
Copy link
Member

@viiibhav is this now possible for the May release?

@viiibhav
Copy link
Collaborator Author

@ksbeattie yes it should be ready by then.

@ksbeattie
Copy link
Member

@viiibhav any news on this?

@viiibhav
Copy link
Collaborator Author

viiibhav commented May 1, 2023

@ksbeattie Still working on it, was caught up with defense and graduation.

@ksbeattie
Copy link
Member

Moving this to the Aug release, in hopes it might get done by then.

@lbianchi-lbl
Copy link
Contributor

No news as whether this is being worked on for the Aug release.

@lbianchi-lbl
Copy link
Contributor

@viiibhav let us know if there are any updates on whether or when you'll be able to resume work on this.

@ksbeattie
Copy link
Member

@viiibhav closing this due to inactivity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backlog Things we'd like to get to someday Priority:Normal Normal Priority Issue or PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants