Skip to content

Adds two-salt diafiltration unit model #139

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

Open
wants to merge 67 commits into
base: main
Choose a base branch
from

Conversation

mollydougher
Copy link
Contributor

@mollydougher mollydougher commented Mar 19, 2025

Summary/Motivation:

This PR adds a unit model and sample flowsheet for diafiltration system with two salts. This multi-component model for diafiltration takes lithium and cobalt as a case study, but there will be generalizations to additional solutes in future PRs.

Changes proposed in this PR:

  • Adds a high-fidelity, DAE unit model for a multi-salt diafiltration separation.
  • Provides an example flowsheet using this unit model.

Limitations of the Model:

  • Describes the transport of a two-cation system with a common anion, specifically lithium chloride and cobalt chloride.
  • Omits the formation of a boundary layer due to concentration polarization, partitioning at the surface of the membrane, and assumes the membrane is uncharged.
  • Approximates the ion-ion interactions using linearized expressions for cross-diffusion coefficients.

Planned Improvements (left for future PRs, not necessarily in this order):

  • Generalize the model to salt solutions other than lithium chloride and cobalt chloride.
  • Extend the model to salt solutions with 2 cations and 2 anions.
  • Extend the model to salt solutions with m cations and n anions.
  • Include Donnan exclusion (i.e., incorporate the membrane partition coefficient, H, in the model)
  • Include the boundary layer formation due to concentration polarization.

Reviewer's checklist / merge requirements:

  • The head branch (i.e. the "source" of the changes) is not the main branch on the PR author's fork
  • Documentation
  • Tests
  • Diagnostic tests for models

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

@mollydougher mollydougher self-assigned this Mar 19, 2025
Copy link

codecov bot commented Mar 20, 2025

Codecov Report

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

Project coverage is 97.16%. Comparing base (c31ea34) to head (0f02b2e).

Files with missing lines Patch % Lines
.../nanofiltration/diafiltration_solute_properties.py 94.28% 2 Missing ⚠️
.../nanofiltration/diafiltration_stream_properties.py 93.54% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #139      +/-   ##
==========================================
+ Coverage   97.03%   97.16%   +0.13%     
==========================================
  Files          65       71       +6     
  Lines        9715    10305     +590     
==========================================
+ Hits         9427    10013     +586     
- Misses        288      292       +4     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mollydougher
Copy link
Contributor Author

@bpaul4 @MarcusHolly Besides the unit model documentation (which I am still working on), this is ready for another round of reviews.

@mollydougher
Copy link
Contributor Author

@mollydougher the autodoc summary looks great, thanks for writing it. Everything else looks great, too.

What is the plan to address the numerical diagnostics issue?

@bpaul4 The combination of using backward-backward discretization, updating the tolerances for values I expect to be zero (making them numerically zero), and setting specific constraints for the fluxes I expect to be zero (at x=0) seems to have resolved the numerical diagnostics issues.

Copy link

@dallan-keylogic dallan-keylogic left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines +57 to +62
# add the membrane unit model
m.fs.membrane = TwoSaltDiafiltration(
property_package=m.fs.properties,
NFEx=10,
NFEz=5,
)

Choose a reason for hiding this comment

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

I tried solving the model with different NFEx and NFEz values. When changing the NFEz, the model solves great even with large NFEz values. However, an error is reported when trying different NFEx values. The error points to a port mismatch in the m.fs.retentate_stream (line 127) and m.fs.permeate_stream (line 131) arcs due to the flow_vol port variable having mismatched indices on ports fs.membrane.retentate_outlet and fs.retentate_block.inlet, and fs.membrane.permeate_outlet and fs.permeate_block.inlet, respectively. As far as I can tell, this is caused by two things:

  • The SoluteProductParameter block having its variables defined on an x domain generated within diafiltration_solute_product_properties.py separately and independently from the membrane unit model block (TwoSaltDiafiltration). So when a user changes the NFEx value for the membrane model (line 60 in diafiltration_flowsheet_two_salt.py), they also need to navigate to the products properties file and set the nfe value there (line 53 in diafiltration_solute_product_properties.py).
  • Even when the NFEx and the nfe values are set to be equal in the two separate files, the indices of the variables of the membrane model and port are mismatched for most number of finite elements values (nfe=10 seems to be a lucky number).

If doable, it would be nice to have the feature of setting the number of finite elements in a single location at the flowsheet level, and have it automatically propagate to the membrane unit model and to the property package for the products. Otherwise, please include a comment instructing potential users to change the number of finite elements in both locations.

Finally, please look into having the membrane unit model and product properties' x domain indices match for different number of finite element values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your comment @aostace01. I will start with @dallan-keylogic's suggestion to address this.

With regard to nfe=10 being lucky, I believe this is due to the two independent discretizations you mentioned, which use slightly different methods, so there is a significant digit issue between the ports.

@aostace01
Copy link

Hi, @mollydougher. I found a bug - please see my comment. Everything else looks good to me; the PR can be merged as soon as the bug is addressed.

@dallan-keylogic
Copy link

Thank you @aostace01 for catching that. However, I disagree about making nfe a flowsheet-level variable.

Typically how we deal with this situation is to not have the property state variables indexed by space and time, but rather create a StateBlock that is indexed by state and time. So instead of accessing properties.flow_mol[t, x] we access properties[t, x].flow_mol. See the ControlVolume1D as an example. Since that's a pretty complex unit model, let me know if you have any questions.

@dallan-keylogic dallan-keylogic self-requested a review May 13, 2025 14:03
@aostace01 aostace01 self-requested a review May 14, 2025 14:09
@mollydougher
Copy link
Contributor Author

@aostace01 @dallan-keylogic I believe the aforementioned bug has been addressed. Please let me know if you have any remaining comments.

Copy link

@dallan-keylogic dallan-keylogic left a comment

Choose a reason for hiding this comment

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

Something I hadn't noticed until this recent changeset is that you're not putting individual streams for the retentate and permeate, but the entire flow and concentration profile across the entire length of the membrane. Is the plan to stack these membrane models lengthwise? If so, the inlet port should also be indexed by length, and you should then create terminal channel models.

If you're not stacking them lengthwise, you should add a permeate material balance, sum up all the material flows on the permeate side, then add a port with an outlet stream that is not indexed by length.

@mollydougher
Copy link
Contributor Author

Something I hadn't noticed until this recent changeset is that you're not putting individual streams for the retentate and permeate, but the entire flow and concentration profile across the entire length of the membrane. Is the plan to stack these membrane models lengthwise? If so, the inlet port should also be indexed by length, and you should then create terminal channel models.

If you're not stacking them lengthwise, you should add a permeate material balance, sum up all the material flows on the permeate side, then add a port with an outlet stream that is not indexed by length.

@dallan-keylogic Thank you for catching this. There may be some discussion further down the line, but currently the intention is to stage this membrane model such that the permeate of the previous membrane is the feed/retentate inlet of the next. The permeate mass balances already existed, but I have updated the Port References to reflect that the permeate and retentate product streams are collected at the end of the membrane module. Please let me know if there are any remaining concerns.

Copy link

@dallan-keylogic dallan-keylogic left a comment

Choose a reason for hiding this comment

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

Alright, looks good. Thank you for making changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Priority:High High Priority Issue or PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants