Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/green_steel_cleanup' into gree…
Browse files Browse the repository at this point in the history
…n_steel_cleanup
  • Loading branch information
cfrontin committed Aug 4, 2023
2 parents 88a040a + 45da1e1 commit 2f16962
Show file tree
Hide file tree
Showing 33 changed files with 153 additions and 80 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: Testing

on: [ push ]
on: [ push, pull_request ]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.10" ]
python-version: [ "3.8" ]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Hybrid Optimization and Performance Platform

![CI Tests](https://github.com/NREL/HOPP/actions/workflows/ci.yml/badge.svg)

As part of NREL's [Hybrid Energy Systems Research](https://www.nrel.gov/wind/hybrid-energy-systems-research.html), this
software assesses optimal designs for the deployment of utility-scale hybrid energy plants, particularly considering wind,
solar and storage.
Expand Down
1 change: 0 additions & 1 deletion hopp/eco/electrolyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def run_electrolyzer_physics(

adjusted_installed_cost = hybrid_plant.grid._financial_model.Outputs.adjusted_installed_cost
#NB: adjusted_installed_cost does NOT include the electrolyzer cost
print("ADJ. INST. COST ", adjusted_installed_cost)
# system_rating = electrolyzer_size
system_rating = wind_size_mw + solar_size_mw
H2_Results, H2A_Results = run_h2_PEM(energy_to_electrolyzer_kw, electrolyzer_size_mw,
Expand Down
11 changes: 10 additions & 1 deletion hopp/eco/hopp_mgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ def setup_hopp(
hopp_site_input_data["lat"] = plant_config["project_location"]["lat"]
hopp_site_input_data["lon"] = plant_config["project_location"]["lon"]
hopp_site_input_data["year"] = plant_config["wind_resource_year"]
hopp_site_input_data["no_wind"] = not plant_config["project_parameters"]["wind"]
hopp_site_input_data["no_solar"] = not plant_config["project_parameters"]["solar"]

# set desired schedule based on electrolyzer capacity
desired_schedule = [plant_config["electrolyzer"]["rating"]] * 8760

# generate HOPP SiteInfo class instance
print(hopp_site_input_data)
hopp_site = SiteInfo(
hopp_site_input_data,
hub_height=turbine_config["hub_height"],
Expand Down Expand Up @@ -157,6 +159,11 @@ def setup_hopp(
grid_connected_hopp = plant_config["project_parameters"]["grid_connection"]

# add these specific inputs to a dictionary for transfer
if "solar_om_cost_kw" in plant_config["project_parameters"]:
solar_om_cost_kw = plant_config["project_parameters"]["solar_om_cost_kw"]
else:
solar_om_cost_kw = 0.0

hopp_h2_args = {
"wind_size_mw": wind_size_mw,
"wind_om_cost_kw": wind_om_cost_kw,
Expand All @@ -175,6 +182,7 @@ def setup_hopp(
"grid_connected_hopp": grid_connected_hopp,
"turbine_parent_path": "../../input/turbines/",
"ppa_price": plant_config["project_parameters"]["ppa_price"],
"solar_om_cost_kw": solar_om_cost_kw
}

################ return all the inputs for hopp
Expand Down Expand Up @@ -211,10 +219,11 @@ def run_hopp(hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args, verbose=
hopp_h2_args["load"],
hopp_h2_args["custom_powercurve"],
hopp_h2_args["electrolyzer_size"],
solar_om_cost_kw=hopp_h2_args["solar_om_cost_kw"],
grid_connected_hopp=hopp_h2_args["grid_connected_hopp"],
wind_om_cost_kw=hopp_h2_args["wind_om_cost_kw"],
turbine_parent_path=hopp_h2_args["turbine_parent_path"],
ppa_price=hopp_h2_args["ppa_price"],
ppa_price=hopp_h2_args["ppa_price"]
)

# store results for later use
Expand Down
54 changes: 37 additions & 17 deletions hopp/eco/hydrogen_mgmt.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import numpy as np
import pandas as pd

from ORBIT import ProjectManager, load_config
from ORBIT.core import Vessel
from ORBIT.core.library import initialize_library
from ORBIT.phases.design import DesignPhase
from ORBIT.phases.install import InstallPhase

from hopp.simulation.technologies.hydrogen.h2_transport.h2_compression import Compressor
from hopp.simulation.technologies.hydrogen.h2_storage.pressure_vessel.compressed_gas_storage_model_20221021.Compressed_all import PressureVessel
from hopp.simulation.technologies.hydrogen.h2_storage.pipe_storage import (
Expand All @@ -9,15 +15,15 @@
from hopp.simulation.technologies.hydrogen.h2_storage.on_turbine.on_turbine_hydrogen_storage import (
PressurizedTower,
)

from hopp.simulation.technologies.hydrogen.h2_transport.h2_export_pipe import run_pipe_analysis
from hopp.simulation.technologies.hydrogen.h2_transport.h2_pipe_array import run_pipe_array_const_diam
from hopp.simulation.technologies.offshore.fixed_platform import (
install_platform,
calc_platform_opex,
calc_substructure_mass_and_cost,
FixedPlatformDesign,
FixedPlatformInstallation,
calc_platform_opex
)


def run_h2_pipe_array(
plant_config, orbit_project, electrolyzer_physics_results, design_scenario, verbose
):
Expand Down Expand Up @@ -435,32 +441,46 @@ def run_equipment_platform(
) # from kg to tonnes
toparea += h2_storage_results["tank_footprint_m2"]

distance = plant_config["site"]["distance_to_landfall"]
#### initialize
if not ProjectManager.find_key_match("FixedPlatformDesign"):
ProjectManager.register_design_phase(FixedPlatformDesign)
if not ProjectManager.find_key_match("FixedPlatformInstallation"):
ProjectManager.register_install_phase(FixedPlatformInstallation)

installation_cost = install_platform(
topmass,
toparea,
distance,
install_duration=plant_config["platform"]["installation_days"],
)
platform_config = plant_config["platform"]

depth = plant_config["site"]["depth"] # depth of pipe [m]
# assign site parameters
if platform_config["site"]["depth"] == -1:
platform_config["site"]["depth"] = plant_config["site"]["depth"]
if platform_config["site"]["distance"] == -1:
platform_config["site"]["distance"] = plant_config["site"]["distance"]
# assign equipment values

if platform_config["equipment"]["tech_combined_mass"] == -1:
platform_config["equipment"]["tech_combined_mass"] = topmass
if platform_config["equipment"]["tech_required_area"] == -1:
platform_config["equipment"]["tech_required_area"] == toparea
platform = ProjectManager(platform_config)
platform.run()

capex, platform_mass = calc_substructure_mass_and_cost(topmass, toparea, depth)
design_capex = platform.design_results['platform_design']['total_cost']
install_capex = platform.installation_capex
total_capex = design_capex + install_capex

opex_rate = plant_config["platform"]["opex_rate"]
total_opex = calc_platform_opex(capex, opex_rate)
total_opex = calc_platform_opex(total_capex, platform_config["opex_rate"])

total_capex = capex + installation_cost
platform_mass = platform.design_results['platform_design']['mass']
platform_area = platform.design_results['platform_design']['area']

else:
platform_mass = 0.0
platform_area = 0.0
total_capex = 0.0
total_opex = 0.0

platform_results = {
"topmass_kg": topmass,
"toparea_m2": toparea,
"toparea_m2": platform_area,
"platform_mass_tonnes": platform_mass,
"capex": total_capex,
"opex": total_opex,
Expand Down
3 changes: 2 additions & 1 deletion hopp/simulation/hopp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import yaml
from attrs import define, field
from pathlib import Path
from typing import Union

from hopp.simulation.base import BaseClass
from hopp.simulation.hybrid_simulation import HybridSimulation
Expand Down Expand Up @@ -35,7 +36,7 @@ def simulate(self, project_life):
# I/O

@classmethod
def from_file(cls, input_file_path: str | Path, filetype: str = None):
def from_file(cls, input_file_path: Union[str, Path], filetype: str = None):
"""Creates an `Hopp` instance from an input file. Must be filetype YAML.
Args:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"""
import sys
import numpy as np
from hopp.to_organize.H2_Analysis import simple_cash_annuals
from hopp.to_organize.H2_Analysis.simple_cash_annuals import simple_cash_annuals



Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np
import numpy_financial as npf
from hopp.to_organize.H2_Analysis import simple_cash_annuals
from hopp.to_organize.H2_Analysis.simple_cash_annuals import simple_cash_annuals
import warnings
from pytest import approx

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def external_power_supply(self):
TODO: extend model to accept variable voltage, current, and power
This will replicate direct DC-coupled PV system operating at MPP
"""
power_converter_efficiency = 1.0 #this used to be 0.95 but feel free to change as you'd like
power_converter_efficiency = 1.0 # this used to be 0.95 but feel free to change as you'd like
if self.input_dict['voltage_type'] == 'constant':

self.input_dict['P_input_external_kW'] = \
Expand All @@ -120,6 +120,7 @@ def external_power_supply(self):
(self.electrolyzer_system_size_MW * 1000),
(self.input_dict['P_input_external_kW'] -
(self.electrolyzer_system_size_MW * 1000)), 0)

#Current used to be calculated as Power/Voltage but now it uses the IV curve
# self.output_dict['current_input_external_Amps'] = \
# (self.input_dict['P_input_external_kW'] * 1000 *
Expand Down Expand Up @@ -500,6 +501,8 @@ def water_supply(self):
Calculate water supply rate based system efficiency and H2 production
rate
TODO: Add this capability to the model
The 10x multiple is likely too low. See Lampert, David J., Cai, Hao, Wang, Zhichao, Keisman, Jennifer, Wu, May, Han, Jeongwoo, Dunn, Jennifer, Sullivan, John L., Elgowainy, Amgad, Wang, Michael, & Keisman, Jennifer. Development of a Life Cycle Inventory of Water Consumption Associated with the Production of Transportation Fuels. United States. https://doi.org/10.2172/1224980
"""
# ratio of water_used:h2_kg_produced depends on power source
# h20_kg:h2_kg with PV 22-126:1 or 18-25:1 without PV but considering water deminersalisation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def mass(rating_mw):
(m, b), pcov = curve_fit(_electrolyzer_mass_fit, rating_mw_fit, mass_kg_fit)

mass_kg = _electrolyzer_mass_fit(rating_mw, m, b)

return mass_kg

if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from hopp.to_organize.H2_Analysis import simple_cash_annuals
from hopp.to_organize.H2_Analysis.simple_cash_annuals import simple_cash_annuals

class Underground_Pipe_Storage():
"""
Expand Down
2 changes: 1 addition & 1 deletion hopp/simulation/technologies/layout/flicker_mismatch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List, Union, Optional, Sequence
import multiprocessing as mp
import multiprocessing_on_dill as mp
from pathlib import Path
import functools
import copy
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
from pathlib import Path
from itertools import product
import multiprocessing as mp
import multiprocessing_on_dill as mp
from typing import Union
import numpy as np
import matplotlib.pyplot as plt
Expand Down
2 changes: 1 addition & 1 deletion hopp/simulation/technologies/layout/pv_design_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def size_electrical_parameters(
inverter_power=inverter_power,
)

if n_inputs_combiner:
if n_inputs_combiner is not None and n_inputs_inverter is not None:
n_combiners = math.ceil(n_strings / n_inputs_combiner)
# Ensure there are enough inverters for the number of combiner boxes
n_inverters = max(n_inverters, math.ceil(n_combiners / n_inputs_inverter))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ install_phases:
FloatingPlatformInstallation: 0 # Register Install Phase
oss_install_vessel: example_heavy_lift_vessel
site:
depth: 500.5 # site depth [m]
depth: 500.5 # site depth [m] Site depths for floating projects need to be at depths 500 m to 1500 m because of Orbit SemiTaut branch limitations (7/31)
distance: 124 # distance to port [km]
equipment:
tech_required_area: 300. # equipment area [m**2]
Expand Down
33 changes: 15 additions & 18 deletions hopp/simulation/technologies/offshore/fixed_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,11 @@
import os
import math
#
from ORBIT import ProjectManager, load_config
from ORBIT.core import Vessel
from ORBIT.core.library import initialize_library
from ORBIT.phases.design import DesignPhase
from ORBIT.phases.install import InstallPhase
import ORBIT as orbit

class FixedPlatformDesign(DesignPhase):
print("ORBITORBIT: ", orbit)

class FixedPlatformDesign(orbit.phases.design.DesignPhase):
'''
This is a modified class based on ORBIT's [1] design phase. The implementation
is discussed in [2], Section 2.5: Offshore Substation Design. Default values originate
Expand Down Expand Up @@ -96,7 +94,7 @@ def run(self):

_platform = self.config.get('equipment',{})

self.mass = _platform.get('tech_comnined_mass',999) # t
self.mass = _platform.get('tech_combined_mass',999) # t
self.area = _platform.get('tech_required_area', 1000) # m**2

design_cost = _platform.get('topside_design_cost', 4.5e6) # USD
Expand All @@ -107,7 +105,7 @@ def run(self):
total_cost, total_mass = calc_substructure_mass_and_cost(self.mass, self.area,
self.depth, fab_cost, design_cost, steel_cost
)

# Create an ouput dict
self._outputs['fixed_platform'] = {
"mass" : total_mass,
Expand All @@ -132,7 +130,7 @@ def detailed_output(self):

return {}

class FixedPlatformInstallation(InstallPhase):
class FixedPlatformInstallation(orbit.phases.install.InstallPhase):
'''
This is a modified class based on ORBIT's [1] install phase. The implementation
is duscussed in [2], Section 3.6: Offshore Substation Installation. Default values
Expand Down Expand Up @@ -189,7 +187,7 @@ def setup_simulation(self, **kwargs):
vessel_specs = self.config.get("oss_install_vessel", None)
name = vessel_specs.get("name","Offshore Substation Install Vessel")

vessel = Vessel(name, vessel_specs)
vessel = orbit.core.Vessel(name, vessel_specs)
self.env.register(vessel)

vessel.initialize()
Expand All @@ -200,9 +198,9 @@ def setup_simulation(self, **kwargs):
self.depth, fab_cost, design_cost, steel_cost
)

total_mass = substructure_mass # t
self.total_mass = substructure_mass # t
# Call the install_platform function
self.install_capex = install_platform(total_mass, self.area, self.distance, \
self.install_capex = install_platform(self.total_mass, self.area, self.distance, \
install_duration, self.install_vessel)

# An install object needs to have attribute system_capex, installation_capex, and detailed output
Expand Down Expand Up @@ -326,17 +324,16 @@ def calc_platform_opex(capex, opex_rate=0.011):

orbit_libpath = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, os.pardir, 'ORBIT', 'library'))
print(orbit_libpath)
initialize_library(orbit_libpath)
orbit.core.library.initialize_library(orbit_libpath)

config_path = os.path.abspath(__file__)
config_fname = load_config(os.path.join(config_path, os.pardir, "example_fixed_project.yaml"))
config_fname = orbit.load_config(os.path.join(config_path, os.pardir, "example_fixed_project.yaml"))


ProjectManager.register_design_phase(FixedPlatformDesign)
orbit.ProjectManager.register_design_phase(FixedPlatformDesign)

ProjectManager.register_install_phase(FixedPlatformInstallation)
orbit.ProjectManager.register_install_phase(FixedPlatformInstallation)

platform = ProjectManager(config_fname)
platform = orbit.ProjectManager(config_fname)
platform.run()

design_capex = platform.design_results['platform_design']['total_cost']
Expand Down
2 changes: 1 addition & 1 deletion hopp/simulation/technologies/offshore/floating_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- tech_combined_mass: (float): mass of all tech being placed on the platform (kg or tonnes)year
- depth: (float): bathometry at the platform location (m)
- depth: (float): bathometry at the platform location (m) ##Site depths for floating projects need to be at depths 500 m to 1500 m because of Orbit SemiTaut branch limitations (7/31)
- distance_to_port: (float): distance ships must travel from port to site location (km)
Future arguments: (Not used at this time)
Expand Down
4 changes: 3 additions & 1 deletion hopp/simulation/technologies/sites/site_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@ def __init__(self, data,
raise ValueError('The provided desired schedule does not match length of the simulation horizon.')

# FIXME: this a hack
if 'no_wind' in data:
if 'no_wind' in data and data["no_wind"]:
logger.info("Set up SiteInfo with solar resource files: {}".format(self.solar_resource.filename))
elif 'no_solar' in data and data["no_solar"]:
logger.info("Set up SiteInfo with wind resource files: {}".format(self.wind_resource.filename))
else:
logger.info(
"Set up SiteInfo with solar and wind resource files: {}, {}".format(self.solar_resource.filename,
Expand Down
Loading

0 comments on commit 2f16962

Please sign in to comment.