Skip to content

Commit 3b76abc

Browse files
dangunterCopyDemonblnichoksbeattie
authored
Structured flowsheet wrapper util (#1702)
Python subpackage idaes.core.util.structfs to add supporting functions for annotating a flowsheet to control it programmatically, and associated functions to extract useful information during or after the run for display by other tools. Accompanying documentation and tests. --------- Co-authored-by: Sheng Pang <shengdevelop@gmail.com> Co-authored-by: blnicho <blnicho@sandia.gov> Co-authored-by: Keith Beattie <ksbeattie@lbl.gov>
1 parent 7f5c598 commit 3b76abc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+5317
-65
lines changed

.github/actions/setup-idaes/action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ runs:
5858
fi
5959
6060
echo '::group::Output of test commands for IDAES binaries'
61+
echo 'List IDAES binaries directory'
62+
ls -l "$(idaes bin-directory)"
63+
echo 'Try to run ipopt'
6164
"$(idaes bin-directory)"/ipopt -v
6265
echo '::endgroup::'
6366
- name: Install AMPL SCIP (${{ inputs.ampl-scip-pip-target }})

.github/workflows/core.yml

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ on:
44
push:
55
branches:
66
- main
7-
- '*_rel'
7+
- "*_rel"
88
schedule:
99
# run daily at 5:00 am UTC (12 am ET/9 pm PT)
10-
- cron: '0 5 * * *'
10+
- cron: "0 5 * * *"
1111
repository_dispatch:
1212
# to run this, send a POST API call at repos/IDAES/idaes-pse/dispatches with the specified event_type
1313
# e.g. `gh repos/IDAES/idaes-pse/dispatches -F event_type=ci_run_tests`
@@ -38,7 +38,7 @@ concurrency:
3838

3939
env:
4040
# default Python version to use for checks that do not require multiple versions
41-
DEFAULT_PYTHON_VERSION: '3.10'
41+
DEFAULT_PYTHON_VERSION: "3.10"
4242
IDAES_CONDA_ENV_NAME_DEV: idaes-pse-dev
4343
PYTEST_ADDOPTS: "--color=yes"
4444

@@ -54,7 +54,7 @@ jobs:
5454
runs-on: ubuntu-latest
5555
steps:
5656
- uses: actions/checkout@v4
57-
57+
5858
- uses: actions/setup-python@v5
5959
with:
6060
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
@@ -76,10 +76,10 @@ jobs:
7676
steps:
7777
- name: Checkout source
7878
uses: actions/checkout@v4
79-
79+
8080
- name: Run Spell Checker
8181
uses: crate-ci/typos@v1.24.5
82-
with:
82+
with:
8383
config: ./.github/workflows/typos.toml
8484

8585
pytest:
@@ -90,7 +90,7 @@ jobs:
9090
strategy:
9191
fail-fast: false
9292
matrix:
93-
python-version: ['3.10', '3.11', '3.12', '3.13']
93+
python-version: ["3.10", "3.11", "3.12", "3.13"]
9494
os:
9595
- linux
9696
- win64
@@ -99,31 +99,30 @@ jobs:
9999
runner-image: ubuntu-24.04
100100
- os: win64
101101
runner-image: windows-2022
102-
- python-version: '3.11'
102+
- python-version: "3.11"
103103
# only generate coverage report for a single python version in the matrix
104104
# to avoid overloading Codecov
105105
cov-report: true
106106

107-
108107
steps:
109108
- uses: actions/checkout@v5
110-
111109
- uses: ./.github/actions/display-debug-info
112110
- name: Set up Conda environment
113111
uses: conda-incubator/setup-miniconda@v3
114112
with:
115113
activate-environment: ${{ env.IDAES_CONDA_ENV_NAME_DEV }}
116114
python-version: ${{ matrix.python-version }}
117115
miniforge-version: latest
118-
119116
- name: Set up idaes
120117
uses: ./.github/actions/setup-idaes
121118
with:
122-
install-target: -r requirements-dev.txt
119+
install-target: -r requirements-dev.txt
123120
- name: Add pytest CLI options for coverage
124121
if: matrix.cov-report
125122
run: |
126123
echo PYTEST_ADDOPTS="$PYTEST_ADDOPTS --cov --cov-report=xml" >> "$GITHUB_ENV"
124+
- name: Install pandoc
125+
run: conda install -c conda-forge pandoc
127126
- name: Run pytest (not integration)
128127
run: |
129128
pytest --pyargs idaes -m "not integration"
@@ -134,7 +133,7 @@ jobs:
134133
name: coverage-report-${{ matrix.os }}
135134
path: coverage.xml
136135
if-no-files-found: error
137-
136+
138137
upload-coverage:
139138
name: Upload coverage report (Codecov)
140139
needs: [pytest]
@@ -146,7 +145,7 @@ jobs:
146145
steps:
147146
# the checkout step is needed to have access to codecov.yml
148147
- uses: actions/checkout@v4
149-
148+
150149
- uses: actions/download-artifact@v4
151150
with:
152151
name: coverage-report-${{ matrix.report-variant }}
@@ -170,13 +169,15 @@ jobs:
170169
needs: [code-formatting, spell-check]
171170
steps:
172171
- uses: actions/checkout@v4
173-
172+
174173
- name: Set up Conda environment
175174
uses: conda-incubator/setup-miniconda@v3
176175
with:
177176
activate-environment: ${{ env.IDAES_CONDA_ENV_NAME_DEV }}
178177
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
179178
miniforge-version: latest
179+
- name: Install pandoc
180+
run: conda install -c conda-forge pandoc
180181
- name: Set up idaes
181182
uses: ./.github/actions/setup-idaes
182183
with:
@@ -206,7 +207,7 @@ jobs:
206207
# NOTE: using Conda instead of actions/setup-python in this job is not strictly necessary
207208
# as it doesn't need to run on Windows or use the setup-idaes local action,
208209
# but we do it for consistency with the other jobs
209-
210+
210211
- name: Set up Conda environment
211212
uses: conda-incubator/setup-miniconda@v3
212213
with:
@@ -216,7 +217,7 @@ jobs:
216217
- name: Set up idaes
217218
uses: ./.github/actions/setup-idaes
218219
with:
219-
install-target: -r requirements-dev.txt
220+
install-target: -r requirements-dev.txt
220221
- name: Run pylint
221222
run: |
222223
echo "::group::Display pylint version"
@@ -240,7 +241,7 @@ jobs:
240241
runs-on: ubuntu-24.04
241242
steps:
242243
- uses: actions/checkout@v4
243-
244+
244245
- name: Set up Conda environment
245246
uses: conda-incubator/setup-miniconda@v3
246247
with:
@@ -250,7 +251,7 @@ jobs:
250251
- name: Set up idaes
251252
uses: ./.github/actions/setup-idaes
252253
with:
253-
install-target: -r requirements-dev.txt
254+
install-target: -r requirements-dev.txt
254255
- name: Create empty pytest.ini file
255256
run: |
256257
echo "" > pytest.ini

.github/workflows/typos.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,6 @@ PN = "PN"
5858
hd = "hd"
5959
Tge = "Tge"
6060
iy = "iy"
61+
62+
# more chemE abbreviations
63+
HDA = "HDA"

docs/build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def run_apidoc(clean=True, dry_run=False, **kwargs):
7373
"apidoc",
7474
"../idaes",
7575
"../idaes/*tests*",
76+
"../idaes/core/util/structfs", # handled by apidoc2
7677
],
7778
60,
7879
dry_run,

