Пример #1
0
def run(default_config: Dict) -> None:
    config, output_path, run_name = setup_run(default_config)
    recorder = DataRecorder.make_data_recorder(output_path)
    
    max_evaluations = config['max_evaluations']
    optimizer_config = config['optimizer_config']
    
    site_info = SiteInfo(flatirons_site)
    inner_problem = WindOptimizationProblem(site_info, config['num_turbines'])
    problem = WindParametrization(inner_problem)
    
    optimizer = ParametrizedOptimizationDriver(problem, recorder=recorder, **optimizer_config)
    
    figure = plt.figure(1)
    ax = figure.add_subplot(111)
    plt.grid()
    plt.tick_params(which='both', labelsize=15)
    plt.xlabel('x (m)', fontsize=15)
    plt.ylabel('y (m)', fontsize=15)
    site_info.plot()
    
    score, evaluation, best_solution = optimizer.central_solution()
    score, evaluation = problem.objective(best_solution) if score is None else score
    
    print(-1, ' ', score)
    
    optimizer.problem.plot_candidate(best_solution, (1.0, 0, 0), .2)
    
    prev = optimizer.best_solution()[1]
    try:
        while optimizer.num_evaluations() < max_evaluations:
            print('step start')
            optimizer.step()
            print('step end')

            proportion = min(1.0, optimizer.num_evaluations() / max_evaluations)
            g = 1.0 * proportion
            b = 1.0 - g
            a = .5
            color = (b, g, b)
            score, eval, best = optimizer.best_solution()
            score = problem.objective(best) if score is None else score
            problem.plot_candidate(best, color, .3)
            prev = best
            print(optimizer.num_iterations(), ' ', optimizer.num_evaluations(), score)

    except:
        raise RuntimeError("Optimizer error encountered. Try modifying the config to use larger generation_size if"
                           " encountering singular matrix errors.")

    print('best: ', optimizer.best_solution().__repr__())
    optimizer.problem.plot_candidate(optimizer.best_solution()[2], (0, 0, 0), 1.0)

    # Create the figure
    legend_elements = [Line2D([0], [0], marker='o', color='w', markerfacecolor=(0, 0, 0), label='Optimal')]
    plt.legend(handles=legend_elements)
    plt.show()
    
    optimizer.close()
Пример #2
0
def test_create_grid():
    site_info = SiteInfo(flatirons_site)
    bounding_shape = site_info.polygon.buffer(-200)
    site_info.plot()
    turbine_positions = create_grid(bounding_shape, site_info.polygon.centroid,
                                    np.pi / 4, 200, 200, .5)
    expected_positions = [[242., 497.], [383., 355.], [312.,
                                                       709.], [454., 568.],
                          [595., 426.], [525., 780.], [666., 638.],
                          [737., 850.], [878., 709.]]
    for n, t in enumerate(turbine_positions):
        assert (t.x == pytest.approx(expected_positions[n][0], 1e-1))
        assert (t.y == pytest.approx(expected_positions[n][1], 1e-1))
Пример #3
0
def init_simulation_pv():
    """
    Create the simulation object needed to calculate the objective of the problem

    :return: The HOPP simulation as defined for this problem
    """

    # Create the site for the design evaluation
    site = 'irregular'
    location = locations[3]

    if site == 'circular':
        site_data = make_circular_site(lat=location[0], lon=location[1], elev=location[2])
    elif site == 'irregular':
        site_data = make_irregular_site(lat=location[0], lon=location[1], elev=location[2])
    else:
        raise Exception("Unknown site '" + site + "'")

    # Load in weather and price data files
    solar_file = Path(
        __file__).parent.parent / "resource_files" / "solar" / WEATHER_FILE #"Beni_Miha" / "659265_32.69_10.90_2019.csv"
    grid_file = Path(__file__).parent.parent / "resource_files" / "grid" / PRICE_FILE #"tunisia_est_grid_prices.csv"

    # Combine the data into a site definition
    site_info = SiteInfo(site_data, solar_resource_file=solar_file, grid_resource_file=grid_file)

    # set up hybrid simulation with all the required parameters
    solar_size_mw = 200
    battery_capacity_mwh = 15
    battery_capacity_mw = 100
    interconnection_size_mw = 100

    technologies = {'pv': {'system_capacity_kw': solar_size_mw * 1000,
                           'array_type': 2,
                           'dc_ac_ratio': 1.1},
                    'battery': {'system_capacity_kwh': battery_capacity_mwh * 1000,
                                'system_capacity_kw': battery_capacity_mw * 1000},
                    'grid': interconnection_size_mw * 1000}

    # Create the hybrid plant simulation
    # TODO: turn these off to run full year simulation
    dispatch_options = {'is_test_start_year': False,
                        'is_test_end_year': False,
                        'solver': 'gurobi_ampl',
                        'grid_charging': False,  
                        'pv_charging_only': True}

    # TODO: turn-on receiver and field optimization before... initial simulation
    hybrid_plant = HybridSimulation(technologies,
                                    site_info,
                                    interconnect_kw=interconnection_size_mw * 1000,
                                    dispatch_options=dispatch_options)

    # Customize the hybrid plant assumptions here...
    hybrid_plant.pv.value('inv_eff', 95.0)
    hybrid_plant.pv.value('array_type', 0)
    hybrid_plant.pv.dc_degradation = [0] * 25

    return hybrid_plant
Пример #4
0
def site():
    solar_resource_file = Path(__file__).absolute(
    ).parent.parent.parent / "resource_files" / "solar" / "35.2018863_-101.945027_psmv3_60_2012.csv"
    wind_resource_file = Path(__file__).absolute(
    ).parent.parent.parent / "resource_files" / "wind" / "35.2018863_-101.945027_windtoolkit_2012_60min_80m_100m.srw"
    return SiteInfo(flatirons_site,
                    solar_resource_file=solar_resource_file,
                    wind_resource_file=wind_resource_file)
Пример #5
0
def test_ReOPT():

    lat = 39.7555
    lon = -105.2211

    # get resource and create model
    site = SiteInfo(flatirons_site)

    load = [1000 * (sin(x) + pi) for x in range(0, 8760)]
    urdb_label = "5ca4d1175457a39b23b3d45e"  # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898

    solar_model = PVPlant(site, {'system_capacity_kw': 20000})
    wind_model = WindPlant(site, {
        'num_turbines': 10,
        "turbine_rating_kw": 2000
    })
    wind_model._system_model.Resource.wind_resource_filename = os.path.join(
        "data", "39.7555_-105.2211_windtoolkit_2012_60min_60m.srw")
    fin_model = so.default("GenericSystemSingleOwner")

    fileout = os.path.join(filepath, "REoptResultsNoExportAboveLoad.json")

    reopt = REopt(lat=lat,
                  lon=lon,
                  load_profile=load,
                  urdb_label=urdb_label,
                  solar_model=solar_model,
                  wind_model=wind_model,
                  fin_model=fin_model,
                  interconnection_limit_kw=20000,
                  fileout=fileout)
    reopt.set_rate_path(os.path.join(filepath, 'data'))

    reopt_site = reopt.post['Scenario']['Site']
    pv = reopt_site['PV']
    assert (pv['dc_ac_ratio'] == pytest.approx(1.3, 0.01))
    wind = reopt_site['Wind']
    assert (wind['pbi_us_dollars_per_kwh'] == pytest.approx(0.015))

    results = reopt.get_reopt_results()
    assert (isinstance(results, dict))
    print(results["outputs"]["Scenario"]["Site"]["Wind"]
          ['year_one_to_grid_series_kw'])
    if 'error' in results['outputs']['Scenario']["status"]:
        if 'error' in results["messages"].keys():
            if 'Optimization exceeded timeout' in results["messages"]['error']:
                assert True
            else:
                print(results["messages"]['error'])
        elif 'warning' in results["messages"].keys():
            print(results["messages"]['warnings'])
            assert True
    else:
        assert (results["outputs"]["Scenario"]["Site"]["Wind"]["size_kw"] >= 0)

    os.remove(fileout)
Пример #6
0
def test_changing_turbine_rating():
    # powercurve scaling
    model = WindPlant(SiteInfo(flatirons_site), {
        'num_turbines': 24,
        "turbine_rating_kw": 2000
    })
    n_turbs = model.num_turbines
    for n in range(1000, 3000, 150):
        model.turb_rating = n
        assert model.system_capacity_kw == model.turb_rating * n_turbs, "system size error when rating is " + str(
            n)
Пример #7
0
def test_changing_system_capacity():
    # adjust number of turbines, system capacity won't be exactly as requested
    model = WindPlant(SiteInfo(flatirons_site), {
        'num_turbines': 20,
        "turbine_rating_kw": 1000
    })
    rating = model.turb_rating
    for n in range(1000, 20000, 1000):
        model.system_capacity_by_num_turbines(n)
        assert model.turb_rating == rating, str(n)
        assert model.system_capacity_kw == rating * round(n / rating)

    # adjust turbine rating first, system capacity will be exact
    model = WindPlant(SiteInfo(flatirons_site), {
        'num_turbines': 20,
        "turbine_rating_kw": 1000
    })
    for n in range(40000, 60000, 1000):
        model.system_capacity_by_rating(n)
        assert model.system_capacity_kw == pytest.approx(n)
Пример #8
0
def test_changing_n_turbines():
    # test with gridded layout
    model = WindPlant(SiteInfo(flatirons_site), {
        'num_turbines': 10,
        "turbine_rating_kw": 2000
    })
    assert (model.system_capacity_kw == 20000)
    for n in range(1, 20):
        model.num_turbines = n
        assert model.num_turbines == n, "n turbs should be " + str(n)
        assert model.system_capacity_kw == pytest.approx(
            20000, 1), "system capacity different when n turbs " + str(n)
Пример #9
0
def test_changing_rotor_diam_recalc():
    model = WindPlant(SiteInfo(flatirons_site), {
        'num_turbines': 10,
        "turbine_rating_kw": 2000
    })
    assert model.system_capacity_kw == 20000
    diams = range(50, 70, 140)
    for d in diams:
        model.rotor_diameter = d
        assert model.rotor_diameter == d, "rotor diameter should be " + str(d)
        assert model.turb_rating == 2000, "new rating different when rotor diameter is " + str(
            d)
