Skip to content

Commit

Permalink
Merge pull request #317 from jaredthomas68/offshore-constraints
Browse files Browse the repository at this point in the history
Offshore constraints
  • Loading branch information
jaredthomas68 committed May 15, 2024
2 parents 4abc512 + 061fa49 commit ae006c9
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 54 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ solar and storage.
2. To set up `NREL_API_KEY` for resource downloads, first refer to section 7 and 8 above. But for the `.env` file method,
the file should go in the working directory of your Python project, e.g. directory from where you run `python`.

## Parallel Processing for GreenHEART finite differences and design of experiments
GreenHEART is set up to run in parallel using MPI and PETSc for finite differencing and for design of experiments runs through OpenMDAO. To use this capability you will need to follow the addtional installation instruction below:
```
conda install -c conda-forge mpi4py petsc4py
```
For more details on implementation and installation, reference the documentation for OpenMDAO.

To to check that your installation is working, do the following:
```
cd tests/greenheart/
mpirun -n 2 pytest test_openmdao_mpi.py
```

## Getting Started

The [Examples](./examples/) contain Jupyter notebooks and sample YAML files for common usage scenarios in HOPP. These are actively maintained and updated to demonstrate HOPP's capabilities. For full details on simulation options and other features, see the [documentation](https://hopp.readthedocs.io/en/latest/).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ opt_options:
boundary_distance:
flag: False
lower: 0.0
pv_to_platform_area_ratio:
flag: False
upper: 1.0 # relative size of solar pv area to platform area
user: {}
merit_figure: "lcos"
merit_figure_user:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ opt_options:
boundary_distance:
flag: False
lower: 0.0
pv_to_platform_area_ratio:
flag: False
upper: 1.0 # relative size of solar pv area to platform area
user: {}
merit_figure: "lcoa"
merit_figure_user:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ opt_options:
boundary_distance:
flag: False
lower: 0.0
pv_to_platform_area_ratio:
flag: False
upper: 1.0 # relative size of solar pv area to platform area
user: {}
merit_figure: "lcoh"
merit_figure_user:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ opt_options:
boundary_distance:
flag: False
lower: 0.0
pv_to_platform_area_ratio:
flag: False
upper: 1.0 # relative size of solar pv area to platform area
user: {}
merit_figure: "lcoh"
merit_figure_user:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ opt_options:
boundary_distance:
flag: False
lower: 0.0
pv_to_platform_area_ratio:
flag: False
upper: 1.0 # relative size of solar pv area to platform area
user: {}
merit_figure: "lcoh"
merit_figure_user:
Expand Down
6 changes: 5 additions & 1 deletion greenheart/simulation/greenheart_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ class GreenHeartSimulationOutput:
ammonia_capacity (Optional[AmmoniaCapacityModelOutputs]): ammonia capacity information
ammonia_costs (Optional[AmmoniaCostModelOutputs]): ammonia cost information
ammonia_finance (Optional[AmmoniaFinanceModelOutputs]): ammonia finance information
platform_results (Optional[dict]): equipment platform information/outputs if used
"""

# detailed simulation information
Expand Down Expand Up @@ -230,6 +231,8 @@ class GreenHeartSimulationOutput:
ammonia_costs: Optional[AmmoniaCostModelOutputs] = field(default=None)
ammonia_finance: Optional[AmmoniaFinanceModelOutputs] = field(default=None)

platform_results: Optional[dict] = field(default=None)

def setup_greenheart_simulation(config: GreenHeartSimulationConfig):

# run orbit for wind plant construction and other costs
Expand Down Expand Up @@ -909,7 +912,8 @@ def simple_solver(initial_guess=0.0):
steel_finance = None if "steel" not in config.greenheart_config else steel_finance,
ammonia_capacity = None if "ammonia" not in config.greenheart_config else ammonia_capacity,
ammonia_costs = None if "ammonia" not in config.greenheart_config else ammonia_costs,
ammonia_finance = None if "ammonia" not in config.greenheart_config else ammonia_finance
ammonia_finance = None if "ammonia" not in config.greenheart_config else ammonia_finance,
platform_results = platform_results
)

def run_sweeps(simulate=False, verbose=True, show_plots=True, use_profast=True, output_dir="output/"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ equipment:
tech_required_area: 300. # equipment area [m**2]
tech_combined_mass: 1000 # equipment mass [t]
topside_design_cost: 4500000 # topside design cost [USD]
installation_duration: 14 # time at sea [days]
installation_duration: 14 # time at sea [days]

# set input values to -1 to use values calculated or input in other files during GreenHEART run (depth, distance, tech_required_area, tech_combined_mass)
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ equipment:
tech_combined_mass: 1000 # equipment mass [t]
topside_design_cost: 4500000 # topside design cost [USD]
installation_duration: 14 # time at sea [days]

# set input values to -1 to use values calculated or input in other files during GreenHEART run (depth, distance, tech_required_area, tech_combined_mass)
31 changes: 8 additions & 23 deletions greenheart/tools/eco/hopp_mgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def setup_hopp(
save_plots=False,
output_dir="./output/"
):

if "battery" in hopp_config["technologies"].keys() and \
("desired_schedule" not in hopp_config["site"].keys() or hopp_config["site"]["desired_schedule"] == []):
hopp_config["site"]["desired_schedule"] = [greenheart_config["electrolyzer"]["rating"]]*8760
Expand All @@ -46,7 +46,7 @@ def setup_hopp(
greenheart_config["site"]["mean_windspeed"] = np.average(wind_speed)

################ set up HOPP technology inputs
hopp_technologies = {}

if hopp_config["site"]["wind"]:
if hopp_config["technologies"]["wind"]["model_name"] == "floris":
if design_scenario["wind_location"] == "offshore":
Expand Down Expand Up @@ -118,18 +118,14 @@ def setup_hopp(
}
]

hopp_technologies["wind"] = {
"turbine_rating_kw": turbine_config["turbine_rating"] * 1000,
"floris_config": floris_config, # if not specified, use default SAM models
}
hopp_config["technologies"]["wind"]["turbine_rating_kw"] = turbine_config["turbine_rating"] * 1000
hopp_config["technologies"]["wind"]["floris_config"] = floris_config

elif hopp_config["technologies"]["wind"]["model_name"] == "sam":
hopp_technologies["wind"] = {
"turbine_rating_kw": turbine_config["turbine_rating"]
* 1000, # convert from MW to kW
"hub_height": turbine_config["hub_height"],
"rotor_diameter": turbine_config["rotor_diameter"],
}
hopp_config["technologies"]["wind"]["turbine_rating_kw"] = turbine_config["turbine_rating"] * 1000, # convert from MW to kW
hopp_config["technologies"]["wind"]["hub_height"] = turbine_config["hub_height"]
hopp_config["technologies"]["wind"]["rotor_diameter"] = turbine_config["rotor_diameter"]

else:
raise (
ValueError(
Expand All @@ -138,17 +134,6 @@ def setup_hopp(
% (hopp_config["technologies"]["wind"]["model_name"])
)

for key in hopp_technologies["wind"]:
if key in hopp_config["technologies"]["wind"]:
hopp_config["technologies"]["wind"][key] = hopp_technologies["wind"][
key
]
else:
hopp_config["technologies"]["wind"].update(
hopp_technologies["wind"][key]
)


# setup hopp interface
hopp_config_internal = copy.deepcopy(hopp_config)

Expand Down
7 changes: 4 additions & 3 deletions greenheart/tools/eco/hydrogen_mgmt.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy as np
import pandas as pd
import warnings

from ORBIT import ProjectManager, load_config
from ORBIT.core import Vessel
Expand Down Expand Up @@ -569,11 +570,11 @@ def run_equipment_platform(
toparea += battery_area

if hopp_config["site"]["solar"] and design_scenario["pv_location"] == "platform":
solar_area = hopp_results['hybrid_plant'].pv.footprint_area
pv_area = hopp_results['hybrid_plant'].pv.footprint_area
solar_mass = hopp_results['hybrid_plant'].pv.system_mass

if solar_area > toparea:
raise(ValueError(f"Solar area ({solar_area} m^2) must be smaller than platform area ({toparea} m^2)"))
if pv_area > toparea:
warnings.warn(f"Solar area ({pv_area} m^2) must be smaller than platform area ({toparea} m^2)", UserWarning)
topmass += solar_mass

#### initialize
Expand Down
12 changes: 6 additions & 6 deletions greenheart/tools/optimization/gc_PoseOptimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def set_driver(self, opt_prob):
seed=doe_options["seed"],
)
elif doe_options["generator"].lower() == "fullfact":
generator = om.FullFactorialGenerator(levels=int(doe_options["num_samples"]))
generator = om.FullFactorialGenerator(levels=int(doe_options["levels"]))
elif doe_options["generator"].lower() == "plackettburman":
generator = om.PlackettBurmanGenerator()
elif doe_options["generator"].lower() == "boxbehnken":
Expand Down Expand Up @@ -386,13 +386,13 @@ def set_constraints(self, opt_prob, hi: Optional[Union[None, HoppInterface]] = N
if self.config.greenheart_config["opt_options"]["constraints"]["boundary_distance"]["flag"]:
lower = self.config.greenheart_config["opt_options"]["constraints"]["boundary_distance"]["lower"]
opt_prob.model.add_subsystem("con_boundary", subsys=BoundaryDistanceComponent(hopp_interface=self.config.greenheart_config, turbine_x_init=turbine_x_init, turbine_y_init=turbine_y_init), promotes=["*"])
opt_prob.model.add_constraint("boundary_distance_vec", lower=0)
opt_prob.model.add_constraint("boundary_distance_vec", lower=lower)

# solar/platform size
# if self.config.greenheart_config["opt_options"]["constraints"]["solar_platform_ratio"]["flag"]:
# upper = self.config.greenheart_config["opt_options"]["constraints"]["solar_platform_ratio"]["upper"]
# opt_prob.model.add_subsystem("con_boundary", subsys=BoundaryDistanceComponent(hopp_interface=self.config.greenheart_config, turbine_x_init=turbine_x_init, turbine_y_init=turbine_y_init), promotes=["*"])
# opt_prob.model.add_constraint("boundary_distance_vec", lower=0)
if self.config.greenheart_config["opt_options"]["constraints"]["pv_to_platform_area_ratio"]["flag"]:
upper = self.config.greenheart_config["opt_options"]["constraints"]["pv_to_platform_area_ratio"]["upper"]
opt_prob.model.add_subsystem("con_pv_platform_area", subsys=om.ExecComp(['pv_platform_ratio=pv_area/platform_area']), promotes=["*"])
opt_prob.model.add_constraint("pv_platform_ratio", upper=upper)

# User constraints
user_constr = self.config.greenheart_config["opt_options"]["constraints"]["user"]
Expand Down
37 changes: 24 additions & 13 deletions greenheart/tools/optimization/openmdao.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def initialize(self):

def setup(self):
ninputs = 0
hopp_technologies = self.options["config"].hopp_config["technologies"]
# Add inputs
if "turbine_x" in self.options["design_variables"]:
self.add_input("turbine_x", val=self.options["turbine_x_init"], units="m")
Expand All @@ -34,16 +35,21 @@ def setup(self):
self.add_input("turbine_y", val=self.options["turbine_y_init"], units="m")
ninputs += len(self.options["turbine_y_init"])
if "wind_rating_kw" in self.options["design_variables"]:
self.add_input("wind_rating_kw", val=150000, units="kW")
initial_wind_rating = hopp_technologies["wind"]["num_turbines"]*hopp_technologies["wind"]["turbine_rating_kw"]
self.add_input("wind_rating_kw", val=initial_wind_rating, units="kW")
ninputs += 1
if "pv_capacity_kw" in self.options["design_variables"]:
self.add_input("pv_capacity_kw", val=15000, units="kW")
self.add_input("pv_capacity_kw", val=hopp_technologies["pv"]["system_capacity_kw"], units="kW")
ninputs += 1
if "wave_capacity_kw" in self.options["design_variables"]:
initial_wave_rating = hopp_technologies["wave"]["device_rating_kw"]*hopp_technologies["wave"]["num_devices"]
self.add_input("wave_capacity_kw", val=initial_wave_rating, units="kW")
ninputs += 1
if "battery_capacity_kw" in self.options["design_variables"]:
self.add_input("battery_capacity_kw", val=15000, units="kW")
self.add_input("battery_capacity_kw", val=hopp_technologies["battery"]["system_capacity_kw"], units="kW")
ninputs += 1
if "battery_capacity_kwh" in self.options["design_variables"]:
self.add_input("battery_capacity_kwh", val=15000, units="kW*h")
self.add_input("battery_capacity_kwh", val=hopp_technologies["battery"]["system_capacity_kwh"], units="kW*h")
ninputs += 1
if ninputs == 0 or "electrolyzer_rating_kw" in self.options["design_variables"]:
self.add_input("electrolyzer_rating_kw", val=self.options["config"].greenheart_config["electrolyzer"]["rating"]*1E3, units="kW")
Expand All @@ -57,15 +63,14 @@ def setup(self):
if "ammonia" in self.options["config"].greenheart_config.keys():
self.add_output("lcoa", units="USD/kg", val=0.0, desc="levelized cost of ammonia")
if "pv_capacity_kw" in self.options["design_variables"]:
design_scenario = self.options["config"].plant_design_scenario
if self.options["config"].greenheart_config["plant_design"][f"scenario{design_scenario}"]["pv_location"] == "offshore":
if self.options["config"]["greenheart_config"]["opt_options"]["constraints"]["solar_platform_ratio"]["flag"]:
if self.options['config'].design_scenario["pv_location"] == "offshore":
if self.options["config"].greenheart_config["opt_options"]["constraints"]["pv_to_platform_area_ratio"]["flag"]:

self.add_output("solar_area", units="m^2", val=0.0, desc="offshore pv array area")
self.add_output("platform_area", units="m^2", val=0.0, desc="offshore platform area")
self.add_output("pv_area", units="m*m", val=0.0, desc="offshore pv array area")
self.add_output("platform_area", units="m*m", val=0.0, desc="offshore platform area")

self.options["outputs_for_finite_difference"].append(["solar_area"])
self.options["outputs_for_finite_difference"].append(["platform_area"])
self.options["outputs_for_finite_difference"].append("pv_area")
self.options["outputs_for_finite_difference"].append("platform_area")

def compute(self, inputs, outputs):

Expand Down Expand Up @@ -108,8 +113,8 @@ def compute(self, inputs, outputs):
lcoh = greenheart_output.lcoh
steel_finance = greenheart_output.steel_finance
ammonia_finance = greenheart_output.ammonia_finance
# solar_area = greenheart_output.
# platform_area = greenheart_output
pv_area = greenheart_output.hopp_results['hybrid_plant'].pv.footprint_area
platform_area = greenheart_output.platform_results["toparea_m2"]

outputs["lcoe"] = lcoe
outputs["lcoh"] = lcoh
Expand All @@ -119,6 +124,12 @@ def compute(self, inputs, outputs):
if "ammonia" in self.options["config"].greenheart_config.keys():
outputs["lcoa"] = ammonia_finance.sol.get("price")

if "pv_capacity_kw" in self.options["design_variables"]:
if self.options["config"].design_scenario["pv_location"] == "offshore":
if self.options["config"].greenheart_config["opt_options"]["constraints"]["pv_to_platform_area_ratio"]["flag"]:
outputs["pv_area"] = pv_area
outputs["platform_area"] = platform_area

def setup_partials(self):
self.declare_partials(self.options["outputs_for_finite_difference"], '*', method='fd', form="forward")

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ orbit-nrel @ git+https://github.com/WISDEM/ORBIT.git@SemiTaut_Mooring_Update
pyyaml-include <= 1.4.1
electrolyzer @ git+https://github.com/jaredthomas68/electrolyzer.git@smoothing
ProFAST @ git+https://github.com/NREL/ProFAST.git
openmdao
openmdao[all]
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,4 @@ ammonia:
"capital gains tax rate": 0.15
"leverage after tax nominal discount rate": 0.10893
"debt equity ratio of initial financing": 0.624788
"debt interest rate": 0.050049
"debt interest rate": 0.050049

0 comments on commit ae006c9

Please sign in to comment.