docs/conf.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
# For importing from idaes.<modules..>
1414
sys.path.insert(0, os.path.abspath(".."))
15+
sys.path.insert(0, os.path.abspath("."))
1516

1617

1718
# -- General configuration ------------------------------------------------
@@ -36,8 +37,25 @@
3637
"sphinxarg.ext",
3738
"sphinx.ext.doctest",
3839
"sphinx_copybutton",
40+
"nbsphinx",
41+
# MystMD extensions
42+
"myst_parser",
43+
"autodoc2",
3944
]
4045

46+
# Myst autodoc2 (experimental)
47+
autodoc2_packages = [
48+
"../idaes/core/util/structfs",
49+
]
50+
autodoc2_output_dir = "reference_guides/core/util"
51+
autodoc2_render_plugin = "myst"
52+
autodoc2_docstring_parser_regexes = [
53+
# render docstrings in matching files as Markdown
54+
("../idaes/core/util/structfs/.*", "myst"),
55+
]
56+
autodoc2_no_index = True
57+
autodoc2_index_template = None # don't write index.rst
58+
4159
# Put type hints in the description, not signature
4260
autodoc_typehints = "description"
4361

@@ -51,7 +69,10 @@
5169
# You can specify multiple suffix as a list of string:
5270
#
5371
# source_suffix = ['.rst', '.md']
54-
source_suffix = ".rst"
72+
source_suffix = {
73+
".rst": "restructuredtext",
74+
".md": "markdown",
75+
}
5576

5677
# The encoding of source files.
5778
#
@@ -62,7 +83,7 @@
6283

6384
# General information about the project.
6485
project = "IDAES"
65-
copyright = "2016-2024, David Miller et al."
86+
copyright = "2016-2026, David Miller et al."
6687
author = "The IDAES project"
6788

6889

@@ -85,7 +106,14 @@
85106
# List of patterns, relative to source directory, that match files and
86107
# directories to ignore when looking for source files.
87108
# This patterns also effect to html_static_path and html_extra_path
88-
exclude_patterns = ["apidoc/*tests*"]
109+
exclude_patterns = [
110+
"apidoc/*tests*",
111+
"../idaes/core/util/structfs",
112+
"build",
113+
"build/**",
114+
"_build",
115+
"_build/**",
116+
]
89117

90118
# If true, `todo` and `todoList` produce output, else they produce nothing.
91119
todo_include_todos = False
@@ -140,6 +168,11 @@ def __call__(self, app, docname, source):
140168
"pyomo": ("https://pyomo.readthedocs.io/en/stable/", None),
141169
}
142170

171+
# nbsphinx notebook execution
172+
# other options are "never" and "always"
173+
nbsphinx_execute = "auto"
174+
175+
143176
# -- Options for HTML output ----------------------------------------------
144177

145178
html_theme = "sphinx_book_theme"

docs/examples/index.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Examples
2+
========
3+
4+
.. toctree::
5+
:maxdepth: 1
6+
7+
structfs/index
8+
9+
.. note::
10+
11+
Most of the IDAES example flowsheets are available on the
12+
`IDAES examples documentation pages <https://idaes-examples.readthedocs.io/en/latest/>`_.
13+
14+
This section of the documentation contains some internal examples that are targeted at specific features,
15+
currently the "structured flowsheet" (structfs) functionality in :mod:`idaes.core.util.structfs`.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
###############################################################################
2+
# The Institute for the Design of Advanced Energy Systems Integrated Platform
3+
# Framework (IDAES IP) was produced under the DOE Institute for the
4+
# Design of Advanced Energy Systems (IDAES).
5+
#
6+
# Copyright (c) 2018-2026 by the software owners: The Regents of the
7+
# University of California, through Lawrence Berkeley National Laboratory,
8+
# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon
9+
# University, West Virginia University Research Corporation, et al.
10+
# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md
11+
# for full copyright and license information.
12+
#
13+
###############################################################################
14+
"""
15+
Simple Flash flowsheet for use in testing.
16+
"""
17+
18+
from pyomo.environ import ConcreteModel, SolverFactory
19+
from idaes.core import FlowsheetBlock
20+
21+
# Import idaes logger to set output levels
22+
import idaes.logger as idaeslog
23+
from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (
24+
BTXParameterBlock,
25+
)
26+
from idaes.models.unit_models import Flash
27+
from idaes.core.util.structfs.fsrunner import FlowsheetRunner
28+
29+
FS = FlowsheetRunner()
30+
31+
# # Flash Unit Model
32+
#
33+
# Author: Jaffer Ghouse
34+
# Maintainer: Dan Gunter
35+
# Updated: 2023-06-01
36+
37+
38+
@FS.step("build")
39+
def build_model(ctx):
40+
"""Build the model."""
41+
m = ConcreteModel()
42+
m.fs = FlowsheetBlock(dynamic=False)
43+
m.fs.properties = BTXParameterBlock(
44+
valid_phase=("Liq", "Vap"), activity_coeff_model="Ideal", state_vars="FTPz"
45+
)
46+
m.fs.flash = Flash(property_package=m.fs.properties)
47+
# assert degrees_of_freedom(m) == 7
48+
ctx.model = m
49+
50+
51+
@FS.step("set_operating_conditions")
52+
def set_operating_conditions(ctx):
53+
"""Set operating conditions."""
54+
m = ctx.model
55+
m.fs.flash.inlet.flow_mol.fix(1)
56+
m.fs.flash.inlet.temperature.fix(368)
57+
m.fs.flash.inlet.pressure.fix(101325)
58+
m.fs.flash.inlet.mole_frac_comp[0, "benzene"].fix(0.5)
59+
m.fs.flash.inlet.mole_frac_comp[0, "toluene"].fix(0.5)
60+
m.fs.flash.heat_duty.fix(0)
61+
m.fs.flash.deltaP.fix(0)
62+
63+
64+
@FS.step("initialize")
65+
def init_model(ctx):
66+
""" "Initialize the model."""
67+
m = ctx.model
68+
m.fs.flash.initialize(outlvl=idaeslog.INFO)
69+
70+
71+
@FS.step("set_solver")
72+
def set_solver(ctx):
73+
"""Set the solver."""
74+
ctx.solver = SolverFactory("ipopt")
75+
76+
77+
@FS.step("solve_initial")
78+
def solve(ctx):
79+
"""Perform the initial model solve."""
80+
ctx["results"] = ctx.solver.solve(ctx.model, tee=ctx["tee"])
81+
82+
83+
@FS.step("solve_optimization")
84+
def solve_o(ctx):
85+
ctx["results"] = ctx.solver.solve(ctx.model, tee=ctx["tee"])

0 commit comments

Comments
 (0)