Пример #10
0
def init_simulation_csp():
    """
    Create the simulation object needed to calculate the objective of the problem

    :return: The HOPP simulation as defined for this problem
    """

    # Create the site for the design evaluation
    site = 'irregular'
    location = locations[3]

    if site == 'circular':
        site_data = make_circular_site(lat=location[0], lon=location[1], elev=location[2])
    elif site == 'irregular':
        site_data = make_irregular_site(lat=location[0], lon=location[1], elev=location[2])
    else:
        raise Exception("Unknown site '" + site + "'")

    # Load in weather and price data files
    solar_file = Path(
        __file__).parent.parent / "resource_files" / "solar" / WEATHER_FILE #"Beni_Miha" / "659265_32.69_10.90_2019.csv"
    grid_file = Path(__file__).parent.parent / "resource_files" / "grid" / PRICE_FILE #"tunisia_est_grid_prices.csv"

    # Combine the data into a site definition
    site_info = SiteInfo(site_data, solar_resource_file=solar_file, grid_resource_file=grid_file)

    # set up hybrid simulation with all the required parameters
    tower_cycle_mw = 100
    interconnection_size_mw = 100

    technologies = {'tower': {'cycle_capacity_kw': tower_cycle_mw * 1000,
                              'solar_multiple': 2.0,
                              'tes_hours': 12.0,
                              'optimize_field_before_sim': True}, # TODO: turn on
                    'grid': interconnection_size_mw * 1000}

    # Create the hybrid plant simulation
    # TODO: turn these off to run full year simulation
    dispatch_options = {'is_test_start_year': False,
                        'is_test_end_year': False,
                        'solver': 'gurobi_ampl'}

    # TODO: turn-on receiver and field optimization before... initial simulation
    hybrid_plant = HybridSimulation(technologies,
                                    site_info,
                                    interconnect_kw=interconnection_size_mw * 1000,
                                    dispatch_options=dispatch_options)

    return hybrid_plant
Пример #11
0
def test_changing_powercurve():
    # with power curve recalculation requires diameter changes
    model = WindPlant(SiteInfo(flatirons_site), {
        'num_turbines': 24,
        "turbine_rating_kw": 2000
    })
    n_turbs = model.num_turbines
    d_to_r = model.rotor_diameter / model.turb_rating
    for n in range(1000, 3001, 500):
        d = math.ceil(n * d_to_r * 1)
        model.modify_powercurve(d, n)
        assert model.turb_rating == pytest.approx(
            n, 0.1), "turbine rating should be " + str(n)
        assert model.system_capacity_kw == pytest.approx(
            model.turb_rating * n_turbs,
            0.1), "size error when rating is " + str(n)
Пример #12
0
def test_ReOPT():

    lat = 39.7555
    lon = -105.2211

    # get resource and create model
    site = SiteInfo(flatirons_site)

    load = [1000*(sin(x) + pi)for x in range(0, 8760)]
    urdb_label = "5ca4d1175457a39b23b3d45e" # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898


    solar_model = SolarPlant(site, 20000)
    wind_model = WindPlant(site, 20000)
    wind_model.system_model.Resource.wind_resource_filename = os.path.join(
        "data", "39.7555_-105.2211_windtoolkit_2012_60min_60m.srw")
    fin_model = so.default("GenericSystemSingleOwner")

    reopt = REopt(lat=lat,
                  lon=lon,
                  load_profile=load,
                  urdb_label=urdb_label,
                  solar_model=solar_model,
                  wind_model=wind_model,
                  fin_model=fin_model,
                  interconnection_limit_kw=20000,
                  fileout=os.path.join(filepath, "data", "REoptResultsNoExportAboveLoad.json"))
    reopt.set_rate_path(os.path.join(filepath, 'data'))

    reopt_site = reopt.post['Scenario']['Site']
    pv = reopt_site['PV']
    assert(pv['dc_ac_ratio'] == pytest.approx(1.2, 0.01))
    wind = reopt_site['Wind']
    assert(wind['pbi_us_dollars_per_kwh'] == pytest.approx(0.022))

    results = reopt.get_reopt_results(force_download=True)
    assert(isinstance(results, dict))
    print(results["outputs"]["Scenario"]["Site"]["Wind"]['year_one_to_grid_series_kw'])
    assert (results["outputs"]["Scenario"]["Site"]["Wind"]["size_kw"] == pytest.approx(20000, 1))
    assert(results["outputs"]["Scenario"]["Site"]["Financial"]["lcc_us_dollars"] == pytest.approx(17008573.0, 1))
    assert(results["outputs"]["Scenario"]["Site"]["Financial"]["lcc_bau_us_dollars"] == pytest.approx(15511546.0, 1))
    assert(results["outputs"]["Scenario"]["Site"]["ElectricTariff"]["year_one_export_benefit_us_dollars"] == pytest.approx(-15158711.0, 1))
Пример #13
0
NREL_API_KEY = os.getenv("NREL_API_KEY")
set_developer_nrel_gov_key(NREL_API_KEY)  # Set this key manually here if you are not setting it using the .env

# Set wind, solar, and interconnection capacities (in MW)
solar_size_mw = 20
wind_size_mw = 20
interconnection_size_mw = 20

technologies = {'solar': solar_size_mw,  # mw system capacity
                'wind': wind_size_mw,  # mw system capacity
                'grid': interconnection_size_mw}

# Get resource
lat = flatirons_site['lat']
lon = flatirons_site['lon']
site = SiteInfo(flatirons_site)

# Create model
hybrid_plant = HybridSimulation(technologies, site, interconnect_kw=interconnection_size_mw * 1000)

# Setup cost model
hybrid_plant.setup_cost_calculator(create_cost_calculator(interconnection_size_mw))
hybrid_plant.solar.system_capacity_kw = solar_size_mw * 1000
hybrid_plant.wind.system_capacity_by_num_turbines(wind_size_mw * 1000)
hybrid_plant.ppa_price = 0.1
hybrid_plant.simulate(25)

# Save the outputs
annual_energies = hybrid_plant.annual_energies
wind_plus_solar_npv = hybrid_plant.net_present_values.wind + hybrid_plant.net_present_values.solar
npvs = hybrid_plant.net_present_values
Пример #14
0
def init_hybrid_plant():
    """
    Initialize hybrid simulation object using specific project inputs
    :return: HybridSimulation as defined for this problem
    """
    is_test = False  # Turns off full year dispatch and optimize tower and receiver
    
    techs_in_sim = ['tower',
                    'pv',
                    'battery',
                    'grid']

    site_data = {
        "lat": 34.85,
        "lon": -116.9,
        "elev": 641,
        "year": 2012,
        "tz": -8,
        "no_wind": True
        }

    root = "C:/Users/WHamilt2/Documents/Projects/HOPP/CSP_PV_battery_dispatch_plots/"
    solar_file = root + "34.865371_-116.783023_psmv3_60_tmy.csv"
    prices_file = root + "constant_nom_prices.csv"
    schedule_scale = 100  # MWe
    desired_schedule_file = root + "sample_load_profile_normalized.csv"
    # Reading in desired schedule
    with open(desired_schedule_file) as f:
        csvreader = csv.reader(f)
        desired_schedule = []
        for row in csvreader:
            desired_schedule.append(float(row[0])*schedule_scale)

    # If normalized pricing is used, then PPA price must be adjusted after HybridSimulation is initialized
    site = SiteInfo(site_data,
                    solar_resource_file=solar_file,
                    grid_resource_file=prices_file,
                    desired_schedule=desired_schedule
                    )

    technologies = {'tower': {
                        'cycle_capacity_kw':  100 * 1000, #100 * 1000,
                        'solar_multiple': 3.0, #2.0,
                        'tes_hours': 16.0, #16.0,
                        'optimize_field_before_sim': not is_test,
                        'scale_input_params': True,
                        },
                    'trough': {
                        'cycle_capacity_kw': 100 * 1000,
                        'solar_multiple': 4.0,
                        'tes_hours': 20.0
                    },
                    'pv': {
                        'system_capacity_kw': 50 * 1000
                        },
                    'battery': {
                        'system_capacity_kwh': 300 * 1000,
                        'system_capacity_kw': 100 * 1000
                        },
                    'grid': 150 * 1000}

    # Create model
    hybrid_plant = HybridSimulation({key: technologies[key] for key in techs_in_sim}, 
                                    site,
                                    interconnect_kw=technologies['grid'],
                                    dispatch_options={
                                        'is_test_start_year': is_test,
                                        'is_test_end_year': is_test,
                                        'solver': 'xpress',
                                        'grid_charging': False,
                                        'pv_charging_only': True,
                                        },
                                    simulation_options={
                                        'storage_capacity_credit': False,
                                    }
                                    )

    # Defaults:
    # {'cost_per_field_generation': 0.5,
    #  'cost_per_field_start_rel': 1.5,
    #  'cost_per_cycle_generation': 2.0,
    #  'cost_per_cycle_start_rel': 40.0,
    #  'cost_per_change_thermal_input': 0.5}

    csp_dispatch_obj_costs = dict()
    csp_dispatch_obj_costs = {
                              'cost_per_field_generation': 0.0, #0.5,
    #                           'cost_per_field_start_rel': 0.0,
    #                           'cost_per_cycle_generation': 2.0,
                              'cost_per_cycle_start_rel': 0.0,
                              'cost_per_change_thermal_input': 0.5}

    # Set CSP costs
    if hybrid_plant.tower:
        hybrid_plant.tower.dispatch.objective_cost_terms.update(csp_dispatch_obj_costs)
        hybrid_plant.tower.value('cycle_max_frac', 1.0)
    if hybrid_plant.trough:
        hybrid_plant.trough.dispatch.objective_cost_terms.update(csp_dispatch_obj_costs)
        hybrid_plant.trough.value('cycle_max_frac', 1.0)

    # if hybrid_plant.battery:
    #     hybrid_plant.battery.dispatch.lifecycle_cost_per_kWh_cycle = 0.0265 / 100
    #     # hybrid_plant.battery.dispatch.lifecycle_cost_per_kWh_cycle = 1e-6

    if hybrid_plant.pv:
        hybrid_plant.pv.dc_degradation = [0.5] * 25
        hybrid_plant.pv.value('array_type', 2)  # 1-axis tracking
        hybrid_plant.pv.value('tilt', 0)        # Tilt for 1-axis

    # This is required if normalized prices are provided
    hybrid_plant.ppa_price = (0.12,)  # $/kWh

    return hybrid_plant
