Design of experiments in H2I#
One of the key features of H2Integrate is the ability to perform a design of experiments (DOE) for hybrid energy systems.
The design of experiments process uses the driver_config.yaml file to define the design sweep, including the design variables, constraints, and objective functions.
Detailed information on setting up the driver_config.yaml file can be found in the
user guide
Driver config file#
The driver config file defines the analysis type and the optimization or design of experiments settings. For completeness, here is an example of a driver config file for a design of experiments problem:
1description: Driver config for CSVGen design of experiments
2name: driver_config
3
4general:
5 folder_output: ex_22_out
6 create_om_reports: false
7driver:
8 design_of_experiments:
9 flag: true
10 debug_print: true
11 generator: "csvgen"
12 filename: "site_solar_doe.csv"
13 run_parallel: false
14design_variables:
15 solar:
16 system_capacity_DC:
17 flag: true
18 lower: 5000.0
19 upper: 75000.0
20 units: "kW"
21 site:
22 latitude:
23 flag: True
24 units: "deg"
25 lower: -90.0
26 upper: 90.0
27 longitude:
28 flag: True
29 units: "deg"
30 lower: -180.0
31 upper: 180.0
32
33objective:
34 name: finance_subgroup_electricity.LCOE_optimistic
35recorder:
36 file: cases.sql
37 overwrite_recorder: true
38 flag: true
39 includes: ['*']
40 excludes: ['*wind_resource*']
Types of Generators#
H2Integrate currently supports the following types of generators:
“uniform”: uses the
UniformGeneratorgenerator“fullfact”: uses the
FullFactorialGeneratorgenerator“plackettburman”: uses the
PlackettBurmanGeneratorgenerator“boxbehnken”: uses the
BoxBehnkenGeneratorgenerator“latinhypercube”: uses the
LatinHypercubeGeneratorgenerator“csvgen”: uses the
CSVGeneratorgenerator
Documentation for each generator type can be found on OpenMDAO’s documentation page.
Uniform#
driver:
design_of_experiments:
flag: True
generator: "uniform" #type of generator to use
num_samples: 10 #input is specific to this generator
seed: #input is specific to this generator
FullFactorial#
driver:
design_of_experiments:
flag: True
generator: "fullfact" #type of generator to use
levels: 2 #input is specific to this generator
The levels input is the number of evenly spaced levels between each design variable lower and upper bound.
You can check the values that will be used for a specific design variable by running:
import numpy as np
design_variable_values = np.linspace(lower_bound,upper_bound,levels)
PlackettBurman#
driver:
design_of_experiments:
flag: True
generator: "plackettburman" #type of generator to use
BoxBehnken#
driver:
design_of_experiments:
flag: True
generator: "boxbehnken" #type of generator to use
LatinHypercube#
driver:
design_of_experiments:
flag: True
generator: "latinhypercube" #type of generator to use
num_samples: 10 #input is specific to this generator
criterion: "center" #input is specific to this generator
seed: #input is specific to this generator
CSV#
This method is useful if there are specific combinations of designs variables that you want to sweep. An example is shown here:
driver:
design_of_experiments:
flag: True
generator: "csvgen" #type of generator to use
filename: "cases_to_run.csv" #input is specific to this generator
The filename input is the filepath to the csv file to read cases from. The first row of the csv file should contain the names of the design variables. The rest of the rows should contain the values of that design variable you want to run (such as solar.system_capacity_DC or electrolyzer.n_clusters). The values in the csv file are expected to be in the same units specified for that design variable.
Note
You should check the csv file for potential formatting issues before running a simulation. This can be done using the check_file_format_for_csv_generator method in h2integrate/core/utilities.py. Usage of this method is shown in the 20_solar_electrolyzer_doe example in the examples folder.
Demonstration Using Solar and Electrolyzer Capacities#
This csvgen generator example reflects the work to produce the examples/20_solar_electrolyzer_doe
example.
We use the examples/20_solar_electrolyzer_doe/driver_config.yaml to run a design of experiments for
varying combinations of solar power and hydrogen electrolyzer capacities.
4general:
5 folder_output: ex_20_out
6 create_om_reports: true
7driver:
8 design_of_experiments:
9 flag: true
10 debug_print: true
11 generator: "csvgen"
12 filename: "csv_doe_cases.csv"
13 run_parallel: false
14design_variables:
15 solar:
16 system_capacity_DC:
17 flag: true
18 lower: 5000.0
19 upper: 5000000.0
20 units: "kW"
21 electrolyzer:
22 n_clusters:
23 flag: true
24 lower: 1
25 upper: 25
26 units: "unitless"
The different combinations of solar and electrolyzer capacities are listed in the csv file examples/20_solar_electrolyzer_doe/csv_doe_cases.csv:
solar.system_capacity_DC,electrolyzer.n_clusters
25000.0,5.0
50000.0,5.0
100000.0,5.0
200000.0,5.0
50000.0,10.0
100000.0,10.0
200000.0,10.0
250000.0,10.0
300000.0,10.0
500000.0,10.0
Next, we’ll import the required models and functions to complete run a successful design of experiments.
# Import necessary methods and packages
from pathlib import Path
from h2integrate.core.utilities import check_file_format_for_csv_generator, load_yaml
from h2integrate.core.dict_utils import update_defaults
from h2integrate.core.h2integrate_model import H2IntegrateModel
from h2integrate.core.inputs.validation import load_driver_yaml, write_yaml
Setup and first attempt#
First, we need to update the relative file references to ensure the demonstration works.
EXAMPLE_DIR = Path("../../examples/20_solar_electrolyzer_doe").resolve()
config = load_yaml(EXAMPLE_DIR / "20_solar_electrolyzer_doe.yaml")
driver_config = load_yaml(EXAMPLE_DIR / config["driver_config"])
csv_config_fn = EXAMPLE_DIR / driver_config["driver"]["design_of_experiments"]["filename"]
config["driver_config"] = driver_config
config["driver_config"]["driver"]["design_of_experiments"]["filename"] = csv_config_fn
config["technology_config"] = load_yaml(EXAMPLE_DIR / config["technology_config"])
config["plant_config"] = load_yaml(EXAMPLE_DIR / config["plant_config"])
As-is, the model produces a UserWarning that it will not successfully run with the existing
configuration, as shown below.
model = H2IntegrateModel(config)
model.run()
---------------------------------------------------------------------------
UserWarning Traceback (most recent call last)
Cell In[3], line 1
----> 1 model = H2IntegrateModel(config)
2 model.run()
File ~/checkouts/readthedocs.org/user_builds/h2integrate/envs/531/lib/python3.11/site-packages/h2integrate/core/h2integrate_model.py:73, in H2IntegrateModel.__init__(self, config_input)
69 self.connect_technologies()
71 # create driver model
72 # might be an analysis or optimization
---> 73 self.create_driver_model()
File ~/checkouts/readthedocs.org/user_builds/h2integrate/envs/531/lib/python3.11/site-packages/h2integrate/core/h2integrate_model.py:1199, in H2IntegrateModel.create_driver_model(self)
1197 myopt = PoseOptimization(self.driver_config)
1198 if "driver" in self.driver_config:
-> 1199 myopt.set_driver(self.prob)
1200 myopt.set_objective(self.prob)
1201 myopt.set_design_variables(self.prob)
File ~/checkouts/readthedocs.org/user_builds/h2integrate/envs/531/lib/python3.11/site-packages/h2integrate/core/pose_optimization.py:337, in PoseOptimization.set_driver(self, opt_prob)
333 valid_file = check_file_format_for_csv_generator(
334 doe_options["filename"], self.config, check_only=True
335 )
336 if not valid_file:
--> 337 raise UserWarning(
338 f"There may be issues with the csv file {doe_options['filename']}, "
339 f"which may cause errors within OpenMDAO. "
340 "To check this csv file or create a new one, run the function "
341 "h2integrate.core.utilities.check_file_format_for_csv_generator()."
342 )
343 generator = om.CSVGenerator(
344 filename=doe_options["filename"],
345 )
346 else:
UserWarning: There may be issues with the csv file /home/docs/checkouts/readthedocs.org/user_builds/h2integrate/checkouts/531/examples/20_solar_electrolyzer_doe/csv_doe_cases.csv, which may cause errors within OpenMDAO. To check this csv file or create a new one, run the function h2integrate.core.utilities.check_file_format_for_csv_generator().
Fixing the bug#
The UserWarning tells us that there may be an issue with our csv file. We will use the recommended method to create a new csv file that doesn’t have formatting issues.
We’ll take the following steps to try and fix the bug:
Run the
check_file_format_for_csv_generatormethod mentioned in the UserWarning and create a new csv file that is hopefully free of errorsMake a new driver config file that has “filename” point to the new csv file created in Step 1.
Make a new top-level config file that points to the updated driver config file created in Step 2.
# Step 1
new_csv_filename = check_file_format_for_csv_generator(
csv_config_fn,
driver_config,
check_only=False,
overwrite_file=False,
)
new_csv_filename.name
'csv_doe_cases0.csv'
Let’s see the updates to combinations.
solar.system_capacity_DC,electrolyzer.n_clusters
25000.0,5.0
50000.0,5.0
100000.0,5.0
200000.0,5.0
50000.0,10.0
100000.0,10.0
200000.0,10.0
250000.0,10.0
300000.0,10.0
500000.0,10.0
# Step 2
updated_driver = update_defaults(
driver_config["driver"],
"filename",
str(EXAMPLE_DIR / new_csv_filename.name),
)
driver_config["driver"].update(updated_driver)
# Step 3
config["driver_config"] = driver_config
Re-running#
Now that we’ve completed the debugging and fixing steps, lets try to run the simulation again but with our new files.
model = H2IntegrateModel(config)
model.run()
Driver debug print for iter coord: rank0:DOEDriver_CSV|0
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([5.]),
'solar.system_capacity_DC': array([25000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([7.61626063])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|1
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([5.]),
'solar.system_capacity_DC': array([50000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([4.92406874])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|2
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([5.]),
'solar.system_capacity_DC': array([100000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([4.66453898])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|3
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([5.]),
'solar.system_capacity_DC': array([200000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([6.58504271])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|4
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([10.]),
'solar.system_capacity_DC': array([50000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([7.60179634])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|5
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([10.]),
'solar.system_capacity_DC': array([100000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([4.92097224])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|6
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([10.]),
'solar.system_capacity_DC': array([200000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([4.66301442])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|7
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([10.]),
'solar.system_capacity_DC': array([250000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([5.11037226])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|8
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([10.]),
'solar.system_capacity_DC': array([300000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([5.58724086])}
Driver debug print for iter coord: rank0:DOEDriver_CSV|9
--------------------------------------------------------
Design Vars
{'electrolyzer.n_clusters': array([10.]),
'solar.system_capacity_DC': array([500000.])}
Nonlinear constraints
None
Linear constraints
None
Objectives
{'finance_subgroup_hydrogen.LCOH_optimistic': array([7.61137828])}
/home/docs/checkouts/readthedocs.org/user_builds/h2integrate/envs/531/lib/python3.11/site-packages/openmdao/core/group.py:1267: DerivativesWarning:Constraints or objectives [plant.finance_subgroup_hydrogen.hydrogen_finance_optimistic.LCOH_optimistic] cannot be impacted by the design variables of the problem because no partials were defined for them in their parent component(s).