API#
This section describes the application programming interface (API) used to “wrap” flowsheets with the Flowsheet Inspector (FI).
To learn this API by example, skip down to the Minimal Wrapper section.
Overview#
FI instruments an ordinary IDAES flowsheet by placing its build, setup, initialization, and solve operations inside named steps. A wrapped flowsheet has three parts:
A
FlowsheetRunnerinstance (here calledrunner), usually created at module scope.A
main(runner)function that performs the flowsheet work. After the required first argument, which is theFlowsheetRunnerinstance, any additional arguments can be used.Calls to
runner.step_begin()and, optionally,runner.step_end()around flowsheet operations.
FlowsheetRunner object#
The FlowsheetRunner object is the focus of the API. It is usually constructed at module scope.
Constructor#
class FlowsheetRunner(steps: list[str] = None, flowsheet_block: str = "fs", report_db: Optional[bool | str | Path | ReportDB] = True, quiet: bool = False)
- steps
- List of steps in flowsheet. This can be any list of strings, but it is recommended to use the standard step names defined in idaes_fi.common.Steps. This argument is required.
- flowsheet_block
- Name in model of flowsheet block
- report_db
- If True or None, get default DB. If False, do not use a report db. Otherwise, use value as the report database (must be a filename or ReportDB instance).
- quiet
- If True, do not print anything to the console (logging may still occur, but that is controlled separately)
Properties#
Key behaviors of the FlowsheetRunner object are controlled by setting properties.
The following properties are required or recommended to set:
main (required). This is a reference to the function mentioned above in (2), which takes the
FlowsheetRunnerinstance as its first argument and runs the flowsheet. This function can take any additional position or keyword arguments. This should be assigned before the call torun_flowsheet(), typically at module scope right after the main function is defined.model (required, but not before running). The value of the Pyomo model must be assigned to this attribute in
FlowsheetRunner.mainduring the first step of the flowsheet (typically called “build” or using the constantSteps.build). This allows for actions triggered at the end of each step to inspect and report on the state of the model.solve_result (recommended). Set this property to the return value of the Pyomo/IDAES
solver.solve()call. If this is done, the solve result will be automatically extracted and visible in the Flowsheet Inspector interface. This property can be set multiple times in a single step (internally, it is stored as a list), and set in multiple steps (each step’s solves are stored separately), so in other words it can be set any time a solve occurs in the code.
The following properties are optional or read-only:
last_step (optional). The name of the last step to run. If not set, every step is run. If set, after the step matching the given name, execution of the
FlowsheetRunner.mainfunction will stop.report_db_file (read-only). The SQLite file where the database used for tracking status and results – the ‘report’ database – is located. The default location can be changed (or even disabled) with the
report_dbargument passed to theFlowsheetRunnerconstructor. If the report database is disabled, this will beNone.
Usage#
The runner passes itself to the first argument of the main. Assign the constructed Pyomo model to runner.model during the build step so FI’s reporting actions can inspect it. The value returned by main is returned by runner.run_flowsheet().
Minimal wrapper#
The following is the basic pattern for an IDAES flowsheet module. The model construction and solver code are ordinary IDAES/Pyomo code; only the runner and step calls are FI-specific.
from pyomo.environ import ConcreteModel
from idaes.core import FlowsheetBlock
from idaes.core.solvers import get_solver
from idaes_fi import FlowsheetRunner, Steps
#--------------------------------------
# Create FlowsheetRunner object
#--------------------------------------
runner = FlowsheetRunner(steps=[
Steps.build,
Steps.initialize,
Steps.set_solver,
Steps.solve_optimization,
])
#--------------------------------------
# Existing code to build, solve, etc.
# the flowsheet does not change
#--------------------------------------
def build_flowsheet():
model = ConcreteModel()
model.fs = FlowsheetBlock(dynamic=False)
# Add the property package, unit models, arcs, and constraints here.
return model
# initialize, solve, etc..
#--------------------------------------
# Add step_begin/end and set model
# in main function.
#--------------------------------------
def main(runner):
runner.step_begin(Steps.build)
runner.model = model = build_flowsheet()
runner.step_begin(Steps.initialize)
# Initialize units as required by the flowsheet.
runner.step_begin(Steps.set_solver)
solver = get_solver()
runner.step_begin(Steps.solve_optimization)
solver.solve(model, tee=True)
return model
#--------------------------------------
# Run the flowsheet
#--------------------------------------
runner.main = main
if __name__ == "__main__":
model = runner.run_flowsheet()
Step begin/end#
step_begin() closes the previous open step automatically. FI also closes the last open step when run_flowsheet() finishes, so explicit step_end() calls are needed only when some code should be executed that is not part of any step. For example, the scaling step in the test flowsheet is explicitly closed so that the set_degrees_of_freedom() function is not inside either th scaling step or the subsequent initialization step:
runner.step_begin(Steps.set_scaling)
set_scaling(model)
runner.step_end()
set_degrees_of_freedom(model)
runner.step_begin(Steps.initialize)
initialize_flowsheet(model)
Running a wrapped module#
Run a module directly from Python with:
from my_flowsheet import runner
model = runner.run_flowsheet()
Alternatively, use the API loader. It accepts either an importable module name or a filesystem path:
from pathlib import Path
from idaes_fi.api import run_flowsheet
run_flowsheet(module_name="my_flowsheet")
run_flowsheet(path=Path("my_flowsheet.py"))
run_flowsheet() discovers FlowsheetRunner instances in the loaded module. If the module contains one runner, it is selected automatically. If it contains multiple runners, pass flowsheet_name with the runner variable’s
name, for example flowsheet_name="FS2".
run_flowsheet(path=Path("flowsheets.py"), flowsheet_name="FS2")
The flowsheet_block argument to the FlowsheetRunner constructor names the flowsheet block in the Pyomo model and is
used by FI actions such as degree-of-freedom checks. It defaults to "fs".
Steps#
Use the constants in Steps instead of spelling step names manually. The
available names include:
Steps.build
Steps.set_solver
Steps.initialize
Steps.set_operating_conditions
Steps.set_scaling
Steps.solve_initial
Steps.set_autoscaling
Steps.add_costing
Steps.initialize_costing
Steps.setup_optimization
Steps.solve_optimization
The runner.steps list records the steps used by the flowsheet and lets FI
actions interpret the run. It does not execute those steps.
Reports and console output#
By default, FlowsheetRunner writes FI action reports to the IDAES report
database. Use report_db=False to disable database reporting. When database
reporting is disabled, quiet=False prints the captured solver output and
errors at the end of the run; quiet=True suppresses that report.
To use a specific database, pass a ReportDB instance or path object/string from which to construct one:
from pathlib import Path
from idaes_fi import FlowsheetRunner
from idaes_fi.api import ReportDB
runner = FlowsheetRunner(
report_db="reports.sqlite"
# Equivalent: report_db=ReportDB(Path("reports.sqlite"))
)
The default action set collects solver output, diagnostics, unit-model and
variable reports, stream tables, timings, a Mermaid diagram, and the Git hash
when available. The runner’s report_db_file property identifies the active
database when database reporting is enabled.