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

ElectroNP ZO model with UnitModelBlockData base #1020

Merged
merged 22 commits into from
May 11, 2023

Conversation

luohezhiming
Copy link
Contributor

Fixes/Resolves:

Add a electroNP ZO model compatible with ASM2d

Summary/Motivation:

Changes proposed in this PR:

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.

@codecov
Copy link

codecov bot commented May 2, 2023

Codecov Report

Attention: Patch coverage is 97.40260% with 4 lines in your changes missing coverage. Please review.

Project coverage is 95.83%. Comparing base (cd2ec4d) to head (b6d734b).
Report is 329 commits behind head on main.

Files with missing lines Patch % Lines
watertap/unit_models/electroNP_ZO.py 96.92% 4 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1020    +/-   ##
========================================
  Coverage   95.82%   95.83%            
========================================
  Files         299      301     +2     
  Lines       28647    28801   +154     
========================================
+ Hits        27451    27601   +150     
- Misses       1196     1200     +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@pytest.mark.solver
@pytest.mark.skipif(solver is None, reason="Solver not available")
@pytest.mark.component
def test_solution(self, ElectroNP_frame):
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it worth listing out the solution for the byproduct stream too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we don't have any byproduct, so I don't think we need to list them all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought the byproduct stream would include the S_NH4 and S_PO4 that got removed?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @MarcusHolly. The treated stream would be fed back to the headworks of the plant or right before the activated sludge process, while the byproduct stream would comprise the recovered nutrients.

Copy link
Contributor Author

@luohezhiming luohezhiming May 4, 2023

Choose a reason for hiding this comment

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

I added byproduct test for S_PO4 and S_NH4, but to be noticed, since we set the water recovery as 1, so the byproduct stream is assigned to a calculation error value extremely close to 0. So the byproduct concentration will be extremely high and does not make much sense.

Copy link
Contributor

Choose a reason for hiding this comment

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

@luohezhiming Maybe just add an assert value(m.fs.unit.byproduct.flow_vol[0]) so that it's more clear why the byproduct values are so high?

Co-authored-by: MarcusHolly <96305519+MarcusHolly@users.noreply.github.com>
@lbianchi-lbl lbianchi-lbl added iedo Priority:Normal Normal Priority Issue or PR labels May 4, 2023
@lbianchi-lbl lbianchi-lbl requested a review from yalinli2 May 4, 2023 20:12
@adam-a-a adam-a-a changed the title ElectroNP ZO model ElectroNP ZO model with UnitModelBlockData base May 4, 2023
Comment on lines 187 to 195
# Add performance variables
self.recovery_frac_mass_H2O = Var(
self.flowsheet().time,
initialize=0.8,
domain=NonNegativeReals,
units=pyunits.dimensionless,
bounds=(0.0, 1.0000001),
doc="Mass recovery fraction of water in the treated stream",
)
Copy link
Contributor

@adam-a-a adam-a-a May 4, 2023

Choose a reason for hiding this comment

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

Now that we're making this its own unit, separate from the ZO model framework, I wonder if we even need this recovery variable here. If included, I'd think the value would be very close to 1.

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 choose to fix the recovery to 1.

Comment on lines 230 to 233
# default water recovery
@self.Constraint(self.flowsheet().time, doc="Default water recovery equation")
def default_water_recovery_equation(b, t):
return b.recovery_frac_mass_H2O[t] == 1
Copy link
Contributor

Choose a reason for hiding this comment

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

I would just set the recovery_frac_mass_H2O variable to an initial value and then fix the variable within this build instead of having this constraint.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, modified.

)
def default_solute_removal_equation(b, t, j):
if hasattr(self.config.property_package.solute_set, "S_PO4"):
return b.removal_frac_mass_comp[t, "S_PO4"] == 0.98
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I would maybe have parameters defined for the values, then fix removal fractions directly to those parameter values. The user could change the parameter values as desired and have them fixed.

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 add removal_P and removal_N as two mutable parameters.

m = ConcreteModel()
m.fs = FlowsheetBlock(dynamic=False)

m.fs.properties = ModifiedASM2dParameterBlock(
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm.... I just thought about this, and there is a chance, due to how we've compartmentalized the property and reaction models, that some expressions/additional variables/additional constraints implemented in the reaction block could be desired in the unit model (but not sure yet since I didn't think about it deeply). Maybe this isn't an issue yet, and my concern is probably more relevant to our ADM1 formulation rather than ASM2D, but let's keep this in mind since we expect to make additions to the modified ASM2d property/reaction package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is a good point, some additional variables can be added to the unit model instead of properties model. Just for modified ASM2D package, we might need to keep the additonal_solute config for now.

)
def default_solute_removal_equation(b, t, j):
if hasattr(self.config.property_package.solute_set, "S_PO4"):
return b.removal_frac_mass_comp[t, "S_PO4"] == 0.98
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably make a note to go over this with electroN-P team and get their take on whether we should be summing soluble nitrogen (e..g., NH4 + NH2 + NO3 as nitrogen) or if this should suffice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a note to remind us after we talk with electroN-P team.

Comment on lines +475 to +483
for (t, p, j), v in self.solute_removal_equation.items():
iscale.constraint_scaling_transform(
v,
iscale.get_scaling_factor(
self.properties_in[t].get_material_flow_terms(p, j),
default=1,
warning=True,
hint=" for solute removal",
),
Copy link
Contributor

Choose a reason for hiding this comment

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

might not need these scaling transforms if we fix the variables and not build 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.

I still keep the variables for now.

m.fs.unit.inlet.alkalinity[0].fix(61 / 12 * units.mmol / units.liter)

# Unit option
m.fs.unit.energy_electric_flow_mass.fix(0.044 * units.kWh / units.kg)
Copy link
Contributor

Choose a reason for hiding this comment

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

Something just came to mind- we can probably later convert this model to not only focus on ZO formulation. Instead, we could consider having config options would would select between fixed performance or predictive performance from surrogates. In ZO config, removal fractions, etc., would just be fixed to variables, while in predictive mode, the constraint would be activated and the surrogate would be inserted there. Just food for thought.

Copy link
Contributor Author

@luohezhiming luohezhiming May 4, 2023

Choose a reason for hiding this comment

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

It is a good idea, we could upgrade this model to be a surrogate modle with config option to choose, and I believe I can save a lot of time for building some basic vairables. I still name it as electroNP_ZO for now but would consider to rename it when we add the surrogate model.

Copy link
Contributor

@MarcusHolly MarcusHolly left a comment

Choose a reason for hiding this comment

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

Made one more small comment, but otherwise LGTM

@ksbeattie ksbeattie enabled auto-merge (squash) May 11, 2023 22:15
@ksbeattie ksbeattie merged commit 1334584 into watertap-org:main May 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
iedo Priority:Normal Normal Priority Issue or PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants