From dc653a6cbda43495e9a91eb5026c318cb5fc5441 Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Fri, 15 May 2026 12:41:10 -0700 Subject: [PATCH 1/7] ignore template file --- .coveragerc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.coveragerc b/.coveragerc index 9f3ee40..6968d43 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,6 +4,7 @@ omit = */tests/* test/* */test/* + copy_template.py [report] omit = @@ -11,3 +12,4 @@ omit = */tests/* test/* */test/* + copy_template.py From 7439feb39a70e175fc2ce789177d0055ebaa1367 Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Fri, 15 May 2026 12:41:51 -0700 Subject: [PATCH 2/7] refactor --- src/idaes_fi/structfs/actions/__init__.py | 2 + .../structfs/actions/mermaid_diagram.py | 4 +- src/idaes_fi/structfs/actions/model_report.py | 172 ++++++ .../structfs/actions/model_variables.py | 4 +- src/idaes_fi/structfs/actions/solver.py | 4 +- src/idaes_fi/structfs/actions/stream_table.py | 4 +- src/idaes_fi/structfs/actions/timer.py | 4 +- .../structfs/actions/unit_dof_checker.py | 4 +- src/idaes_fi/structfs/tests/test1.ipynb | 568 ++++++++++++++++++ .../structfs/tests/test_runner_actions.py | 39 +- 10 files changed, 792 insertions(+), 13 deletions(-) create mode 100644 src/idaes_fi/structfs/actions/model_report.py create mode 100644 src/idaes_fi/structfs/tests/test1.ipynb diff --git a/src/idaes_fi/structfs/actions/__init__.py b/src/idaes_fi/structfs/actions/__init__.py index 8794f51..1bcabde 100644 --- a/src/idaes_fi/structfs/actions/__init__.py +++ b/src/idaes_fi/structfs/actions/__init__.py @@ -32,6 +32,7 @@ "Timer", "UnitDofChecker", "UnitDofType", + "UnitModelReport", ] _EXPORT_MODULES = { @@ -46,6 +47,7 @@ "Timer": "timer", "UnitDofChecker": "unit_dof_checker", "UnitDofType": "unit_dof_checker", + "UnitModelReport": "model_report", } diff --git a/src/idaes_fi/structfs/actions/mermaid_diagram.py b/src/idaes_fi/structfs/actions/mermaid_diagram.py index 6663149..6d19a27 100644 --- a/src/idaes_fi/structfs/actions/mermaid_diagram.py +++ b/src/idaes_fi/structfs/actions/mermaid_diagram.py @@ -1,8 +1,8 @@ ################################################################################# # Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 # -# "Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)" was produced under the DOE -# Process Optimization and Modeling for Minerals Sustainability ("PrOMMiS") initiative, and is +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is # copyrighted by the software owners: The Regents of the University of California, through Lawrence # Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through # Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West diff --git a/src/idaes_fi/structfs/actions/model_report.py b/src/idaes_fi/structfs/actions/model_report.py new file mode 100644 index 0000000..822c783 --- /dev/null +++ b/src/idaes_fi/structfs/actions/model_report.py @@ -0,0 +1,172 @@ +################################################################################# +# Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 +# +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is +# copyrighted by the software owners: The Regents of the University of California, through Lawrence +# Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through +# Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West +# Virginia University Research Corporation. +# +# NOTICE. This Software was developed under funding from the U.S. Department of Energy and the +# U.S. Government consequently retains certain rights. As such, the U.S. Government has been granted +# for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license +# in the Software to reproduce, distribute copies to the public, prepare derivative works, and perform +# publicly and display publicly, and to permit other to do so. +# +################################################################################# +""" +Unit model report extraction action +""" + +# stdlib +from enum import Enum + +# IDAES and Pyomo +from idaes.core.util.units_of_measurement import report_quantity +from idaes.core.util.model_statistics import ( + number_activated_blocks, + number_activated_constraints, + number_variables, + degrees_of_freedom, +) +from idaes.core.base.unit_model import UnitModelBlockData +from pyomo.network import Arc +from idaes.core.util.exceptions import ConfigurationError + +# Pydantic +from pydantic import BaseModel, Field + +# package +from ..action_base import Action + + +class ModelType(str, Enum): + unit = "unit" + flowsheet = "flowsheet" + + +class UnitModelReport(Action): + """Extract report from unit model. + + The resulting report is structured as one report per + step, each containing details for components that + implement the IDAES reporting functions. + + ``` + step_reports: + step_name: + reports: + component_name: + model_type + performance + stream_table + degrees of freedom (dof) + time_point (always 0) + ``` + + + """ + + class PerfReport(BaseModel): + """Report for UnitModelReport""" + + model_type: ModelType + performance: dict = Field(default={}) + stream_table: dict = Field(default={}) + dof: dict = Field(default={}) + time_point: float = 0.0 + + class ComponentReports(BaseModel): + reports: dict[str, UnitModelReport.PerfReport] = Field(default={}) + + class Report(BaseModel): + # report for each step; the report for the run is just the last one + step_reports: dict[str, UnitModelReport.ComponentReports] = Field(default={}) + last_step: str = "" + + def __init__(self, *args, **kwargs): + """Constructor.""" + super().__init__(*args, **kwargs) + self._dof = True # XXX: allow user to control + self._rpt = self.Report() + + def after_step(self, name: str): + """Get all the component reports in the model, after each step.""" + self._rpt.step_reports[name] = self._get_component_reports() + self._rpt.last_step = name # make it easy to find last report + + def _get_component_reports(self) -> dict[str, ComponentReports]: + m, r = self._runner.model, self.ComponentReports() + for comp in m.component_objects(): + comp_name = comp.name + # print(f"{comp_name} ({type(comp_name)})") + if not comp_name in r and self._has_report(comp): + r.reports[comp_name] = self._get_report(comp) + return r + + @staticmethod + def _has_report(comp: object): + return isinstance(comp, UnitModelBlockData) and hasattr( + comp, "_get_performance_contents" + ) + + def _get_report(self, comp): + time_point = 0.0 + + is_fs = hasattr(comp, "is_flowsheet") and comp.is_flowsheet + rpt = self.PerfReport( + model_type="flowsheet" if is_fs else "unit", time_point=time_point + ) + + # Get DoF and model stats + if self._dof: + rpt.dof = dict( + dof_stat=degrees_of_freedom(comp), + num_variables=number_variables(comp), + num_act_constraints=number_activated_constraints(comp), + num_act_blocks=number_activated_blocks(comp), + ) + + # Get performance variables + performance = comp._get_performance_contents(time_point=time_point) + print(f"@@ 1 with performance={performance}") + if performance is None or performance == {}: + self.log.warning( + f"Empty performance contents for {rpt.model_type.value} model {comp}" + ) + else: + print(f"@@ 2 with performance={performance}") + # reformat variable values + for section in ("vars",): + try: + performance_section = performance[section] + except KeyError: + self.log.error(f"Missing 'vars' section in model report for {comp}") + continue + for k, v in performance_section.items(): + # serialize pyomo value objects as dicts + if hasattr(v, "value"): + performance[section][k] = { + "value": report_quantity(v).m, + "units": str(report_quantity(v).u), + "fixed": v.fixed, + "bounds": v.bounds, + } + # leave other objects alone + rpt.performance = performance + + # Get stream table + try: + stream_table = comp._get_stream_table_contents(time_point=time_point) + stream_dict = stream_table.to_dict() + stream_dict["Units"] = {k: str(v) for k, v in stream_dict["Units"].items()} + except (AttributeError, ConfigurationError): + stream_dict = {} + rpt.stream_table = stream_dict + + return rpt + + def report(self) -> Report: + """Report containing unit model or flowsheet report values after each step.""" + return self._rpt diff --git a/src/idaes_fi/structfs/actions/model_variables.py b/src/idaes_fi/structfs/actions/model_variables.py index 0548beb..ffd8587 100644 --- a/src/idaes_fi/structfs/actions/model_variables.py +++ b/src/idaes_fi/structfs/actions/model_variables.py @@ -1,8 +1,8 @@ ################################################################################# # Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 # -# "Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)" was produced under the DOE -# Process Optimization and Modeling for Minerals Sustainability ("PrOMMiS") initiative, and is +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is # copyrighted by the software owners: The Regents of the University of California, through Lawrence # Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through # Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West diff --git a/src/idaes_fi/structfs/actions/solver.py b/src/idaes_fi/structfs/actions/solver.py index af648ce..5335500 100644 --- a/src/idaes_fi/structfs/actions/solver.py +++ b/src/idaes_fi/structfs/actions/solver.py @@ -1,8 +1,8 @@ ################################################################################# # Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 # -# "Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)" was produced under the DOE -# Process Optimization and Modeling for Minerals Sustainability ("PrOMMiS") initiative, and is +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is # copyrighted by the software owners: The Regents of the University of California, through Lawrence # Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through # Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West diff --git a/src/idaes_fi/structfs/actions/stream_table.py b/src/idaes_fi/structfs/actions/stream_table.py index fbf9be5..6012558 100644 --- a/src/idaes_fi/structfs/actions/stream_table.py +++ b/src/idaes_fi/structfs/actions/stream_table.py @@ -1,8 +1,8 @@ ################################################################################# # Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 # -# "Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)" was produced under the DOE -# Process Optimization and Modeling for Minerals Sustainability ("PrOMMiS") initiative, and is +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is # copyrighted by the software owners: The Regents of the University of California, through Lawrence # Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through # Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West diff --git a/src/idaes_fi/structfs/actions/timer.py b/src/idaes_fi/structfs/actions/timer.py index 1bc80ee..1736aa7 100644 --- a/src/idaes_fi/structfs/actions/timer.py +++ b/src/idaes_fi/structfs/actions/timer.py @@ -1,8 +1,8 @@ ################################################################################# # Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 # -# "Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)" was produced under the DOE -# Process Optimization and Modeling for Minerals Sustainability ("PrOMMiS") initiative, and is +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is # copyrighted by the software owners: The Regents of the University of California, through Lawrence # Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through # Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West diff --git a/src/idaes_fi/structfs/actions/unit_dof_checker.py b/src/idaes_fi/structfs/actions/unit_dof_checker.py index 4f71330..ab1be4b 100644 --- a/src/idaes_fi/structfs/actions/unit_dof_checker.py +++ b/src/idaes_fi/structfs/actions/unit_dof_checker.py @@ -1,8 +1,8 @@ ################################################################################# # Process Optimization and Modeling for Minerals Sustainability (PrOMMiS) Copyright (c) 2023-2026 # -# "Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)" was produced under the DOE -# Process Optimization and Modeling for Minerals Sustainability ("PrOMMiS") initiative, and is +# “Process Optimization and Modeling for Minerals Sustainability (PrOMMiS)” was produced under the DOE +# Process Optimization and Modeling for Minerals Sustainability (“PrOMMiS”) initiative, and is # copyrighted by the software owners: The Regents of the University of California, through Lawrence # Berkeley National Laboratory, National Technology & Engineering Solutions of Sandia, LLC through # Sandia National Laboratories, Carnegie Mellon University, University of Notre Dame, and West diff --git a/src/idaes_fi/structfs/tests/test1.ipynb b/src/idaes_fi/structfs/tests/test1.ipynb new file mode 100644 index 0000000..8ff3645 --- /dev/null +++ b/src/idaes_fi/structfs/tests/test1.ipynb @@ -0,0 +1,568 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 33, + "id": "cd3e734c", + "metadata": {}, + "outputs": [], + "source": [ + "from demo_flowsheet_structured import FS\n", + "from idaes.core.util.units_of_measurement import report_quantity\n", + "from idaes.core.util.model_statistics import number_activated_blocks, number_activated_constraints, number_variables, degrees_of_freedom\n", + "from idaes.core.base.unit_model import UnitModelBlockData\n", + "from pyomo.network import Arc" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f6838ed3", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "get indexes of first step '-' and last step '-' in steps ['build', 'set_solver', 'initialize', 'set_operating_conditions', 'set_scaling', 'solve_initial', 'set_autoscaling', 'add_costing', 'initialize_costing', 'setup_optimization', 'solve_optimization']\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_comp[benzene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_comp[toluene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_phase_equilibrium[benzene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_phase_equilibrium[toluene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_P_vap[benzene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_P_vap[toluene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_comp[benzene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_comp[toluene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_phase_equilibrium[benzene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_phase_equilibrium[toluene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_P_vap[benzene]\n", + "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_P_vap[toluene]\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 9\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 9\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 15\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 10\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 14\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 15\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 14\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 15\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 13\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 14 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 13\n", + "component keys that are not exported as part of the NL file. Skipping.\n", + "WARNING: model contains export suffix 'scaling_factor' that contains 14 keys\n", + "that are not Var, Constraint, Objective, or the model. Skipping.\n" + ] + } + ], + "source": [ + "FS.run_steps()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "8f0886ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[31mSignature:\u001b[39m f03.report(time_point=\u001b[32m0\u001b[39m, dof=\u001b[38;5;28;01mFalse\u001b[39;00m, ostream=\u001b[38;5;28;01mNone\u001b[39;00m, prefix=\u001b[33m''\u001b[39m)\n", + "\u001b[31mDocstring:\u001b[39m \n", + "\u001b[31mSource:\u001b[39m \n", + " \u001b[38;5;28;01mdef\u001b[39;00m report(self, time_point=\u001b[32m0\u001b[39m, dof=\u001b[38;5;28;01mFalse\u001b[39;00m, ostream=\u001b[38;5;28;01mNone\u001b[39;00m, prefix=\u001b[33m\"\"\u001b[39m):\n", + "\n", + " time_point = float(time_point)\n", + "\n", + " \u001b[38;5;28;01mif\u001b[39;00m ostream \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + " ostream = sys.stdout\n", + "\n", + " \u001b[38;5;66;03m# Get DoF and model stats\u001b[39;00m\n", + " \u001b[38;5;28;01mif\u001b[39;00m dof:\n", + " dof_stat = degrees_of_freedom(self)\n", + " nv = number_variables(self)\n", + " nc = number_activated_constraints(self)\n", + " nb = number_activated_blocks(self)\n", + "\n", + " \u001b[38;5;66;03m# Get components to report in performance section\u001b[39;00m\n", + " performance = self._get_performance_contents(time_point=time_point)\n", + "\n", + " \u001b[38;5;66;03m# Get stream table\u001b[39;00m\n", + " stream_table = self._get_stream_table_contents(time_point=time_point)\n", + "\n", + " \u001b[38;5;66;03m# Set model type output\u001b[39;00m\n", + " \u001b[38;5;28;01mif\u001b[39;00m hasattr(self, \u001b[33m\"is_flowsheet\"\u001b[39m) \u001b[38;5;28;01mand\u001b[39;00m self.is_flowsheet:\n", + " model_type = \u001b[33m\"Flowsheet\"\u001b[39m\n", + " \u001b[38;5;28;01melse\u001b[39;00m:\n", + " model_type = \u001b[33m\"Unit\"\u001b[39m\n", + "\n", + " \u001b[38;5;66;03m# Write output\u001b[39;00m\n", + " max_str_length = \u001b[32m84\u001b[39m\n", + " tab = \u001b[33m\" \"\u001b[39m * \u001b[32m4\u001b[39m\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"=\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", + "\n", + " lead_str = f\"{prefix}{model_type} : {self.name}\"\n", + " trail_str = f\"Time: {time_point}\"\n", + " mid_str = \u001b[33m\" \"\u001b[39m * (max_str_length - len(lead_str) - len(trail_str))\n", + " ostream.write(lead_str + mid_str + trail_str)\n", + "\n", + " \u001b[38;5;28;01mif\u001b[39;00m dof:\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"=\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(f\"{prefix}{tab}Local Degrees of Freedom: {dof_stat}\")\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(\n", + " f\"{prefix}{tab}Total Variables: {nv}{tab}\"\n", + " f\"Activated Constraints: {nc}{tab}\"\n", + " f\"Activated Blocks: {nb}\"\n", + " )\n", + "\n", + " \u001b[38;5;28;01mif\u001b[39;00m performance \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + " \u001b[38;5;66;03m# PYLINT-WHY: pylint has no way of knowing that performance is supposed to be dict-like\u001b[39;00m\n", + " \u001b[38;5;66;03m# pylint: disable=unsubscriptable-object\u001b[39;00m\n", + " \u001b[38;5;66;03m# PYLINT-TODO: alternatively, have the function return an empty dict and test with `if performance:`\u001b[39;00m\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"-\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(f\"{prefix}{tab}Unit Performance\")\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m * \u001b[32m2\u001b[39m)\n", + " \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"vars\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m performance.keys() \u001b[38;5;28;01mand\u001b[39;00m len(performance[\u001b[33m\"vars\"\u001b[39m]) > \u001b[32m0\u001b[39m:\n", + " ostream.write(f\"{prefix}{tab}Variables: \\n\\n\")\n", + "\n", + " tabular_writer(\n", + " ostream,\n", + " prefix + tab,\n", + " ((k, v) \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;28;01min\u001b[39;00m performance[\u001b[33m\"vars\"\u001b[39m].items()),\n", + " (\u001b[33m\"Value\"\u001b[39m, \u001b[33m\"Units\"\u001b[39m, \u001b[33m\"Fixed\"\u001b[39m, \u001b[33m\"Bounds\"\u001b[39m),\n", + " \u001b[38;5;28;01mlambda\u001b[39;00m k, v: [\n", + " \u001b[33m\"{:#.5g}\"\u001b[39m.format(report_quantity(v).m),\n", + " report_quantity(v).u,\n", + " v.fixed,\n", + " v.bounds,\n", + " ],\n", + " )\n", + "\n", + " \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"exprs\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m performance.keys() \u001b[38;5;28;01mand\u001b[39;00m len(performance[\u001b[33m\"exprs\"\u001b[39m]) > \u001b[32m0\u001b[39m:\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(f\"{prefix}{tab}Expressions: \\n\\n\")\n", + "\n", + " tabular_writer(\n", + " ostream,\n", + " prefix + tab,\n", + " ((k, v) \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;28;01min\u001b[39;00m performance[\u001b[33m\"exprs\"\u001b[39m].items()),\n", + " (\n", + " \u001b[33m\"Value\"\u001b[39m,\n", + " \u001b[33m\"Units\"\u001b[39m,\n", + " ),\n", + " \u001b[38;5;28;01mlambda\u001b[39;00m k, v: [\n", + " \u001b[33m\"{:#.5g}\"\u001b[39m.format(report_quantity(v).m),\n", + " report_quantity(v).u,\n", + " ],\n", + " )\n", + "\n", + " \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"params\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m performance.keys() \u001b[38;5;28;01mand\u001b[39;00m len(performance[\u001b[33m\"params\"\u001b[39m]) > \u001b[32m0\u001b[39m:\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(f\"{prefix}{tab}Parameters: \\n\\n\")\n", + "\n", + " tabular_writer(\n", + " ostream,\n", + " prefix + tab,\n", + " ((k, v) \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;28;01min\u001b[39;00m performance[\u001b[33m\"params\"\u001b[39m].items()),\n", + " (\u001b[33m\"Value\"\u001b[39m, \u001b[33m\"Units\"\u001b[39m, \u001b[33m\"Mutable\"\u001b[39m),\n", + " \u001b[38;5;28;01mlambda\u001b[39;00m k, v: [\n", + " report_quantity(v).m,\n", + " report_quantity(v).u,\n", + " \u001b[38;5;28;01mnot\u001b[39;00m v.is_constant(),\n", + " ],\n", + " )\n", + "\n", + " \u001b[38;5;28;01mif\u001b[39;00m stream_table \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"-\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(f\"{prefix}{tab}Stream Table\")\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", + " ostream.write(\n", + " textwrap.indent(\n", + " stream_table_dataframe_to_string(stream_table), prefix + tab\n", + " )\n", + " )\n", + " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"=\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", + "\u001b[31mFile:\u001b[39m ~/miniforge3/envs/idaes-fi-py3.13/lib/python3.13/site-packages/idaes/core/base/process_base.py\n", + "\u001b[31mType:\u001b[39m method" + ] + } + ], + "source": [ + "f03.report??" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b53f994e", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "def report(model, time_point=0, dof=True, prefix=\"\"):\n", + "\n", + " time_point = float(time_point)\n", + "\n", + " # Get DoF and model stats\n", + " if dof:\n", + " dof_stat = degrees_of_freedom(model)\n", + " nv = number_variables(model)\n", + " nc = number_activated_constraints(model)\n", + " nb = number_activated_blocks(model)\n", + "\n", + " # Get components to report in performance section\n", + " performance = model._get_performance_contents(time_point=time_point)\n", + " if performance is None:\n", + " performance = {}\n", + " else:\n", + " for section in (\"vars\",):\n", + " for k, v in performance.get(section, {}).items():\n", + " if hasattr(v, \"value\"):\n", + " performance[section][k] = {\"value\": report_quantity(v).m, \"units\": str(report_quantity(v).u), \"fixed\": v.fixed, \"bounds\": v.bounds}\n", + "\n", + " # Get stream table\n", + " try:\n", + " stream_table = model._get_stream_table_contents(time_point=time_point)\n", + " stream_dict = stream_table.to_dict()\n", + " stream_dict[\"Units\"] = {k: str(v) for k, v in stream_dict[\"Units\"].items()}\n", + " except AttributeError:\n", + " stream_dict = {}\n", + "\n", + " # Set model type output\n", + " if hasattr(model, \"is_flowsheet\") and model.is_flowsheet:\n", + " model_type = \"Flowsheet\"\n", + " else:\n", + " model_type = \"Unit\"\n", + "\n", + " return dict(performance=performance, stream_table=stream_dict, model_type=model_type, dof={\"dof\": dof_stat, \"num_var\": nv, \"num_act_constraints\": nc, \"num_act_blocks\": nb})" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "d04cbfd0", + "metadata": {}, + "outputs": [], + "source": [ + "def has_report(c):\n", + " return isinstance(c, UnitModelBlockData) and hasattr(c, \"_get_performance_contents\")" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "dd8d8a00", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fs.M01\n", + "fs.H02\n", + "fs.F03\n" + ] + } + ], + "source": [ + "unit_reports = {}\n", + "for obj in FS.model.fs.component_objects(Arc):\n", + " streams = list(obj.values()) if obj.is_indexed() else [obj]\n", + " for s in streams:\n", + " for node in (s.source.parent_block(), s.dest.parent_block()):\n", + " if not node in unit_reports and has_report(node):\n", + " print(node.name)\n", + " unit_reports[node] = report(node)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "a7daa735", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{: {'performance': {},\n", + " 'stream_table': {'Units': {'flow_mol': 'mole / second',\n", + " 'mole_frac_comp benzene': 'dimensionless',\n", + " 'mole_frac_comp toluene': 'dimensionless',\n", + " 'temperature': 'kelvin',\n", + " 'pressure': 'pascal'},\n", + " 'inlet_1': {'flow_mol': 1.0,\n", + " 'mole_frac_comp benzene': 0.99999,\n", + " 'mole_frac_comp toluene': 1e-05,\n", + " 'temperature': 370.0,\n", + " 'pressure': 101325.0},\n", + " 'inlet_2': {'flow_mol': 1.0,\n", + " 'mole_frac_comp benzene': 1e-05,\n", + " 'mole_frac_comp toluene': 0.99999,\n", + " 'temperature': 380.0,\n", + " 'pressure': 130000.0},\n", + " 'Outlet': {'flow_mol': 2.0,\n", + " 'mole_frac_comp benzene': 0.5,\n", + " 'mole_frac_comp toluene': 0.5,\n", + " 'temperature': 368.1219881350423,\n", + " 'pressure': 101324.99999999999}},\n", + " 'model_type': 'Unit',\n", + " 'dof': {'dof': 0,\n", + " 'num_var': 71,\n", + " 'num_act_constraints': 61,\n", + " 'num_act_blocks': 4}},\n", + " : {'performance': {'vars': {'Heat Duty': {'value': 18094.429605324694,\n", + " 'units': 'watt',\n", + " 'fixed': False,\n", + " 'bounds': (None, None)}}},\n", + " 'stream_table': {'Units': {'flow_mol': 'mole / second',\n", + " 'mole_frac_comp benzene': 'dimensionless',\n", + " 'mole_frac_comp toluene': 'dimensionless',\n", + " 'temperature': 'kelvin',\n", + " 'pressure': 'pascal'},\n", + " 'Inlet': {'flow_mol': 2.0,\n", + " 'mole_frac_comp benzene': 0.5,\n", + " 'mole_frac_comp toluene': 0.5,\n", + " 'temperature': 368.1219881350423,\n", + " 'pressure': 101324.99999999999},\n", + " 'Outlet': {'flow_mol': 2.0000000000000004,\n", + " 'mole_frac_comp benzene': 0.5,\n", + " 'mole_frac_comp toluene': 0.5,\n", + " 'temperature': 370.0,\n", + " 'pressure': 101324.99999999999}},\n", + " 'model_type': 'Unit',\n", + " 'dof': {'dof': 5,\n", + " 'num_var': 47,\n", + " 'num_act_constraints': 41,\n", + " 'num_act_blocks': 4}},\n", + " : {'performance': {'vars': {'Heat Duty': {'value': 1e-06,\n", + " 'units': 'watt',\n", + " 'fixed': True,\n", + " 'bounds': (None, None)},\n", + " 'Pressure Change': {'value': 1e-06,\n", + " 'units': 'pascal',\n", + " 'fixed': True,\n", + " 'bounds': (None, None)}}},\n", + " 'stream_table': {'Units': {'flow_mol': 'mole / second',\n", + " 'mole_frac_comp benzene': 'dimensionless',\n", + " 'mole_frac_comp toluene': 'dimensionless',\n", + " 'temperature': 'kelvin',\n", + " 'pressure': 'pascal'},\n", + " 'Inlet': {'flow_mol': 2.0000000000000004,\n", + " 'mole_frac_comp benzene': 0.5,\n", + " 'mole_frac_comp toluene': 0.5,\n", + " 'temperature': 370.0,\n", + " 'pressure': 101324.99999999999},\n", + " 'Vapor Outlet': {'flow_mol': 1.367289468949744,\n", + " 'mole_frac_comp benzene': 0.5693647139537593,\n", + " 'mole_frac_comp toluene': 0.43063528604624063,\n", + " 'temperature': 370.00000000042576,\n", + " 'pressure': 101325.00000099998},\n", + " 'Liquid Outlet': {'flow_mol': 0.6327105310502565,\n", + " 'mole_frac_comp benzene': 0.35010263263951724,\n", + " 'mole_frac_comp toluene': 0.6498973673604828,\n", + " 'temperature': 370.00000000042576,\n", + " 'pressure': 101325.00000099998}},\n", + " 'model_type': 'Unit',\n", + " 'dof': {'dof': 5,\n", + " 'num_var': 48,\n", + " 'num_act_constraints': 41,\n", + " 'num_act_blocks': 5}}}" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "unit_reports" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "94f02a53", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(FS.model.fs.F03, UnitModelBlockData)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "838b1f59", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "idaes.core.base.process_block._ScalarFlash" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(f03)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97c7545c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "idaes-fi-py3.13", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/idaes_fi/structfs/tests/test_runner_actions.py b/src/idaes_fi/structfs/tests/test_runner_actions.py index 6b1ef9f..a67cf34 100644 --- a/src/idaes_fi/structfs/tests/test_runner_actions.py +++ b/src/idaes_fi/structfs/tests/test_runner_actions.py @@ -18,7 +18,7 @@ # stdlib import pprint -import time +from types import SimpleNamespace import pytest from pytest import approx @@ -33,6 +33,7 @@ StreamTable, CaptureSolverOutput, GetSolverResults, + UnitModelReport, ) from . import flash_flowsheet @@ -41,6 +42,7 @@ def set_tmp_db(tmp_path): dbpath = tmp_path / "test_runner_actions.db" flash_flowsheet.FS.set_report_db(dbfile=dbpath) + return dbpath class FakeRunner: @@ -291,3 +293,38 @@ def test_get_solver_results(failed): # Check that ScalarData values were copied over assert r.values["value"] == added_value.value assert r.values["dvalue"] == added_dict.value + + +@pytest.mark.integration +def test_unit_model_report(set_tmp_db): + struct_fs = flash_flowsheet.FS + struct_fs.build() + runner = FakeRunner() + runner.model = struct_fs.model + rpt_action = UnitModelReport(runner) + rpt_action.after_step("step1") + + rpt = rpt_action.report() + assert rpt + print(rpt.model_dump_json(indent=2)) + + last = rpt.step_reports[rpt.last_step].reports + # expect 2 components + assert len(last) == 2 + for expected in ("fs.flash", "fs.flash.split"): + assert expected in last + + +@pytest.mark.unit +def test_unit_model_report_bad_perf(): + action = UnitModelReport(FakeRunner()) + # this component will trigger both the missing-var-section in performance contents + # and the attribute error for _get_stream_table_contents + comp = SimpleNamespace( + { + "_get_performance_contents": lambda time_point: {"foo": {}}, + } + ) + action._dof = False # avoids calls involving `comp` + print("> get report") + action._get_report(comp) From ae41541389e79cb9b45a2bc2388361107e1155da Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Fri, 15 May 2026 12:43:52 -0700 Subject: [PATCH 3/7] temp file --- src/idaes_fi/structfs/tests/test1.ipynb | 568 ------------------------ 1 file changed, 568 deletions(-) delete mode 100644 src/idaes_fi/structfs/tests/test1.ipynb diff --git a/src/idaes_fi/structfs/tests/test1.ipynb b/src/idaes_fi/structfs/tests/test1.ipynb deleted file mode 100644 index 8ff3645..0000000 --- a/src/idaes_fi/structfs/tests/test1.ipynb +++ /dev/null @@ -1,568 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 33, - "id": "cd3e734c", - "metadata": {}, - "outputs": [], - "source": [ - "from demo_flowsheet_structured import FS\n", - "from idaes.core.util.units_of_measurement import report_quantity\n", - "from idaes.core.util.model_statistics import number_activated_blocks, number_activated_constraints, number_variables, degrees_of_freedom\n", - "from idaes.core.base.unit_model import UnitModelBlockData\n", - "from pyomo.network import Arc" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "f6838ed3", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "get indexes of first step '-' and last step '-' in steps ['build', 'set_solver', 'initialize', 'set_operating_conditions', 'set_scaling', 'solve_initial', 'set_autoscaling', 'add_costing', 'initialize_costing', 'setup_optimization', 'solve_optimization']\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_comp[benzene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_comp[toluene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_phase_equilibrium[benzene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_phase_equilibrium[toluene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_P_vap[benzene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_1_state[0.0].eq_P_vap[toluene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_comp[benzene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_comp[toluene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_phase_equilibrium[benzene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_phase_equilibrium[toluene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_P_vap[benzene]\n", - "2026-05-14 02:57:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.M01.inlet_2_state[0.0].eq_P_vap[toluene]\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 9\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 9\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 15\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 10\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 2 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 14\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 15\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 14\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 5\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 15\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 8\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 7\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 6 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 13\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 14 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 13\n", - "component keys that are not exported as part of the NL file. Skipping.\n", - "WARNING: model contains export suffix 'scaling_factor' that contains 14 keys\n", - "that are not Var, Constraint, Objective, or the model. Skipping.\n" - ] - } - ], - "source": [ - "FS.run_steps()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "8f0886ee", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[31mSignature:\u001b[39m f03.report(time_point=\u001b[32m0\u001b[39m, dof=\u001b[38;5;28;01mFalse\u001b[39;00m, ostream=\u001b[38;5;28;01mNone\u001b[39;00m, prefix=\u001b[33m''\u001b[39m)\n", - "\u001b[31mDocstring:\u001b[39m \n", - "\u001b[31mSource:\u001b[39m \n", - " \u001b[38;5;28;01mdef\u001b[39;00m report(self, time_point=\u001b[32m0\u001b[39m, dof=\u001b[38;5;28;01mFalse\u001b[39;00m, ostream=\u001b[38;5;28;01mNone\u001b[39;00m, prefix=\u001b[33m\"\"\u001b[39m):\n", - "\n", - " time_point = float(time_point)\n", - "\n", - " \u001b[38;5;28;01mif\u001b[39;00m ostream \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - " ostream = sys.stdout\n", - "\n", - " \u001b[38;5;66;03m# Get DoF and model stats\u001b[39;00m\n", - " \u001b[38;5;28;01mif\u001b[39;00m dof:\n", - " dof_stat = degrees_of_freedom(self)\n", - " nv = number_variables(self)\n", - " nc = number_activated_constraints(self)\n", - " nb = number_activated_blocks(self)\n", - "\n", - " \u001b[38;5;66;03m# Get components to report in performance section\u001b[39;00m\n", - " performance = self._get_performance_contents(time_point=time_point)\n", - "\n", - " \u001b[38;5;66;03m# Get stream table\u001b[39;00m\n", - " stream_table = self._get_stream_table_contents(time_point=time_point)\n", - "\n", - " \u001b[38;5;66;03m# Set model type output\u001b[39;00m\n", - " \u001b[38;5;28;01mif\u001b[39;00m hasattr(self, \u001b[33m\"is_flowsheet\"\u001b[39m) \u001b[38;5;28;01mand\u001b[39;00m self.is_flowsheet:\n", - " model_type = \u001b[33m\"Flowsheet\"\u001b[39m\n", - " \u001b[38;5;28;01melse\u001b[39;00m:\n", - " model_type = \u001b[33m\"Unit\"\u001b[39m\n", - "\n", - " \u001b[38;5;66;03m# Write output\u001b[39;00m\n", - " max_str_length = \u001b[32m84\u001b[39m\n", - " tab = \u001b[33m\" \"\u001b[39m * \u001b[32m4\u001b[39m\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"=\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", - "\n", - " lead_str = f\"{prefix}{model_type} : {self.name}\"\n", - " trail_str = f\"Time: {time_point}\"\n", - " mid_str = \u001b[33m\" \"\u001b[39m * (max_str_length - len(lead_str) - len(trail_str))\n", - " ostream.write(lead_str + mid_str + trail_str)\n", - "\n", - " \u001b[38;5;28;01mif\u001b[39;00m dof:\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"=\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(f\"{prefix}{tab}Local Degrees of Freedom: {dof_stat}\")\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(\n", - " f\"{prefix}{tab}Total Variables: {nv}{tab}\"\n", - " f\"Activated Constraints: {nc}{tab}\"\n", - " f\"Activated Blocks: {nb}\"\n", - " )\n", - "\n", - " \u001b[38;5;28;01mif\u001b[39;00m performance \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - " \u001b[38;5;66;03m# PYLINT-WHY: pylint has no way of knowing that performance is supposed to be dict-like\u001b[39;00m\n", - " \u001b[38;5;66;03m# pylint: disable=unsubscriptable-object\u001b[39;00m\n", - " \u001b[38;5;66;03m# PYLINT-TODO: alternatively, have the function return an empty dict and test with `if performance:`\u001b[39;00m\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"-\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(f\"{prefix}{tab}Unit Performance\")\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m * \u001b[32m2\u001b[39m)\n", - " \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"vars\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m performance.keys() \u001b[38;5;28;01mand\u001b[39;00m len(performance[\u001b[33m\"vars\"\u001b[39m]) > \u001b[32m0\u001b[39m:\n", - " ostream.write(f\"{prefix}{tab}Variables: \\n\\n\")\n", - "\n", - " tabular_writer(\n", - " ostream,\n", - " prefix + tab,\n", - " ((k, v) \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;28;01min\u001b[39;00m performance[\u001b[33m\"vars\"\u001b[39m].items()),\n", - " (\u001b[33m\"Value\"\u001b[39m, \u001b[33m\"Units\"\u001b[39m, \u001b[33m\"Fixed\"\u001b[39m, \u001b[33m\"Bounds\"\u001b[39m),\n", - " \u001b[38;5;28;01mlambda\u001b[39;00m k, v: [\n", - " \u001b[33m\"{:#.5g}\"\u001b[39m.format(report_quantity(v).m),\n", - " report_quantity(v).u,\n", - " v.fixed,\n", - " v.bounds,\n", - " ],\n", - " )\n", - "\n", - " \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"exprs\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m performance.keys() \u001b[38;5;28;01mand\u001b[39;00m len(performance[\u001b[33m\"exprs\"\u001b[39m]) > \u001b[32m0\u001b[39m:\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(f\"{prefix}{tab}Expressions: \\n\\n\")\n", - "\n", - " tabular_writer(\n", - " ostream,\n", - " prefix + tab,\n", - " ((k, v) \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;28;01min\u001b[39;00m performance[\u001b[33m\"exprs\"\u001b[39m].items()),\n", - " (\n", - " \u001b[33m\"Value\"\u001b[39m,\n", - " \u001b[33m\"Units\"\u001b[39m,\n", - " ),\n", - " \u001b[38;5;28;01mlambda\u001b[39;00m k, v: [\n", - " \u001b[33m\"{:#.5g}\"\u001b[39m.format(report_quantity(v).m),\n", - " report_quantity(v).u,\n", - " ],\n", - " )\n", - "\n", - " \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"params\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m performance.keys() \u001b[38;5;28;01mand\u001b[39;00m len(performance[\u001b[33m\"params\"\u001b[39m]) > \u001b[32m0\u001b[39m:\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(f\"{prefix}{tab}Parameters: \\n\\n\")\n", - "\n", - " tabular_writer(\n", - " ostream,\n", - " prefix + tab,\n", - " ((k, v) \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;28;01min\u001b[39;00m performance[\u001b[33m\"params\"\u001b[39m].items()),\n", - " (\u001b[33m\"Value\"\u001b[39m, \u001b[33m\"Units\"\u001b[39m, \u001b[33m\"Mutable\"\u001b[39m),\n", - " \u001b[38;5;28;01mlambda\u001b[39;00m k, v: [\n", - " report_quantity(v).m,\n", - " report_quantity(v).u,\n", - " \u001b[38;5;28;01mnot\u001b[39;00m v.is_constant(),\n", - " ],\n", - " )\n", - "\n", - " \u001b[38;5;28;01mif\u001b[39;00m stream_table \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"-\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(f\"{prefix}{tab}Stream Table\")\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m)\n", - " ostream.write(\n", - " textwrap.indent(\n", - " stream_table_dataframe_to_string(stream_table), prefix + tab\n", - " )\n", - " )\n", - " ostream.write(\u001b[33m\"\\n\"\u001b[39m + \u001b[33m\"=\"\u001b[39m * max_str_length + \u001b[33m\"\\n\"\u001b[39m)\n", - "\u001b[31mFile:\u001b[39m ~/miniforge3/envs/idaes-fi-py3.13/lib/python3.13/site-packages/idaes/core/base/process_base.py\n", - "\u001b[31mType:\u001b[39m method" - ] - } - ], - "source": [ - "f03.report??" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b53f994e", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "\n", - "def report(model, time_point=0, dof=True, prefix=\"\"):\n", - "\n", - " time_point = float(time_point)\n", - "\n", - " # Get DoF and model stats\n", - " if dof:\n", - " dof_stat = degrees_of_freedom(model)\n", - " nv = number_variables(model)\n", - " nc = number_activated_constraints(model)\n", - " nb = number_activated_blocks(model)\n", - "\n", - " # Get components to report in performance section\n", - " performance = model._get_performance_contents(time_point=time_point)\n", - " if performance is None:\n", - " performance = {}\n", - " else:\n", - " for section in (\"vars\",):\n", - " for k, v in performance.get(section, {}).items():\n", - " if hasattr(v, \"value\"):\n", - " performance[section][k] = {\"value\": report_quantity(v).m, \"units\": str(report_quantity(v).u), \"fixed\": v.fixed, \"bounds\": v.bounds}\n", - "\n", - " # Get stream table\n", - " try:\n", - " stream_table = model._get_stream_table_contents(time_point=time_point)\n", - " stream_dict = stream_table.to_dict()\n", - " stream_dict[\"Units\"] = {k: str(v) for k, v in stream_dict[\"Units\"].items()}\n", - " except AttributeError:\n", - " stream_dict = {}\n", - "\n", - " # Set model type output\n", - " if hasattr(model, \"is_flowsheet\") and model.is_flowsheet:\n", - " model_type = \"Flowsheet\"\n", - " else:\n", - " model_type = \"Unit\"\n", - "\n", - " return dict(performance=performance, stream_table=stream_dict, model_type=model_type, dof={\"dof\": dof_stat, \"num_var\": nv, \"num_act_constraints\": nc, \"num_act_blocks\": nb})" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "d04cbfd0", - "metadata": {}, - "outputs": [], - "source": [ - "def has_report(c):\n", - " return isinstance(c, UnitModelBlockData) and hasattr(c, \"_get_performance_contents\")" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "id": "dd8d8a00", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "fs.M01\n", - "fs.H02\n", - "fs.F03\n" - ] - } - ], - "source": [ - "unit_reports = {}\n", - "for obj in FS.model.fs.component_objects(Arc):\n", - " streams = list(obj.values()) if obj.is_indexed() else [obj]\n", - " for s in streams:\n", - " for node in (s.source.parent_block(), s.dest.parent_block()):\n", - " if not node in unit_reports and has_report(node):\n", - " print(node.name)\n", - " unit_reports[node] = report(node)" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "a7daa735", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{: {'performance': {},\n", - " 'stream_table': {'Units': {'flow_mol': 'mole / second',\n", - " 'mole_frac_comp benzene': 'dimensionless',\n", - " 'mole_frac_comp toluene': 'dimensionless',\n", - " 'temperature': 'kelvin',\n", - " 'pressure': 'pascal'},\n", - " 'inlet_1': {'flow_mol': 1.0,\n", - " 'mole_frac_comp benzene': 0.99999,\n", - " 'mole_frac_comp toluene': 1e-05,\n", - " 'temperature': 370.0,\n", - " 'pressure': 101325.0},\n", - " 'inlet_2': {'flow_mol': 1.0,\n", - " 'mole_frac_comp benzene': 1e-05,\n", - " 'mole_frac_comp toluene': 0.99999,\n", - " 'temperature': 380.0,\n", - " 'pressure': 130000.0},\n", - " 'Outlet': {'flow_mol': 2.0,\n", - " 'mole_frac_comp benzene': 0.5,\n", - " 'mole_frac_comp toluene': 0.5,\n", - " 'temperature': 368.1219881350423,\n", - " 'pressure': 101324.99999999999}},\n", - " 'model_type': 'Unit',\n", - " 'dof': {'dof': 0,\n", - " 'num_var': 71,\n", - " 'num_act_constraints': 61,\n", - " 'num_act_blocks': 4}},\n", - " : {'performance': {'vars': {'Heat Duty': {'value': 18094.429605324694,\n", - " 'units': 'watt',\n", - " 'fixed': False,\n", - " 'bounds': (None, None)}}},\n", - " 'stream_table': {'Units': {'flow_mol': 'mole / second',\n", - " 'mole_frac_comp benzene': 'dimensionless',\n", - " 'mole_frac_comp toluene': 'dimensionless',\n", - " 'temperature': 'kelvin',\n", - " 'pressure': 'pascal'},\n", - " 'Inlet': {'flow_mol': 2.0,\n", - " 'mole_frac_comp benzene': 0.5,\n", - " 'mole_frac_comp toluene': 0.5,\n", - " 'temperature': 368.1219881350423,\n", - " 'pressure': 101324.99999999999},\n", - " 'Outlet': {'flow_mol': 2.0000000000000004,\n", - " 'mole_frac_comp benzene': 0.5,\n", - " 'mole_frac_comp toluene': 0.5,\n", - " 'temperature': 370.0,\n", - " 'pressure': 101324.99999999999}},\n", - " 'model_type': 'Unit',\n", - " 'dof': {'dof': 5,\n", - " 'num_var': 47,\n", - " 'num_act_constraints': 41,\n", - " 'num_act_blocks': 4}},\n", - " : {'performance': {'vars': {'Heat Duty': {'value': 1e-06,\n", - " 'units': 'watt',\n", - " 'fixed': True,\n", - " 'bounds': (None, None)},\n", - " 'Pressure Change': {'value': 1e-06,\n", - " 'units': 'pascal',\n", - " 'fixed': True,\n", - " 'bounds': (None, None)}}},\n", - " 'stream_table': {'Units': {'flow_mol': 'mole / second',\n", - " 'mole_frac_comp benzene': 'dimensionless',\n", - " 'mole_frac_comp toluene': 'dimensionless',\n", - " 'temperature': 'kelvin',\n", - " 'pressure': 'pascal'},\n", - " 'Inlet': {'flow_mol': 2.0000000000000004,\n", - " 'mole_frac_comp benzene': 0.5,\n", - " 'mole_frac_comp toluene': 0.5,\n", - " 'temperature': 370.0,\n", - " 'pressure': 101324.99999999999},\n", - " 'Vapor Outlet': {'flow_mol': 1.367289468949744,\n", - " 'mole_frac_comp benzene': 0.5693647139537593,\n", - " 'mole_frac_comp toluene': 0.43063528604624063,\n", - " 'temperature': 370.00000000042576,\n", - " 'pressure': 101325.00000099998},\n", - " 'Liquid Outlet': {'flow_mol': 0.6327105310502565,\n", - " 'mole_frac_comp benzene': 0.35010263263951724,\n", - " 'mole_frac_comp toluene': 0.6498973673604828,\n", - " 'temperature': 370.00000000042576,\n", - " 'pressure': 101325.00000099998}},\n", - " 'model_type': 'Unit',\n", - " 'dof': {'dof': 5,\n", - " 'num_var': 48,\n", - " 'num_act_constraints': 41,\n", - " 'num_act_blocks': 5}}}" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "unit_reports" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "id": "94f02a53", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(FS.model.fs.F03, UnitModelBlockData)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "838b1f59", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "idaes.core.base.process_block._ScalarFlash" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(f03)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "97c7545c", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "idaes-fi-py3.13", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 687d67021790c3ccdd9432a3cd126cf586d5118d Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Fri, 15 May 2026 12:59:39 -0700 Subject: [PATCH 4/7] fixed test and exception message --- src/idaes_fi/structfs/actions/model_report.py | 2 -- src/idaes_fi/structfs/common.py | 9 +++++---- src/idaes_fi/structfs/fsrunner.py | 19 +++++++++---------- src/idaes_fi/structfs/runner.py | 2 +- src/idaes_fi/structfs/tests/test_fsrunner.py | 2 +- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/idaes_fi/structfs/actions/model_report.py b/src/idaes_fi/structfs/actions/model_report.py index 822c783..557401a 100644 --- a/src/idaes_fi/structfs/actions/model_report.py +++ b/src/idaes_fi/structfs/actions/model_report.py @@ -130,13 +130,11 @@ def _get_report(self, comp): # Get performance variables performance = comp._get_performance_contents(time_point=time_point) - print(f"@@ 1 with performance={performance}") if performance is None or performance == {}: self.log.warning( f"Empty performance contents for {rpt.model_type.value} model {comp}" ) else: - print(f"@@ 2 with performance={performance}") # reformat variable values for section in ("vars",): try: diff --git a/src/idaes_fi/structfs/common.py b/src/idaes_fi/structfs/common.py index 33abf77..420d61b 100644 --- a/src/idaes_fi/structfs/common.py +++ b/src/idaes_fi/structfs/common.py @@ -37,13 +37,14 @@ class ActionNames(Enum): - SOLVER_OUTPUT = "solver_output" - SOLVER_RESULTS = "solver_results" DIAGNOSTICS = "diagnostics" - MODEL_VARIABLES = "model_variables" + DOF = "degrees_of_freedom" MERMAID_DIAGRAM = "mermaid_diagram" + MODEL_REPORTS = "model_reports" + MODEL_VARIABLES = "model_variables" + SOLVER_OUTPUT = "solver_output" + SOLVER_RESULTS = "solver_results" STREAM_TABLE = "stream_table" - DOF = "degrees_of_freedom" TIMINGS = "timings" diff --git a/src/idaes_fi/structfs/fsrunner.py b/src/idaes_fi/structfs/fsrunner.py index 43f21d2..be6ba66 100644 --- a/src/idaes_fi/structfs/fsrunner.py +++ b/src/idaes_fi/structfs/fsrunner.py @@ -330,22 +330,21 @@ def __init__(self, solve_steps: list[str] = None, **kwargs): Diagnostics, StreamTable, UnitDofChecker, + UnitModelReport, ) super().__init__(**kwargs) - self.add_action(ActionNames.TIMINGS.value, Timer) - self.add_action( - ActionNames.DOF.value, - UnitDofChecker, - "fs", - [Steps.build, Steps.solve_initial, Steps.solve_optimization], - ) - self.add_action(ActionNames.SOLVER_OUTPUT.value, CaptureSolverOutput) - self.add_action(ActionNames.SOLVER_RESULTS.value, GetSolverResults) + dof_steps = [Steps.build, Steps.solve_initial, Steps.solve_optimization] + self.add_action(ActionNames.DIAGNOSTICS.value, Diagnostics) - self.add_action(ActionNames.MODEL_VARIABLES.value, ModelVariables) + self.add_action(ActionNames.DOF.value, UnitDofChecker, "fs", dof_steps) self.add_action(ActionNames.MERMAID_DIAGRAM.value, MermaidDiagram) + self.add_action(ActionNames.MODEL_REPORTS.value, UnitModelReport) + self.add_action(ActionNames.MODEL_VARIABLES.value, ModelVariables) + self.add_action(ActionNames.SOLVER_OUTPUT.value, CaptureSolverOutput) + self.add_action(ActionNames.SOLVER_RESULTS.value, GetSolverResults) self.add_action(ActionNames.STREAM_TABLE.value, StreamTable) + self.add_action(ActionNames.TIMINGS.value, Timer) def build(self): """Run just the build step""" diff --git a/src/idaes_fi/structfs/runner.py b/src/idaes_fi/structfs/runner.py index c081b09..a2c7181 100644 --- a/src/idaes_fi/structfs/runner.py +++ b/src/idaes_fi/structfs/runner.py @@ -426,7 +426,7 @@ def _run_steps( if self._failed: _log.error("Run failed") else: - for action in self._actions.values(): + for action_name, action in self._actions.items(): try: action.after_run() except Exception as err: diff --git a/src/idaes_fi/structfs/tests/test_fsrunner.py b/src/idaes_fi/structfs/tests/test_fsrunner.py index 2883f5a..729800f 100644 --- a/src/idaes_fi/structfs/tests/test_fsrunner.py +++ b/src/idaes_fi/structfs/tests/test_fsrunner.py @@ -236,7 +236,7 @@ def solve_initial(ctx): def extra_checks(runner): if runnerclass is FlowsheetRunner: assert runner.failed - assert "stream_table.after_run" in runner.failed_actions + assert len(runner.failed_actions) == 1 # stop after 1 failure runner.run_steps(save_report=False) extra_checks(runner) From 86b1f1582c3527ce2ecfd7d48a3f71594b809860 Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Fri, 15 May 2026 16:24:03 -0700 Subject: [PATCH 5/7] drop units w/no perf by default --- src/idaes_fi/structfs/actions/model_report.py | 146 ++++++++++++++---- .../structfs/tests/test_runner_actions.py | 14 +- 2 files changed, 128 insertions(+), 32 deletions(-) diff --git a/src/idaes_fi/structfs/actions/model_report.py b/src/idaes_fi/structfs/actions/model_report.py index 557401a..da7c3a7 100644 --- a/src/idaes_fi/structfs/actions/model_report.py +++ b/src/idaes_fi/structfs/actions/model_report.py @@ -46,47 +46,128 @@ class ModelType(str, Enum): flowsheet = "flowsheet" +class PerfReport(BaseModel): + """Report for UnitModelReport""" + + model_type: ModelType + performance: dict = Field(default={}) + stream_table: dict = Field(default={}) + dof: dict = Field(default={}) + time_point: float = 0.0 + + +class ComponentReports(BaseModel): + reports: dict[str, PerfReport] = Field(default={}) + + class UnitModelReport(Action): """Extract report from unit model. - The resulting report is structured as one report per - step, each containing details for components that - implement the IDAES reporting functions. + The 'Report' in the name of this class refers to the `report()` method you + call on the IDAES unit model, not to the Report class or `report()` method + in this class. + The resulting report is structured as a set of `reports` (in the unit model + method sense), one for each component in the overall model that implements + the reporting interface. Below is an example from the simplest + flowsheet with one Flash unit, with one step (`step1`) and one unit (`fs.flash`). ``` - step_reports: - step_name: - reports: - component_name: - model_type - performance - stream_table - degrees of freedom (dof) - time_point (always 0) + { + "step_reports": { + "step1": { + "reports": { + "fs.flash": { + "model_type": "unit", + "performance": { + "vars": { + "Heat Duty": { + "value": 0.0, + "units": "watt", + "fixed": false, + "bounds": [ + null, + null + ] + }, + "Pressure Change": { + "value": 0.0, + "units": "pascal", + "fixed": false, + "bounds": [ + null, + null + ] + } + } + }, + "stream_table": { + "Units": { + "flow_mol": "mole / second", + "mole_frac_comp benzene": "dimensionless", + "mole_frac_comp toluene": "dimensionless", + "temperature": "kelvin", + "pressure": "pascal" + }, + "Inlet": { + "flow_mol": 1.0, + "mole_frac_comp benzene": 0.5, + "mole_frac_comp toluene": 0.5, + "temperature": 298.15, + "pressure": 101325.0 + }, + "Vapor Outlet": { + "flow_mol": 0.5, + "mole_frac_comp benzene": 0.5, + "mole_frac_comp toluene": 0.5, + "temperature": 298.15, + "pressure": 101325.0 + }, + "Liquid Outlet": { + "flow_mol": 0.5, + "mole_frac_comp benzene": 0.5, + "mole_frac_comp toluene": 0.5, + "temperature": 298.15, + "pressure": 101325.0 + } + }, + "dof": { + "dof_stat": 7, + "num_variables": 48, + "num_act_constraints": 41, + "num_act_blocks": 5 + }, + "time_point": 0.0 + } + } + } + }, + "last_step": "step1" + } ``` """ - class PerfReport(BaseModel): - """Report for UnitModelReport""" - - model_type: ModelType - performance: dict = Field(default={}) - stream_table: dict = Field(default={}) - dof: dict = Field(default={}) - time_point: float = 0.0 - - class ComponentReports(BaseModel): - reports: dict[str, UnitModelReport.PerfReport] = Field(default={}) - class Report(BaseModel): # report for each step; the report for the run is just the last one - step_reports: dict[str, UnitModelReport.ComponentReports] = Field(default={}) + step_reports: dict[str, ComponentReports] = Field(default={}) last_step: str = "" def __init__(self, *args, **kwargs): - """Constructor.""" + """Constructor. + + Args: + args: Passed to superclass + kwargs: Passed to superclass, except: + - 'allow_empty_performance': If True, include units with + no performance data (but + possibly stream tables). + """ + if "allow_empty_performance" in kwargs: + self._allow_empty_perf = bool(kwargs["allow_empty_performance"]) + del kwargs["allow_empty_performance"] + else: + self._allow_empty_perf = False super().__init__(*args, **kwargs) self._dof = True # XXX: allow user to control self._rpt = self.Report() @@ -97,12 +178,14 @@ def after_step(self, name: str): self._rpt.last_step = name # make it easy to find last report def _get_component_reports(self) -> dict[str, ComponentReports]: - m, r = self._runner.model, self.ComponentReports() + m, r = self._runner.model, ComponentReports() for comp in m.component_objects(): comp_name = comp.name # print(f"{comp_name} ({type(comp_name)})") if not comp_name in r and self._has_report(comp): - r.reports[comp_name] = self._get_report(comp) + rpt = self._get_report(comp) + if rpt is not None: + r.reports[comp_name] = rpt return r @staticmethod @@ -115,7 +198,7 @@ def _get_report(self, comp): time_point = 0.0 is_fs = hasattr(comp, "is_flowsheet") and comp.is_flowsheet - rpt = self.PerfReport( + rpt = PerfReport( model_type="flowsheet" if is_fs else "unit", time_point=time_point ) @@ -131,9 +214,12 @@ def _get_report(self, comp): # Get performance variables performance = comp._get_performance_contents(time_point=time_point) if performance is None or performance == {}: - self.log.warning( + self.log.debug( f"Empty performance contents for {rpt.model_type.value} model {comp}" ) + if not self._allow_empty_perf: + self.log.debug(f"Skipping {comp} due to empty performance data") + return None # stop! else: # reformat variable values for section in ("vars",): diff --git a/src/idaes_fi/structfs/tests/test_runner_actions.py b/src/idaes_fi/structfs/tests/test_runner_actions.py index a67cf34..7e6160a 100644 --- a/src/idaes_fi/structfs/tests/test_runner_actions.py +++ b/src/idaes_fi/structfs/tests/test_runner_actions.py @@ -306,10 +306,20 @@ def test_unit_model_report(set_tmp_db): rpt = rpt_action.report() assert rpt - print(rpt.model_dump_json(indent=2)) + print(rpt.model_dump_json(indent=4)) last = rpt.step_reports[rpt.last_step].reports - # expect 2 components + # expect 1 component since only flash has performance + assert len(last) == 1 + for expected in ("fs.flash",): + assert expected in last + + # allow empty performance now + rpt_action = UnitModelReport(runner, allow_empty_performance=True) + rpt_action.after_step("step1") + rpt = rpt_action.report() + last = rpt.step_reports[rpt.last_step].reports + # should get 2 components, one of them with empty performance data assert len(last) == 2 for expected in ("fs.flash", "fs.flash.split"): assert expected in last From a003ff99f74e2e20c4c6cb390677e243c01ebb30 Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Fri, 15 May 2026 16:28:52 -0700 Subject: [PATCH 6/7] SimpleNamespace for older python --- src/idaes_fi/structfs/tests/test_runner_actions.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/idaes_fi/structfs/tests/test_runner_actions.py b/src/idaes_fi/structfs/tests/test_runner_actions.py index 7e6160a..52daaf2 100644 --- a/src/idaes_fi/structfs/tests/test_runner_actions.py +++ b/src/idaes_fi/structfs/tests/test_runner_actions.py @@ -330,11 +330,7 @@ def test_unit_model_report_bad_perf(): action = UnitModelReport(FakeRunner()) # this component will trigger both the missing-var-section in performance contents # and the attribute error for _get_stream_table_contents - comp = SimpleNamespace( - { - "_get_performance_contents": lambda time_point: {"foo": {}}, - } - ) + comp = SimpleNamespace(_get_performance_contents=lambda time_point: {"foo": {}}) action._dof = False # avoids calls involving `comp` print("> get report") action._get_report(comp) From b58c15a5efff9f1d18ca36eee0492507b9cab6ca Mon Sep 17 00:00:00 2001 From: Dan Gunter Date: Mon, 18 May 2026 05:51:06 -0700 Subject: [PATCH 7/7] add exprs/params, debugging --- src/idaes_fi/structfs/actions/model_report.py | 35 ++++++++++++++++--- src/idaes_fi/structfs/fsrunner.py | 4 +-- src/idaes_fi/structfs/logutil.py | 10 ++++++ src/idaes_fi/structfs/runner.py | 4 +-- src/idaes_fi/structfs/simple_wrap.py | 21 +++++++---- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/idaes_fi/structfs/actions/model_report.py b/src/idaes_fi/structfs/actions/model_report.py index da7c3a7..59d46e3 100644 --- a/src/idaes_fi/structfs/actions/model_report.py +++ b/src/idaes_fi/structfs/actions/model_report.py @@ -21,6 +21,7 @@ # stdlib from enum import Enum +import logging # IDAES and Pyomo from idaes.core.util.units_of_measurement import report_quantity @@ -195,6 +196,7 @@ def _has_report(comp: object): ) def _get_report(self, comp): + self.log.debug("begin _get_report()") time_point = 0.0 is_fs = hasattr(comp, "is_flowsheet") and comp.is_flowsheet @@ -212,6 +214,7 @@ def _get_report(self, comp): ) # Get performance variables + debug = self.log.isEnabledFor(logging.DEBUG) performance = comp._get_performance_contents(time_point=time_point) if performance is None or performance == {}: self.log.debug( @@ -222,21 +225,41 @@ def _get_report(self, comp): return None # stop! else: # reformat variable values - for section in ("vars",): + for section in ("vars", "exprs", "params"): try: performance_section = performance[section] except KeyError: - self.log.error(f"Missing 'vars' section in model report for {comp}") + if section == "vars": + self.log.warning( + f"Missing '{section}' section in model report for {comp}" + ) continue + if debug: + self.log.debug(f"section {section} for {comp.name}") for k, v in performance_section.items(): - # serialize pyomo value objects as dicts - if hasattr(v, "value"): - performance[section][k] = { + if section == "vars": + d = { "value": report_quantity(v).m, "units": str(report_quantity(v).u), "fixed": v.fixed, "bounds": v.bounds, } + elif section == "exprs": + d = { + "value": report_quantity(v).m, + "units": str(report_quantity(v).u), + } + elif section == "params": + d = { + "value": report_quantity(v).m, + "units": str(report_quantity(v).u), + "mutable": not v.is_constant(), + } + else: + raise RuntimeError( + f"Internal logic error: bad performance section {section}" + ) + performance[section][k] = d # leave other objects alone rpt.performance = performance @@ -249,6 +272,8 @@ def _get_report(self, comp): stream_dict = {} rpt.stream_table = stream_dict + self.log.debug("end _get_report()") + return rpt def report(self) -> Report: diff --git a/src/idaes_fi/structfs/fsrunner.py b/src/idaes_fi/structfs/fsrunner.py index be6ba66..014e679 100644 --- a/src/idaes_fi/structfs/fsrunner.py +++ b/src/idaes_fi/structfs/fsrunner.py @@ -335,13 +335,13 @@ def __init__(self, solve_steps: list[str] = None, **kwargs): super().__init__(**kwargs) dof_steps = [Steps.build, Steps.solve_initial, Steps.solve_optimization] - + # note: put solver_output first to re-enable stdout + self.add_action(ActionNames.SOLVER_OUTPUT.value, CaptureSolverOutput) self.add_action(ActionNames.DIAGNOSTICS.value, Diagnostics) self.add_action(ActionNames.DOF.value, UnitDofChecker, "fs", dof_steps) self.add_action(ActionNames.MERMAID_DIAGRAM.value, MermaidDiagram) self.add_action(ActionNames.MODEL_REPORTS.value, UnitModelReport) self.add_action(ActionNames.MODEL_VARIABLES.value, ModelVariables) - self.add_action(ActionNames.SOLVER_OUTPUT.value, CaptureSolverOutput) self.add_action(ActionNames.SOLVER_RESULTS.value, GetSolverResults) self.add_action(ActionNames.STREAM_TABLE.value, StreamTable) self.add_action(ActionNames.TIMINGS.value, Timer) diff --git a/src/idaes_fi/structfs/logutil.py b/src/idaes_fi/structfs/logutil.py index 127d58e..ec22314 100644 --- a/src/idaes_fi/structfs/logutil.py +++ b/src/idaes_fi/structfs/logutil.py @@ -49,3 +49,13 @@ def unquiet(): lg = logging.getLogger(k) lg.setLevel(v) del g_quiet[k] + + +def init_fi(): + """Initialize logging for flowsheet inspector""" + log = logging.getLogger("idaes_fi") + if not log.hasHandlers(): + h = logging.StreamHandler() + fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s") + h.setFormatter(fmt) + log.addHandler(h) diff --git a/src/idaes_fi/structfs/runner.py b/src/idaes_fi/structfs/runner.py index a2c7181..c50d126 100644 --- a/src/idaes_fi/structfs/runner.py +++ b/src/idaes_fi/structfs/runner.py @@ -329,7 +329,7 @@ def _run_steps( p = None if mod.__name__ == "__main__": # if in VSCode, use special attr - nb_path = getattr(mod, "__vsc_ipynb_file__") + nb_path = getattr(mod, "__vsc_ipynb_file__", None) if nb_path: p = Path(nb_path) # clear any existing values @@ -356,7 +356,7 @@ def _run_steps( self._last_run_steps = [] # get indexes of first/last step - _log.warning( + _log.info( f"get indexes of first step '{names[0]}' and last step '{names[1]}' " f"in steps {self._step_names}" ) diff --git a/src/idaes_fi/structfs/simple_wrap.py b/src/idaes_fi/structfs/simple_wrap.py index 79dded6..ba8fc8d 100644 --- a/src/idaes_fi/structfs/simple_wrap.py +++ b/src/idaes_fi/structfs/simple_wrap.py @@ -27,7 +27,8 @@ # package from .fsrunner import BaseFlowsheetRunner -from .common import RESULT_FLOWSHEET_KEY, ActionNames +from .common import RESULT_FLOWSHEET_KEY, ActionNames, Steps +from .logutil import init_fi _log = logging.getLogger(__name__) @@ -49,25 +50,33 @@ def __init__(self, *args, **kwargs): MermaidDiagram, StreamTable, Diagnostics, + UnitModelReport, ) super().__init__(*args, **kwargs) self.main_func = None self.main_func_args = [] self.main_func_kwargs = {} - self.add_action(ActionNames.TIMINGS.value, Timer) - self.add_action(ActionNames.DOF.value, UnitDofChecker, "fs", ["build"]) + dof_steps = [Steps.build, Steps.solve_initial, Steps.solve_optimization] + # note: put solver_output first so stdout is re-enabled after + # solve steps for all other actions self.add_action(ActionNames.SOLVER_OUTPUT.value, CaptureSolverOutput) - self.add_action(ActionNames.SOLVER_RESULTS.value, GetSolverResults) - self.add_action(ActionNames.MODEL_VARIABLES.value, ModelVariables) + self.add_action(ActionNames.DIAGNOSTICS.value, Diagnostics) + self.add_action(ActionNames.DOF.value, UnitDofChecker, "fs", dof_steps) self.add_action(ActionNames.MERMAID_DIAGRAM.value, MermaidDiagram) + self.add_action(ActionNames.MODEL_REPORTS.value, UnitModelReport) + self.add_action(ActionNames.MODEL_VARIABLES.value, ModelVariables) + self.add_action(ActionNames.SOLVER_RESULTS.value, GetSolverResults) self.add_action(ActionNames.STREAM_TABLE.value, StreamTable) - self.add_action(ActionNames.DIAGNOSTICS.value, Diagnostics) + self.add_action(ActionNames.TIMINGS.value, Timer) # Global flowsheet runner, will create as needed _FS = SimpleFlowsheetRunner() +# Init logging +init_fi() + class _Wrapper: """Wrapper to create fi_main() decorator."""