diff --git a/client/client.gen.go b/client/client.gen.go index 37e49d44f..cf50a8c0f 100644 --- a/client/client.gen.go +++ b/client/client.gen.go @@ -74,6 +74,10 @@ type BatteryConfig struct { // PDemand Minimum charge demand per time step (Wh) PDemand []float32 `json:"p_demand,omitempty"` + // SCapacity The capacity at 100% SOC in Wh. If not specified s_capacity will be set to s_max. + // s_initial must be less or equal s_capacity, otherwise the optimization will return an error. + SCapacity float32 `json:"s_capacity,omitempty"` + // SGoal Goal state of charge for this battery at each time step (Wh) SGoal []float32 `json:"s_goal,omitempty"` diff --git a/openapi.yaml b/openapi.yaml index 7b70bd4d8..7447c542e 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -63,7 +63,8 @@ paths: $ref: "#/components/schemas/OptimizationInput" example: batteries: - - s_min: 5000 + - s_capacity: 52000 + s_min: 5000 s_max: 50000 s_initial: 15000 s_goal: [0, 0, 40000, 0, 0, 0] @@ -207,6 +208,12 @@ components: - True: The battery can discharge to the grid at any time. The actual decision is subject to the optimization. - False: (default) The battery cannot be discharged while power is exported to the grid. + s_capacity: + type: number + minimum: 0 + description: | + The capacity at 100% SOC in Wh. If not specified s_capacity will be set to s_max. + s_initial must be less or equal s_capacity, otherwise the optimization will return an error. s_min: type: number minimum: 0 diff --git a/src/optimizer/app.py b/src/optimizer/app.py index a7a823f6b..860d1b406 100644 --- a/src/optimizer/app.py +++ b/src/optimizer/app.py @@ -67,6 +67,7 @@ def handle_validation_error(error): battery_config_model = api.model('BatteryConfig', { 'charge_from_grid': fields.Boolean(required=False, description='Controls whether the battery can be charged from the grid.'), 'discharge_to_grid': fields.Boolean(required=False, description='Controls whether the battery can discharge to grid.'), + 's_capacity': fields.Float(required=False, description='Capacity at 100% state of charge (Wh)'), 's_min': fields.Float(required=True, description='Minimum state of charge (Wh)'), 's_max': fields.Float(required=True, description='Maximum state of charge (Wh)'), 's_initial': fields.Float(required=True, description='Initial state of charge (Wh)'), @@ -156,6 +157,7 @@ def post(self): batteries.append(BatteryConfig( charge_from_grid=bat_data.get('charge_from_grid', False), discharge_to_grid=bat_data.get('discharge_to_grid', False), + s_capacity=bat_data.get('s_capacity', bat_data['s_max']), s_min=bat_data['s_min'], s_max=bat_data['s_max'], s_initial=bat_data['s_initial'], diff --git a/src/optimizer/optimizer.py b/src/optimizer/optimizer.py index 5fc11b002..d03026e12 100644 --- a/src/optimizer/optimizer.py +++ b/src/optimizer/optimizer.py @@ -25,6 +25,7 @@ class GridConfig: class BatteryConfig: charge_from_grid: bool discharge_to_grid: bool + s_capacity: float s_min: float s_max: float s_initial: float @@ -83,6 +84,7 @@ def __init__(self, strategy: OptimizationStrategy, grid: GridConfig, batteries: # scaling for penalty parameters. Make sure goal_penalty is always positive self.prc_e_goal_pen = np.min([self.max_import_price, 0.1e-3]) * 10e1 self.prc_p_goal_pen = np.min([self.max_import_price, 0.1e-3]) * np.max(self.time_series.dt) / 3600 * 10e1 + self.prc_soc_exc_pen = np.min([self.max_import_price, 0.1e-3]) * 10e2 # penalty for exceeding grid import limit. Result shall not become infeasible but report the violation # with helpful information @@ -136,7 +138,7 @@ def _setup_variables(self): self.variables['s'] = {} for i, bat in enumerate(self.batteries): self.variables['s'][i] = [ - pulp.LpVariable(f"s_{i}_{t}", lowBound=bat.s_min, upBound=bat.s_max) + pulp.LpVariable(f"s_{i}_{t}", lowBound=0, upBound=bat.s_capacity) for t in self.time_steps ] @@ -157,6 +159,10 @@ def _setup_variables(self): for t in self.time_steps: self.variables['p_demand_pen'][i][t] = pulp.LpVariable(f"p_demand_pen_{i}_{t}", lowBound=0) + # penalty variable for staying above max SOC and below min SOC + self.variables['s_max_pen'] = [[pulp.LpVariable(f"s_max_pen_{i}_{t}", lowBound=0) for t in self.time_steps] for i in range(len(self.batteries))] + self.variables['s_min_pen'] = [[pulp.LpVariable(f"s_min_pen_{i}_{t}", lowBound=0) for t in self.time_steps] for i in range(len(self.batteries))] + # Grid import/export variables [Wh] self.variables['n'] = [pulp.LpVariable(f"n_{t}", lowBound=0) for t in self.time_steps] self.variables['e'] = [pulp.LpVariable(f"e_{t}", lowBound=0) for t in self.time_steps] @@ -248,6 +254,12 @@ def _setup_target_function(self): if self.is_grid_demand_rate_active: objective += - self.grid.prc_p_exc_imp * self.variables['p_max_imp_exc'] + ############################################################################ + # Penalties for exceeding battery SOC limits at start + for i, bat in enumerate(self.batteries): + for t in self.time_steps: + objective += - self.prc_soc_exc_pen * (self.variables['s_max_pen'][i][t] + self.variables['s_min_pen'][i][t]) + ############################################################################ # Penalties for goals that cannot be met for i, bat in enumerate(self.batteries): @@ -275,7 +287,8 @@ def _setup_target_function(self): # penalty for exceeding the grid export limit if self.grid.p_max_exp is not None: # negative target function contribution in a maximizing optimization - objective += - self.prc_e_grid_exp_pen * self.variables['e_exp_lim_exc'][t] + # decrease penalty slightly over time to push limit exceeding to late times + objective += - self.prc_e_grid_exp_pen * (1.0 - t * 1e-5) * self.variables['e_exp_lim_exc'][t] ############################################################################# # Secondary strategies to implement preferences without impact to actual cost @@ -311,8 +324,6 @@ def _add_energy_balance_constraints(self): Add constraints related to the energy balance to the model. """ - self.time_steps = range(self.T) - # Constraint (2): Power balance for each time step: # - solar yield # - household consumption @@ -396,6 +407,13 @@ def _add_battery_constraints(self): """ Add constraints related to battery behavior to the model. """ + # constraint for the max and min SOC. If the battery starts with an initial SOC + # greater than the maximum SOC or lesser than min SOC, maximum discharging is forced until the max. + # SOC is reached or max. charing will be forced until min SOC is reached. + for i, bat in enumerate(self.batteries): + for t in range(0, self.T): + self.problem += (self.variables['s_max_pen'][i][t] >= self.variables['s'][i][t] - bat.s_max) + self.problem += (self.variables['s_min_pen'][i][t] >= bat.s_min - self.variables['s'][i][t]) # Constraint (3): Battery dynamics for i, bat in enumerate(self.batteries): diff --git a/test_cases/013-grid-export-limit-hit.json b/test_cases/013-grid-export-limit-hit.json index 4652aa2bb..4992181b2 100644 --- a/test_cases/013-grid-export-limit-hit.json +++ b/test_cases/013-grid-export-limit-hit.json @@ -1,544 +1,545 @@ { - "expected_response": { - "batteries": [ - { - "charging_power": [ - 0, - 2555.5557, - 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 + "request": { + "batteries": [ + { + "c_max": 11040, + "c_min": 1380, + "charge_from_grid": true, + "d_max": 0, + "p_a": 0.0002896641, + "s_initial": 30000, + "s_max": 32300, + "s_min": 0 + }, + { + "c_max": 5200, + "c_min": 0, + "d_max": 6000, + "p_a": 0.0002896641, + "s_initial": 13500, + "s_max": 22000, + "s_min": 2000 + } ], - "discharging_power": [ - 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 + "eta_c": 0.9, + "eta_d": 0.9, + "grid": { + "p_max_exp": 15000 + }, + "strategy": { + "charging_strategy": "charge_before_export" + }, + "time_series": { + "dt": [ + 81, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600 + ], + "ft": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 91.5, + 1033, + 3818.5, + 7855.25, + 12491.25, + 19117, + 26291.75, + 30120.5, + 27300, + 21269, + 16182.25, + 9423.75, + 3060.5, + 417.25, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "gt": [ + 7.658946, + 205.172, + 197.64842, + 200.67107, + 196.42561, + 193.05663, + 200.7194, + 206.49055, + 298.78296, + 332.6198, + 446.1498, + 521.9399, + 619.1512, + 508.2315, + 516.21515, + 543.53925, + 493.03003, + 479.14084, + 391.6659, + 480.65515, + 489.5534, + 430.70206, + 430.9873, + 306.92404, + 229.76839, + 205.172, + 197.64842, + 200.67107, + 196.42561, + 193.05663, + 200.7194 + ], + "p_E": [ + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012, + 0.00012 + ], + "p_N": [ + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251, + 0.0003251 + ] + } + }, + "expected_response": { + "status": "Optimal", + "objective_value": 17.529475918803602, + "limit_violations": { + "grid_import_limit_exceeded": false, + "grid_export_limit_hit": true + }, + "batteries": [ + { + "charging_power": [ + 0.0, + 2555.5556, + 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 + ], + "discharging_power": [ + 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 + ], + "state_of_charge": [ + 30000.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0, + 32300.0 + ] + }, + { + "charging_power": [ + 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, + 3600.7848, + 5200.0, + 5200.0, + 1003.2904, + 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 + ], + "discharging_power": [ + 7.658946, + 2760.7276, + 197.64842, + 200.67107, + 196.42561, + 193.05663, + 200.7194, + 206.49055, + 298.78296, + 241.1198, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 13.7373, + 306.92404, + 229.76839, + 205.172, + 197.64842, + 200.67107, + 196.42561, + 193.05663, + 200.7194 + ], + "state_of_charge": [ + 13491.49, + 10424.015, + 10204.406, + 9981.4378, + 9763.1871, + 9548.6797, + 9325.6582, + 9096.2242, + 8764.2432, + 8496.3323, + 8496.3323, + 8496.3323, + 8496.3323, + 8496.3323, + 11737.039, + 16417.039, + 21097.039, + 22000.0, + 22000.0, + 22000.0, + 22000.0, + 22000.0, + 21984.736, + 21643.71, + 21388.411, + 21160.443, + 20940.833, + 20717.865, + 20499.615, + 20285.107, + 20062.086 + ] + } ], - "state_of_charge": [ - 30000, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300, - 32300 - ] - }, - { - "charging_power": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 3600.785, - 5200, - 5200, - 1003.2904, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 + "grid_import": [ + 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 + ], + "grid_export": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 586.8502, + 3296.5601, + 7236.0988, + 11983.019, + 15000.0, + 15000.0, + 15000.0, + 15000.0, + 15000.0, + 15000.0, + 8934.1966, + 2629.7979, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 ], - "discharging_power": [ - 7.658946, - 2760.7275, - 197.64842, - 200.67107, - 196.42561, - 193.05663, - 200.7194, - 206.49055, - 298.78296, - 241.1198, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 13.7373, - 306.92404, - 229.76839, - 205.172, - 197.64842, - 200.67107, - 196.42561, - 193.05663, - 200.7194 + "flow_direction": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 ], - "state_of_charge": [ - 13491.49, - 10424.015, - 10204.406, - 9981.4375, - 9763.1875, - 9548.68, - 9325.658, - 9096.225, - 8764.243, - 8496.332, - 8496.332, - 8496.332, - 8496.332, - 8496.332, - 11737.039, - 16417.04, - 21097.04, - 22000, - 22000, - 22000, - 22000, - 22000, - 21984.736, - 21643.71, - 21388.41, - 21160.443, - 20940.832, - 20717.865, - 20499.615, - 20285.107, - 20062.086 + "grid_import_overshoot": [], + "grid_export_overshoot": [ + 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, + 5548.2108, + 9427.47, + 10817.569, + 5877.3341, + 701.59485, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 ] - } - ], - "flow_direction": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "grid_export": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 586.8502, - 3296.56, - 7236.0986, - 11983.019, - 15000, - 15000, - 15000, - 15000, - 15000, - 15000, - 8934.196, - 2629.7979, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "grid_export_overshoot": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 5548.211, - 9427.47, - 10817.569, - 5877.334, - 701.59485, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "grid_import": [ - 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 - ], - "limit_violations": { - "grid_export_limit_hit": true - }, - "objective_value": 17.529476, - "status": "Optimal" - }, - "request": { - "batteries": [ - { - "c_max": 11040, - "c_min": 1380, - "charge_from_grid": true, - "d_max": 0, - "p_a": 0.0002896641, - "s_initial": 30000, - "s_max": 32300, - "s_min": 0 - }, - { - "c_max": 5200, - "c_min": 0, - "d_max": 6000, - "p_a": 0.0002896641, - "s_initial": 13500, - "s_max": 22000, - "s_min": 2000 - } - ], - "eta_c": 0.9, - "eta_d": 0.9, - "grid": { - "p_max_exp": 15000 - }, - "strategy": { - "charging_strategy": "charge_before_export" - }, - "time_series": { - "dt": [ - 81, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600, - 3600 - ], - "ft": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 91.5, - 1033, - 3818.5, - 7855.25, - 12491.25, - 19117, - 26291.75, - 30120.5, - 27300, - 21269, - 16182.25, - 9423.75, - 3060.5, - 417.25, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "gt": [ - 7.658946, - 205.172, - 197.64842, - 200.67107, - 196.42561, - 193.05663, - 200.7194, - 206.49055, - 298.78296, - 332.6198, - 446.1498, - 521.9399, - 619.1512, - 508.2315, - 516.21515, - 543.53925, - 493.03003, - 479.14084, - 391.6659, - 480.65515, - 489.5534, - 430.70206, - 430.9873, - 306.92404, - 229.76839, - 205.172, - 197.64842, - 200.67107, - 196.42561, - 193.05663, - 200.7194 - ], - "p_E": [ - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012, - 0.00012 - ], - "p_N": [ - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251, - 0.0003251 - ] } - }, - "test_items": {} } \ No newline at end of file diff --git a/test_cases/015-low-soc-initial.json b/test_cases/015-low-soc-initial.json new file mode 100644 index 000000000..e5239a192 --- /dev/null +++ b/test_cases/015-low-soc-initial.json @@ -0,0 +1,786 @@ +{ + "request": { + "batteries": [ + { + "c_max": 6000, + "c_min": 0, + "charge_from_grid": true, + "d_max": 6000, + "p_a": 0.00020706838, + "s_initial": 1974, + "s_max": 39900, + "s_min": 8400 + } + ], + "eta_c": 0.9, + "eta_d": 0.9, + "strategy": { + "charging_strategy": "charge_before_export", + "discharging_strategy": "discharge_before_import" + }, + "time_series": { + "dt": [ + 413, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900, + 900 + ], + "ft": [ + 42.829517, + 117.90625, + 184.5, + 284, + 383.5, + 483, + 580.25, + 675.25, + 770.25, + 865.25, + 949.65625, + 1023.46875, + 1097.2812, + 1171.0938, + 1220.8125, + 1246.4375, + 1272.0625, + 1297.6875, + 1309.125, + 1306.375, + 1303.625, + 1300.875, + 1287.5938, + 1263.7812, + 1239.9688, + 1216.1562, + 1177.0625, + 1122.6875, + 1068.3125, + 1013.9375, + 953.84375, + 888.03125, + 822.21875, + 756.40625, + 685.875, + 610.625, + 535.375, + 460.125, + 388.5, + 320.5, + 252.5, + 184.5, + 134.625, + 102.875, + 71.125, + 39.375, + 21.03125, + 16.09375, + 11.15625, + 6.21875, + 3.34375, + 2.53125, + 1.71875, + 0.90625, + 0.4375, + 0.3125, + 0.1875, + 0.0625, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "gt": [ + 243.95557, + 604.6492, + 664.46844, + 779.43054, + 843.9433, + 560.1741, + 611.1316, + 726.5455, + 891.4414, + 869.8892, + 823.48755, + 864.5042, + 858.7838, + 999.27374, + 946.434, + 989.94696, + 985.7407, + 989.56305, + 866.4419, + 954.3256, + 1038.281, + 1070.8118, + 949.5032, + 967.7589, + 883.9835, + 938.48254, + 1036.398, + 1123.3627, + 979.25433, + 907.6862, + 895.05225, + 915.8246, + 991.37335, + 914.05194, + 725.0468, + 736.06726, + 818.8465, + 850.3086, + 965.40735, + 932.8851, + 1026.857, + 925.7166, + 753.00476, + 651.9048, + 707.0695, + 732.41125, + 674.97876, + 645.9333, + 760.82385, + 1341.6025, + 881.1887, + 896.5443, + 710.9952, + 651.2604, + 650.04443, + 657.67615, + 678.31714, + 738.9317, + 622.9179, + 632.80914, + 660.9862, + 670.3786, + 767.84235, + 713.2995, + 529.0616, + 399.75082 + ], + "p_E": [ + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05, + 7.39e-05 + ], + "p_N": [ + 0.0002954, + 0.0003007, + 0.0003279, + 0.0003252, + 0.0003093, + 0.0002749, + 0.0003269, + 0.0003093, + 0.0002779, + 0.0002622, + 0.0002918, + 0.0002629, + 0.000248, + 0.0002418, + 0.0002732, + 0.0002545, + 0.0002531, + 0.0002449, + 0.0002573, + 0.0002498, + 0.0002506, + 0.0002446, + 0.0002516, + 0.0002496, + 0.0002474, + 0.0002446, + 0.0002429, + 0.0002457, + 0.0002484, + 0.0002634, + 0.0002371, + 0.0002494, + 0.0002526, + 0.0002614, + 0.0002324, + 0.0002459, + 0.0002613, + 0.0002895, + 0.0002446, + 0.0002895, + 0.000309, + 0.0003378, + 0.0002818, + 0.0003045, + 0.0003131, + 0.0003248, + 0.0003146, + 0.000323, + 0.0003235, + 0.0003134, + 0.0003239, + 0.0002982, + 0.0003037, + 0.0002774, + 0.0002995, + 0.0002777, + 0.000259, + 0.0002558, + 0.0002662, + 0.0002706, + 0.0002775, + 0.0002664, + 0.0002714, + 0.000265, + 0.0002623, + 0.0002512 + ] + } + }, + "expected_response": { + "status": "Optimal", + "objective_value": -6.4224528091250015, + "limit_violations": { + "grid_import_limit_exceeded": false, + "grid_export_limit_hit": false + }, + "batteries": [ + { + "charging_power": [ + 688.33333, + 1500.0, + 1500.0, + 1500.0, + 1500.0, + 451.66667, + 0.0, + 0.0, + 0.0, + 0.0, + 126.1687, + 158.96455, + 238.4974, + 1500.0, + 274.3785, + 256.49054, + 286.3218, + 308.12445, + 442.6831, + 352.0494, + 265.344, + 230.0632, + 338.0906, + 296.0223, + 355.9853, + 277.67366, + 1405.912, + 0.0, + 89.05817, + 106.2513, + 1500.0, + 0.0, + 0.0, + 0.0, + 1500.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 + ], + "discharging_power": [ + 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, + 774.357, + 741.2166, + 0.0, + 549.0298, + 635.9445, + 693.03625, + 653.94751, + 629.83955, + 749.6676, + 1335.3837, + 877.84495, + 0.0, + 709.27645, + 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 + ], + "state_of_charge": [ + 2593.5, + 3943.5, + 5293.5, + 6643.5, + 7993.5, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8513.5518, + 8656.6199, + 8871.2676, + 10221.268, + 10468.208, + 10699.05, + 10956.739, + 11234.051, + 11632.466, + 11949.311, + 12188.12, + 12395.177, + 12699.459, + 12965.879, + 13286.265, + 13536.172, + 14801.493, + 14801.493, + 14881.645, + 14977.271, + 16327.271, + 16327.271, + 16327.271, + 16327.271, + 17677.271, + 17677.271, + 17677.271, + 17677.271, + 17677.271, + 17677.271, + 16816.874, + 15993.3, + 15993.3, + 15383.267, + 14676.662, + 13906.622, + 13180.014, + 12480.192, + 11647.228, + 10163.468, + 9188.0849, + 9188.0849, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0, + 8400.0 + ] + } + ], + "grid_import": [ + 889.45939, + 1986.7429, + 1979.9684, + 1995.4305, + 1960.4433, + 528.84077, + 30.8816, + 51.2955, + 121.1914, + 4.6392, + 0.0, + 0.0, + 0.0, + 1328.1799, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1265.2475, + 0.6752, + 0.0, + 0.0, + 1441.2085, + 27.79335, + 169.1546, + 157.64569, + 1539.1718, + 125.44226, + 283.4715, + 390.1836, + 576.90735, + 612.3851, + 0.0, + 0.0, + 618.37976, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 894.01305, + 0.0, + 650.35415, + 649.60693, + 657.36365, + 678.12964, + 738.8692, + 622.9179, + 632.80914, + 660.9862, + 670.3786, + 767.84235, + 713.2995, + 529.0616, + 399.75082 + ], + "grid_export": [ + 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 + ], + "flow_direction": [ + 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 + ], + "grid_import_overshoot": [], + "grid_export_overshoot": [] + } +} \ No newline at end of file diff --git a/test_cases/018-high-soc-initial.json b/test_cases/018-high-soc-initial.json new file mode 100644 index 000000000..e4da5f1b5 --- /dev/null +++ b/test_cases/018-high-soc-initial.json @@ -0,0 +1,590 @@ +{ + "request": { + "batteries": [ + { + "s_capacity": 40000, + "c_max": 3680, + "c_min": 1380, + "charge_from_grid": true, + "d_max": 0, + "p_a": 0.0014, + "s_initial": 40000, + "s_max": 36000, + "s_min": 0 + }, + { + "s_capacity": 10000, + "c_max": 1500, + "c_min": 230, + "charge_from_grid": true, + "d_max": 1500, + "p_a": 0.0014, + "s_initial": 9500, + "s_max": 9000, + "s_min": 2000 + }, + { + "c_max": 6000, + "c_min": 0, + "charge_from_grid": true, + "d_max": 6000, + "p_a": 0.0014, + "s_initial": 3700, + "s_max": 8000, + "s_min": 0 + } + ], + "eta_c": 0.9, + "eta_d": 0.9, + "strategy": { + "charging_strategy": "charge_before_export" + }, + "time_series": { + "dt": [ + 2252, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600, + 3600 + ], + "ft": [ + 136.1108, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1.9970086, + 371.65152, + 1518.0308, + 3046.566, + 4353.2085, + 5225.4478, + 5656.539, + 5741.9253, + 5448.66, + 4804.3555, + 3832.6248, + 2577.8723, + 1275.6185, + 318.86014, + 0, + 0, + 0, + 0 + ], + "gt": [ + 336.00156, + 449.8323, + 378.46527, + 370.42828, + 312.28534, + 272.7593, + 253.7327, + 239.72609, + 245.93584, + 219.31503, + 196.79982, + 256.73517, + 375.90344, + 404.6813, + 587.8364, + 590.7609, + 575.03467, + 504.14923, + 506.61774, + 484.8687, + 582.5783, + 513.6071, + 465.6819, + 422.35416, + 530.52875, + 449.8323, + 378.46527, + 370.42828, + 312.28534 + ], + "p_E": [ + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05, + 6.43e-05 + ], + "p_N": [ + 0.00015453, + 0.00015075, + 0.0001171, + 0.00010771, + 0.0001007, + 0.00010121, + 0.00010059, + 9.957e-05, + 9.784e-05, + 9.696e-05, + 9.801e-05, + 0.0001026, + 0.00010091, + 9.598e-05, + 7.911e-05, + 1.135e-05, + 0, + -2e-08, + -8.6e-07, + -9.9e-07, + -1e-08, + 4.099e-05, + 8.76e-05, + 0.00011647, + 0.00015839, + 0.00013954, + 0.00010995, + 9.295e-05, + 8.256e-05 + ] + } + }, + "expected_response": { + "status": "Optimal", + "objective_value": 7.637739539462, + "limit_violations": { + "grid_import_limit_exceeded": false, + "grid_export_limit_hit": false + }, + "batteries": [ + { + "charging_power": [ + 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 + ], + "discharging_power": [ + 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 + ], + "state_of_charge": [ + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0, + 40000.0 + ] + }, + { + "charging_power": [ + 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, + 976.15008, + 0.0, + 0.0, + 0.0, + 0.0, + 1500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1283.9089 + ], + "discharging_power": [ + 450.0, + 318.42271, + 0.0, + 0.0, + 0.0, + 272.7593, + 253.7327, + 239.72609, + 245.93584, + 219.31503, + 196.79982, + 254.73816, + 4.25192, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 211.66861, + 449.8323, + 378.46527, + 0.0, + 0.0 + ], + "state_of_charge": [ + 9000.0, + 8646.197, + 8646.197, + 8646.197, + 8646.197, + 8343.1311, + 8061.2059, + 7794.8436, + 7521.5815, + 7277.8981, + 7059.2317, + 6776.1893, + 6771.4649, + 6771.4649, + 6771.4649, + 7650.0, + 7650.0, + 7650.0, + 7650.0, + 7650.0, + 9000.0, + 9000.0, + 9000.0, + 9000.0, + 8764.8127, + 8264.999, + 7844.482, + 7844.482, + 9000.0 + ] + }, + { + "charging_power": [ + 250.10924, + 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, + 6000.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharging_power": [ + 0.0, + 131.40959, + 378.46527, + 370.42828, + 312.28534, + 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 + ], + "state_of_charge": [ + 3925.0983, + 3779.0877, + 3358.5707, + 2946.9837, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 2600.0, + 8000.0, + 8000.0, + 8000.0, + 8000.0, + 8000.0, + 8000.0, + 8000.0, + 8000.0, + 8000.0 + ] + } + ], + "grid_import": [ + 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, + 3278.2228, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 370.42828, + 1596.1942 + ], + "grid_export": [ + 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, + 1113.3495, + 2458.7296, + 2786.2975, + 4650.4131, + 5152.3898, + 5235.3076, + 4963.7913, + 0.0, + 3319.0177, + 2112.1904, + 853.26434, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "flow_direction": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0 + ], + "grid_import_overshoot": [], + "grid_export_overshoot": [] + } +} \ No newline at end of file diff --git a/tests/testbench.py b/tests/testbench.py index 7e82141a1..4de724a32 100644 --- a/tests/testbench.py +++ b/tests/testbench.py @@ -178,8 +178,12 @@ # add battery data for the diagram for i, bat in enumerate(response.json["batteries"]): + # figure out what value to take for 100% SOC + bat_capacity = request["batteries"][i]["s_max"] + if "s_capacity" in request["batteries"][i]: + bat_capacity = request["batteries"][i]["s_capacity"] df_diagram[f"P_bat{i}"] = np.divide(np.subtract(bat["discharging_power"], bat["charging_power"]), ts_input["dt"]) - df_diagram[f"SOC_bat{i}"] = np.divide(bat["state_of_charge"], request["batteries"][i]["s_max"])*100 + df_diagram[f"SOC_bat{i}"] = np.divide(bat["state_of_charge"], bat_capacity)*100 df_diagram[f"P_bat{i}_exp"] = np.divide(np.subtract(expected_response["batteries"][i]["discharging_power"], expected_response["batteries"][i]["charging_power"]), ts_input["dt"])