Пример #15
0
def run_hopp_calc(Site, scenario_description, bos_details,
                  total_hybrid_plant_capacity_mw, solar_size_mw, wind_size_mw,
                  nameplate_mw, interconnection_size_mw,
                  load_resource_from_file, ppa_price, results_dir):
    """ run_hopp_calc Establishes sizing models, creates a wind or solar farm based on the desired sizes,
     and runs SAM model calculations for the specified inputs.
     save_outputs contains a dictionary of all results for the hopp calculation.

    :param scenario_description: Project scenario - 'greenfield' or 'solar addition'.
    :param bos_details: contains bos details including type of analysis to conduct (cost/mw, json lookup, HybridBOSSE).
    :param total_hybrid_plant_capacity_mw: capacity in MW of hybrid plant.
    :param solar_size_mw: capacity in MW of solar component of plant.
    :param wind_size_mw: capacity in MW of wind component of plant.
    :param nameplate_mw: nameplate capacity of total plant.
    :param interconnection_size_mw: interconnection size in MW.
    :param load_resource_from_file: flag determining whether resource is loaded directly from file or through
     interpolation routine.
    :param ppa_price: PPA price in USD($)
    :return: collection of outputs from SAM and hybrid-specific calculations (includes e.g. AEP, IRR, LCOE),
     plus wind and solar filenames used
    (save_outputs)
    """
    # Get resource data
    if load_resource_from_file:
        pass
    else:
        Site[
            'resource_filename_solar'] = ""  # Unsetting resource filename to force API download of wind resource
        Site[
            'resource_filename_wind'] = ""  # Unsetting resource filename to force API download of solar resource

    site = SiteInfo(Site,
                    solar_resource_file=sample_site['resource_filename_solar'],
                    wind_resource_file=sample_site['resource_filename_wind'])

    #TODO: Incorporate this in SiteInfo
    # if 'roll_tz' in Site.keys():
    #     site.solar_resource.roll_timezone(Site['roll_tz'], Site['roll_tz'])

    # Set up technology and cost model info
    technologies = {
        'solar': solar_size_mw,  # mw system capacity
        'wind': wind_size_mw,  # mw system capacity
        'grid': interconnection_size_mw
    }  # mw interconnect

    # Create model
    hybrid_plant = HybridSimulation(technologies,
                                    site,
                                    interconnect_kw=interconnection_size_mw *
                                    1000)

    hybrid_plant.setup_cost_calculator(
        create_cost_calculator(interconnection_size_mw,
                               bos_details['BOSSource'], scenario_description))

    hybrid_plant.ppa_price = ppa_price
    hybrid_plant.discount_rate = 6.4
    hybrid_plant.solar.system_capacity_kw = solar_size_mw * 1000
    hybrid_plant.wind.system_capacity_by_num_turbines(wind_size_mw * 1000)
    actual_solar_pct = hybrid_plant.solar.system_capacity_kw / \
                       (hybrid_plant.solar.system_capacity_kw + hybrid_plant.wind.system_capacity_kw)

    logger.info("Run with solar percent {}".format(actual_solar_pct))
    hybrid_plant.simulate()
    outputs = hybrid_plant.hybrid_outputs()
    for k, v in outputs.items():
        outputs[k] = [v]

    return outputs, site.wind_resource.filename, site.solar_resource.filename
Пример #16
0
wind_size_mw = 50
interconnection_size_mw = 50

technologies = {'pv': {
                    'system_capacity_kw': solar_size_mw * 1000
                },
                'wind': {
                    'num_turbines': 10,
                    'turbine_rating_kw': 2000
                }}

# Get resource
lat = flatirons_site['lat']
lon = flatirons_site['lon']
prices_file = examples_dir.parent / "resource_files" / "grid" / "pricing-data-2015-IronMtn-002_factors.csv"
site = SiteInfo(flatirons_site, grid_resource_file=prices_file)

# Create model
hybrid_plant = HybridSimulation(technologies, site, interconnect_kw=interconnection_size_mw * 1000)

hybrid_plant.pv.system_capacity_kw = solar_size_mw * 1000
hybrid_plant.wind.system_capacity_by_num_turbines(wind_size_mw * 1000)
hybrid_plant.ppa_price = 0.1
hybrid_plant.pv.dc_degradation = [0] * 25
hybrid_plant.simulate(25)

# Save the outputs
annual_energies = hybrid_plant.annual_energies
wind_plus_solar_npv = hybrid_plant.net_present_values.wind + hybrid_plant.net_present_values.pv
npvs = hybrid_plant.net_present_values
Пример #17
0
def setup_power_calcs(scenario, solar_size_mw, storage_size_mwh,
                      storage_size_mw, interconnection_size_mw):
    """
    A function to facilitate plant setup for POWER calculations, assuming one wind turbine.
    
    INPUT VARIABLES
    scenario: dict, the H2 scenario of interest
    solar_size_mw: float, the amount of solar capacity in MW
    storage_size_mwh: float, the amount of battery storate capacity in MWh
    storage_size_mw: float, the amount of battery storate capacity in MW
    interconnection_size_mw: float, the interconnection size in MW

    OUTPUTS
    hybrid_plant: the hybrid plant object from HOPP for power calculations
    """

    # Set API key
    load_dotenv()
    NREL_API_KEY = os.getenv("NREL_API_KEY")
    set_developer_nrel_gov_key(
        NREL_API_KEY
    )  # Set this key manually here if you are not setting it using the .env

    # Step 1: Establish output structure and special inputs
    year = 2013
    sample_site['year'] = year
    useful_life = 30
    custom_powercurve = True
    electrolyzer_size = 50000

    sample_site['lat'] = scenario['Lat']
    sample_site['lon'] = scenario['Long']
    tower_height = scenario['Tower Height']

    scenario['Useful Life'] = useful_life

    site = SiteInfo(sample_site, hub_height=tower_height)

    technologies = {
        'pv': {
            'system_capacity_kw': solar_size_mw * 1000
        },
        'wind': {
            'num_turbines': 1,
            'turbine_rating_kw': scenario['Turbine Rating'] * 1000,
            'hub_height': scenario['Tower Height'],
            'rotor_diameter': scenario['Rotor Diameter']
        },
        'grid': electrolyzer_size,
        'battery': {
            'system_capacity_kwh': storage_size_mwh * 1000,
            'system_capacity_kw': storage_size_mw * 1000
        }
    }
    dispatch_options = {'battery_dispatch': 'heuristic'}
    hybrid_plant = HybridSimulation(technologies,
                                    site,
                                    interconnect_kw=electrolyzer_size,
                                    dispatch_options=dispatch_options)

    hybrid_plant.wind._system_model.Turbine.wind_resource_shear = 0.33

    if custom_powercurve:
        powercurve_file = open(scenario['Powercurve File'])
        powercurve_data = json.load(powercurve_file)
        powercurve_file.close()
        hybrid_plant.wind._system_model.Turbine.wind_turbine_powercurve_windspeeds = \
            powercurve_data['turbine_powercurve_specification']['wind_speed_ms']
        hybrid_plant.wind._system_model.Turbine.wind_turbine_powercurve_powerout = \
            powercurve_data['turbine_powercurve_specification']['turbine_power_output']

    hybrid_plant.pv.system_capacity_kw = solar_size_mw * 1000

    return hybrid_plant
Пример #18
0
def init_hybrid_plant(techs_in_sim: list,
                      is_test: bool = False,
                      ud_techs: dict = {}):
    """
    Initialize hybrid simulation object using specific project inputs
    :param techs_in_sim: List of technologies to include in the simulation
    :param is_test: if True, runs dispatch for the first and last 5 days of the year
        and turns off tower and receiver optimization
    :param ud_techs: Dictionary containing technology initialization parameters required by HybridSimulation

    :return: HybridSimulation as defined for this problem
    """
    schedule_scale = 100  # MWe
    grid_interconnect_mw = 100  # MWe
    example_root = get_example_path_root()

    # Set plant location
    site_data = {
        "lat": 34.8653,
        "lon": -116.7830,
        "elev": 561,
        "tz": 1,
        "no_wind": True
    }
    solar_file = example_root + "02_weather_data/daggett_ca_34.865371_-116.783023_psmv3_60_tmy.csv"
    prices_file = example_root + "03_cost_load_price_data/constant_norm_prices.csv"
    desired_schedule_file = example_root + "03_cost_load_price_data/desired_schedule_normalized.csv"
    # Reading in desired schedule
    with open(desired_schedule_file) as f:
        csvreader = csv.reader(f)
        desired_schedule = []
        for row in csvreader:
            desired_schedule.append(float(row[0]) * schedule_scale)

    # If normalized pricing is used, then PPA price must be adjusted after HybridSimulation is initialized
    site = SiteInfo(site_data,
                    solar_resource_file=solar_file,
                    grid_resource_file=prices_file,
                    desired_schedule=desired_schedule)

    # Load in system costs
    with open(example_root +
              "03_cost_load_price_data/system_costs_SAM.json") as f:
        cost_info = json.load(f)

    # Initializing technologies
    if ud_techs:
        technologies = ud_techs
    else:
        technologies = {
            'tower': {
                'cycle_capacity_kw': 200 * 1000,  #100
                'solar_multiple': 4.0,  #2.0
                'tes_hours': 20.0,  #14
                'optimize_field_before_sim': not is_test,
                'scale_input_params': True,
            },
            'trough': {
                'cycle_capacity_kw': 200 * 1000,
                'solar_multiple': 6.0,
                'tes_hours': 28.0
            },
            'pv': {
                'system_capacity_kw': 120 * 1000
            },
            'battery': {
                'system_capacity_kwh': 200 * 1000,
                'system_capacity_kw': 100 * 1000
            },
            'grid': grid_interconnect_mw * 1000
        }

    # Create hybrid simulation class based on the technologies needed in the simulation
    sim_techs = {key: technologies[key] for key in techs_in_sim}
    sim_techs['grid'] = technologies['grid']

    hybrid_plant = HybridSimulation(sim_techs,
                                    site,
                                    interconnect_kw=technologies['grid'],
                                    dispatch_options={
                                        'is_test_start_year': is_test,
                                        'is_test_end_year': is_test,
                                        'solver': 'cbc',
                                        'grid_charging': False,
                                        'pv_charging_only': True
                                    },
                                    cost_info=cost_info['cost_info'])

    csp_dispatch_obj_costs = {
        'cost_per_field_generation': 0.5,
        'cost_per_field_start_rel': 0.0,
        'cost_per_cycle_generation': 2.0,
        'cost_per_cycle_start_rel': 0.0,
        'cost_per_change_thermal_input': 0.5
    }

    # Set CSP costs
    if hybrid_plant.tower:
        hybrid_plant.tower.ssc.set(cost_info['tower_costs'])
        hybrid_plant.tower.dispatch.objective_cost_terms = csp_dispatch_obj_costs
    if hybrid_plant.trough:
        hybrid_plant.trough.ssc.set(cost_info['trough_costs'])
        hybrid_plant.trough.dispatch.objective_cost_terms = csp_dispatch_obj_costs

    # Set O&M costs for all technologies
    for tech in ['tower', 'trough', 'pv', 'battery']:
        if not tech in techs_in_sim:
            cost_info["SystemCosts"].pop(tech)

    hybrid_plant.assign(cost_info["SystemCosts"])

    # Set financial parameters for singleowner model
    with open(example_root +
              '03_cost_load_price_data/financial_parameters_SAM.json') as f:
        fin_info = json.load(f)

    hybrid_plant.assign(fin_info["FinancialParameters"])
    hybrid_plant.assign(fin_info["TaxCreditIncentives"])
    hybrid_plant.assign(fin_info["Revenue"])
    hybrid_plant.assign(fin_info["Depreciation"])
    hybrid_plant.assign(fin_info["PaymentIncentives"])

    # Set specific technology assumptions here
    if hybrid_plant.pv:
        hybrid_plant.pv.dc_degradation = [0.5] * 25
        hybrid_plant.pv.value('array_type', 2)  # 1-axis tracking
        hybrid_plant.pv.value('tilt', 0)  # Tilt for 1-axis

    # This is required if normalized prices are provided
    hybrid_plant.ppa_price = (0.10, )  # $/kWh

    return hybrid_plant
Пример #19
0
def test_capacity_credit(site):
    site = SiteInfo(data=flatirons_site,
                    solar_resource_file=solar_resource_file,
                    wind_resource_file=wind_resource_file,
                    capacity_hours=capacity_credit_hours)
    wind_pv_battery = {
        key: technologies[key]
        for key in ('pv', 'wind', 'battery')
    }
    hybrid_plant = HybridSimulation(wind_pv_battery,
                                    site,
                                    interconnect_kw=interconnection_size_kw)
    hybrid_plant.battery.dispatch.lifecycle_cost_per_kWh_cycle = 0.01
    hybrid_plant.ppa_price = (0.03, )
    hybrid_plant.pv.dc_degradation = [0] * 25

    # Backup values for resetting before tests
    gen_max_feasible_orig = hybrid_plant.battery.gen_max_feasible
    capacity_hours_orig = hybrid_plant.site.capacity_hours
    interconnect_kw_orig = hybrid_plant.interconnect_kw

    def reinstate_orig_values():
        hybrid_plant.battery.gen_max_feasible = gen_max_feasible_orig
        hybrid_plant.site.capacity_hours = capacity_hours_orig
        hybrid_plant.interconnect_kw = interconnect_kw_orig

    # Test when 0 gen_max_feasible
    reinstate_orig_values()
    hybrid_plant.battery.gen_max_feasible = [0] * 8760
    capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent(
        hybrid_plant.interconnect_kw)
    assert capacity_credit_battery == approx(0, rel=0.05)
    # Test when representative gen_max_feasible
    reinstate_orig_values()
    hybrid_plant.battery.gen_max_feasible = [2500] * 8760
    capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent(
        hybrid_plant.interconnect_kw)
    assert capacity_credit_battery == approx(50, rel=0.05)
    # Test when no capacity hours
    reinstate_orig_values()
    hybrid_plant.battery.gen_max_feasible = [2500] * 8760
    hybrid_plant.site.capacity_hours = [False] * 8760
    capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent(
        hybrid_plant.interconnect_kw)
    assert capacity_credit_battery == approx(0, rel=0.05)
    # Test when no interconnect capacity
    reinstate_orig_values()
    hybrid_plant.battery.gen_max_feasible = [2500] * 8760
    hybrid_plant.interconnect_kw = 0
    capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent(
        hybrid_plant.interconnect_kw)
    assert capacity_credit_battery == approx(0, rel=0.05)

    # Test integration with system simulation
    reinstate_orig_values()
    cap_payment_mw = 100000
    hybrid_plant.assign({"cp_capacity_payment_amount": [cap_payment_mw]})

    hybrid_plant.simulate()

    total_gen_max_feasible = np.array(hybrid_plant.pv.gen_max_feasible) \
                           + np.array(hybrid_plant.wind.gen_max_feasible) \
                           + np.array(hybrid_plant.battery.gen_max_feasible)
    assert sum(hybrid_plant.grid.gen_max_feasible) == approx(sum(np.minimum(hybrid_plant.grid.interconnect_kw * hybrid_plant.site.interval / 60, \
                                                                            total_gen_max_feasible)), rel=0.01)

    total_nominal_capacity = hybrid_plant.pv.calc_nominal_capacity(hybrid_plant.interconnect_kw) \
                           + hybrid_plant.wind.calc_nominal_capacity(hybrid_plant.interconnect_kw) \
                           + hybrid_plant.battery.calc_nominal_capacity(hybrid_plant.interconnect_kw)
    assert total_nominal_capacity == approx(18845.8, rel=0.01)
    assert total_nominal_capacity == approx(
        hybrid_plant.grid.hybrid_nominal_capacity, rel=0.01)

    capcred = hybrid_plant.capacity_credit_percent
    assert capcred['pv'] == approx(8.03, rel=0.05)
    assert capcred['wind'] == approx(33.25, rel=0.10)
    assert capcred['battery'] == approx(58.95, rel=0.05)
    assert capcred['hybrid'] == approx(43.88, rel=0.05)

    cp_pay = hybrid_plant.capacity_payments
    np_cap = hybrid_plant.system_nameplate_mw  # This is not the same as nominal capacity...
    assert cp_pay['pv'][1] / (np_cap['pv']) / (capcred['pv'] / 100) == approx(
        cap_payment_mw, 0.05)
    assert cp_pay['wind'][1] / (np_cap['wind']) / (capcred['wind'] /
                                                   100) == approx(
                                                       cap_payment_mw, 0.05)
    assert cp_pay['battery'][1] / (np_cap['battery']) / (capcred['battery'] /
                                                         100) == approx(
                                                             cap_payment_mw,
                                                             0.05)
    assert cp_pay['hybrid'][1] / (np_cap['hybrid']) / (capcred['hybrid'] /
                                                       100) == approx(
                                                           cap_payment_mw,
                                                           0.05)

    aeps = hybrid_plant.annual_energies
    assert aeps.pv == approx(9882421, rel=0.05)
    assert aeps.wind == approx(33637983, rel=0.05)
    assert aeps.battery == approx(-31287, rel=0.05)
    assert aeps.hybrid == approx(43489117, rel=0.05)

    npvs = hybrid_plant.net_present_values
    assert npvs.pv == approx(-565098, rel=5e-2)
    assert npvs.wind == approx(-1992106, rel=5e-2)
    assert npvs.battery == approx(-4773045, rel=5e-2)
    assert npvs.hybrid == approx(-5849767, rel=5e-2)

    taxes = hybrid_plant.federal_taxes
    assert taxes.pv[1] == approx(86826, rel=5e-2)
    assert taxes.wind[1] == approx(348124, rel=5e-2)
    assert taxes.battery[1] == approx(239607, rel=5e-2)
    assert taxes.hybrid[1] == approx(633523, rel=5e-2)

    apv = hybrid_plant.energy_purchases_values
    assert apv.pv[1] == approx(0, rel=5e-2)
    assert apv.wind[1] == approx(0, rel=5e-2)
    assert apv.battery[1] == approx(40158, rel=5e-2)
    assert apv.hybrid[1] == approx(2980, rel=5e-2)

    debt = hybrid_plant.debt_payment
    assert debt.pv[1] == approx(0, rel=5e-2)
    assert debt.wind[1] == approx(0, rel=5e-2)
    assert debt.battery[1] == approx(0, rel=5e-2)
    assert debt.hybrid[1] == approx(0, rel=5e-2)

    esv = hybrid_plant.energy_sales_values
    assert esv.pv[1] == approx(353105, rel=5e-2)
    assert esv.wind[1] == approx(956067, rel=5e-2)
    assert esv.battery[1] == approx(80449, rel=5e-2)
    assert esv.hybrid[1] == approx(1352445, rel=5e-2)

    depr = hybrid_plant.federal_depreciation_totals
    assert depr.pv[1] == approx(762811, rel=5e-2)
    assert depr.wind[1] == approx(2651114, rel=5e-2)
    assert depr.battery[1] == approx(1486921, rel=5e-2)
    assert depr.hybrid[1] == approx(4900847, rel=5e-2)

    insr = hybrid_plant.insurance_expenses
    assert insr.pv[0] == approx(0, rel=5e-2)
    assert insr.wind[0] == approx(0, rel=5e-2)
    assert insr.battery[0] == approx(0, rel=5e-2)
    assert insr.hybrid[0] == approx(0, rel=5e-2)

    om = hybrid_plant.om_total_expenses
    assert om.pv[1] == approx(74993, rel=5e-2)
    assert om.wind[1] == approx(420000, rel=5e-2)
    assert om.battery[1] == approx(75000, rel=5e-2)
    assert om.hybrid[1] == approx(569993, rel=5e-2)

    rev = hybrid_plant.total_revenues
    assert rev.pv[1] == approx(393226, rel=5e-2)
    assert rev.wind[1] == approx(1288603, rel=5e-2)
    assert rev.battery[1] == approx(375215, rel=5e-2)
    assert rev.hybrid[1] == approx(2229976, rel=5e-2)

    tc = hybrid_plant.tax_incentives
    assert tc.pv[1] == approx(1123104, rel=5e-2)
    assert tc.wind[1] == approx(504569, rel=5e-2)
    assert tc.battery[1] == approx(0, rel=5e-2)
    assert tc.hybrid[1] == approx(1646170, rel=5e-2)
Пример #20
0
def run_hopp_calc(Site, scenario_description, bos_details, total_hybrid_plant_capacity_mw, solar_size_mw, wind_size_mw,
                    nameplate_mw, interconnection_size_mw, load_resource_from_file,
                    ppa_price, results_dir):
    """ run_hopp_calc Establishes sizing models, creates a wind or solar farm based on the desired sizes,
     and runs SAM model calculations for the specified inputs.
     save_outputs contains a dictionary of all results for the hopp calculation.

    :param scenario_description: Project scenario - 'greenfield' or 'solar addition'.
    :param bos_details: contains bos details including type of analysis to conduct (cost/mw, json lookup, HybridBOSSE).
    :param total_hybrid_plant_capacity_mw: capacity in MW of hybrid plant.
    :param solar_size_mw: capacity in MW of solar component of plant.
    :param wind_size_mw: capacity in MW of wind component of plant.
    :param nameplate_mw: nameplate capacity of total plant.
    :param interconnection_size_mw: interconnection size in MW.
    :param load_resource_from_file: flag determining whether resource is loaded directly from file or through
     interpolation routine.
    :param ppa_price: PPA price in USD($)
    :return: collection of outputs from SAM and hybrid-specific calculations (includes e.g. AEP, IRR, LCOE),
     plus wind and solar filenames used
    (save_outputs)
    """
    # Get resource data
    # sample_site['lat'] = Site['Lat']
    # sample_site['lon'] = Site['Lon']
    # # site = SiteInfo(sample_site, solar_resource_file=Site['resource_filename_solar'],
    # #                 wind_resource_file=Site['resource_filename_wind'])
    if load_resource_from_file:
        pass
    else:
        Site['resource_filename_solar'] = ""  # Unsetting resource filename to force API download of wind resource
        Site['resource_filename_wind'] = ""  # Unsetting resource filename to force API download of solar resource

    site = SiteInfo(Site)

    if 'roll_tz' in Site.keys():
        site.solar_resource.roll_timezone(Site['roll_tz'], Site['roll_tz'])

    # Set up technology and cost model info
    technologies = {'solar': solar_size_mw,          # mw system capacity
                    'wind': wind_size_mw            # mw system capacity
                    }

    # Create model
    hybrid_plant = HybridSimulation(technologies, site, interconnect_kw=interconnection_size_mw * 1000)

    # hybrid_plant.setup_cost_calculator(create_cost_calculator(bos_cost_source='boslookup', interconnection_mw=interconnection_size_mw))
    hybrid_plant.setup_cost_calculator(create_cost_calculator(bos_cost_source=bos_details['BOSSource'],
                                                                    interconnection_mw=interconnection_size_mw,
                                                              modify_costs=bos_details['Modify Costs'],
                                                              cost_reductions=bos_details,
                                                              wind_installed_cost_mw=1696000,
                                                              solar_installed_cost_mw=1088600,
                                                              storage_installed_cost_mw=0,
                                                              storage_installed_cost_mwh=0,
                                                              ))

    hybrid_plant.ppa_price = ppa_price
    hybrid_plant.discount_rate = 6.4
    hybrid_plant.solar.system_capacity_kw = solar_size_mw * 1000
    hybrid_plant.wind.rotor_diameter = 100
    # Modify Wind Turbine Coordinates
    Nx = 8
    Ny = 5
    spacing = hybrid_plant.wind.row_spacing
    turbine_x = np.linspace(0, (Nx * spacing) - spacing, Nx)
    turbine_y = np.linspace(0, (Ny * spacing) - spacing, Ny)
    turbX = np.zeros(Nx * Ny)
    turbY = np.zeros(Nx * Ny)
    count = 0
    for i in range(Nx):
        for j in range(Ny):
            turbX[count] = turbine_x[i]
            turbY[count] = turbine_y[j]
            count = count + 1
    hybrid_plant.wind.modify_coordinates(list(turbX), list(turbY))
    hybrid_plant.wind.system_capacity_by_num_turbines(wind_size_mw * 1000)
    actual_solar_pct = hybrid_plant.solar.system_capacity_kw / \
                       (hybrid_plant.solar.system_capacity_kw + hybrid_plant.wind.system_capacity_kw)

    logger.info("Run with solar percent {}".format(actual_solar_pct))
    hybrid_plant.simulate()
    outputs = hybrid_plant.hybrid_outputs()
    for k, v in outputs.items():
        outputs[k] = [v]

    return outputs, site.wind_resource.filename, site.solar_resource.filename
Пример #21
0
def site():
    return SiteInfo(flatirons_site,
                    solar_resource_file=solar_resource_file,
                    wind_resource_file=wind_resource_file)
Пример #22
0
def h2_main():
    """
    Runs a Hydrogen Levelized Cost (HLC/LCOH) analysis for scenarios contained in "default_h2_scenarios.csv"
    """


    # Step 1: Establish output structure and special inputs
    # save_all_runs = pd.DataFrame()
    save_outputs_dict = establish_save_output_dict()
    year = 2013
    sample_site['year'] = year
    useful_life = 30
    critical_load_factor_list = [1]
    run_reopt_flag = False
    custom_powercurve = True
    storage_used = True
    battery_can_grid_charge = False
    grid_connected_hopp = False
    interconnection_size_mw = 100
    electrolyzer_sizes = [50]

    # which plots to show
    plot_power_production = False
    plot_battery = False
    plot_grid = False
    plot_h2 = False
    plot_reopt = False

    # Step 2: Load scenarios from .csv and enumerate
    # scenarios_df = pd.read_csv('H2 Baseline Future Scenarios Test Refactor.csv')
    parent_path = os.path.abspath(os.path.dirname(__file__))
    scenarios_df = pd.read_csv(os.path.join(parent_path,'default_h2_scenarios.csv'))
    for electrolyzer_size in electrolyzer_sizes:
        for critical_load_factor in critical_load_factor_list:
            for i, scenario in scenarios_df.iterrows():
                # TODO: Make scenario_choice, lookup all other values from dataframe from csv.

                # TODO:
                # -Pass through rotor diameter to pySAM
                # -Add wind, solar, storage installed costs
                # -Fix "H2 H2 xxx" text
                # print(scenario)

                kw_continuous = electrolyzer_size*1000
                load = [kw_continuous for x in
                        range(0, 8760)]  # * (sin(x) + pi) Set desired/required load profile for plant

                scenario_choice = scenario['Scenario Name']
                site_name = scenario['Site Name']
                sample_site['lat'] = scenario['Lat']
                sample_site['lon'] = scenario['Long']
                lat = scenario['Lat']
                lon = scenario['Long']
                atb_year = scenario['ATB Year']
                ptc_avail = scenario['PTC Available']
                itc_avail = scenario['ITC Available']
                forced_sizes = scenario['Force Plant Size']
                force_electrolyzer_cost = scenario['Force Electrolyzer Cost']
                if forced_sizes:
                    forced_wind_size = scenario['Wind Size MW']
                    forced_solar_size = scenario['Solar Size MW']
                    forced_storage_size_mw = scenario['Storage Size MW']
                    forced_storage_size_mwh = scenario['Storage Size MWh']
                else:
                    print("Using ReOPT for sizing. REopt will be turned on and may not find a solution")
                    run_reopt_flag = True

                if force_electrolyzer_cost:
                    forced_electrolyzer_cost = scenario['Electrolyzer Cost KW']

                tower_height = scenario['Tower Height']
                rotor_diameter = scenario['Rotor Diameter']
                turbine_rating = scenario['Turbine Rating']
                wind_cost_kw = scenario['Wind Cost KW']
                custom_powercurve_path = scenario['Powercurve File']
                solar_cost_kw = scenario['Solar Cost KW']
                storage_cost_kw = scenario['Storage Cost KW']
                storage_cost_kwh = scenario['Storage Cost KWh']
                debt_equity_split = scenario['Debt Equity']

                buy_price = scenario['Buy From Grid ($/kWh)']
                sell_price = scenario['Sell To Grid ($/kWh)']

                #Todo: Add useful life to .csv scenario input instead
                scenario['Useful Life'] = useful_life

                site = SiteInfo(sample_site, hub_height=tower_height)

                # Step 3: Set up REopt run
                # ------------------------- #

                wind_size_mw, solar_size_mw, storage_size_mw,\
                storage_size_mwh, storage_hours, reopt_results, REoptResultsDF = run_reopt(site, scenario, load,
                                                             interconnection_size_mw*1000,
                                                             critical_load_factor, useful_life,
                  battery_can_grid_charge, storage_used, run_reopt_flag)

                # Step 4: Set up HOPP run
                # ------------------------- #
                if forced_sizes:
                    solar_size_mw = forced_solar_size
                    wind_size_mw = forced_wind_size
                    storage_size_mw = forced_storage_size_mw
                    storage_size_mwh = forced_storage_size_mwh

                # TODO: Replace electrolyzer size with interconnection size after testing
                # technologies = {'solar': solar_size_mw,  # mw system capacity
                #                 'wind': wind_size_mw,  # mw system capacity
                #                 'collection_system': True}
                technologies = {'pv':
                                    {'system_capacity_kw': solar_size_mw * 1000},
                                'wind':
                                    {'num_turbines': np.floor(scenario['Wind Size MW'] / scenario['Turbine Rating']),
                                     'turbine_rating_kw': scenario['Turbine Rating']*1000,
                                     'hub_height': scenario['Tower Height'],
                                     'rotor_diameter': scenario['Rotor Diameter']},
                                'battery': {
                                    'system_capacity_kwh': storage_size_mwh * 1000,
                                    'system_capacity_kw': storage_size_mw * 1000
                                    }
                                }

                hybrid_plant, combined_pv_wind_power_production_hopp, combined_pv_wind_curtailment_hopp,\
                energy_shortfall_hopp, annual_energies, wind_plus_solar_npv, npvs, lcoe =  \
                    hopp_for_h2(site, scenario, technologies,
                                wind_size_mw, solar_size_mw, storage_size_mw, storage_size_mwh, storage_hours,
                    wind_cost_kw, solar_cost_kw, storage_cost_kw, storage_cost_kwh,
                    kw_continuous, load,
                    custom_powercurve,
                    electrolyzer_size, grid_connected_hopp=True)

                wind_installed_cost = hybrid_plant.wind.total_installed_cost
                solar_installed_cost = hybrid_plant.pv.total_installed_cost
                hybrid_installed_cost = hybrid_plant.grid.total_installed_cost

                if plot_power_production:
                    plt.figure(figsize=(4,4))
                    plt.title("HOPP power production")
                    plt.plot(combined_pv_wind_power_production_hopp[200:300],label="wind + pv")
                    plt.plot(energy_shortfall_hopp[200:300],label="shortfall")
                    plt.plot(combined_pv_wind_curtailment_hopp[200:300],label="curtailment")
                    plt.plot(load[200:300],label="electrolyzer rating")
                    plt.xlabel("time (hour)")
                    plt.ylabel("power production")
                    # plt.ylim(0,250000)
                    plt.legend()
                    plt.tight_layout()
                    plt.show()

                # Step 5: Run Simple Dispatch Model
                # ------------------------- #

                bat_model = SimpleDispatch()
                bat_model.Nt = len(energy_shortfall_hopp)
                bat_model.curtailment = combined_pv_wind_curtailment_hopp
                bat_model.shortfall = energy_shortfall_hopp

                bat_model.battery_storage = storage_size_mwh * 1000
                bat_model.charge_rate = storage_size_mw * 1000
                bat_model.discharge_rate = storage_size_mw * 1000

                battery_used, excess_energy, battery_SOC = bat_model.run()
                combined_pv_wind_storage_power_production_hopp = combined_pv_wind_power_production_hopp + battery_used

                if plot_battery:
                    plt.figure(figsize=(7,4))
                    plt.subplot(121)
                    plt.plot(combined_pv_wind_curtailment_hopp[200:300],label="curtailment")
                    plt.plot(energy_shortfall_hopp[200:300],label="shortfall")
                    plt.plot(battery_SOC[200:300],label="state of charge")
                    # plt.ylim(0,350000)
                    # plt.plot(excess_energy[200:300],label="excess")
                    plt.plot(battery_used[200:300],"--",label="battery used")
                    plt.legend()

                    plt.subplot(122)
                    plt.plot(combined_pv_wind_storage_power_production_hopp[200:300],label="wind+pv+storage")
                    plt.plot(combined_pv_wind_power_production_hopp[200:300],"--",label="wind+pv")
                    plt.plot(load[200:300],"--",label="electrolyzer rating")
                    # plt.ylim(0,225000)

                    plt.legend()
                    plt.suptitle("battery dispatch")
                    plt.tight_layout()

                    plt.show()

                if plot_grid:
                    plt.plot(combined_pv_wind_storage_power_production_hopp[200:300],label="before buy from grid")

                sell_price = 0.01
                buy_price = 0.05

                # sell_price = False
                # buy_price = False

                if sell_price:
                    profit_from_selling_to_grid = np.sum(excess_energy)*sell_price
                else:
                    profit_from_selling_to_grid = 0.0

                # buy_price = False # if you want to force no buy from grid
                if buy_price:
                    cost_to_buy_from_grid = 0.0

                    for i in range(len(combined_pv_wind_storage_power_production_hopp)):
                        if combined_pv_wind_storage_power_production_hopp[i] < kw_continuous:
                            cost_to_buy_from_grid += (kw_continuous-combined_pv_wind_storage_power_production_hopp[i])*buy_price
                            combined_pv_wind_storage_power_production_hopp[i] = kw_continuous
                else:
                    cost_to_buy_from_grid = 0.0

                energy_to_electrolyzer = [x if x < kw_continuous else kw_continuous for x in combined_pv_wind_storage_power_production_hopp]

                if plot_grid:
                    plt.plot(combined_pv_wind_storage_power_production_hopp[200:300],"--",label="after buy from grid")
                    plt.plot(energy_to_electrolyzer[200:300],"--",label="energy to electrolyzer")
                    plt.legend()
                    plt.show()

                # Step 6: Run the Python H2A model
                # ------------------------- #
                #TODO: Refactor H2A model call
                # Should take as input (electrolyzer size, cost, electrical timeseries, total system electrical usage (kwh/kg),
                # Should give as ouptut (h2 costs by net cap cost, levelized, total_unit_cost of hydrogen etc)   )

                # electrical_generation_timeseries = combined_pv_wind_storage_power_production_hopp
                electrical_generation_timeseries = np.zeros_like(energy_to_electrolyzer)
                electrical_generation_timeseries[:] = energy_to_electrolyzer[:]

                # Parangat model
                adjusted_installed_cost = hybrid_plant.grid._financial_model.Outputs.adjusted_installed_cost
                #NB: adjusted_installed_cost does NOT include the electrolyzer cost
                useful_life = scenario['Useful Life']
                net_capital_costs = reopt_results['outputs']['Scenario']['Site'] \
                                    ['Financial']['net_capital_costs']

                # intalled costs:
                # hybrid_plant.grid._financial_model.costs

                # system_rating = electrolyzer_size
                system_rating = wind_size_mw + solar_size_mw
                H2_Results, H2A_Results = run_h2_PEM.run_h2_PEM(electrical_generation_timeseries,electrolyzer_size,
                                kw_continuous,forced_electrolyzer_cost,lcoe,adjusted_installed_cost,useful_life,
                                net_capital_costs)

                if plot_h2:
                    hydrogen_hourly_production = H2_Results['hydrogen_hourly_production']
                    plt.figure(figsize=(6,3))

                    plt.subplot(121)
                    plt.plot(electrical_generation_timeseries[200:300])
                    plt.ylim(0,max(electrical_generation_timeseries[200:300])*1.2)
                    plt.plot(load[200:300],label="electrolyzer rating")
                    plt.title("energy to electrolyzer")

                    plt.subplot(122)
                    plt.plot(hydrogen_hourly_production[200:300])
                    plt.ylim(0,max(hydrogen_hourly_production[200:300])*1.2)
                    plt.title("hydrogen production")

                    plt.tight_layout()
                    plt.show()


                # TEMPORARY CORRECTION FOR PEM EFFICIENCY.
                # # Convert H2 production from ~72.55kWh eff to 55.5kWh/kg
                H2_Results['hydrogen_annual_output'] = H2_Results['hydrogen_annual_output'] * 72.55/55.5

                # Step 6.5: Intermediate financial calculation
                total_elec_production = np.sum(electrical_generation_timeseries) #REMOVE
                total_hopp_installed_cost = hybrid_plant.grid._financial_model.SystemCosts.total_installed_cost
                total_electrolyzer_cost = H2A_Results['scaled_total_installed_cost']
                total_system_installed_cost = total_hopp_installed_cost + total_electrolyzer_cost
                annual_operating_cost_hopp = (wind_size_mw * 1000 * 42) + (solar_size_mw * 1000 * 13)
                annual_operating_cost_h2 = H2A_Results['Fixed O&M'] * H2_Results['hydrogen_annual_output']
                total_annual_operating_costs = annual_operating_cost_hopp + annual_operating_cost_h2 + cost_to_buy_from_grid - profit_from_selling_to_grid
                # h_lcoe_no_op_cost = lcoe_calc((H2_Results['hydrogen_annual_output']), total_system_installed_cost,
                #                    0, 0.07, useful_life)

                h_lcoe = lcoe_calc((H2_Results['hydrogen_annual_output']), total_system_installed_cost,
                                   total_annual_operating_costs, 0.07, useful_life)

                # Cashflow Financial Calculation (Not sure that this includes electrical prices)
                discount_rate = scenario['Discount Rate']
                cf_wind_annuals = hybrid_plant.wind._financial_model.Outputs.cf_annual_costs
                cf_solar_annuals = hybrid_plant.pv._financial_model.Outputs.cf_annual_costs
                cf_h2_annuals = H2A_Results['expenses_annual_cashflow'] # This might be unreliable. 
                cf_df = pd.DataFrame([cf_wind_annuals, cf_solar_annuals, cf_h2_annuals[:len(cf_wind_annuals)]],['Wind', 'Solar', 'H2'])
                results_dir = Path(__file__).parent / 'results/'
                cf_df.to_csv(os.path.join(results_dir, "Annual Cashflows_{}_{}_{}_discount_{}.csv".format(site_name, scenario_choice, atb_year, discount_rate)))

                #NPVs of wind, solar, H2

                npv_wind_costs = npf.npv(discount_rate, cf_wind_annuals)
                npv_solar_costs = npf.npv(discount_rate, cf_solar_annuals)
                npv_h2_costs = npf.npv(discount_rate, cf_h2_annuals)
                npv_total_costs = npv_wind_costs+npv_solar_costs+npv_h2_costs
                LCOH_cf_method = -npv_total_costs / (H2_Results['hydrogen_annual_output'] * useful_life)
                financial_summary_df = pd.DataFrame([scenario['Useful Life'], scenario['Wind Cost KW'], scenario['Solar Cost KW'], forced_electrolyzer_cost,
                                                     scenario['Debt Equity'], atb_year, ptc_avail, itc_avail,
                                                     discount_rate, npv_wind_costs, npv_solar_costs, npv_h2_costs, LCOH_cf_method],
                                                    ['Useful Life', 'Wind Cost KW', 'Solar Cost KW', 'Electrolyzer Cost KW', 'Debt Equity',
                                                     'ATB Year', 'PTC available', 'ITC available', 'Discount Rate', 'NPV Wind Expenses', 'NPV Solar Expenses', 'NPV H2 Expenses', 'LCOH cf method'])
                financial_summary_df.to_csv(os.path.join(results_dir, 'Financial Summary.csv'))

                # Gut Check H2 calculation (non-levelized)
                total_installed_and_operational_lifetime_cost = total_system_installed_cost + (30 * total_annual_operating_costs)
                lifetime_h2_production = 30 * H2_Results['hydrogen_annual_output']
                gut_check_h2_cost_kg = total_installed_and_operational_lifetime_cost / lifetime_h2_production

                # Step 7: Print  results



                print_reults = False
                print_h2_results = True

                if print_reults:
                    # ------------------------- #
                    #TODO: Tidy up these print statements
                    print("Future Scenario: {}".format(scenario['Scenario Name']))
                    print("Wind Cost per KW: {}".format(scenario['Wind Cost KW']))
                    print("PV Cost per KW: {}".format(scenario['Solar Cost KW']))
                    print("Storage Cost per KW: {}".format(scenario['Storage Cost kW']))
                    print("Storage Cost per KWh: {}".format(scenario['Storage Cost kWh']))
                    print("Wind Size built: {}".format(wind_size_mw))
                    print("PV Size built: {}".format(solar_size_mw))
                    print("Storage Size built: {}".format(storage_size_mw))
                    print("Storage Size built: {}".format(storage_size_mwh))
                    print("Levelized cost of Electricity (HOPP): {}".format(lcoe))
                    print("Total Yearly Electrical Output: {}".format(total_elec_production))
                    print("Total Yearly Hydrogen Production: {}".format(H2_Results['hydrogen_annual_output']))
                    print("Levelized Cost H2/kg (new method - no operational costs)".format(h_lcoe_no_op_cost))
                    print("Capacity Factor of Electrolyzer: {}".format(H2_Results['cap_factor']))

                if print_h2_results:
                    print('Total Lifetime H2(kg) produced: {}'.format(lifetime_h2_production))
                    print("Gut-check H2 cost/kg: {}".format(gut_check_h2_cost_kg))
                    print("h_lcoe: ", h_lcoe)
                    print("LCOH CF Method (doesn't include elec)", LCOH_cf_method)
                    # print("Levelized cost of H2 (electricity feedstock) (HOPP): {}".format(
                    #     H2_Results['feedstock_cost_h2_levelized_hopp']))
                    # print("Levelized cost of H2 (excl. electricity) (H2A): {}".format(H2A_Results['Total Hydrogen Cost ($/kgH2)']))
                    # print("Total unit cost of H2 ($/kg) : {}".format(H2_Results['total_unit_cost_of_hydrogen']))
                    # print("kg H2 cost from net cap cost/lifetime h2 production (HOPP): {}".format(
                    #     H2_Results['feedstock_cost_h2_via_net_cap_cost_lifetime_h2_hopp']))

                    

                # Step 8: Plot REopt results

                if plot_reopt:
                    plot_reopt_results(REoptResultsDF, site_name, atb_year, critical_load_factor,
                                    useful_life, tower_height,
                                    wind_size_mw, solar_size_mw, storage_size_mw, storage_size_mwh, lcoe,
                                    H2_Results['feedstock_cost_h2_via_net_cap_cost_lifetime_h2_hopp'],
                                    H2_Results['feedstock_cost_h2_levelized_hopp'],
                                    hybrid_installed_cost, H2A_Results['Total Hydrogen Cost ($/kgH2)'],
                                    H2_Results['total_unit_cost_of_hydrogen'],
                                    output_dir='results/',
                                    monthly_separation=False, reopt_was_run=run_reopt_flag)

                # Step 9: Plot HOPP Production, Curtailment, and Hydrogen Production Profiles
                #TODO: Add this

                # Step 10: Save outputs
                # ------------------------- #
                #TODO: Place in function
                save_outputs_dict['Site Name'].append(site_name)
                save_outputs_dict['Scenario Choice'].append(scenario_choice)
                save_outputs_dict['Site Lat'].append(lat)
                save_outputs_dict['Site Lon'].append(lon)
                save_outputs_dict['ATB Year'].append(atb_year)
                save_outputs_dict['Resource Year'].append(year)
                save_outputs_dict['Critical Load Factor'].append(critical_load_factor)
                save_outputs_dict['kW continuous load'].append(kw_continuous)
                save_outputs_dict['Useful Life'].append(useful_life)
                save_outputs_dict['PTC'].append(ptc_avail)
                save_outputs_dict['ITC'].append(itc_avail)
                save_outputs_dict['Discount Rate'].append(discount_rate)
                save_outputs_dict['Debt Equity'].append(debt_equity_split)
                save_outputs_dict['Hub Height (m)'].append(tower_height)
                save_outputs_dict['Storage Enabled'].append(storage_used)
                save_outputs_dict['Wind Cost kW'].append(wind_cost_kw)
                save_outputs_dict['Solar Cost kW'].append(solar_cost_kw)
                save_outputs_dict['Storage Cost kW'].append(storage_cost_kw)
                save_outputs_dict['Storage Cost kWh'].append(storage_cost_kwh)
                save_outputs_dict['Storage Hours'].append(storage_hours)
                save_outputs_dict['Wind MW built'].append(wind_size_mw)
                save_outputs_dict['Solar MW built'].append(solar_size_mw)
                save_outputs_dict['Storage MW built'].append(storage_size_mw)
                save_outputs_dict['Storage MWh built'].append(storage_size_mwh)
                save_outputs_dict['Battery Can Grid Charge'].append(battery_can_grid_charge)
                save_outputs_dict['Built Interconnection Size'].append(hybrid_plant.interconnect_kw)
                save_outputs_dict['REOpt Interconnection Size'].append(interconnection_size_mw*1000)
                save_outputs_dict['Total Installed Cost $(HOPP)'].append(total_hopp_installed_cost)
                save_outputs_dict['Total Yearly Electrical Output'].append(total_elec_production)
                save_outputs_dict['LCOE'].append(lcoe)
                save_outputs_dict['Total Annual H2 production (kg)'].append(H2_Results['hydrogen_annual_output'])
                save_outputs_dict['Gut-Check Cost/kg H2 (non-levelized, includes elec if used)'].append(gut_check_h2_cost_kg)
                save_outputs_dict['Levelized Cost/kg H2 (lcoe using installed and operation costs)'].append(h_lcoe)
                save_outputs_dict['Levelized Cost/kg H2 (CF Method - using annual cashflows per technology)'].append(LCOH_cf_method)
                # save_outputs_dict['Levelized Cost H2/kg (new method - no operational costs)'].append(h_lcoe_no_op_cost)
                # save_outputs_dict['Levelized H2 Elec Feedstock Cost/kg (HOPP)'].append(H2_Results['feedstock_cost_h2_levelized_hopp'])
                # save_outputs_dict['Levelized cost of H2 (excl. electricity) (H2A)'].append(H2A_Results['Total Hydrogen Cost ($/kgH2)'])
                # save_outputs_dict['H2 Elec Feedstock Cost/kg (HOPP) Net Cap Cost Method'].append(H2_Results['feedstock_cost_h2_via_net_cap_cost_lifetime_h2_hopp'])
                # save_outputs_dict['Total H2 cost/kg (H2A)'].append(H2_Results['total_unit_cost_of_hydrogen'])
                save_outputs_dict['REOpt Energy Shortfall'].append(np.sum(REoptResultsDF['energy_shortfall']))
                save_outputs_dict['REOpt Curtailment'].append(np.sum(REoptResultsDF['combined_pv_wind_curtailment']))
                save_outputs_dict['Grid Connected HOPP'].append(grid_connected_hopp)
                save_outputs_dict['HOPP Total Generation'].append(np.sum(hybrid_plant.grid.generation_profile[0:8759]))
                save_outputs_dict['Wind Capacity Factor'].append(hybrid_plant.wind._system_model.Outputs.capacity_factor)
                save_outputs_dict['HOPP Energy Shortfall'].append(np.sum(energy_shortfall_hopp))
                save_outputs_dict['HOPP Curtailment'].append(np.sum(combined_pv_wind_curtailment_hopp))
                save_outputs_dict['Battery Generation'].append(np.sum(battery_used))
                save_outputs_dict['Electricity to Grid'].append(np.sum(excess_energy))
                save_outputs_dict['Electrolyzer Size'].append(H2A_Results['electrolyzer_size'])
                save_outputs_dict['Electrolyzer Total System Size'].append(H2A_Results['total_plant_size'])
                save_outputs_dict['H2A scaled total install cost'].append(H2A_Results['scaled_total_installed_cost'])
                save_outputs_dict['H2A scaled total install cost per kw'].append(H2A_Results['scaled_total_installed_cost_kw'])


        # save_all_runs = save_all_runs.append(save_outputs_dict, sort=False)

    # Create dataframe from outputs and save

    save_outputs = True
    if save_outputs:
        save_outputs_dict_df = pd.DataFrame(save_outputs_dict)
        save_outputs_dict_df.to_csv(os.path.join(results_dir, "H2_Analysis_{}.csv".format('Main')))
Пример #23
0
def run(default_config: Dict) -> None:
    config, output_path, run_name = setup_run(default_config)
    recorder = DataRecorder.make_data_recorder(output_path)

    max_evaluations = config['max_evaluations']
    
    location_index = config['location']
    location = locations[location_index]
    
    site = config['site']
    site_data = None
    if site == 'circular':
        site_data = make_circular_site(lat=location[0], lon=location[1], elev=location[2])
    elif site == 'irregular':
        site_data = make_irregular_site(lat=location[0], lon=location[1], elev=location[2])
    else:
        raise Exception("Unknown site '" + site + "'")
    
    site_info = SiteInfo(site_data)
    inner_problem = HybridOptimizationProblem(site_info, config['num_turbines'], config['solar_capacity'])
    problem = HybridParametrization(inner_problem)
    
    optimizer = ParametrizedOptimizationDriver(problem, recorder=recorder, **config['optimizer_config'])
    
    figure = plt.figure(1)
    axes = figure.add_subplot(111)
    axes.set_aspect('equal')
    plt.grid()
    plt.tick_params(which='both', labelsize=15)
    plt.xlabel('x (m)', fontsize=15)
    plt.ylabel('y (m)', fontsize=15)
    site_info.plot()

    score, evaluation, best_solution = optimizer.central_solution()
    score, evaluation = problem.objective(best_solution) if score is None else score
    
    print(-1, ' ', score, evaluation)
    
    print('setup 1')
    
    num_substeps = 1
    figure, axes = plt.subplots(dpi=200)
    axes.set_aspect(1)
    animation_writer = PillowWriter(2 * num_substeps)
    animation_writer.setup(figure, os.path.join(output_path, 'trajectory.gif'), dpi=200)
    
    print('setup 2')
    _, _, central_solution = optimizer.central_solution()
    
    print('setup 3')
    bounds = problem.inner_problem.site_info.polygon.bounds
    site_sw_bound = np.array([bounds[0], bounds[1]])
    site_ne_bound = np.array([bounds[2], bounds[3]])
    site_center = .5 * (site_sw_bound + site_ne_bound)
    max_delta = max(bounds[2] - bounds[0], bounds[3] - bounds[1])
    reach = (max_delta / 2) * 1.3
    min_plot_bound = site_center - reach
    max_plot_bound = site_center + reach
    
    print('setup 4')
    
    best_score, best_evaluation, best_solution = 0.0, 0.0, None
    
    def plot_candidate(candidate):
        nonlocal best_score, best_evaluation, best_solution
        axes.cla()
        axes.set(xlim=(min_plot_bound[0], max_plot_bound[0]), ylim=(min_plot_bound[1], max_plot_bound[1]))
        wind_color = (153 / 255, 142 / 255, 195 / 255)
        solar_color = (241 / 255, 163 / 255, 64 / 255)
        central_color = (.5, .5, .5)
        conforming_candidate, _, __ = problem.make_conforming_candidate_and_get_penalty(candidate)
        problem.plot_candidate(conforming_candidate, figure, axes, central_color, central_color, alpha=.7)
        
        if best_solution is not None:
            conforming_best, _, __ = problem.make_conforming_candidate_and_get_penalty(best_solution)
            problem.plot_candidate(conforming_best, figure, axes, wind_color, solar_color, alpha=1.0)
            axes.set_xlabel('Best Solution AEP: {}'.format(best_evaluation))
        else:
            axes.set_xlabel('')
        
        axes.legend([
            Line2D([0], [0], color=wind_color, lw=8),
            Line2D([0], [0], color=solar_color, lw=8),
            Line2D([0], [0], color=central_color, lw=8),
            ],
            ['Wind Layout', 'Solar Layout', 'Mean Search Vector'],
            loc='lower left')
        animation_writer.grab_frame()
    
    print('plot candidate')
    
    plot_candidate(central_solution)
    
    central_prev = central_solution
    # TODO: make a smooth transition between points
    # TODO: plot exclusion zones
    print('begin')

    try:
        while optimizer.num_evaluations() < max_evaluations:

            print('step start')
            logger.info("Starting step, num evals {}".format(optimizer.num_evaluations()))
            optimizer.step()
            print('step end')

            proportion = min(1.0, optimizer.num_evaluations() / max_evaluations)
            g = 1.0 * proportion
            b = 1.0 - g
            a = .5
            color = (b, g, b)
            best_score, best_evaluation, best_solution = optimizer.best_solution()
            central_score, central_evaluation, central_solution = optimizer.central_solution()

            a1 = optimizer.converter.convert_from(central_prev)
            b1 = optimizer.converter.convert_from(central_solution)
            a = np.array(a1, dtype=np.float64)
            b = np.array(b1, dtype=np.float64)

            for i in range(num_substeps):
                p = (i + 1) / num_substeps
                c = (1 - p) * a + p * b
                candidate = optimizer.converter.convert_to(c)
                plot_candidate(candidate)

            central_prev = central_solution
            print(optimizer.num_iterations(), ' ', optimizer.num_evaluations(), best_score, best_evaluation)
    except:
        raise RuntimeError("Optimizer error encountered. Try modifying the config to use larger generation_size if"
                           " encountering singular matrix errors.")

    animation_writer.finish()

    optimizer.close()

    print("Results and animation written to " + os.path.abspath(output_path))
Пример #24
0
    def test_hopp_for_h2(self):
        scenario = {}
        scenario['Useful Life'] = 30
        scenario['Powercurve File'] = self.examples_dir / "powercurve_2018_COE"
        scenario['Tower Height'] = 90
        scenario['Rotor Diameter'] = 116
        scenario['Debt Equity'] = 90
        scenario['ITC Available'] = 'no'
        scenario['PTC Available'] = 'yes'

        interconnection_size_mw = 150
        wind_size_mw = 150
        solar_size_mw = 50
        electrolyzer_size = 5
        storage_size_mw = 50
        storage_size_mwh = 100
        storage_hours = storage_size_mwh / storage_size_mw
        wind_cost_kw = 1454
        solar_cost_kw = 1080
        storage_cost_kw = 101
        storage_cost_kwh = 116
        kw_continuous = 1000 * electrolyzer_size
        load = [kw_continuous for x in range(0, 8760)]
        custom_powercurve = True
        if os.path.exists(scenario['Powercurve File']):
            with open(scenario['Powercurve File'], 'r') as f:
                powercurve_data = json.load(f)
                turb_size = max(
                    powercurve_data['turbine_powercurve_specification']
                    ['turbine_power_output'])
        technologies = {
            'pv': {
                'system_capacity_kw': solar_size_mw * 1e3
            },
            'wind': {
                'num_turbines': int(wind_size_mw / turb_size),
                'turbine_rating_kw': turb_size,
                'hub_height': scenario['Tower Height'],
                'rotor_diameter': scenario['Rotor Diameter']
            },
            'battery': {
                'system_capacity_kwh': storage_size_mwh * 1000,
                'system_capacity_kw': storage_size_mw * 1000
            }
        }

        solar_resource_file = Path(__file__).absolute(
        ).parent.parent.parent / "resource_files" / "solar" / "35.2018863_-101.945027_psmv3_60_2012.csv"
        wind_resource_file = Path(__file__).absolute(
        ).parent.parent.parent / "resource_files" / "wind" / "35.2018863_-101.945027_windtoolkit_2012_60min_80m_100m.srw"
        sample_site['site_num'] = 1
        Site = SiteInfo(sample_site,
                        solar_resource_file=solar_resource_file,
                        wind_resource_file=wind_resource_file)

        hybrid_plant, combined_pv_wind_power_production_hopp, combined_pv_wind_curtailment_hopp, \
            energy_shortfall_hopp, annual_energies, wind_plus_solar_npv, npvs, lcoe = hopp_for_h2(
                Site, scenario, technologies, wind_size_mw, solar_size_mw, storage_size_mw, storage_size_mwh,
                storage_hours, wind_cost_kw, solar_cost_kw, storage_cost_kw, storage_cost_kwh, kw_continuous,
                load, custom_powercurve, interconnection_size_mw, grid_connected_hopp=True
            )

        df_produced = pd.DataFrame()
        df_produced['combined_pv_wind_power_production_hopp'] = [
            sum(combined_pv_wind_power_production_hopp)
        ]
        df_produced['combined_pv_wind_curtailment_hopp'] = [
            sum(combined_pv_wind_curtailment_hopp)
        ]
        df_produced['energy_shortfall_hopp'] = [sum(energy_shortfall_hopp)]
        df_produced['annual_energies'] = annual_energies
        df_produced['wind_plus_solar_npv'] = wind_plus_solar_npv
        df_produced['npvs'] = npvs
        df_produced['lcoe'] = lcoe

        results_path = os.path.join(self.test_dir, 'results')
        if not os.path.exists(results_path):
            os.mkdir(results_path)
        df_produced.to_csv(os.path.join(
            results_path, 'hopp_for_h2_test_results_produced.csv'),
                           index=False)
        df_produced = pd.read_csv(
            os.path.join(results_path,
                         'hopp_for_h2_test_results_produced.csv'))
        df_expected = pd.read_csv(
            os.path.join(self.test_dir,
                         'expected_hopp_for_h2_test_results.csv'))

        assert df_produced[
            'combined_pv_wind_power_production_hopp'].values == approx(
                df_expected['combined_pv_wind_power_production_hopp'].values,
                1e-4)
        assert df_produced[
            'combined_pv_wind_curtailment_hopp'].values == approx(
                df_expected['combined_pv_wind_curtailment_hopp'].values, 1e-4)
        assert df_produced['energy_shortfall_hopp'].values == approx(
            df_expected['energy_shortfall_hopp'].values, 1e-3)
        assert df_produced['wind_plus_solar_npv'].values == approx(
            df_expected['wind_plus_solar_npv'].values, 1e-2)
        shutil.rmtree(results_path)
Пример #25
0
site_data = None

if site == 'circular':
    site_data = make_circular_site(lat=location[0],
                                   lon=location[1],
                                   elev=location[2])
elif site == 'irregular':
    site_data = make_irregular_site(lat=location[0],
                                    lon=location[1],
                                    elev=location[2])
else:
    raise Exception("Unknown site '" + site + "'")

g_file = Path(__file__).absolute(
).parent.parent.parent / "resource_files" / "grid" / "pricing-data-2015-IronMtn-002_factors.csv"
site_info = SiteInfo(site_data, grid_resource_file=g_file)

# set up hybrid simulation with all the required parameters
solar_size_mw = 100
wind_size_mw = 100
interconnection_size_mw = 150

technologies = {
    'pv': {
        'system_capacity_kw':
        solar_size_mw * 1000,
        'layout_params':
        PVGridParameters(x_position=0.5,
                         y_position=0.5,
                         aspect_power=0,
                         gcr=0.5,
Пример #26
0
def test_desired_schedule_dispatch():

    # Creating a contrived schedule
    daily_schedule = [interconnect_mw]*10
    daily_schedule.extend([20] * 8)
    daily_schedule.append(interconnect_mw + 5)
    daily_schedule.extend([0] * 5)
    desired_schedule = daily_schedule*365

    desired_schedule_site = SiteInfo(flatirons_site,
                                     desired_schedule=desired_schedule)
    tower_pv_battery = {key: technologies[key] for key in ('pv', 'tower', 'battery')}

    # Default case doesn't leave enough head room for battery operations
    tower_pv_battery['tower'] = {'cycle_capacity_kw': 35 * 1000,
                                 'solar_multiple': 2.0,
                                 'tes_hours': 10.0}

    tower_pv_battery['pv'] = {'system_capacity_kw': 80 * 1000}

    hybrid_plant = HybridSimulation(tower_pv_battery, desired_schedule_site, interconnect_mw * 1000,
                                    dispatch_options={'is_test_start_year': True,
                                                      'is_test_end_year': False,
                                                      'grid_charging': False,
                                                      'pv_charging_only': True,
                                                      'include_lifecycle_count': False
                                                      })
    hybrid_plant.ppa_price = (0.06, )

    # Constant price
    # hybrid_plant.site.elec_prices = [100] * hybrid_plant.site.n_timesteps
    hybrid_plant.simulate(1)

    system_generation = hybrid_plant.dispatch_builder.dispatch.system_generation
    system_load = hybrid_plant.dispatch_builder.dispatch.system_load
    electricity_sold = hybrid_plant.grid.dispatch.electricity_sold
    electricity_purchased = hybrid_plant.grid.dispatch.electricity_purchased
    gen_limit = hybrid_plant.grid.dispatch.generation_transmission_limit
    transmission_limit = hybrid_plant.grid.value('grid_interconnection_limit_kwac')

    schedule = daily_schedule*2
    # System generation does not exceed schedule limits
    for t in hybrid_plant.dispatch_builder.pyomo_model.forecast_horizon:
        assert gen_limit[t] * 1e3 <= transmission_limit
        assert system_generation[t] - system_load[t] <= schedule[t] + 1e-3
        if system_generation[t] > system_load[t]:
            assert electricity_sold[t] == pytest.approx(system_generation[t] - system_load[t], 1e-3)
            assert electricity_purchased[t] == pytest.approx(0.0, 1e-3)
        else:
            assert electricity_purchased[t] == pytest.approx(system_load[t] - system_generation[t], 1e-3)
            assert electricity_sold[t] == pytest.approx(0.0, 1e-3)

    # Battery charges and discharges
    assert sum(hybrid_plant.battery.dispatch.charge_power) > 0.0
    assert sum(hybrid_plant.battery.dispatch.discharge_power) > 0.0

    # PV can be curtailed
    assert sum(hybrid_plant.pv.dispatch.generation) <= sum(hybrid_plant.pv.dispatch.available_generation)

    # CSP can run
    assert sum(hybrid_plant.tower.dispatch.cycle_generation) > 0.0
    assert sum(hybrid_plant.tower.dispatch.receiver_thermal_power) > 0.0
Пример #27
0
def init_simulation():
    """
    Create the simulation object needed to calculate the objective of the problem
    TODO: make this representative of the design variables, is there currently a tradeoff in objectives?

    :return: The HOPP simulation as defined for this problem
    """

    site = 'irregular'
    location = locations[1]

    if site == 'circular':
        site_data = make_circular_site(lat=location[0],
                                       lon=location[1],
                                       elev=location[2])
    elif site == 'irregular':
        site_data = make_irregular_site(lat=location[0],
                                        lon=location[1],
                                        elev=location[2])
    else:
        raise Exception("Unknown site '" + site + "'")

    solar_file = examples_dir.parent / "resource_files" / "solar" / "Beni_Miha" / "659265_32.69_10.90_2019.csv"
    grid_file = examples_dir.parent / "resource_files" / "grid" / "tunisia_est_grid_prices.csv"

    site_info = SiteInfo(site_data,
                         solar_resource_file=solar_file,
                         grid_resource_file=grid_file)

    # set up hybrid simulation with all the required parameters
    solar_size_mw = 200
    tower_cycle_mw = 125
    # battery_capacity_mwh = 15
    interconnection_size_mw = 400

    technologies = {
        'tower': {
            'cycle_capacity_kw': tower_cycle_mw * 1000,
            'solar_multiple': 2.0,
            'tes_hours': 12.0,
            'optimize_field_before_sim': False
        },  # TODO: turn on
        'pv': {
            'system_capacity_kw': solar_size_mw * 1000
        },
        # 'battery': {'system_capacity_kwh': battery_capacity_mwh * 1000,
        #             'system_capacity_kw': battery_capacity_mwh * 1000 / 10},
        'grid': interconnection_size_mw * 1000
    }

    # Create model
    # TODO: turn these off to run full year simulation
    dispatch_options = {'is_test_start_year': True, 'is_test_end_year': False}

    # TODO: turn-on receiver and field optimization before... initial simulation
    hybrid_plant = HybridSimulation(technologies,
                                    site_info,
                                    interconnect_kw=interconnection_size_mw *
                                    1000,
                                    dispatch_options=dispatch_options)

    # Customize the hybrid plant assumptions here...
    hybrid_plant.pv.value('inv_eff', 95.0)
    hybrid_plant.pv.value('array_type', 0)

    return hybrid_plant