Source code for VeraGridEngine.Simulations.OPF.opf_ts_driver

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.  
# SPDX-License-Identifier: MPL-2.0
import datetime

import numpy as np
import pandas as pd
from typing import Union
from VeraGridEngine.Devices.multi_circuit import MultiCircuit
from VeraGridEngine.enumerations import SolverType, TimeGrouping, EngineType, SimulationTypes
from VeraGridEngine.Simulations.OPF.opf_options import OptimalPowerFlowOptions
from VeraGridEngine.Simulations.OPF.Formulations.linear_opf_ts import run_linear_opf_ts
from VeraGridEngine.Simulations.OPF.simple_dispatch_ts import run_greedy_dispatch_ts
from VeraGridEngine.Simulations.OPF.ac_opf_worker import run_nonlinear_opf
from VeraGridEngine.Simulations.OPF.opf_ts_results import OptimalPowerFlowTimeSeriesResults
from VeraGridEngine.Simulations.PowerFlow.power_flow_options import PowerFlowOptions
from VeraGridEngine.Simulations.driver_template import TimeSeriesDriverTemplate
from VeraGridEngine.Compilers.circuit_to_newton_pa import newton_pa_linear_opf, newton_pa_nonlinear_opf
from VeraGridEngine.Simulations.Clustering.clustering_results import ClusteringResults
from VeraGridEngine.basic_structures import IntVec, Vec, get_time_groups
import VeraGridEngine.Compilers.circuit_to_gslv as gslv


[docs] class OptimalPowerFlowTimeSeriesDriver(TimeSeriesDriverTemplate): __slots__ = ( "options", "all_solved", ) name = 'Optimal power flow time series' tpe = SimulationTypes.OPFTimeSeries_run def __init__(self, grid: MultiCircuit, options: Union[OptimalPowerFlowOptions, None] = None, time_indices: Union[IntVec, None] = None, clustering_results: Union[ClusteringResults, None] = None, engine: EngineType = EngineType.VeraGrid): """ OptimalPowerFlowTimeSeriesDriver class constructor :param grid: MultiCircuit Object :param options: OPF options (optional) :param time_indices: array of time indices to simulate (optional) :param engine: Calculation engine to use (if available) (optional) """ TimeSeriesDriverTemplate.__init__(self, grid=grid, time_indices=time_indices if time_indices is not None else grid.get_all_time_indices(), clustering_results=clustering_results, engine=engine) # Options to use self.options = options if options else OptimalPowerFlowOptions() # find the number of time steps nt = len(self.time_indices) if self.time_indices is not None else 1 # OPF results self.results: OptimalPowerFlowTimeSeriesResults = OptimalPowerFlowTimeSeriesResults( bus_names=self.grid.get_bus_names(), branch_names=self.grid.get_branch_names(add_hvdc=False, add_vsc=False, add_switch=True), load_names=self.grid.get_load_names(), generator_names=self.grid.get_generator_names(), battery_names=self.grid.get_battery_names(), shunt_like_names=self.grid.get_shunt_like_devices_names(), hvdc_names=self.grid.get_hvdc_names(), vsc_names=self.grid.get_vsc_names(), fuel_names=self.grid.get_fuel_names(), emission_names=self.grid.get_emission_names(), technology_names=self.grid.get_technology_names(), fluid_node_names=self.grid.get_fluid_node_names(), fluid_path_names=self.grid.get_fluid_path_names(), fluid_injection_names=self.grid.get_fluid_injection_names(), nt=nt, time_array=self.grid.time_profile[self.time_indices] if self.time_indices is not None else [ datetime.datetime.now()], bus_types=np.ones(self.grid.get_bus_number(), dtype=int), clustering_results=clustering_results ) self.all_solved = True @property def pf_options(self) -> PowerFlowOptions: """ Get the PowerFlow options provides with the OpfOptions :return: PowerFlowOptions """ return self.options.power_flow_options
[docs] def get_steps(self): """ Get time steps list of strings """ return [l.strftime('%d-%m-%Y %H:%M') for l in pd.to_datetime(self.grid.time_profile)]
[docs] def run_linear_opf(self): """ :return: """ # DC optimal power flow opf_vars, model = run_linear_opf_ts( grid=self.grid, time_indices=self.time_indices, dispatch_mode=self.options.dispatch_mode, solver_type=self.options.mip_solver, zonal_grouping=self.options.zonal_grouping, skip_generation_limits=self.options.skip_generation_limits, consider_contingencies=self.options.consider_contingencies, contingency_groups_used=self.grid.contingency_groups, ramp_constraints=self.options.consider_ramps, consider_time_up_down=self.options.consider_time_up_down, area_spinning_reserve=self.options.area_spinning_reserve, lodf_threshold=self.options.lodf_tolerance, inter_aggregation_info=self.options.inter_aggregation_info, use_glsk_as_cost=self.options.use_glsk_as_cost, add_losses_approximation=self.options.add_losses_approximation, logger=self.logger, progress_text=self.report_text, progress_func=self.report_progress, verbose=self.options.verbose, robust=self.options.robust, mip_framework=self.options.mip_framework ) self.results.voltage = opf_vars.bus_vars.Vm * np.exp(1j * opf_vars.bus_vars.Va) self.results.bus_shadow_prices = opf_vars.bus_vars.shadow_prices self.results.load_power = opf_vars.load_vars.p self.results.load_shedding = opf_vars.load_vars.shedding self.results.load_shedding_cost = opf_vars.load_vars.shedding_cost self.results.battery_power = opf_vars.batt_vars.p self.results.battery_energy = opf_vars.batt_vars.e self.results.generator_power = opf_vars.gen_vars.p self.results.generator_shedding = opf_vars.gen_vars.shedding self.results.generator_cost = opf_vars.gen_vars.cost # self.results.generator_fuel = opf_vars.gen_vars.fuel # self.results.generator_emissions = opf_vars.gen_vars.emissions self.results.generator_producing = opf_vars.gen_vars.producing self.results.generator_starting_up = opf_vars.gen_vars.starting_up self.results.generator_shutting_down = opf_vars.gen_vars.shedding self.results.generator_invested = opf_vars.gen_vars.invested self.results.Sf = opf_vars.branch_vars.flows self.results.St = -opf_vars.branch_vars.flows self.results.overloads = opf_vars.branch_vars.flow_slacks_pos - opf_vars.branch_vars.flow_slacks_neg self.results.overloads_cost = opf_vars.branch_vars.overload_cost self.results.losses = opf_vars.branch_vars.losses self.results.loading = opf_vars.branch_vars.loading self.results.tap_angle = opf_vars.branch_vars.tap_angles self.results.hvdc_Pf = opf_vars.hvdc_vars.flows self.results.hvdc_loading = opf_vars.hvdc_vars.loading self.results.vsc_Pf = opf_vars.vsc_vars.flows self.results.vsc_loading = opf_vars.vsc_vars.loading self.results.fluid_node_current_level = opf_vars.fluid_node_vars.current_level self.results.fluid_node_flow_in = opf_vars.fluid_node_vars.flow_in self.results.fluid_node_flow_out = opf_vars.fluid_node_vars.flow_out self.results.fluid_node_p2x_flow = opf_vars.fluid_node_vars.p2x_flow self.results.fluid_node_spillage = opf_vars.fluid_node_vars.spillage self.results.fluid_path_flow = opf_vars.fluid_path_vars.flow self.results.fluid_injection_flow = opf_vars.fluid_inject_vars.flow self.results.system_fuel = opf_vars.sys_vars.system_fuel self.results.system_emissions = opf_vars.sys_vars.system_emissions self.results.system_energy_cost = opf_vars.sys_vars.system_unit_energy_cost self.results.system_total_energy_cost = opf_vars.sys_vars.system_total_energy_cost self.results.power_by_technology = opf_vars.sys_vars.power_by_technology # set converged for all t to the value of acceptable solution self.results.converged = np.array([opf_vars.acceptable_solution] * opf_vars.nt) if self.options.report_formulation: self.results.report_text = model.model_as_string()
[docs] def run_linear_opf_indices(self, time_indices: IntVec, energy_0: Vec, fluid_level_0: Vec): # run an opf for the group interval only if the group is within the start:end boundaries # DC optimal power flow opf_vars, model = run_linear_opf_ts( grid=self.grid, time_indices=time_indices, dispatch_mode=self.options.dispatch_mode, solver_type=self.options.mip_solver, zonal_grouping=self.options.zonal_grouping, skip_generation_limits=self.options.skip_generation_limits, consider_contingencies=self.options.consider_contingencies, contingency_groups_used=self.options.contingency_groups_used, ramp_constraints=self.options.consider_ramps, consider_time_up_down=self.options.consider_time_up_down, area_spinning_reserve=self.options.area_spinning_reserve, lodf_threshold=self.options.lodf_tolerance, inter_aggregation_info=self.options.inter_aggregation_info, energy_0=energy_0, fluid_level_0=fluid_level_0, use_glsk_as_cost=self.options.use_glsk_as_cost, add_losses_approximation=self.options.add_losses_approximation, logger=self.logger, verbose=self.options.verbose, robust=self.options.robust, mip_framework=self.options.mip_framework ) self.results.voltage[time_indices, :] = opf_vars.bus_vars.Vm * np.exp(1j * opf_vars.bus_vars.Va) self.results.bus_shadow_prices[time_indices, :] = opf_vars.bus_vars.shadow_prices self.results.load_power[time_indices, :] = opf_vars.load_vars.p self.results.load_shedding[time_indices, :] = opf_vars.load_vars.shedding self.results.load_shedding_cost[time_indices, :] = opf_vars.load_vars.shedding_cost self.results.battery_power[time_indices, :] = opf_vars.batt_vars.p self.results.battery_energy[time_indices, :] = opf_vars.batt_vars.e self.results.generator_power[time_indices, :] = opf_vars.gen_vars.p self.results.generator_shedding[time_indices, :] = opf_vars.gen_vars.shedding self.results.generator_cost[time_indices, :] = opf_vars.gen_vars.cost self.results.generator_producing[time_indices, :] = opf_vars.gen_vars.producing self.results.generator_starting_up[time_indices, :] = opf_vars.gen_vars.starting_up self.results.generator_shutting_down[time_indices, :] = opf_vars.gen_vars.shedding self.results.generator_invested[time_indices, :] = opf_vars.gen_vars.invested self.results.Sf[time_indices, :] = opf_vars.branch_vars.flows self.results.St[time_indices, :] = -opf_vars.branch_vars.flows self.results.overloads[time_indices, :] = (opf_vars.branch_vars.flow_slacks_pos - opf_vars.branch_vars.flow_slacks_neg) self.results.overloads_cost[time_indices, :] = opf_vars.branch_vars.overload_cost self.results.losses[time_indices, :] = opf_vars.branch_vars.losses self.results.loading[time_indices, :] = opf_vars.branch_vars.loading self.results.tap_angle[time_indices, :] = opf_vars.branch_vars.tap_angles self.results.hvdc_Pf[time_indices, :] = opf_vars.hvdc_vars.flows self.results.hvdc_loading[time_indices, :] = opf_vars.hvdc_vars.loading self.results.vsc_Pf[time_indices, :] = opf_vars.vsc_vars.flows self.results.vsc_loading[time_indices, :] = opf_vars.vsc_vars.loading self.results.fluid_node_current_level[time_indices, :] = opf_vars.fluid_node_vars.current_level self.results.fluid_node_flow_in[time_indices, :] = opf_vars.fluid_node_vars.flow_in self.results.fluid_node_flow_out[time_indices, :] = opf_vars.fluid_node_vars.flow_out self.results.fluid_node_p2x_flow[time_indices, :] = opf_vars.fluid_node_vars.p2x_flow self.results.fluid_node_spillage[time_indices, :] = opf_vars.fluid_node_vars.spillage self.results.fluid_path_flow[time_indices, :] = opf_vars.fluid_path_vars.flow self.results.fluid_injection_flow[time_indices, :] = opf_vars.fluid_inject_vars.flow self.results.system_fuel[time_indices, :] = opf_vars.sys_vars.system_fuel self.results.system_emissions[time_indices, :] = opf_vars.sys_vars.system_emissions self.results.system_energy_cost[time_indices] = opf_vars.sys_vars.system_unit_energy_cost self.results.system_total_energy_cost[time_indices] = opf_vars.sys_vars.system_total_energy_cost self.results.power_by_technology[time_indices] = opf_vars.sys_vars.power_by_technology # set converged for all t to the value of acceptable solution self.results.converged[time_indices] = np.array([opf_vars.acceptable_solution] * opf_vars.nt) return model
[docs] def run_greedy_dispatch(self): """ :return: """ # AC optimal power flow (load_profile, gen_dispatch, batt_dispatch, battery_energy, load_shedding, gen_curtailment) = run_greedy_dispatch_ts( grid=self.grid, time_indices=self.time_indices, text_prog=self.report_text, prog_func=self.report_progress, logger=self.logger ) self.results.generator_power[self.time_indices, :] = gen_dispatch # already in MW self.results.generator_shedding[self.time_indices, :] = gen_curtailment self.results.battery_power[self.time_indices, :] = batt_dispatch self.results.battery_energy[self.time_indices, :] = battery_energy self.results.load_shedding[self.time_indices, :] = load_shedding self.results.load_power[self.time_indices, :] = load_profile self.results.converged[self.time_indices] = True
[docs] def run_greedy_dispatch_indices(self, time_indices: IntVec): """ :param time_indices: :return: """ # Greedy dispatch (load_profile, gen_dispatch, batt_dispatch, battery_energy, load_shedding, gen_curtailment) = run_greedy_dispatch_ts( grid=self.grid, time_indices=time_indices, text_prog=self.report_text, prog_func=self.report_progress, logger=self.logger ) self.results.generator_power[time_indices, :] = gen_dispatch # already in MW self.results.generator_shedding[time_indices, :] = gen_curtailment self.results.battery_power[time_indices, :] = batt_dispatch self.results.battery_energy[time_indices, :] = battery_energy self.results.load_shedding[time_indices, :] = load_shedding self.results.load_power[time_indices, :] = load_profile self.results.converged[time_indices] = True
[docs] def run_non_linear_opf(self): """ :return: """ self.report_progress(0.0) for it, t in enumerate(self.time_indices): # report progress self.report_text('Nonlinear OPF at ' + str(self.grid.time_profile[t]) + '...') self.report_progress2(it, len(self.time_indices)) # run opf res = run_nonlinear_opf( grid=self.grid, opf_options=self.options, t_idx=t, # for the first power flow, use the given strategy # for the successive ones, use the previous solution # Sbus_pf0=self.results.Sbus[it - 1, :] if it > 0 else None, # voltage_pf0=self.results.voltage[it - 1, :] if it > 0 else None, logger=self.logger ) Sbase = self.grid.Sbase self.results.voltage[it, :] = res.V self.results.Sbus[it, :] = res.S * Sbase self.results.bus_shadow_prices[it, :] = res.lam_p # self.results.load_shedding = npa_res.load_shedding[0, :] # self.results.battery_power = npa_res.battery_p[0, :] # self.results.battery_energy = npa_res.battery_energy[0, :] self.results.generator_power[it, :] = res.Pg * Sbase self.results.generator_reactive_power[it, :] = res.Qg * Sbase self.results.generator_cost[it, :] = res.Pcost self.results.shunt_like_reactive_power[it, :] = res.Qsh * Sbase self.results.Sf[it, :] = res.Sf * Sbase self.results.St[it, :] = res.St * Sbase self.results.overloads[it, :] = (res.sl_sf - res.sl_st) * Sbase self.results.loading[it, :] = res.loading self.results.tap_angle[it, :] = res.tap_phase self.results.tap_module[it, :] = res.tap_module self.results.hvdc_Pf[it, :] = res.hvdc_Pf self.results.hvdc_loading[it, :] = res.hvdc_loading self.results.converged[it] = res.converged if self.is_cancel(): return None # Compute the emissions, fuel costs and energy used (self.results.system_fuel, self.results.system_emissions, self.results.system_energy_cost) = self.get_fuel_emissions_energy_calculations( gen_p=self.results.generator_power, gen_cost=self.results.generator_cost ) return None
[docs] def run_nonlinear_opf_indices(self, time_indices: IntVec): """ :param time_indices: :return: """ self.report_progress(0.0) for it, t in enumerate(time_indices): # report progress self.report_text('Nonlinear OPF at ' + str(self.grid.time_profile[t]) + '...') # run opf res = run_nonlinear_opf( grid=self.grid, opf_options=self.options, t_idx=t, # for the first power flow, use the given strategy # for the successive ones, use the previous solution # Sbus_pf0=self.results.Sbus[it - 1, :] if it > 0 else None, # voltage_pf0=self.results.voltage[it - 1, :] if it > 0 else None, logger=self.logger ) Sbase = self.grid.Sbase self.results.voltage[it, :] = res.V self.results.Sbus[it, :] = res.S * Sbase self.results.bus_shadow_prices[it, :] = res.lam_p # self.results.load_shedding = npa_res.load_shedding[0, :] # self.results.battery_power = npa_res.battery_p[0, :] # self.results.battery_energy = npa_res.battery_energy[0, :] self.results.generator_power[it, :] = res.Pg * Sbase self.results.generator_reactive_power[it, :] = res.Qg * Sbase self.results.generator_cost[it, :] = res.Pcost self.results.shunt_like_reactive_power[it, :] = res.Qsh * Sbase self.results.Sf[it, :] = res.Sf * Sbase self.results.St[it, :] = res.St * Sbase self.results.overloads[it, :] = (res.sl_sf - res.sl_st) * Sbase self.results.loading[it, :] = res.loading self.results.tap_angle[it, :] = res.tap_phase self.results.tap_module[it, :] = res.tap_module self.results.hvdc_Pf[it, :] = res.hvdc_Pf self.results.hvdc_loading[it, :] = res.hvdc_loading self.results.converged[it] = res.converged if self.is_cancel(): return None return None
[docs] def opf(self, remote=False, batteries_energy_0=None): """ Run a power flow for every circuit :param remote: is this function being called from the time series? :param batteries_energy_0: initial state of the batteries, if None the default values are taken :return: OptimalPowerFlowResults object """ if not remote: self.report_progress(0.0) self.report_text('Formulating problem...') if self.options.solver == SolverType.LINEAR_OPF: self.run_linear_opf() elif self.options.solver == SolverType.NONLINEAR_OPF: self.run_non_linear_opf() elif self.options.solver == SolverType.GREEDY_DISPATCH_OPF: self.run_greedy_dispatch() else: self.logger.add_error('Solver not supported in this mode', str(self.options.solver)) return self.results if not remote: self.report_progress(0.0) self.report_text('Running all in an external solver, this may take a while...') return self.results
[docs] def opf_by_groups(self) -> None: """ Run the OPF by groups """ self.report_progress(0.0) self.report_text('Making groups...') # get the partition points of the time series groups = get_time_groups(t_array=self.grid.time_profile[self.time_indices], grouping=self.options.time_grouping) n = len(groups) i = 1 energy_0: Union[Vec, None] = None # at the beginning fluid_level_0: Union[Vec, None] = None while i < n and not self.is_cancel(): start_ = groups[i - 1] end_ = groups[i] # Grab the last time index in the last group if i == n - 1: time_indices = np.arange(start_, end_ + 1) else: time_indices = np.arange(start_, end_) # show progress message msg = f"Group {i} -> {start_} : {end_} [{end_ - start_}]" if self.options.verbose > 0: print(msg) self.report_text('Running OPF for the time group {0} ' 'start {1} - end {2} in external solver...'.format(i, start_, end_)) if self.options.solver == SolverType.LINEAR_OPF: model = self.run_linear_opf_indices(time_indices=time_indices, energy_0=energy_0, fluid_level_0=fluid_level_0) energy_0 = self.results.battery_energy[end_ - 1, :] fluid_level_0 = self.results.fluid_node_current_level[end_ - 1, :] if self.options.report_formulation: self.results.report_text = f"## {msg}\n\n" + model.model_as_string() elif self.options.solver == SolverType.NONLINEAR_OPF: self.run_nonlinear_opf_indices(time_indices=time_indices) elif self.options.solver == SolverType.GREEDY_DISPATCH_OPF: self.run_greedy_dispatch_indices(time_indices=time_indices) else: self.logger.add_error('Solver not supported in this mode', str(self.options.solver)) return None # update progress bar self.report_progress2(i, len(groups)) if self.is_cancel(): return None i += 1 return None
[docs] def add_report(self, eps: float = 1e-6) -> None: """ Add a report of the results (in-place) """ if self.progress_text: self.report_text("Creating report") nt = len(self.time_indices) for t, t_idx in enumerate(self.time_indices): self.report_progress2(t, nt) t_name = str(self.results.time_array[t]) for gen_name, gen_shedding in zip(self.results.generator_names, self.results.generator_shedding[t, :]): if gen_shedding > eps: self.logger.add_warning("Generation shedding {}".format(t_name), device=gen_name, value=gen_shedding, expected_value=0.0) for load_name, load_shedding in zip(self.results.load_names, self.results.load_shedding[t, :]): if load_shedding > eps: self.logger.add_warning("Load shedding {}".format(t_name), device=load_name, value=load_shedding, expected_value=0.0) for fluid_node_name, fluid_node_spillage in zip(self.results.fluid_node_names, self.results.fluid_node_spillage[t, :]): if fluid_node_spillage > eps: self.logger.add_warning("Fluid node spillage {}".format(t_name), device=fluid_node_name, value=fluid_node_spillage, expected_value=0.0) for name, val in zip(self.results.branch_names, self.results.loading[t, :]): if val > (1.0 + eps): self.logger.add_warning("Overload {}".format(t_name), device=name, value=val * 100, expected_value=100.0) va = np.angle(self.results.voltage[t, :]) for i, bus in enumerate(self.grid.buses): if va[i] > bus.angle_max: self.logger.add_warning("Overvoltage {}".format(t_name), device=bus.name, value=va[i], expected_value=bus.angle_max) elif va[i] < bus.angle_min: self.logger.add_warning("Undervoltage {}".format(t_name), device=bus.name, value=va[i], expected_value=bus.angle_min)
[docs] def run(self): """ :return: """ self.tic() if self.engine == EngineType.GSLV: if not gslv.GSLV_AVAILABLE: self.engine = EngineType.VeraGrid self.logger.add_warning('GSLV not available, falling back to VeraGrid') else: if self.options.solver != SolverType.LINEAR_OPF: self.engine = EngineType.VeraGrid self.logger.add_warning('GSLV OPF time series only supports LINEAR_OPF, falling back to VeraGrid') else: pass else: pass if self.engine == EngineType.VeraGrid: if self.options.time_grouping == TimeGrouping.NoGrouping: self.opf() else: if self.time_indices is None: self.opf() else: if len(self.time_indices) == 0: self.opf() else: self.opf_by_groups() elif self.engine == EngineType.NewtonPA: if self.time_indices is None: ti = 0 use_time_series = False else: use_time_series = True if self.using_clusters: ti = np.arange(0, len(self.time_indices)) else: ti = self.time_indices if self.options.solver == SolverType.LINEAR_OPF: self.report_text('Running Linear OPF with Newton...') gslv_res = newton_pa_linear_opf(circuit=self.grid, opf_options=self.options, pf_opt=PowerFlowOptions(), time_series=use_time_series, time_indices=self.time_indices) self.results.voltage[ti, :] = gslv_res.voltage_module * np.exp(1j * gslv_res.voltage_angle) self.results.bus_shadow_prices[ti, :] = gslv_res.nodal_shadow_prices self.results.load_shedding[ti, :] = gslv_res.load_shedding self.results.battery_power[ti, :] = gslv_res.battery_power self.results.battery_energy[ti, :] = gslv_res.battery_energy self.results.generator_power[ti, :] = gslv_res.generator_power self.results.Sf[ti, :] = gslv_res.branch_flows self.results.St[ti, :] = -gslv_res.branch_flows self.results.overloads[ti, :] = gslv_res.branch_overloads self.results.loading[ti, :] = gslv_res.branch_loading self.results.tap_angle[ti, :] = gslv_res.branch_tap_angle # self.results.Sbus[ti, :] = problem.get_power_injections() self.results.hvdc_Pf[ti, :] = gslv_res.hvdc_flows self.results.hvdc_loading[ti, :] = gslv_res.hvdc_loading self.results.fluid_node_current_level[ti, :] = gslv_res.fluid_node_vars.current_level self.results.fluid_node_flow_in[ti, :] = gslv_res.fluid_node_vars.flow_in self.results.fluid_node_flow_out[ti, :] = gslv_res.fluid_node_vars.flow_out self.results.fluid_node_p2x_flow[ti, :] = gslv_res.fluid_node_vars.p2x_flow self.results.fluid_node_spillage[ti, :] = gslv_res.fluid_node_vars.spillage self.results.fluid_path_flow[ti, :] = gslv_res.fluid_path_vars.flow self.results.fluid_injection_flow[ti, :] = gslv_res.fluid_inject_vars.flow if self.options.solver == SolverType.NONLINEAR_OPF: self.report_text('Running Non-Linear OPF with Newton...') # pack the results gslv_res = newton_pa_nonlinear_opf(circuit=self.grid, pf_opt=self.pf_options, opf_opt=self.options, time_series=use_time_series, time_indices=self.time_indices) self.results.voltage[ti, :] = gslv_res.voltage self.results.Sbus[ti, :] = gslv_res.Scalc self.results.bus_shadow_prices[ti, :] = gslv_res.bus_shadow_prices self.results.load_shedding[ti, :] = gslv_res.load_shedding self.results.battery_power[ti, :] = gslv_res.battery_p # self.results.battery_energy[ti, :] = npa_res.battery_energy self.results.generator_power[ti, :] = gslv_res.generator_p self.results.Sf[ti, :] = gslv_res.Sf self.results.St[ti, :] = gslv_res.St self.results.overloads[ti, :] = gslv_res.branch_overload self.results.loading[ti, :] = gslv_res.Loading # self.results.phase_shift[ti, :] = npa_res.branch_tap_angle # self.results.Sbus[ti, :] = problem.get_power_injections() self.results.hvdc_Pf[ti, :] = gslv_res.hvdc_Pf self.results.hvdc_loading[ti, :] = gslv_res.hvdc_loading self.results.fluid_node_current_level[ti, :] = gslv_res.fluid_node_vars.current_level self.results.fluid_node_flow_in[ti, :] = gslv_res.fluid_node_vars.flow_in self.results.fluid_node_flow_out[ti, :] = gslv_res.fluid_node_vars.flow_out self.results.fluid_node_p2x_flow[ti, :] = gslv_res.fluid_node_vars.p2x_flow self.results.fluid_node_spillage[ti, :] = gslv_res.fluid_node_vars.spillage self.results.fluid_path_flow[ti, :] = gslv_res.fluid_path_vars.flow self.results.fluid_injection_flow[ti, :] = gslv_res.fluid_inject_vars.flow elif self.engine == EngineType.GSLV: if self.time_indices is None: ti = 0 use_time_series = False else: use_time_series = True if self.using_clusters: ti = np.arange(0, len(self.time_indices)) else: ti = self.time_indices if self.options.solver == SolverType.LINEAR_OPF: self.report_text('Running Linear OPF with GSLV...') gslv_res = gslv.gslv_opf(circuit=self.grid, opf_options=self.options, time_series=use_time_series, time_indices=self.time_indices, logger=self.logger) self.results.voltage[ti, :] = gslv_res.voltage self.results.Sbus[ti, :] = gslv_res.Sbus.real self.results.bus_shadow_prices[ti, :] = gslv_res.bus_shadow_prices self.results.load_shedding[ti, :] = gslv_res.load_shedding self.results.battery_power[ti, :] = gslv_res.battery_power self.results.battery_energy[ti, :] = gslv_res.battery_energy self.results.generator_power[ti, :] = gslv_res.generator_power self.results.Sf[ti, :] = gslv_res.Sf.real self.results.St[ti, :] = gslv_res.St.real self.results.overloads[ti, :] = gslv_res.overloads.real self.results.loading[ti, :] = gslv_res.loading.real self.results.tap_angle[ti, :] = gslv_res.tap_angle self.results.hvdc_Pf[ti, :] = gslv_res.hvdc_Pf self.results.hvdc_loading[ti, :] = gslv_res.hvdc_loading self.results.fluid_node_current_level[ti, :] = gslv_res.fluid_node_current_level self.results.fluid_node_flow_in[ti, :] = gslv_res.fluid_node_flow_in self.results.fluid_node_flow_out[ti, :] = gslv_res.fluid_node_flow_out self.results.fluid_node_p2x_flow[ti, :] = gslv_res.fluid_node_p2x_flow self.results.fluid_node_spillage[ti, :] = gslv_res.fluid_node_spillage self.results.fluid_path_flow[ti, :] = gslv_res.fluid_path_flow self.results.fluid_injection_flow[ti, :] = gslv_res.fluid_injection_flow else: if self.options.time_grouping == TimeGrouping.NoGrouping: self.opf() else: if self.time_indices is None: self.opf() else: if len(self.time_indices) == 0: self.opf() else: self.opf_by_groups() if self.options.generate_report: self.add_report() self.toc()