def test_chained_dependencies(): config_chained = deepcopy(config) config_chained["spi_vessel"] = "test_scour_protection_vessel" config_chained["scour_protection"] = {"tons_per_substructure": 200} config_chained["install_phases"] = { "ScourProtectionInstallation": 0, "MonopileInstallation": ("ScourProtectionInstallation", 0.1), "TurbineInstallation": ("MonopileInstallation", 0.5), } project = ProjectManager(config_chained) project.run_project() df = pd.DataFrame(project.project_actions) sp = list(df.loc[df["phase"] == "ScourProtectionInstallation"]["time"]) mp = list(df.loc[df["phase"] == "MonopileInstallation"]["time"]) tu = list(df.loc[df["phase"] == "TurbineInstallation"]["time"]) assert min(sp) == 0 assert min(mp) == (max(sp) - min(sp)) * 0.1 assert min(tu) == (max(mp) - min(mp)) * 0.5 + min(mp)
def test_index_starts(m_start, t_start): """ Tests functionality related to passing index starts into 'install_phases' sub-dict. """ _target_diff = t_start - m_start config_with_index_starts = deepcopy(config) config_with_index_starts["install_phases"] = { "MonopileInstallation": m_start, "TurbineInstallation": t_start, } project = ProjectManager(config_with_index_starts) project.run_project() df = pd.DataFrame(project.project_actions) _m = df.loc[df["phase"] == "MonopileInstallation"].iloc[0] _t = df.loc[df["phase"] == "TurbineInstallation"].iloc[0] _diff = (_t["time"] - _t["duration"]) - (_m["time"] - _m["duration"]) assert _diff == _target_diff
def test_monthly_revenue(): project = ProjectManager(complete_project) project.run_project() _ = project.monthly_revenue # Can't generate revenue with "incomplete" project config = deepcopy(complete_project) _ = config["install_phases"].pop("TurbineInstallation") project = ProjectManager(config) project.run_project() with pytest.raises(ValueError): _ = project.monthly_revenue
def test_capex_categories(): project = ProjectManager(complete_project) project.run() baseline = project.capex_breakdown _ = project.capex_breakdown_per_kw new_config = deepcopy(complete_project) new_config["install_phases"]["ExportCableInstallation_1"] = 0 new_project = ProjectManager(new_config) new_project.run() new_breakdown = new_project.capex_breakdown assert new_breakdown["Export System"] > baseline["Export System"] assert new_breakdown["Export System Installation"] > baseline["Export System Installation"]
def test_kwargs_in_ProjectManager(): base = deepcopy(config_wtiv) base["install_phases"] = ["TurbineInstallation"] project = ProjectManager(base) project.run_project() baseline = project.phase_times["TurbineInstallation"] keywords = [ "tower_section_fasten_time", "tower_section_release_time", "tower_section_attach_time", "nacelle_fasten_time", "nacelle_release_time", "nacelle_attach_time", "blade_fasten_time", "blade_release_time", "blade_attach_time", "site_position_time", "crane_reequip_time", ] failed = [] for kw in keywords: default = pt[kw] processes = {kw: default + 2} new_config = deepcopy(base) new_config["processes"] = processes new_project = ProjectManager(new_config) new_project.run_project() new_time = new_project.phase_times["TurbineInstallation"] if new_time > baseline: pass else: failed.append(kw) if failed: raise Exception(f"'{failed}' not affecting results.") else: assert True
def test_phase_start_dates_with_weather(): m_start = "03/01/2010" t_start = "05/01/2010" config_with_start_dates = deepcopy(config) config_with_start_dates["install_phases"] = { "MonopileInstallation": m_start, "TurbineInstallation": t_start, } project = ProjectManager(config_with_start_dates, weather=weather_df) project.run_project() df = pd.DataFrame(project.project_actions) _fmt = "%m/%d/%Y" _target_diff = (datetime.strptime(t_start, _fmt) - datetime.strptime(m_start, _fmt)).days * 24 _m = df.loc[df["phase"] == "MonopileInstallation"].iloc[0] _t = df.loc[df["phase"] == "TurbineInstallation"].iloc[0] _diff = (_t["time"] - _t["duration"]) - (_m["time"] - _m["duration"]) assert _diff == _target_diff
def test_phase_start_dates(m_start, t_start): """ Tests functionality related to passing start dates into 'install_phases' sub-dict. """ config_with_start_dates = deepcopy(config) config_with_start_dates["install_phases"] = { "MonopileInstallation": m_start, "TurbineInstallation": t_start, } project = ProjectManager(config_with_start_dates) project.run_project() df = pd.DataFrame(project.project_actions) _fmt = "%m/%d/%Y" _target_diff = (datetime.strptime(t_start, _fmt) - datetime.strptime(m_start, _fmt)).days * 24 _m = df.loc[df["phase"] == "MonopileInstallation"].iloc[0] _t = df.loc[df["phase"] == "TurbineInstallation"].iloc[0] _diff = (_t["time"] - _t["duration"]) - (_m["time"] - _m["duration"]) assert _diff == _target_diff
def test_cash_flow(): project = ProjectManager(complete_project) project.run_project() _ = project.cash_flow # Can't generate revenue with "incomplete" project but cash flow will still # be reported config = deepcopy(complete_project) _ = config["install_phases"].pop("TurbineInstallation") project = ProjectManager(config) project.run_project() cash_flow = project.cash_flow assert all(v <= 0 for v in cash_flow.values())
def test_duplicate_phase_definitions(): config_with_duplicates = deepcopy(config) config_with_duplicates["MonopileInstallation_1"] = {"plant": {"num_turbines": 5}} config_with_duplicates["MonopileInstallation_2"] = { "plant": {"num_turbines": 5}, "site": {"distance": 100}, } config_with_duplicates["install_phases"] = { "MonopileInstallation_1": 0, "MonopileInstallation_2": 800, "TurbineInstallation": 1600, } project = ProjectManager(config_with_duplicates) project.run() df = pd.DataFrame(project.actions).groupby(["phase", "action"]).count()["time"] assert df.loc[("MonopileInstallation_1", "Drive Monopile")] == 5 assert df.loc[("MonopileInstallation_2", "Drive Monopile")] == 5 assert df.loc[("TurbineInstallation", "Attach Tower Section")] == 10
def test_project_costs(): project = ProjectManager(complete_project) baseline = project.project_capex config = deepcopy(complete_project) config["project_parameters"] = {"site_auction_price": 50e6} project = ProjectManager(config) assert project.project_capex != baseline config = deepcopy(complete_project) config["project_parameters"] = {"site_assessment_cost": 25e6} project = ProjectManager(config) assert project.project_capex != baseline config = deepcopy(complete_project) config["project_parameters"] = {"construction_plan_cost": 25e6} project = ProjectManager(config) assert project.project_capex != baseline config = deepcopy(complete_project) config["project_parameters"] = {"installation_plan_cost": 25e6} project = ProjectManager(config) assert project.project_capex != baseline
def test_floating_phase_cost_passing(): project = ProjectManager(floating) project.run_project() assert_almost_equal( project.phases["MooringSystemDesign"].total_cost, project.phases["MooringSystemInstallation"].system_capex, ) assert_almost_equal( project.phases["SemiSubmersibleDesign"].total_cost, project.phases["MooredSubInstallation"].system_capex, ) assert_almost_equal( project.phases["ArraySystemDesign"].total_cost, project.phases["ArrayCableInstallation"].system_capex, ) assert_almost_equal( project.phases["ExportSystemDesign"].total_cost, project.phases["ExportCableInstallation"].system_capex, ) assert_almost_equal( project.phases["OffshoreSubstationDesign"].total_cost, project.phases["OffshoreSubstationInstallation"].system_capex, ) spar = deepcopy(floating) spar["design_phases"].remove("SemiSubmersibleDesign") spar["design_phases"].append("SparDesign") project = ProjectManager(spar) project.run_project() assert_almost_equal( project.phases["SparDesign"].total_cost, project.phases["MooredSubInstallation"].system_capex, )
def test_design_phases(): config_with_design = deepcopy(config) # Add MonopileDesign config_with_design["design_phases"] = ["MonopileDesign"] # Add required parameters config_with_design["site"]["mean_windspeed"] = 9 config_with_design["turbine"]["rotor_diameter"] = 200 config_with_design["turbine"]["rated_windspeed"] = 10 config_with_design["monopile_design"] = {} # Remove monopile sub dictionary _ = config_with_design.pop("monopile") project = ProjectManager(config_with_design) project.run_project() assert isinstance(project.config["monopile"], dict) project = ProjectManager(config_with_design) project.run_project()
def test_expected_config_merging(): """ Tests for merging of expected configs """ config1 = { "site": { "distance": "float", "depth": "float" }, "plant": { "num_turbines": "int" }, } config2 = { "site": { "distance": "float", "wave_height": "float" }, "monopile": { "diameter": "float" }, } config = ProjectManager.merge_dicts(config1, config2) assert config == { "site": { "distance": "float", "depth": "float", "wave_height": "float", }, "plant": { "num_turbines": "int" }, "monopile": { "diameter": "float" }, }
def test_ProjectProgress_with_complete_project(): project = ProjectManager(complete_project) project.run_project() _ = project.progress.parse_logs("Substructure") _ = project.progress.parse_logs("Turbine") _ = project.progress.parse_logs("Array String") _ = project.progress.parse_logs("Export System") _ = project.progress.parse_logs("Offshore Substation") _ = project.progress.complete_export_system _ = project.progress.complete_array_strings _ = project.progress.energize_points new = deepcopy(complete_project) new["plant"]["num_turbines"] = 61 # Uneven strings project = ProjectManager(new) project.run_project() _ = project.progress.energize_points
def test_kwargs_in_ProjectManager(anchor, key): base = deepcopy(config) base["mooring_system"]["anchor_type"] = anchor base["install_phases"] = ["MooringSystemInstallation"] project = ProjectManager(base) project.run_project() baseline = project.phase_times["MooringSystemInstallation"] keywords = ["mooring_system_load_time", "mooring_site_survey_time", key] failed = [] for kw in keywords: default = pt[kw] processes = {kw: default + 2} new_config = deepcopy(base) new_config["processes"] = processes new_project = ProjectManager(new_config) new_project.run_project() new_time = new_project.phase_times["MooringSystemInstallation"] if new_time > baseline: pass else: failed.append(kw) if failed: raise Exception(f"'{failed}' not affecting results.") else: assert True
def test_kwargs_in_ProjectManager(): base = deepcopy(config) base["install_phases"] = ["ScourProtectionInstallation"] project = ProjectManager(base) project.run() baseline = project.phase_times["ScourProtectionInstallation"] keywords = ["drop_rocks_time"] failed = [] for kw in keywords: default = pt[kw] processes = {kw: default + 2} new_config = deepcopy(base) new_config["processes"] = processes new_project = ProjectManager(new_config) new_project.run() new_time = new_project.phase_times["ScourProtectionInstallation"] if new_time > baseline: pass else: failed.append(kw) if failed: raise Exception(f"'{failed}' not affecting results.") else: assert True
def test_phase_specific_definitions(): """ Tests that phase specific information makes it to phase_config. """ project = ProjectManager(config) phase_config = project.create_config_for_phase("MonopileInstallation") assert phase_config["wtiv"]["name"] == "Phase Specific WTIV" assert phase_config["site"]["distance"] == 500 phase_config = project.create_config_for_phase("TurbineInstallation") assert phase_config["wtiv"]["name"] == "Example WTIV" assert phase_config["site"]["distance"] == 50 project.run_project()
def test_npv(): project = ProjectManager(complete_project) project.run_project() baseline = project.npv config = deepcopy(complete_project) config["ncf"] = 0.35 project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["offtake_price"] = 70 project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["project_lifetime"] = 30 project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["discount_rate"] = 0.03 project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["opex_rate"] = 120 project = ProjectManager(config) project.run_project() assert project.npv != baseline
def test_npv(): project = ProjectManager(complete_project) project.run_project() baseline = project.npv config = deepcopy(complete_project) config["project_parameters"] = {"ncf": 0.35} project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["project_parameters"] = {"offtake_price": 70} project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["project_parameters"] = {"project_lifetime": 30} project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["project_parameters"] = {"discount_rate": 0.03} project = ProjectManager(config) project.run_project() assert project.npv != baseline config = deepcopy(complete_project) config["project_parameters"] = {"opex_rate": 120} project = ProjectManager(config) project.run_project() assert project.npv != baseline
def test_orbit_version_ProjectManager(): config = ProjectManager.compile_input_dict( ["MonopileDesign", "MonopileInstallation"]) assert "orbit_version" in config.keys()
def test_kwargs_for_export_install_in_ProjectManager(): new_export_system = { "cable": { "linear_density": 50.0, "sections": [1000], "number": 1 }, "system_cost": 200e6, } new_site = {"distance": 50, "depth": 20} base = deepcopy(base_config) base["export_system"] = new_export_system base["site"] = new_site base["install_phases"] = ["ExportCableInstallation"] project = ProjectManager(base) project.run_project() baseline = project.phase_times["ExportCableInstallation"] keywords = [ "onshore_construction_time", "cable_load_time", "site_position_time", "cable_prep_time", "cable_lower_time", "cable_pull_in_time", "cable_termination_time", "cable_splice_time", "tow_plow_speed", "pull_winch_speed", "cable_raise_time", ] failed = [] for kw in keywords: default = pt[kw] if "speed" in kw: _new = default - 0.05 if _new <= 0: raise Exception(f"'{kw}' is less than 0.") processes = {kw: _new} else: processes = {kw: default + 2} new_config = deepcopy(base) new_config["processes"] = processes new_project = ProjectManager(new_config) new_project.run_project() new_time = new_project.phase_times["ExportCableInstallation"] if new_time > baseline: pass else: failed.append(kw) if failed: raise Exception(f"ExpInstall: '{failed}' not affecting results.") else: assert True
def test_resolve_project_capacity(): # Missing turbine rating config1 = {"plant": {"capacity": 600, "num_turbines": 40}} out1 = ProjectManager.resolve_project_capacity(config1) assert out1["plant"]["capacity"] == config1["plant"]["capacity"] assert out1["plant"]["num_turbines"] == config1["plant"]["num_turbines"] assert out1["turbine"]["turbine_rating"] == 15 # Missing plant capacity config2 = { "plant": { "num_turbines": 40 }, "turbine": { "turbine_rating": 15 }, } out2 = ProjectManager.resolve_project_capacity(config2) assert out2["plant"]["capacity"] == 600 assert out2["plant"]["num_turbines"] == config2["plant"]["num_turbines"] assert out2["turbine"]["turbine_rating"] == config2["turbine"][ "turbine_rating"] # Missing number of turbines config3 = {"plant": {"capacity": 600}, "turbine": {"turbine_rating": 15}} out3 = ProjectManager.resolve_project_capacity(config3) assert out3["plant"]["capacity"] == config3["plant"]["capacity"] assert out3["plant"]["num_turbines"] == 40 assert out3["turbine"]["turbine_rating"] == config3["turbine"][ "turbine_rating"] # Test for float precision config4 = { "plant": { "capacity": 600, "num_turbines": 40 }, "turbine": { "turbine_rating": 15.0 }, } out4 = ProjectManager.resolve_project_capacity(config4) assert out4["plant"]["capacity"] == config4["plant"]["capacity"] assert out4["plant"]["num_turbines"] == config4["plant"]["num_turbines"] assert out4["turbine"]["turbine_rating"] == config4["turbine"][ "turbine_rating"] # Non matching calculated value config5 = { "plant": { "capacity": 700, "num_turbines": 40 }, "turbine": { "turbine_rating": 15.0 }, } with pytest.raises(AttributeError): _ = ProjectManager.resolve_project_capacity(config5) # Test for not enough information config6 = {"plant": {"capacity": 600}} out6 = ProjectManager.resolve_project_capacity(config6) assert out6["plant"]["capacity"] == config6["plant"]["capacity"] with pytest.raises(KeyError): _ = out6["turbine"]["turbine_rating"] with pytest.raises(KeyError): _ = out6["plant"]["num_turbines"]
def test_exceptions(): incomplete_config = deepcopy(config) _ = incomplete_config["site"].pop("depth") with pytest.raises(MissingInputs): project = ProjectManager(incomplete_config) project.run_project() wrong_phases = deepcopy(config) wrong_phases["install_phases"].append("IncorrectPhaseName") with pytest.raises(PhaseNotFound): project = ProjectManager(wrong_phases) project.run_project() bad_dates = deepcopy(config) bad_dates["install_phases"] = { "MonopileInstallation": "03/01/2015", "TurbineInstallation": "05/01/2015", } with pytest.raises(WeatherProfileError): project = ProjectManager(bad_dates, weather=weather_df) project.run_project()