-
Notifications
You must be signed in to change notification settings - Fork 238
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 diagnostics tests for all unit models #1375
Conversation
… into diagnostics_testing
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.
Everything looks good to me, I just had a couple of comments.
@@ -83,15 +83,15 @@ def build(self): | |||
|
|||
# Heat capacity of water | |||
self.cp_mol = Param( | |||
mutable=False, | |||
mutable=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.
What was the reason for making the Params mutable?
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 they have units, Pyomo was implicitly making them mutable and logging a warning. I changed them to be explicitly mutable to suppress the warning.
@@ -548,21 +556,11 @@ def test_build(self, btx): | |||
assert number_total_constraints(btx) == 38 | |||
assert number_unused_variables(btx) == 0 | |||
|
|||
@pytest.mark.integration |
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 the Diagnostics check include looking for the correct units on each variable, as "test_units" looks for?
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.
No, but I felt that was somewhat superfluous if units were consistent.
idaes/core/util/scaling.py
Outdated
# There appears to be a bug in the ASL which causes terminal failures | ||
# if you try to create multiple ASL structs with different external | ||
# functions in the same process. This causes pytest to crash during testing. | ||
# To avoid this, register all known external functions before we call | ||
# PyNumero. | ||
ext_funcs = ["cubic_roots", "general_helmholtz_external", "functions"] | ||
library_set = set() | ||
libraries = [] | ||
|
||
for f in ext_funcs: | ||
library = find_library(f) | ||
if library not in library_set: | ||
library_set.add(library) | ||
libraries.append(library) | ||
|
||
if "AMPLFUNC" in os.environ: | ||
env_str = "\n".join([os.environ["AMPLFUNC"], *libraries]) | ||
else: | ||
env_str = "\n".join(libraries) | ||
|
||
os.environ["AMPLFUNC"] = env_str | ||
|
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 in the scaling module? I could see two different approaches for implementing this workaround:
- We don't attempt to fix this for users (until we get a bug report about it), and just add the workaround to a testing module somewhere.
- We attempt to fix this for users.
This seems to be going with option 2. However, I would argue that, if we're trying to give users access to the workaround, we should put this in a high-level module or __init__.py
. I say this because users could hit this issue in a wide variety of situations in which scaling
is not imported. If we just want to narrowly fix the issue with the tests, I would put this in the test module that is triggering the bug (I believe test_model_diagnostics
).
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 function that is being used for the Jacobian is here, hence why I moved it from the diagnostics. I think this is the only place we use PyNumero for now as well, so it would cover many user cases. The reason I did not put it in the top level __init__.py
is just because I am hesitant to have put code there (mostly learning from @lbianchi-lbl's warnings).
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 could lead to very confusing behavior where a user experiences a bug only if idaes.core.util.scaling
is not imported. I would make sure this is run either whenever IDAES is imported or only in the specific test file that is causing a problem. I am happy to hear specific arguments why the former is a bad idea, or to take a third opinion into consideration.
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.
@Robbybp I agree with your point that this code being run inconsistently depending on whether idaes.core.util.scaling
is imported should be avoided as it could lead to very hard to debug behaviors.
As a clarification, my take is that nonstandard code being run as import-time side effects should be avoided if possible, for the reason above and others (including there not being an easy way to "opt out" or "undo" the changes if needed). However, if the choice is between "import-time side effects that are run always" vs "import-time side effects that only run sometimes", the first is IMO preferable. Additionally, since there's already a lot happening in idaes/__init__.py
/idaes/config.py
, I can see the argument for keeping all this type of code in one(-ish) place.
In summary, I agree with you in that idaes/__init__.py
(or any other module that's unconditionally/consistently imported) would be the most reasonable place to put this code.
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 small question, but this looks good. I think this is a great step to take. (And thanks for catching the SuperLU error.)
m.fs.unit.inlet.conc_mol_comp[0, "SodiumAcetate"].fix(0.0) | ||
m.fs.unit.inlet.conc_mol_comp[0, "Ethanol"].fix(0.0) | ||
m.fs.unit.inlet.conc_mol_comp[0, "SodiumAcetate"].fix(1e-8) | ||
m.fs.unit.inlet.conc_mol_comp[0, "Ethanol"].fix(1e-8) |
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 was the warning that required this change? I assume something like a natural log of each concentration?
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.
Value fixed at exactly the bound. Whilst that is not a "real" error in the numerical sense (as it is a fixed variable), it is poor modeling practice to have 0 concentrations (and thus zero flows) so I set it to an EPS instead.
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 disagree that having 0 concentrations is poor practice. As long as at least one concentration is non-zero, we don't have a zero total flow. That said, I think this change is fine.
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.
You don't have to deal with zero total flow, but if you either have a power-law concentration expression or calculate entropy of mixing anywhere, you end up with an evaluation error.
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.
For my own education/curiosity, where do concentration power-law expressions show up?
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 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, and the fact that thermo properties often have logs or inverse functions in them is why I consider it bad practice to set any concentration/mass/mole fraction to absolute zero. I don't think this particular case actually has any of those, but I try to do this everywhere to err on the side of caution (and maybe get others to learn from me).
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 updates look good to me.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1375 +/- ##
==========================================
+ Coverage 77.61% 77.62% +0.01%
==========================================
Files 391 391
Lines 64355 64375 +20
Branches 14251 14257 +6
==========================================
+ Hits 49946 49970 +24
- Misses 11832 11834 +2
+ Partials 2577 2571 -6 ☔ View full report in Codecov by Sentry. |
Fixes #1374
Summary/Motivation:
This PR adds tests using the diagnostics toolbox to all unit model test cases. Also adds catch for error identified in #1374
Changes proposed in this PR:
Legal Acknowledgement
By contributing to this software project, I agree to the following terms and conditions for my contribution: