def test_battery_optimization_against_forecast(): model = energypylinear.Battery(power=4, capacity=6, efficiency=1.0) info = model.optimize(prices=[10, 10, 10], forecasts=[10, 10, 10]) result = [res['Net [MW]'] for res in info] unittest.TestCase().assertCountEqual(result, [0, 0, 0])
def test_carbon_optimization(prices, initial_charge, carbon): power = 4 capacity = 6 model = energypylinear.Battery(power=power, capacity=capacity, efficiency=1.0) p_info = model.optimize(prices=prices, initial_charge=initial_charge, freq="60T") p_dispatch = [res["Actual Cost [$/60T]"] for res in p_info] # now we optimize for carbon and see if our net cost was higher c_info = model.optimize( prices=prices, carbon=carbon, initial_charge=initial_charge, freq="60T", objective="carbon", ) c_dispatch = [res["Actual Cost [$/60T]"] for res in c_info] assert sum(c_dispatch) >= sum(p_dispatch)
def test_battery_energy_balance(): prices = np.random.uniform(-100, 100, 96) power = 1.0 capacity = 1.0 timestep = '1hr' efficiency = 0.9 initial_charge = 1.0 model = energypylinear.Battery( power=power, capacity=capacity, efficiency=efficiency ) info = model.optimize( prices=prices, initial_charge=initial_charge, freq="60T" ) energy_in = sum([res['Import [MW]'] for res in info]) energy_out = sum([res['Export [MW]'] for res in info]) initial_charge = info[0]['Initial Charge [MWh]'] final_charge = info[-1]['Final Charge [MWh]'] balance = energy_in - energy_out + (initial_charge - final_charge) np.testing.assert_almost_equal(balance, 0)
def test_battery_optimization(prices, initial_charge, expected_dispatch): power = 4 capacity = 6 mdl = energypylinear.Battery(power=power, capacity=capacity, efficiency=1.0) info = mdl.optimize(prices=prices, initial_charge=initial_charge, freq="60T") dispatch = [res["Net [MW]"] for res in info] unittest.TestCase().assertCountEqual(dispatch, expected_dispatch)
def test_batt_efficiency(prices, initial_charge, efficiency, expected_dispatch): power = 1.0 capacity = 1.0 freq = "60T" mdl = energypylinear.Battery(power=power, capacity=capacity, efficiency=efficiency) info = mdl.optimize(prices=prices, initial_charge=initial_charge, freq=freq) dispatch = [res["Net [MW]"] for res in info] np.testing.assert_array_equal(dispatch, expected_dispatch)
def test_view_output(): """Test that we output the expected view for a simple problem.""" prices = [10, 20, 10] model = energypylinear.Battery(power=2, capacity=4, efficiency=1.0) info = model.optimize(prices=prices, initial_charge=0, freq="60T") expected_header = [ "Import [MW]", "Export [MW]", "Gross [MW]", "Net [MW]", "Losses [MW]", "Initial Charge [MWh]", "Final Charge [MWh]", "Price [$/MWh]", "Forecast Price [$/MWh]", "Carbon Intensity [tC/MWh]", "Actual Cost [$/60T]", "Forecast Cost [$/60T]", "Carbon Cost [tC/60T]", ] csvfile = io.StringIO() fieldnames = info[0].keys() writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for row in info: writer.writerow(row) csv_rows = csvfile.getvalue().split("\r\n") for header in expected_header: assert header in csv_rows[0] # order of the csv columns is different than from expected_header actual_header = [col for col in csv_rows[0].split(",")] row_one = [col for col in csv_rows[1].split(",")] row_two = [col for col in csv_rows[2].split(",")] assert float(row_one[actual_header.index("Import [MW]")]) == 2.0 assert float(row_one[actual_header.index("Export [MW]")]) == 0.0 assert float(row_one[actual_header.index("Price [$/MWh]")]) == 10 assert float(row_two[actual_header.index("Import [MW]")]) == 0.0 assert float(row_two[actual_header.index("Export [MW]")]) == 2.0 assert float(row_two[actual_header.index("Price [$/MWh]")]) == 20
def test_batt_efficiency(prices, initial_charge, efficiency, expected_dispatch): power = 1.0 capacity = 1.0 timestep = '1hr' model = energypylinear.Battery( power=power, capacity=capacity, efficiency=efficiency ) info = model.optimize( prices=prices, initial_charge=initial_charge, timestep=timestep ) dispatch = [res['Net [MW]'] for res in info] np.testing.assert_array_equal( dispatch, expected_dispatch )
def test_view_output(): """Test that we output the expected view for a simple problem.""" prices = [10, 20, 10] model = energypylinear.Battery( power=2, capacity=4, efficiency=1.0 ) info = model.optimize( prices=prices, initial_charge=0, timestep='1hr' ) expected_header = ['Import [MW]', 'Export [MW]', 'Gross [MW]', 'Net [MW]', 'Losses [MW]', 'Initial charge [MWh]', 'Final charge [MWh]', 'Prices [$/MWh]', 'Forecast [$/MWh]', 'Actual [$/1hr]', 'Forecast [$/1hr]'] csvfile = io.StringIO() fieldnames = info[0].keys() writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for row in info: writer.writerow(row) csv_rows = csvfile.getvalue().split('\r\n') for header in expected_header: assert(header in csv_rows[0]) # order of the csv columns is different than from expected_header actual_header = [col for col in csv_rows[0].split(',')] row_one = [col for col in csv_rows[1].split(',')] row_two = [col for col in csv_rows[2].split(',')] assert float(row_one[actual_header.index('Import [MW]')]) == 2.0 assert float(row_one[actual_header.index('Export [MW]')]) == 0.0 assert float(row_one[actual_header.index('Prices [$/MWh]')]) == 10 assert float(row_two[actual_header.index('Import [MW]')]) == 0.0 assert float(row_two[actual_header.index('Export [MW]')]) == 2.0 assert float(row_two[actual_header.index('Prices [$/MWh]')]) == 20
def test_cost_calculation(power, capacity, initial_charge, timestep): prices = [10, 20, 30, -90, 50, 2000, -1] model = energypylinear.Battery(power=power, capacity=capacity, efficiency=1.0) info = model.optimize(prices=prices, initial_charge=initial_charge, timestep=timestep) dispatch = [res['Net [MW]'] for res in info if res['Net [MW]'] is not None] timestep = model.timestep step = model.step check_actual_costs = sum( [d * p for d, p in zip(dispatch[:-1], prices[:-1])]) / step actual = 'Actual [$/{}]'.format(timestep) actual_costs = [res[actual] for res in info if res[actual] is not None] assert round(check_actual_costs, 6) == round(sum(actual_costs[:-1]), 6)
def test_power_capacity_initial_charge(power, capacity, initial_charge): prices = [10, 20, 30, 40, 10, 50] model = energypylinear.Battery(power=power, capacity=capacity) info = model.optimize(prices=prices, initial_charge=initial_charge) # check we don't charge or discharge more than battery rating dispatch = map_values(info, "Gross [MW]") assert max(dispatch) <= power assert min(dispatch) >= -power # check we don't exceed battery capacity i_charges = map_values(info, "Initial Charge [MWh]") assert max(i_charges) <= capacity assert min(i_charges) >= 0 f_charges = map_values(info, "Final Charge [MWh]") assert max(f_charges) <= capacity assert min(f_charges) >= 0 # check we set initial charge correctly assert i_charges[0] == initial_charge # check gross is greater or eq to net gross = [abs(x) for x in map_values(info, "Gross [MW]")] net = [abs(x) for x in map_values(info, "Net [MW]")] losses = map_values(info, "Losses [MW]") assert all([g >= n for g, n in zip(gross, net)]) # check losses are smaller or eq to export export = map_values(info, "Export [MW]") losses = map_values(info, "Losses [MW]") assert all([l <= e for l, e in zip(losses, export)])
def test_carbon_optimization(): prices = [10, 50, 10, 50, 10] forecasts = [50, 10, 50, 10, 50] carbons = [0.1, 0.2, 0.3, 0.4, 0.5] model = epl.Battery(power=2, capacity=4, efficiency=1.0) price_opt = model.optimize( prices.copy(), forecasts=forecasts.copy(), carbon=carbons.copy(), freq="30T", objective="price", ) price_cost = sum([d["Actual Cost [$/30T]"] for d in price_opt]) forecast_opt = model.optimize( prices.copy(), forecasts=forecasts.copy(), carbon=carbons.copy(), freq="30T", objective="carbon", ) forecast_cost = sum([d["Actual Cost [$/30T]"] for d in forecast_opt]) carbon_opt = model.optimize( prices.copy(), forecasts=forecasts.copy(), carbon=carbons.copy(), freq="30T", objective="carbon", ) carbon_cost = sum([d["Actual Cost [$/30T]"] for d in carbon_opt]) assert price_cost < carbon_cost assert forecast_cost > price_cost