diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0505337..54f5104 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,9 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt -r requirements-dev.txt + - name: Install Julia dependencies + run: julia -e 'import Pkg; Pkg.add("SDDP");Pkg.add("Clp");Pkg.add("JuMP");Pkg.add("PythonCall")' + - name: Test run: | pytest @@ -32,3 +35,5 @@ jobs: run: | python -m mypy src + + diff --git a/src/multi_stock_bellman_value_calculation.py b/src/multi_stock_bellman_value_calculation.py index 9480c03..f99b5ee 100644 --- a/src/multi_stock_bellman_value_calculation.py +++ b/src/multi_stock_bellman_value_calculation.py @@ -31,6 +31,7 @@ from tqdm import tqdm from pathlib import Path import juliacall +import os as os jl = juliacall.Main @@ -218,6 +219,7 @@ def initialize_antares_problems( verbose: bool = False, save_protos: bool = False, load_from_protos: bool = False, + saving_dir: Optional[str] = None, ) -> Dict[TimeScenarioIndex, AntaresProblem]: """ Creates Instances of the Antares problem for every week / scenario @@ -241,15 +243,19 @@ def initialize_antares_problems( week_range = tqdm(week_range, desc="Problem initialization", colour="Yellow") for week in week_range: for scenario in range(param.len_scenario): - proto_path = ( - output_path - + f"/protos/problem-{param.name_scenario[scenario]}-{week+1}.pkl" - ) - already_processed = Path(proto_path).is_file() and load_from_protos + if saving_dir is not None: + proto_path = ( + saving_dir + + f"/problem-{param.name_scenario[scenario]}-{week+1}.pkl" + ) + already_processed = Path(proto_path).is_file() and load_from_protos + else: + already_processed = False m = AntaresProblem( scenario=scenario, week=week, path=output_path, + saving_directory=saving_dir, itr=1, name_solver=name_solver, name_scenario=param.name_scenario[scenario], @@ -507,6 +513,7 @@ def get_all_costs( list_models: Dict[TimeScenarioIndex, AntaresProblem], multi_stock_management: MultiStockManagement, controls_list: np.ndarray, + saving_dir: Optional[str] = None, verbose: bool = False, already_init: bool = False, keep_intermed_res: bool = False, @@ -529,7 +536,9 @@ def get_all_costs( tot_iter = 0 times = [] n_reservoirs = len(multi_stock_management.dict_reservoirs) - filename = "get_all_costs_run.pkl" + if keep_intermed_res or already_init: + assert saving_dir is not None + filename = saving_dir + "/get_all_costs_run.pkl" # Initializing the n_weeks*n_scenarios*n_controls*n_stocks values to fill shape_controls = list(controls_list.shape) @@ -582,12 +591,13 @@ def Lget_costs( output_path: str, name_solver: str, controls_list: np.ndarray, + saving_directory: str, verbose: bool = False, direct_bellman_calc: bool = True, load_from_protos: bool = False, prefix: str = "", ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - filename = f"{prefix}get_all_costs_run_{output_path[-27:]}.pkl" + filename = f"{saving_directory}/{prefix}get_all_costs_run_{output_path.replace('/','_')[-27:]}.pkl" # Initializing the n_weeks*n_scenarios*n_controls(*n_stocks) values to fill shape_controls = list(controls_list.shape) @@ -609,15 +619,18 @@ def Lget_costs( for week in week_range: if week >= week_start: for scenario in range(param.len_scenario): + if not (os.path.exists(saving_directory)): + os.makedirs(saving_directory) proto_path = ( - output_path - + f"/protos/problem-{param.name_scenario[scenario]}-{week+1}.pkl" + saving_directory + + f"/problem-{param.name_scenario[scenario]}-{week+1}.pkl" ) already_processed = load_from_protos and Path(proto_path).is_file() m = AntaresProblem( scenario=scenario, week=week, path=output_path, + saving_directory=saving_directory, itr=1, name_solver=name_solver, name_scenario=param.name_scenario[scenario], @@ -978,7 +991,7 @@ def select_controls_to_explore( """ # This only a first version: if not rng: - rng = rng = np.random.default_rng() + rng = rng = np.random.default_rng(seed=12345) n_reservoirs = len(multi_stock_management.dict_reservoirs) n_weeks, n_scenarios = param.len_week, param.len_scenario controls_to_explore = np.zeros((n_weeks, n_scenarios, 1, n_reservoirs)) @@ -1237,6 +1250,7 @@ def cutting_plane_method( nSteps_bellman: int, method: str, correlations: np.ndarray, + saving_dir: str, maxiter: Optional[int] = None, precision: float = 5e-2, interp_mode: bool = False, @@ -1382,6 +1396,7 @@ def cutting_plane_method( param=param, multi_stock_management=multi_stock_management, controls_list=controls_list, + saving_directory=saving_dir, output_path=output_path, name_solver=name_solver, verbose=verbose, @@ -1446,6 +1461,7 @@ def iter_bell_vals( starting_pt: np.ndarray, nSteps_bellman: int, method: str, + saving_dir: str, name_solver: str = "CLP", precision: float = 1e-2, maxiter: int = 2, @@ -1534,6 +1550,7 @@ def iter_bell_vals( param=param, multi_stock_management=multi_stock_management, output_path=output_path, + saving_directory=saving_dir, name_solver=name_solver, controls_list=controls_list, load_from_protos=True, @@ -1574,6 +1591,7 @@ def iter_bell_vals( name_solver=name_solver, starting_pt=starting_pt, costs_approx=costs_approx, + saving_dir=saving_dir, costs=costs, future_costs_approx=future_costs_approx, nSteps_bellman=nSteps_bellman, @@ -1626,6 +1644,7 @@ def sddp_cutting_planes( costs_approx: LinearCostEstimator, costs: np.ndarray, level_init: np.ndarray, + saving_dir: str, normalization: Dict[str, float], maxiter: Optional[int] = None, precision: float = 1e-2, @@ -1675,6 +1694,7 @@ def sddp_cutting_planes( param.len_scenario, julia_reservoirs, julia_capp, + saving_dir, normalization["euro"], normalization["energy"], ) @@ -1688,6 +1708,7 @@ def sddp_cutting_planes( param.len_scenario, julia_reservoirs, julia_capp, + saving_dir, normalization["euro"], normalization["energy"], ) @@ -1716,6 +1737,7 @@ def sddp_cutting_planes( multi_stock_management=multi_stock_management, controls_list=controls, output_path=output_path, + saving_directory=saving_dir, name_solver=name_solver, verbose=False, load_from_protos=True, @@ -1763,6 +1785,7 @@ def iter_bell_vals_v2( output_path: str, n_controls_init: int, starting_pt: np.ndarray, + saving_dir: str, normalization: Dict[str, float], name_solver: str = "CLP", precision: float = 1e-2, @@ -1800,6 +1823,7 @@ def iter_bell_vals_v2( param=param, multi_stock_management=multi_stock_management, output_path=output_path, + saving_directory=saving_dir, name_solver=name_solver, controls_list=controls_list, load_from_protos=False, @@ -1816,6 +1840,7 @@ def iter_bell_vals_v2( output_path=output_path, name_solver=name_solver, costs_approx=costs_approx, + saving_dir=saving_dir, costs=costs, level_init=starting_pt, precision=precision, diff --git a/src/optimization.py b/src/optimization.py index 5aad338..f331b79 100644 --- a/src/optimization.py +++ b/src/optimization.py @@ -52,6 +52,7 @@ def __init__( week: int, path: str, itr: int = 1, + saving_directory: Optional[str] = None, name_solver: str = "CLP", name_scenario: int = -1, already_processed: bool = False, @@ -67,6 +68,8 @@ def __init__( Week considered path:str : Path where mps files are stored + path:str : + Path where intermediate result are stored itr:int : Antares iteration considered (Default value = 1) @@ -85,18 +88,19 @@ def __init__( mps_path = path + f"/problem-{name_scenario}-{week+1}--optim-nb-{itr}.mps" model = model_builder.ModelBuilder() # type: ignore[no-untyped-call] model.import_from_mps_file(mps_path) + model_proto = model.export_to_proto() + else: + assert saving_directory is not None + proto_path = saving_directory + f"/problem-{name_scenario}-{week+1}.pkl" try: - model_proto = model.export_to_proto() + with open(proto_path, "rb") as file: + model_proto, var_and_cstr_ids = pkl.load(file) + self.stored_variables_and_constraints_ids = var_and_cstr_ids except FileNotFoundError: print( "Proto directory not found: Make sure the proto directory has been created within the mps directory" ) raise FileNotFoundError - else: - proto_path = path + f"/protos/problem-{name_scenario}-{week+1}.pkl" - with open(proto_path, "rb") as file: - model_proto, var_and_cstr_ids = pkl.load(file) - self.stored_variables_and_constraints_ids = var_and_cstr_ids solver = pywraplp.Solver.CreateSolver(name_solver) assert solver, "Couldn't find any supported solver" diff --git a/src/sddp.jl b/src/sddp.jl index 9d4d0fc..0db5407 100644 --- a/src/sddp.jl +++ b/src/sddp.jl @@ -1,11 +1,9 @@ module Jl_SDDP using SDDP -using HiGHS using Clp using JuMP using Base -using Xpress using PythonCall # Define types for structured data @@ -36,7 +34,7 @@ struct Normalizer end # Function to convert Python data to Julia structures -function formater(n_weeks, n_scenarios, reservoirs_data, costs_approx_data, norm_euros::Float64=1e7, norm_enrgy::Float64=1e5) +function formater(n_weeks, n_scenarios, reservoirs_data, costs_approx_data, saving_dir::String, norm_euros::Float64=1e7, norm_enrgy::Float64=1e5) round_energy = 4 round_euro = 8 round_price = 5 @@ -66,7 +64,7 @@ function formater(n_weeks, n_scenarios, reservoirs_data, costs_approx_data, norm round.(pyconvert(Matrix, costs_approx_data[i,j]["duals"]) /norm_price, digits=round_price), # € / MWh ) for j in 1:size_ca_data[2]] for i in 1:size_ca_data[1]] - return (n_weeks, n_scenarios, reservoirs, costs_approx, norms) + return (n_weeks, n_scenarios, reservoirs, costs_approx, norms, saving_dir) end @@ -76,7 +74,7 @@ function generate_model(n_weeks::Int, n_scenarios::Int, reservoirs::Vector{Main. stages = 3*n_weeks, sense = :Min, lower_bound = 0.0, - optimizer = Xpress.Optimizer, + optimizer = Clp.Optimizer, # optimizer = HiGHS.Optimizer, # cut_oracle = SDDP.LevelOneCutOracle() ) do subproblem, stage @@ -165,17 +163,17 @@ function stability_report(model) return SDDP.numerical_stability_report(model) end -function reinit_cuts(n_weeks::Int, n_scenarios::Int, reservoirs::Vector{Main.Jl_SDDP.Reservoir}, costs_approx::Vector{Vector{Main.Jl_SDDP.LinInterp}}, norms::Normalizer) +function reinit_cuts(n_weeks::Int, n_scenarios::Int, reservoirs::Vector{Main.Jl_SDDP.Reservoir}, costs_approx::Vector{Vector{Main.Jl_SDDP.LinInterp}}, norms::Normalizer, saving_dir::String) model = generate_model(n_weeks, n_scenarios, reservoirs, costs_approx, norms) - SDDP.write_cuts_to_file(model, "dev/cuts/sddp_current_cuts") + SDDP.write_cuts_to_file(model, saving_dir*"/sddp_current_cuts") end -function manage_reservoirs(n_weeks::Int, n_scenarios::Int, reservoirs::Vector{Main.Jl_SDDP.Reservoir}, costs_approx::Vector{Vector{Main.Jl_SDDP.LinInterp}}, norms::Normalizer) +function manage_reservoirs(n_weeks::Int, n_scenarios::Int, reservoirs::Vector{Main.Jl_SDDP.Reservoir}, costs_approx::Vector{Vector{Main.Jl_SDDP.LinInterp}}, norms::Normalizer, saving_dir::String) model = generate_model(n_weeks, n_scenarios, reservoirs, costs_approx, norms) - SDDP.read_cuts_from_file(model, "dev/cuts/sddp_current_cuts") + SDDP.read_cuts_from_file(model, saving_dir*"/sddp_current_cuts") # Training the model SDDP.train(model, stopping_rules = [SDDP.BoundStalling(40, 1e1)], iteration_limit = 700, cut_type = SDDP.MULTI_CUT) - SDDP.write_cuts_to_file(model, "dev/cuts/sddp_current_cuts") + SDDP.write_cuts_to_file(model, saving_dir*"/sddp_current_cuts") #Simulating simulation_results = get_trajectory(n_weeks, n_scenarios, reservoirs, model, norms) diff --git a/tests/test_bellman_value_exact_two_stocks.py b/tests/test_bellman_value_exact_two_stocks.py index 5ebb9d8..fa2436a 100644 --- a/tests/test_bellman_value_exact_two_stocks.py +++ b/tests/test_bellman_value_exact_two_stocks.py @@ -233,14 +233,14 @@ def test_solve_with_bellman_multi_stock() -> None: take_into_account_z_and_y=True, ) - assert Vu == pytest.approx(37137636.63290527) + assert Vu == pytest.approx(37137635.6914607) assert slope == pytest.approx( - {"area_1": -73.24028513205391, "area_2": -73.23898617935389} + {"area_1": -73.24037883117816, "area_2": -73.23892027017814} ) assert xf == pytest.approx( - {"area_1": 315840.91164340125, "area_2": 668042.065886599} + {"area_1": 315840.9116434012, "area_2": 668042.0658865988} ) @@ -281,16 +281,16 @@ def test_bellman_value_exact_calculation_multi_stock() -> None: ) assert lb == pytest.approx(419088906.63159156) - assert ub == pytest.approx(798837417.3288715) + assert ub == pytest.approx(798837417.3288709) assert vb[0]["intercept"] == pytest.approx( np.array( [ [9.9394519e09, 5.4620790e09, 3.9168504e09, 3.8727749e09, 3.8727749e09], [7.1669325e09, 2.6933955e09, 1.1481676e09, 1.1040920e09, 1.1040919e09], - [5.2626033e09, 1.2132852e09, 3.5723606e08, 3.0289030e08, 3.0289018e08], - [3.5595026e09, 7.8633613e08, 3.2175795e08, 2.8026579e08, 2.8026570e08], - [2.9601462e09, 7.8094682e08, 3.1681254e08, 2.8026576e08, 2.8026570e08], + [5.2626033e09, 1.2132852e09, 3.5723610e08, 3.0289034e08, 3.0289021e08], + [3.4945702e09, 7.5961869e08, 3.2175792e08, 2.8026576e08, 2.8026566e08], + [2.8697385e09, 7.2652160e08, 2.9893891e08, 2.8026563e08, 2.8026560e08], ] ) ) @@ -319,11 +319,11 @@ def test_bellman_value_exact_calculation_multi_stock() -> None: -2.1557803e02, ], [ - -4.7500000e03, - -1.6168346e02, - -1.3884966e02, - -1.1292289e-03, - -3.4511198e-05, + -8.9999971e03, + -2.0065604e03, + -1.3885049e02, + -1.4066651e-03, + -1.3115490e-03, ], [0.0000000e00, 0.0000000e00, 0.0000000e00, 0.0000000e00, 0.0000000e00], ] @@ -354,17 +354,17 @@ def test_bellman_value_exact_calculation_multi_stock() -> None: 0.0000000e00, ], [ - -1.3333334e04, + -1.2000000e04, -2.6754175e03, - -1.8513576e02, - -1.2687439e-03, + -1.8513564e02, + -1.2703377e-03, 0.0000000e00, ], [ - -1.1000000e04, + -1.1000001e04, -2.6754175e03, - -1.5888432e02, - -1.2624825e-03, + -1.5355794e02, + -1.1774984e-03, 0.0000000e00, ], ] diff --git a/tests/test_bellman_value_precalculated_reward_two_stocks.py b/tests/test_bellman_value_precalculated_reward_two_stocks.py new file mode 100644 index 0000000..4d9b9db --- /dev/null +++ b/tests/test_bellman_value_precalculated_reward_two_stocks.py @@ -0,0 +1,317 @@ +from functions_iterative import ( + TimeScenarioParameter, + ReservoirManagement, +) +from multi_stock_bellman_value_calculation import * +from calculate_reward_and_bellman_values import ( + MultiStockManagement, +) +from read_antares_data import Reservoir +import pytest +import numpy as np + + +def test_bellman_value_precalculated_multi_stock() -> None: + + param = TimeScenarioParameter(len_week=5, len_scenario=1) + + reservoir_1 = Reservoir("test_data/two_nodes", "area_1") + reservoir_management_1 = ReservoirManagement( + reservoir=reservoir_1, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + reservoir_2 = Reservoir("test_data/two_nodes", "area_2") + reservoir_management_2 = ReservoirManagement( + reservoir=reservoir_2, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + multi_management = MultiStockManagement( + [reservoir_management_1, reservoir_management_2] + ) + + levels, _, bellman_costs, bellman_controls, slopes, _ = precalculated_method( + param=param, + multi_stock_management=multi_management, + output_path="test_data/two_nodes", + xNsteps=5, + Nsteps_bellman=5, + name_solver="CLP", + controls_looked_up="line+diagonal", + verbose=True, + ) + + assert levels == pytest.approx( + np.array( + [ + [ + [0.0, 628377.6569], + [277853.0681, 0.0], + [175340.436, 628377.6569], + [277853.0681, 396540.564], + [409896.721, 628377.6569], + [277853.0681, 927000.529], + [589466.8605, 628377.6569], + [277853.0681, 1333106.7645], + [769037.0, 628377.6569], + [277853.0681, 1739213.0], + ], + [ + [0.0, 628377.6569], + [277853.0681, 0.0], + [178416.584, 628377.6569], + [277853.0681, 403497.416], + [486031.384, 628377.6569], + [277853.0681, 1099182.616], + [627534.192, 628377.6569], + [277853.0681, 1419197.808], + [769037.0, 628377.6569], + [277853.0681, 1739213.0], + ], + [ + [0.0, 628377.6569], + [277853.0681, 0.0], + [180723.695, 628377.6569], + [277853.0681, 408715.055], + [488338.495, 628377.6569], + [277853.0681, 1104400.255], + [628687.7475, 628377.6569], + [277853.0681, 1421806.6275], + [769037.0, 628377.6569], + [277853.0681, 1739213.0], + ], + [ + [0.0, 628377.6569], + [277853.0681, 0.0], + [183030.806, 628377.6569], + [277853.0681, 413932.694], + [491414.643, 628377.6569], + [277853.0681, 1111357.107], + [630225.8215, 628377.6569], + [277853.0681, 1425285.0535], + [769037.0, 628377.6569], + [277853.0681, 1739213.0], + ], + [ + [0.0, 628377.6569], + [277853.0681, 0.0], + [185337.917, 628377.6569], + [277853.0681, 419150.333], + [493721.754, 628377.6569], + [277853.0681, 1116574.746], + [631379.377, 628377.6569], + [277853.0681, 1427893.873], + [769037.0, 628377.6569], + [277853.0681, 1739213.0], + ], + ] + ) + ) + + assert bellman_controls == pytest.approx( + np.array( + [ + [ + [[0.0], [181530.9463735]], + [[129383.263], [-304244.94783314]], + [[26870.633], [181530.95748161]], + [[129383.263], [61185.617]], + [[160609.60919247], [124748.89981205]], + [[129383.263], [155975.24600452]], + [[219514.397], [124748.89981205]], + [[103827.55582937], [419664.0]], + [[306936.0], [124748.89981205]], + [[103827.55582937], [419664.0]], + ], + [ + [[13776.0], [263393.096]], + [[217424.96382709], [-28367.50984833]], + [[52773.40570775], [144306.62825704]], + [[116288.634], [78091.78486466]], + [[193278.43809968], [197279.2502553]], + [[116288.634], [203738.091]], + [[259392.28273827], [263393.096]], + [[116288.634], [419664.0]], + [[306936.0], [263393.096]], + [[116288.634], [419664.0]], + ], + [ + [[0.0], [256443.244]], + [[144342.19194326], [-294442.21953368]], + [[11011.94606618], [184605.26872985]], + [[146538.83712493], [41945.38354514]], + [[175156.44001992], [246067.35263216]], + [[86019.81249675], [419664.0]], + [[206364.00587073], [256443.244]], + [[86019.81249675], [419664.0]], + [[298585.86061946], [256443.244]], + [[113212.486], [419664.0]], + ], + [ + [[0.0], [251428.605]], + [[138754.63237131], [-310705.16724054]], + [[16174.115], [79192.47066511]], + [[110996.375], [-16964.93476829]], + [[19371.83979269], [85278.13317882]], + [[0.0], [419664.0]], + [[158812.70351082], [95447.16468272]], + [[0.0], [419664.0]], + [[297623.88351082], [95447.16468272]], + [[0.0], [419664.0]], + ], + [ + [[0.0], [-272320.61380624]], + [[34066.10375969], [-322182.0]], + [[14066.07670537], [-77354.29218551]], + [[12630.48903407], [-200568.15764549]], + [[153336.66361319], [31852.9091698]], + [[15945.4810059], [419664.0]], + [[290994.29361319], [31852.9091698]], + [[15945.4810059], [419664.0]], + [[306936.0], [31993.30673435]], + [[15945.4810059], [419664.0]], + ], + ] + ) + ) + + assert bellman_costs == pytest.approx( + np.array( + [ + [ + 1.00000010e16, + 1.00000013e16, + 1.00000000e16, + 1.00000000e16, + 1.00000000e16, + 1.00000000e16, + 1.00000005e16, + 1.00000014e16, + 1.00000014e16, + 1.00000039e16, + ], + [ + 1.00000025e16, + 1.00000041e16, + 1.00000003e16, + 1.00000002e16, + 1.00000000e16, + 1.00000001e16, + 1.00000005e16, + 1.00000013e16, + 1.00000013e16, + 1.00000048e16, + ], + [ + 1.00000034e16, + 1.00000059e16, + 1.00000003e16, + 1.00000008e16, + 1.00000001e16, + 1.00000001e16, + 1.00000005e16, + 1.00000010e16, + 1.00000009e16, + 1.00000036e16, + ], + [ + 1.00000042e16, + 1.00000061e16, + 1.00000004e16, + 1.00000011e16, + 1.00000001e16, + 1.00000001e16, + 1.00000005e16, + 1.00000010e16, + 1.00000009e16, + 1.00000034e16, + ], + [ + 1.00000050e16, + 1.00000035e16, + 1.00000000e16, + 1.00000000e16, + 1.00000000e16, + 1.00000000e16, + 1.00000004e16, + 1.00000009e16, + 1.00000008e16, + 1.00000033e16, + ], + ] + ) + ) + + assert slopes == pytest.approx( + np.array( + [ + [ + [-6.00000000e03, 0.00000000e00], + [-2.11898256e03, -6.00000000e03], + [-1.15170000e02, 0.00000000e00], + [-1.15170000e02, -1.15170000e02], + [0.00000000e00, 0.00000000e00], + [0.00000000e00, 0.00000000e00], + [3.00000000e03, 0.00000000e00], + [0.00000000e00, 6.00000000e03], + [6.00000000e03, 0.00000000e00], + [0.00000000e00, 6.00000000e03], + ], + [ + [-1.30000000e04, 0.00000000e00], + [-5.11898000e03, -1.20000000e04], + [-9.00000000e03, 0.00000000e00], + [-2.75281053e03, -3.11517000e03], + [-5.95800000e01, -5.95800000e01], + [-1.19160000e02, 0.00000000e00], + [3.00000000e03, -1.19160000e02], + [-1.19160000e02, 6.00000000e03], + [9.00000000e03, -1.19160000e02], + [-1.19160000e02, 1.20000000e04], + ], + [ + [-1.90000000e04, 0.00000000e00], + [-7.04933707e03, -1.39835533e04], + [-2.75281035e03, -3.11516983e03], + [-5.85635493e03, -6.06353718e03], + [-1.22642731e02, -1.31127492e02], + [-1.66240000e02, 0.00000000e00], + [2.94042000e03, -3.20801251e02], + [-1.66240000e02, 3.00000000e03], + [3.00000000e03, -5.00000000e02], + [-1.66240000e02, 1.20000000e04], + ], + [ + [-2.50000000e04, 0.00000000e00], + [-9.04534212e03, -1.33182190e04], + [-3.01822810e03, -3.11517000e03], + [-6.00811264e03, -6.06354000e03], + [-1.58015064e-01, -1.38714297e02], + [-1.66240000e02, 0.00000000e00], + [3.00000000e03, -1.38850000e02], + [-1.66240000e02, 3.00000000e03], + [3.00000000e03, -1.38850000e02], + [-1.66240000e02, 9.00000000e03], + ], + [ + [-3.10000000e04, 0.00000000e00], + [-9.04534000e03, -1.93182200e04], + [-2.70028202e02, -1.13365718e02], + [-2.70028202e02, -1.13365718e02], + [0.00000000e00, 0.00000000e00], + [-1.66240000e02, 0.00000000e00], + [3.00000000e03, 0.00000000e00], + [-1.66240000e02, 3.00000000e03], + [3.00000000e03, 0.00000000e00], + [-1.66240000e02, 9.00000000e03], + ], + ] + ) + ) diff --git a/tests/test_fast_uv_two_stocks.py b/tests/test_fast_uv_two_stocks.py new file mode 100644 index 0000000..95a057b --- /dev/null +++ b/tests/test_fast_uv_two_stocks.py @@ -0,0 +1,154 @@ +from functions_iterative import ( + TimeScenarioParameter, + ReservoirManagement, +) +from multi_stock_bellman_value_calculation import * +from calculate_reward_and_bellman_values import ( + MultiStockManagement, +) +from read_antares_data import Reservoir +import pytest + + +def test_fast_usage_values_multi_stock() -> None: + + param = TimeScenarioParameter(len_week=5, len_scenario=1) + + reservoir_1 = Reservoir("test_data/two_nodes", "area_1") + reservoir_management_1 = ReservoirManagement( + reservoir=reservoir_1, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + reservoir_2 = Reservoir("test_data/two_nodes", "area_2") + reservoir_management_2 = ReservoirManagement( + reservoir=reservoir_2, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + multi_management = MultiStockManagement( + [reservoir_management_1, reservoir_management_2] + ) + + mrg_prices = { + "area_1": dict(mean=42.77, std=31.80), + "area_2": dict(mean=41.71, std=3.53), + } + + uvs = generate_fast_uvs_v2( + param=param, multi_stock_management=multi_management, mrg_prices=mrg_prices + ) + + assert uvs[0] == pytest.approx( + np.array( + [ + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [100.0, 100.0], + [74.57, 45.24], + [72.93923077, 45.05897436], + [71.30846154, 44.87794872], + [69.67769231, 44.69692308], + [68.04692308, 44.51589744], + [66.41615385, 44.33487179], + [64.78538462, 44.15384615], + [63.15461538, 43.97282051], + [61.52384615, 43.79179487], + [59.89307692, 43.61076923], + [58.26230769, 43.42974359], + [56.63153846, 43.24871795], + [55.00076923, 43.06769231], + [53.37, 42.88666667], + [51.73923077, 42.70564103], + [50.10846154, 42.52461538], + [48.47769231, 42.34358974], + [46.84692308, 42.1625641], + [45.21615385, 41.98153846], + [43.58538462, 41.80051282], + [41.95461538, 41.61948718], + [40.32384615, 41.43846154], + [38.69307692, 41.2574359], + [37.06230769, 41.07641026], + [35.43153846, 40.89538462], + [33.80076923, 40.71435897], + [32.17, 40.53333333], + [30.53923077, 40.35230769], + [28.90846154, 40.17128205], + [27.27769231, 39.99025641], + [25.64692308, 39.80923077], + [24.01615385, 39.62820513], + [22.38538462, 39.44717949], + [20.75461538, 39.26615385], + [19.12384615, 39.08512821], + [17.49307692, 38.90410256], + [15.86230769, 38.72307692], + [14.23153846, 38.54205128], + [12.60076923, 38.36102564], + [10.97, 38.18], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + ] + ) + ) diff --git a/tests/test_iterative_two_stocks.py b/tests/test_iterative_two_stocks.py new file mode 100644 index 0000000..9611a0e --- /dev/null +++ b/tests/test_iterative_two_stocks.py @@ -0,0 +1,789 @@ +from functions_iterative import ( + TimeScenarioParameter, + ReservoirManagement, +) +from multi_stock_bellman_value_calculation import * +from calculate_reward_and_bellman_values import ( + MultiStockManagement, +) +from read_antares_data import Reservoir +import numpy as np +import pytest + + +def test_bellman_value_iterative_method() -> None: + + param = TimeScenarioParameter(len_week=5, len_scenario=1) + + reservoir_1 = Reservoir("test_data/two_nodes", "area_1") + reservoir_management_1 = ReservoirManagement( + reservoir=reservoir_1, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + reservoir_2 = Reservoir("test_data/two_nodes", "area_2") + reservoir_management_2 = ReservoirManagement( + reservoir=reservoir_2, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + multi_management = MultiStockManagement( + [reservoir_management_1, reservoir_management_2] + ) + + ( + bell_costs, + _, + _, + levels, + opt_trajectory, + _, + ) = iter_bell_vals( + param=param, + multi_stock_management=multi_management, + n_controls_init=2, + output_path="test_data/two_nodes", + saving_dir="dev/test", + name_solver="CLP", + nSteps_bellman=5, + starting_pt=np.array( + [ + mng.reservoir.bottom_rule_curve[0] * 0.7 + + mng.reservoir.upper_rule_curve[0] * 0.3 + for mng in multi_management.dict_reservoirs.values() + ] + ), + maxiter=10, + precision=1e-3, + method="lines", + correlations=None, + interp_mode=False, + divisor={"euro": 1e8, "energy": 1e4}, + rounding=6, + verbose=False, + keep_intermed_res=False, + already_init=True, + ) + + assert bell_costs == pytest.approx( + np.array( + [ + [ + 1.00000082e16, + 1.00000026e16, + 1.00000029e16, + 1.00000013e16, + 1.00000007e16, + 1.00000013e16, + 1.00000008e16, + 1.00000028e16, + 1.00000014e16, + 1.00000059e16, + ], + [ + 1.00000098e16, + 1.00000049e16, + 1.00000048e16, + 1.00000034e16, + 1.00000017e16, + 1.00000034e16, + 1.00000008e16, + 1.00000047e16, + 1.00000010e16, + 1.00000082e16, + ], + [ + 1.00000086e16, + 1.00000063e16, + 1.00000039e16, + 1.00000027e16, + 1.00000009e16, + 1.00000027e16, + 1.00000008e16, + 1.00000036e16, + 1.00000013e16, + 1.00000062e16, + ], + [ + 1.00000072e16, + 1.00000055e16, + 1.00000029e16, + 1.00000018e16, + 1.00000001e16, + 1.00000018e16, + 1.00000005e16, + 1.00000028e16, + 1.00000009e16, + 1.00000052e16, + ], + [ + 1.00000056e16, + 1.00000035e16, + 1.00000027e16, + 1.00000017e16, + 1.00000000e16, + 1.00000017e16, + 1.00000004e16, + 1.00000027e16, + 1.00000008e16, + 1.00000050e16, + ], + ] + ) + ) + + assert levels == pytest.approx( + np.array( + [ + [ + [0.0, 473537.71], + [275341.70467312, 0.0], + [175340.436, 473537.71], + [275341.70467312, 396540.564], + [409896.721, 473537.71], + [275341.70467312, 927000.529], + [589466.8605, 473537.71], + [275341.70467312, 1333106.7645], + [769037.0, 473537.71], + [275341.70467312, 1739213.0], + ], + [ + [0.0, 861645.70886979], + [319468.07, 0.0], + [178416.584, 861645.70886979], + [319468.07, 403497.416], + [486031.384, 861645.70886979], + [319468.07, 1099182.616], + [627534.192, 861645.70886979], + [319468.07, 1419197.808], + [769037.0, 861645.70886979], + [319468.07, 1739213.0], + ], + [ + [0.0, 652959.3016887], + [305692.07, 0.0], + [180723.695, 652959.3016887], + [305692.07, 408715.055], + [488338.495, 652959.3016887], + [305692.07, 1104400.255], + [628687.7475, 652959.3016887], + [305692.07, 1421806.6275], + [769037.0, 652959.3016887], + [305692.07, 1739213.0], + ], + [ + [0.0, 413932.694], + [291825.07, 0.0], + [183030.806, 413932.694], + [291825.07, 413932.694], + [491414.643, 413932.694], + [291825.07, 1111357.107], + [630225.8215, 413932.694], + [291825.07, 1425285.0535], + [769037.0, 413932.694], + [291825.07, 1739213.0], + ], + [ + [0.0, 628377.6569], + [277853.0681, 0.0], + [185337.917, 628377.6569], + [277853.0681, 419150.333], + [493721.754, 628377.6569], + [277853.0681, 1116574.746], + [631379.377, 628377.6569], + [277853.0681, 1427893.873], + [769037.0, 628377.6569], + [277853.0681, 1739213.0], + ], + ] + ) + ) + + assert opt_trajectory == pytest.approx( + np.array( + [ + [[277853.0681], [628377.6569]], + [[291825.07], [413932.694]], + [[305692.07], [652959.3016887]], + [[178416.584], [755857.14726466]], + [[192192.58], [396540.564]], + [[205989.58], [703083.3378084]], + ] + ) + ) + + +def test_bellman_value_iterative_method_with_sddp() -> None: + + param = TimeScenarioParameter(len_week=5, len_scenario=1) + + reservoir_1 = Reservoir("test_data/two_nodes", "area_1") + reservoir_management_1 = ReservoirManagement( + reservoir=reservoir_1, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + reservoir_2 = Reservoir("test_data/two_nodes", "area_2") + reservoir_management_2 = ReservoirManagement( + reservoir=reservoir_2, + penalty_bottom_rule_curve=3000, + penalty_upper_rule_curve=3000, + penalty_final_level=3000, + force_final_level=True, + ) + + multi_management = MultiStockManagement( + [reservoir_management_1, reservoir_management_2] + ) + + _, bellman_costs, _, _ = iter_bell_vals_v2( + param=param, + multi_stock_management=multi_management, + n_controls_init=2, + output_path="test_data/two_nodes", + saving_dir="dev/test", + starting_pt=np.array( + [ + mng.reservoir.bottom_rule_curve[0] * 0.7 + + mng.reservoir.upper_rule_curve[0] * 0.3 + for mng in multi_management.dict_reservoirs.values() + ] + ), + normalization={"euro": 1e9, "energy": 1e4}, + name_solver="CLP", + maxiter=3, + precision=1e-2, + interp_mode=False, + verbose=False, + ) + + assert np.array(bellman_costs) == pytest.approx( + np.array( + [ + [ + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + ], + [ + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + ], + [ + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + ], + [ + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + ], + [ + 1.08894279e09, + 1.03183608e09, + 9.74729376e08, + 9.17622668e08, + 8.60515960e08, + 8.03409252e08, + 7.46302545e08, + 6.89195837e08, + 6.32089129e08, + 5.74982421e08, + 5.17875713e08, + 4.60769005e08, + 4.03662297e08, + 3.46555589e08, + 2.89448881e08, + 2.32342173e08, + 1.75235465e08, + 1.36094554e08, + 1.01830530e08, + 6.75665050e07, + 4.03626535e07, + 1.75199703e07, + 3.19143564e05, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 1.33484703e07, + 3.91783663e07, + 6.50082624e07, + 9.08381584e07, + 1.16668054e08, + 1.42497950e08, + 1.68327847e08, + 1.94157743e08, + 2.19987639e08, + 2.45817535e08, + 2.71647431e08, + 2.97477327e08, + 3.23307223e08, + 3.49137119e08, + 3.74967015e08, + 4.09547347e08, + 4.46798584e08, + 4.84049822e08, + 5.21301059e08, + 5.58552297e08, + 5.95803535e08, + 6.33054772e08, + 6.70306010e08, + 7.07557248e08, + 7.44808485e08, + 7.82059723e08, + 8.19310960e08, + 8.56562198e08, + 8.93813436e08, + 9.31064673e08, + 9.68315911e08, + 1.00556715e09, + 1.04281839e09, + 1.08006962e09, + 1.11732086e09, + 1.15457210e09, + 1.19182334e09, + 1.22907457e09, + 1.26632581e09, + 1.30357705e09, + 1.34082829e09, + 1.37807952e09, + 1.41533076e09, + 1.45258200e09, + ], + ] + ) + )