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

BSM2-P Effluent Metrics w/o Flowsheet Constraints #1492

Merged
merged 38 commits into from
Oct 3, 2024

Conversation

MarcusHolly
Copy link
Contributor

Fixes/Resolves:

This PR is similar to #1489, but it does not attempt to implement the effluent violation constraints in the BSM2_P_extension flowsheet - this will be handled in a separate PR. The constraints related to the effluent metrics are derived from the work of Flores-Alsina (specifically perf_plant_ss.m).

Summary/Motivation:

Define the effluent metrics for the BSM2_P_extension flowsheet

Changes proposed in this PR:

  • Adds effluent metrics to the modified ASM2d property package
  • Updates the BSM2 GUI flowsheet images
  • Updates unit model tests affected by the changes made to the modified ASM2d prop pack

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.

Comment on lines 267 to 273
# for v in metadata.list_supported_properties():
# if metadata[v.name].method is not None:
# if model.props[1].is_property_constructed(v.name):
# raise PropertyAttributeError(
# "Property {v_name} is an on-demand property, but was found "
# "on the stateblock without being demanded".format(v_name=v.name)
# )
Copy link
Contributor

Choose a reason for hiding this comment

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

Why comment these out?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this test was written to check that the constraints for VSS, ISS, TSS, COD, etc. were only built on-demand, but I've changed them from constraints to expressions, so I don't think this test applies anymore. On that note, I don't think I need to be touching the effluent properties in the flowsheet anymore either.

@ksbeattie ksbeattie added the Priority:Normal Normal Priority Issue or PR label Sep 26, 2024
)
print(
"BOD5 concentration",
pyo.value(m.fs.FeedWater.properties[0].BOD5["effluent"]),
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the index be influent instead of effluent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes - although technically it should be "raw". I honestly forgot I even printed the influent metrics already, but this is just something I added without putting too much thought in. I should verify that the equations for influent really are identical to the equations for effluents. We know, at the very least, that the equation for BOD5 differs slightly, which is why we have this config option implemented

Comment on lines 678 to 702
iscale.set_scaling_factor(self.TSS, 1e1)

if self.is_property_constructed("COD"):
if iscale.get_scaling_factor(self.COD) is None:
iscale.set_scaling_factor(self.COD, 1e1)

if self.is_property_constructed("BOD5"):
if iscale.get_scaling_factor(self.BOD5) is None:
iscale.set_scaling_factor(self.BOD5, 1e1)

if self.is_property_constructed("SNKj"):
if iscale.get_scaling_factor(self.SNKj) is None:
iscale.set_scaling_factor(self.SNKj, 1e2)

if self.is_property_constructed("SNOX"):
if iscale.get_scaling_factor(self.SNOX) is None:
iscale.set_scaling_factor(self.SNOX, 1e3)

if self.is_property_constructed("SP_organic"):
if iscale.get_scaling_factor(self.SP_organic) is None:
iscale.set_scaling_factor(self.SP_organic, 1e2)

if self.is_property_constructed("SP_inorganic"):
if iscale.get_scaling_factor(self.SP_inorganic) is None:
iscale.set_scaling_factor(self.SP_inorganic, 1e3)
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 need to scale the expressions? Have you tried without this added scaling?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just tried removing scaling in the latest commit - it still fails

@MarcusHolly MarcusHolly marked this pull request as ready for review October 2, 2024 16:10
Comment on lines 210 to 214
"Total suspended solids concentration", ":math:`TSS_{out} <= TSS_{max}`"
"Chemical oxygen demand", ":math:`COD_{out} <= COD_{max}`"
"Total phosphorus concentration", ":math:`P_{out} <= P_{max}`"
"Total nitrogen concentration", ":math:`N_{out} <= N_{max}`"
"5-day biological oxygen demand", ":math:`BOD5_{out} <= BOD5_{max}`"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be removed until we are able to incorporate as constraints?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure - I'll get rid of the change in this PR and leave it in the follow-up PR (1503)

Comment on lines -206 to -221
# check that properties are not built if not demanded
for v in metadata.list_supported_properties():
if metadata[v.name].method is not None:
if model.props[1].is_property_constructed(v.name):
raise PropertyAttributeError(
"Property {v_name} is an on-demand property, but was found "
"on the stateblock without being demanded".format(v_name=v.name)
)

# check that properties are built if demanded
for v in metadata.list_supported_properties():
if metadata[v.name].method is not None:
if not hasattr(model.props[1], v.name):
raise PropertyAttributeError(
"Property {v_name} is an on-demand property, but was not built "
"when demanded".format(v_name=v.name)
Copy link
Contributor

Choose a reason for hiding this comment

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

can you remind me why this has to be removed?

Copy link
Contributor Author

@MarcusHolly MarcusHolly Oct 2, 2024

Choose a reason for hiding this comment

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

Theses tests were originally written to check that the constraints for VSS, ISS, and TSS (which were present before this PR) were only built on-demand, but I've changed them from constraints to expressions, so I think these tests don't apply anymore. If we were to keep them, the test that checks that properties are not built if not demanded would fail

Copy link
Contributor

@adam-a-a adam-a-a left a comment

Choose a reason for hiding this comment

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

Nice work! I am curious how scaling became an issue after including expressions. I wonder if it had something to do with taking away variables/constraints, which had scaling, and replacing with the expressions. In effect, model scaling was slightly altered to a less stable form.

@adam-a-a adam-a-a enabled auto-merge (squash) October 2, 2024 19:36
Copy link
Contributor

@luohezhiming luohezhiming left a comment

Choose a reason for hiding this comment

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

Some minor change needs to be addressed and a few comments


self.eq_ISS = pyo.Constraint(rule=rule_ISS)
self.SNKj = pyo.Expression(rule=_SNKj, doc="Kjeldahl nitrogen")
Copy link
Contributor

Choose a reason for hiding this comment

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

I am curious why SNKj is short for "Kjeldahl nitrogen", I remember generally we called "total Kjeldahl nitrogen" as TKN

Copy link
Contributor Author

@MarcusHolly MarcusHolly Oct 2, 2024

Choose a reason for hiding this comment

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

The Flores-Alsina files are inconsistent when naming this variable. Sometimes they simply refer to it as Kjeldahl nitrogen and other times they refer to it as total Kjeldahl nitrogen. I opted to use the former, but I agree with you that we should just refer to this as TKN, even though Flores-Alsina always denotes it as SNKj

)
print(
"SNOX concentration",
pyo.value(m.fs.Treated.properties[0].SNOX),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pyo.value(m.fs.Treated.properties[0].SNOX),
pyo.value(m.fs.FeedWater.properties[0].SNOX),

print(
"SNOX concentration",
pyo.value(m.fs.Treated.properties[0].SNOX),
pyo.units.get_units(m.fs.Treated.properties[0].SNOX),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pyo.units.get_units(m.fs.Treated.properties[0].SNOX),
pyo.units.get_units(m.fs.FeedWater.properties[0].SNOX),

@adam-a-a adam-a-a merged commit b27f929 into watertap-org:main Oct 3, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Priority:Normal Normal Priority Issue or PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants