h2integrate.core.h2integrate_model#
Classes
|
|
|
- class h2integrate.core.h2integrate_model.State(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)#
- INITIALIZED = 0#
- SETUP = 1#
- RUN = 2#
- POST_PROCESS = 3#
- class h2integrate.core.h2integrate_model.H2IntegrateModel(config_input)#
- _load_component_config(config_key, config_value, config_path, validator_func)#
Helper method to load and validate a component configuration.
- Parameters:
config_key (str) -- Key name for the configuration (e.g., "driver_config")
config_value (dict | str) -- Configuration value from main config
config_path (Path | None) -- Path to main config file (None if dict)
validator_func (callable) -- Validation function to apply
- Returns:
tuple --
- (validated_config, config_file_path, parent_path)
validated_config: Validated configuration dictionary
config_file_path: Path to config file (None if dict)
parent_path: Parent directory of config file (None if dict)
- load_config(config_input)#
Load and validate configuration files for the H2I model.
This method loads the main configuration and the component configuration files (driver, technology, and plant). Each configuration can be provided either as a dictionary or as a file path. When file paths are provided, the method resolves them using multiple search strategies.
- Parameters:
config_input (dict | str | Path) --
Main configuration containing references to driver, technology, and plant configurations. This can be:
A dictionary containing the configuration data directly.
A string or Path pointing to a YAML file containing the configuration.
Behavior
If
config_inputis a dict, uses it directly as the main configuration.If
config_inputis a path, usesget_path()to resolve and load the YAML file from multiple search locations (absolute path, relative to CWD, relative to the H2Integrate package).For component configs provided as dicts, validates them directly using
load_driver_yaml,load_tech_yaml, andload_plant_yaml.For component configs provided as paths and a file-based main config, uses
find_file()to search relative to the main config directory first, then falls back to other search locations (CWD, H2Integrate package, glob patterns).For component configs provided as paths and a dict-based main config, uses
get_path()with standard search locations (absolute, CWD, H2Integrate package).
- Sets:
self.name (str): Name of the system from main config. self.system_summary (str): Summary description from main config. self.driver_config (dict): Validated driver configuration. self.technology_config (dict): Validated technology configuration. self.plant_config (dict): Validated plant configuration. self.driver_config_path (Path | None): Path to driver config file (None if dict). self.tech_config_path (Path | None): Path to technology config file (None if dict). self.plant_config_path (Path | None): Path to plant config file (None if dict). self.tech_parent_path (Path | None): Parent directory of technology config file. self.plant_parent_path (Path | None): Parent directory of plant config file.
Note
The parent path attributes (
tech_parent_path,plant_parent_path) are used later to resolve relative paths to custom models and other referenced files within the technology and plant configurations.Example
>>> # Using filepaths >>> model = H2IntegrateModel("main_config.yaml")
>>> # Using mixed dict and filepaths >>> config = { ... "name": "my_system", ... "driver_config": "driver.yaml", ... "technology_config": {"technologies": {...}}, ... "plant_config": "plant.yaml", ... } >>> model = H2IntegrateModel(config)
- create_custom_models(model_config, config_parent_path, model_types, prefix='')#
This method loads custom models from the specified directory and adds them to the supported models dictionary.
- Parameters:
model_config (dict) -- dictionary containing models, such as
technology_config["technologies"].config_parent_path (Path) -- parent path of the input file that
model_configcomes from. Should either beplant_config_path.parentortech_config_path.parent.model_types (list[str]) -- list of key names to search for in
model_config.values(). Should be["performance_model", "cost_model", "financial_model"]ifmodel_configistechnology_config["technologies"].prefix (str, optional) -- Prefix of
model_class_name,model_locationandmodel. Defaults to "". Should be"finance_"if looking for custom system finance models.
- collect_custom_models()#
Collect custom models from the technology configuration and system finance models found in the plant configuration.
- create_site_model()#
Create and configure site component(s) for the system.
This method initializes a site group for each site provided in
self.plant_config["sites"].This method creates an OpenMDAO Group for each site that contains the location definition and resources models (if provided in the configuration) for that site.
- create_site_group(plant_config_dict, site_config)#
Create and configure a site Group for the input site configuration.
- Parameters:
plant_config_dict (dict) -- The plant config dictionary formatted for the resource models
site_config (dict) -- Information that defines each site, such as latitude, longitude, and resource models.
- Returns:
om.Group -- OpenMDAO group for a site
- create_plant_model()#
Create the plant-level model.
This method creates an OpenMDAO group that contains all the technologies. It uses the plant configuration but not the driver or technology configuration.
Information at this level might be used by any technology and info stored here is the same for each technology. This includes site information, project parameters, control strategy, and finance parameters.
- _classify_slc_technologies()#
Classify technologies for system-level control.
Uses
self.tech_control_classifiers(populated bycreate_technology_models()) to partition technologies into fixed, flexible, dispatchable, and storage lists. Also identifies the single demand technology and its commodity.SLC demand is supplied by a demand component (for example,
GenericDemandComponent). When SLC is enabled, only one demand component is currently supported.- Returns:
dict --
Classification dictionary (
slc_topology) with keys:"demand_tech"(str): Name of the demand technology (the tech whose performance model is aDemandComponent)."demand_commodity"(str): Commodity the demand technology consumes (e.g."electricity","hydrogen")."demand_commodity_rate_units"(str | None): Units string for the demand commodity rate (e.g."kW","kg/h"), orNoneif not specified in the demand tech config."tech_to_commodity"(set[tuple[str, str]]): Set of(tech_name, commodity)pairs for every technology that the SLC controls or reads from. Built from outgoing edges of the technology graph and filtered to fixed, flexible, dispatchable, storage, and feedstock classifiers."technology_graph"(nx.DiGraph): Directed graph of technology interconnections, with edge attributecommodityindicating the commodity carried on each edge. Used by cost-aware controllers to trace upstream feedstocks."tech_control_classifiers"(dict[str, str]): Mapping of tech name to its_control_classifier(one of"fixed","flexible","dispatchable","storage","feedstock"). Determines how the SLC interacts with each tech.
- add_system_level_controller(slc_topology)#
Add a system-level controller component and connect it within the plant.
Instantiates the controller specified by
control_strategyin the plant configuration, adds it as an OpenMDAO subsystem named"system_level_controller", configures solvers on the plant group to resolve the feedback loop, and creates all necessary OpenMDAO connections between the controller and the technology models it dispatches.The method executes in five sequential steps:
Select and instantiate the controller - Looks up the class from
supported_modelsusing thecontrol_strategystring (e.g."DemandFollowingControl","ProfitMaximizationControl"). RaisesValueErrorif the strategy name is not found. The instantiated component is added toself.plantas"system_level_controller".Configure the plant-level nonlinear solver - Because the controller creates a feedback loop (controller outputs become technology inputs, whose outputs feed back to the controller), a nonlinear solver is required. Solver type and options are read from
plant_config["system_level_control"]["solver_options"]viaSLCSolverOptionsConfig. ADirectSolveris set as the linear solver and is largely inconsequential as we're not propagating derivatives at this time.Connect technology outputs to controller inputs - For each
(tech_name, commodity)pair inslc_topology["tech_to_commodity"]:Feedstock techs: Only the commodity output (
{tech_name}_source.{commodity}_out) is connected to the controller. Feedstocks have no demand-input connection.Fixed techs: Only the commodity output (
{tech_name}.{commodity}_out) is connected to the controller. Fixed techs always produce and receive no demand-input connection.Flexible / dispatchable / storage techs: Both the commodity output (
{tech_name}.{commodity}_out) and rated production ({tech_name}.rated_{commodity}_production) are connected as controller inputs. The controller's per-tech{tech_name}_{commodity}_set_pointoutput is then connected to the tech group's{commodity}_set_pointinput. Every controlled tech group is expected to expose this input — either via a user-definedcontrol_strategyor via the auto-injectedPassthroughController— which converts the set-point signal into the appropriate performance-model command value.
Connect marginal-cost inputs for cost-aware strategies - Only executed when
control_strategyis"CostMinimizationControl"or"ProfitMaximizationControl". Additional cost-aware control strategies would need to be added here. For each dispatchable tech, thecost_per_techspecification determines which cost signal is connected:"VarOpEx": connects the tech's ownVarOpExoutput."feedstock": uses graph traversal (nx.ancestors) on thetechnology_graphto find all upstream feedstock technologies at any depth and connects each feedstock'sVarOpExoutput. This is consistent with the_find_feedstock_techsmethod used by the controller component internally."buy_price": the controller's{tech_name}_buy_priceinput is connected input-to-input to the technology's own buy-price input (electricity_buy_pricefor Grid,pricefor Feedstock) so a singleprob.set_val()on the tech propagates to the SLC. The default value still comes from the tech config.Numeric scalar: no connection needed; the value is used directly as a constant marginal cost.
Connect the demand profile - Connects the demand technology's output (
{demand_tech}.{demand_commodity}_demand_out) to the controller's demand input (system_level_controller.{demand_commodity}_demand). This relies on the current SLC constraint that exactly one demand component is defined.
- Parameters:
slc_topology (dict) --
Pre-computed dictionary produced by
_classify_slc_technologies(). Expected keys:"demand_tech"(str): Name of the demand technology."demand_commodity"(str): Commodity the demand consumes."tech_to_commodity"(set[tuple[str, str]]): Set of(tech_name, commodity)pairs for all controlled techs."tech_control_classifiers"(dict[str, str]): Mapping of tech name to classifier ("fixed","flexible","dispatchable","storage","feedstock")."storage_techs_to_control"(dict[str, bool]): Whether each storage tech has its own sub-controller."technology_graph"(nx.DiGraph): Directed graph of technology interconnections.
- Raises:
ValueError -- If
control_strategyis not found inself.supported_models.
Side Effects
Adds
"system_level_controller"subsystem toself.plant.Sets
self.plant.nonlinear_solverandself.plant.linear_solver.Creates OpenMDAO connections within
self.plant.
- create_technology_models()#
- _process_model(model_type, individual_tech_config, tech_group)#
- _check_time_step(model_name, model_object)#
- _check_control_classifier(model_name, model_object)#
- _add_passthrough_controller(tech_group, perf_comp, individual_tech_config)#
Automatically add a PassthroughController to a tech group if appropriate.
A controller is auto-inserted only when:
the technology has no user-defined
control_strategyin its config,the performance model exposes a
_control_classifierof"flexible","dispatchable", or"storage",the performance model has set
commodityandcommodity_rate_unitsattributes (typically set in itsinitialize()), or those values can be read from the individual tech config.
The controller's
{commodity}_set_pointinput becomes the tech group's external set-point-input promoted at the tech group level, and its{commodity}_command_valueoutput is auto-connected (via promotion) to the performance model's{commodity}_command_valueinput if one exists.
- create_finance_model()#
Create and configure the finance model(s) for the plant.
This method initializes finance subsystems for the plant based on the configuration provided in
self.plant_config["finance_parameters"]. It supports both default (single-model) setups and multiple/distinct (subgroup-specific) finance models.Within this framework, a finance subgroup serves as a flexible grouping mechanism for calculating finance metrics across different subsets of technologies. These groupings can draw on varying finance inputs or models within the same simulation. To support a wide range of use cases, such as evaluating metrics for only part of a larger system, finance subgroups may reference multiple finance_groups and may overlap partially or fully with the technologies included in other finance subgroups.
Behavior
If
finance_parametersis not defined in the plant configuration, no finance model is created.If no subgroups are defined, all technologies are grouped together under a default finance group.
commodityandfinance_modelare required in this case.If subgroups are provided, each subgroup defines its own set of technologies, associated commodity, and finance model(s). Each subgroup is nested under a unique name of your choice under ["finance_parameters"]["subgroups"] in the plant configuration.
Subsystems such as
AdjustedCapexOpexCompandGenericProductionSummerPerformanceModel, and the selected finance models are added to each subgroup's finance group.If commodity_stream is provided for a subgroup, the output of the technology specified as the commodity_stream must be the same as the specified commodity for that subgroup.
Supports both global finance models and technology-specific finance models. Technology-specific finance models are defined in the technology configuration.
- Raises:
ValueError -- If ["finance_parameters"]["finance_group"] is incomplete (e.g., missing
commodityorfinance_model) when no subgroups are defined.ValueError -- If a subgroup has an invalid technology.
ValueError -- If a specified finance model is not found in
self.supported_models.
Side Effects
Updates
self.plant_config["finance_parameters"]["finance_group"]if only a single finance model is provided (wraps it in a default finance subgroup).Constructs and attaches OpenMDAO finance subsystem groups to the plant model under names
finance_subgroup_<subgroup_name>.Stores processed subgroup configurations in
self.finance_subgroups.
Example
Suppose
plant_config["finance_parameters"]["finance_group"]defines a single finance model without subgroups:>>> self.plant_config["finance_parameters"]["finance_group"] = { ... "commodity": "hydrogen", ... "finance_model": "ProFastLCO", ... "model_inputs": {"discount_rate": 0.08}, ... } >>> self.create_finance_model() # Creates a default subgroup containing all technologies and # attaches a ProFAST finance model component to the plant.
- _connect_multivariable_stream(source_tech, dest_tech, stream_name, combiner_counts, splitter_counts)#
Connect a multivariable stream between source and destination technologies.
Handles combiner indexing (numbered inputs), splitter indexing (numbered outputs), and direct connections. Updates combiner_counts/splitter_counts dicts in-place.
- Parameters:
source_tech (str) -- Name of the source technology.
dest_tech (str) -- Name of the destination technology.
stream_name (str) -- Name of the multivariable stream (key in multivariable_streams).
combiner_counts (dict) -- Tracks the next input index per combiner technology.
splitter_counts (dict) -- Tracks the next output index per splitter technology.
- connect_technologies()#
- create_driver_model()#
Add the driver to the OpenMDAO model and add recorder.
- setup()#
Extremely light wrapper to setup the OpenMDAO problem and track setup status.
- run()#
- post_process(print_results=True, summarize_sql=False, show_plots=False)#
Post-process the results of the OpenMDAO model.
Prints the inputs and outputs to all systems in the model, excluding any variables with "resource_data" in the name since those are large dictionary variables that are not correctly formatted when printing.
- Parameters:
print_results (bool) -- If True, print a summary of all model inputs and outputs. Defaults to True.
summarize_sql (bool) -- If True and a recorder file was written, convert the SQL recorder file to a CSV summary. Defaults to False.
show_plots (bool) -- If True, run post-processing plots for any performance models that support them. Defaults to False.
- static print_results(model, includes=None, excludes=None, show_units=True)#
Print hierarchical inputs plus explicit/implicit outputs (means only) using Rich.
Order of rows preserves OpenMDAO's original ordering from list_inputs/list_outputs. Group rows are emitted lazily the first time a variable within that path appears.
- create_xdsm(outfile='connections_xdsm')#
Create an XDSM diagram from the plant technology interconnections.
This method reads
technology_interconnectionsfromself.plant_configand delegates diagram generation toh2integrate.core.utilities.create_xdsm_from_config().- Parameters:
outfile (str, optional) -- Base filename for the generated XDSM output. The default is
"connections_xdsm".- Raises:
ValueError -- If
technology_interconnectionsis empty or missing from the plant configuration.
- create_technology_graph(tech_interconnections)#
Create a directed graph of the technology interconnections.
Builds a NetworkX directed graph where nodes represent technologies and edges represent connections between them. If a connection includes a commodity (length-4 entry), it is stored as an edge attribute.
- Parameters:
tech_interconnections (list) -- list of technology interconnections
- Returns:
nx.DiGraph --
- A directed graph with technologies as nodes and
interconnections as edges.
- _check_tech_connections()#
Check that commodity streams between technologies are valid.
Validates that each commodity in a length-4 technology interconnection is output by the source technology and accepted as input by the destination technology. Does not check length-3 connections or missing input commodity streams.
- Raises:
ValueError -- If any commodity connection is invalid.
- static _split_indices_from_connected_parameter_definition(connected_parameter)#
Extract and parse slice indices from connected parameter definitions for OpenMDAO connections.
This function processes parameter names containing slice patterns in square brackets (e.g., "power[0:8760]") and generates OpenMDAO-compatible src_indices for connections between variables of different shapes.
- Parameters:
connected_parameter (list[str]) --
A two-element list containing: - [0] source parameter name, optionally with pattern like "var[slice_spec]" - [1] destination parameter name, optionally with pattern like "var[slice_spec]"
Example: ["power[0:8760]", "demand[:]"]
- Returns:
tuple --
- A two-element tuple containing:
connected_parameter (list[str]): The parameter names with slices removed (e.g., ["power", "demand"])
src_indices: OpenMDAO slicer object for indexing source outputs to match destination input shapes. Returns om.slicer[slice] for indexing.
Note
If the destination has a slice pattern, it must include the length ":N" (e.g., "[0:N]"), the function extracts N as the destination length and multiplies the source slice by this factor to create properly scaled indices. The length is required because the length is not known in the OpenMDAO model until prob.setup() has been called.