Source code for VeraGridEngine.Compilers.Gslv.common

# 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
from __future__ import annotations
from VeraGridEngine.Devices.Aggregation.facility import Facility
from VeraGridEngine.Devices.Associations.emission_gas import EmissionGas
from VeraGridEngine.Devices.Associations.fuel import Fuel
from VeraGridEngine.Devices.Associations.technology import Technology
from VeraGridEngine.Devices.Injections.controllable_shunt import ControllableShunt
from VeraGridEngine.Devices.Injections.generator import Generator
from VeraGridEngine.Devices.Injections.load import Load
from VeraGridEngine.Devices.Injections.shunt import Shunt
from VeraGridEngine.Devices.Injections.static_generator import StaticGenerator
from VeraGridEngine.Devices.Parents.shunt_parent import ShuntParent
from typing import List, Dict, Union, TYPE_CHECKING


from VeraGridEngine.Compilers.Gslv.activation import (pg, generator_control_mode_dict,
                                                           tap_module_control_mode_dict,
                                                           tap_phase_control_mode_dict, shunt_connection_type_dict,
                                                           GSLV_AVAILABLE)
from VeraGridEngine.basic_structures import IntVec, Vec
from VeraGridEngine.Devices.Profiles import AnyProfile
from VeraGridEngine.enumerations import (GeneratorControlMode, TapModuleControl, TapPhaseControl)

if TYPE_CHECKING:  # Only imports the below statements during type checking
    pass


[docs] def get_gslv_mip_solvers_list() -> List[str]: """ Get list of available MIP solvers :return: """ if GSLV_AVAILABLE: return list() else: return list()
[docs] def convert_tap_module_control_mode_dict(data: Dict[int, TapModuleControl]) -> Dict[int, "pg.TapModuleControl"]: """ Function to convert a dictionary of TapModuleControl modes to pg.TapModuleControl modes :param data: :return: """ return {i: tap_module_control_mode_dict[val] for i, val in data.items()}
[docs] def convert_tap_module_control_mode_lst(data: List[TapModuleControl]) -> List["pg.TapModuleControl"]: """ Function to convert a list of TapModuleControl modes to pg.TapModuleControl modes :param data: :return: """ return [tap_module_control_mode_dict[val] for val in data]
[docs] def convert_tap_phase_control_mode_dict(data: Dict[int, TapPhaseControl]) -> Dict[int, "pg.TapPhaseControl"]: """ Function to convert a dictionary of TapPhaseControl modes to pg.TapPhaseControl modes :param data: :return: """ return {i: tap_phase_control_mode_dict[val] for i, val in data.items()}
[docs] def convert_tap_phase_control_mode_lst(data: List[TapPhaseControl]) -> List["pg.TapPhaseControl"]: """ Function to convert a list of TapPhaseControl modes to pg.TapPhaseControl modes :param data: :return: """ return [tap_phase_control_mode_dict[val] for val in data]
[docs] def fill_profile(gslv_profile: "pg.Profiledouble|pg.Profilebool|pg.Profileint|pg.Profileuint", gc_profile: AnyProfile, use_time_series: bool, time_indices: Union[IntVec, None], n_time: int = 1, default_val: int | float | bool | TapPhaseControl | TapModuleControl = 0 ) -> "pg.Profiledouble|pg.Profilebool|pg.Profileint|pg.Profileuint": """ Generates a default time series :param gslv_profile: Profile from gslv to fill in :param gc_profile: Profile from veragrid to convert :param use_time_series: use time series? :param time_indices: time series indices if any (optional) :param n_time: number of time steps :param default_val: Default value :return: Filled GSLV profile. """ if use_time_series: if gc_profile.is_sparse: if time_indices is None: default_val = gc_profile.sparse_array.default_value if isinstance(default_val, TapPhaseControl): data = convert_tap_phase_control_mode_dict(data=gc_profile.sparse_array.get_map()) # we pick all the profile gslv_profile.init_sparse(default_value=tap_phase_control_mode_dict[gc_profile.default_value], data=data) elif isinstance(default_val, TapModuleControl): data = convert_tap_module_control_mode_dict(data=gc_profile.sparse_array.get_map()) # we pick all the profile gslv_profile.init_sparse(default_value=tap_module_control_mode_dict[gc_profile.default_value], data=data) elif isinstance(default_val, GeneratorControlMode): data = {i: generator_control_mode_dict[value] for i, value in gc_profile.sparse_array.get_map().items()} gslv_profile.init_sparse(default_value=generator_control_mode_dict[gc_profile.default_value], data=data) else: data = gc_profile.sparse_array.get_map() # we pick all the profile gslv_profile.init_sparse(default_value=gc_profile.default_value, data=data) else: assert len(time_indices) == n_time # we need a sliced version sp_arr2 = gc_profile.sparse_array.slice(time_indices) if isinstance(default_val, TapPhaseControl): data = convert_tap_phase_control_mode_dict(data=sp_arr2.get_map()) gslv_profile.init_sparse(default_value=tap_phase_control_mode_dict[gc_profile.default_value], data=data) elif isinstance(default_val, TapModuleControl): data = convert_tap_module_control_mode_dict(data=sp_arr2.get_map()) gslv_profile.init_sparse(default_value=tap_module_control_mode_dict[gc_profile.default_value], data=data) elif isinstance(default_val, GeneratorControlMode): data = {i: generator_control_mode_dict[value] for i, value in sp_arr2.get_map().items()} gslv_profile.init_sparse(default_value=generator_control_mode_dict[gc_profile.default_value], data=data) else: data = sp_arr2.get_map() gslv_profile.init_sparse(default_value=gc_profile.default_value, data=data) else: if time_indices is None: # we pick all the profile if isinstance(default_val, TapPhaseControl): data = convert_tap_phase_control_mode_lst(data=gc_profile.dense_array) elif isinstance(default_val, TapModuleControl): data = convert_tap_module_control_mode_dict(data=gc_profile.dense_array) elif isinstance(default_val, GeneratorControlMode): data = [generator_control_mode_dict[value] for value in gc_profile.dense_array] else: data = gc_profile.dense_array gslv_profile.init_dense(data) else: assert len(time_indices) == n_time # we need a sliced version if isinstance(default_val, TapPhaseControl): data = convert_tap_phase_control_mode_lst(data=gc_profile.dense_array[time_indices]) elif isinstance(default_val, TapModuleControl): data = convert_tap_module_control_mode_dict(data=gc_profile.dense_array[time_indices]) elif isinstance(default_val, GeneratorControlMode): data = [generator_control_mode_dict[value] for value in gc_profile.dense_array[time_indices]] else: data = gc_profile.dense_array[time_indices] gslv_profile.init_dense(data) else: if isinstance(default_val, TapPhaseControl): gslv_profile.fill(tap_phase_control_mode_dict[default_val]) elif isinstance(default_val, TapModuleControl): gslv_profile.fill(tap_module_control_mode_dict[default_val]) elif isinstance(default_val, GeneratorControlMode): gslv_profile.fill(generator_control_mode_dict[default_val]) else: gslv_profile.fill(default_val) return gslv_profile
[docs] def fill_profile_with_array(gslv_profile: "pg.Profiledouble", arr: Vec, use_time_series: bool, time_indices: Union[IntVec, None], n_time: int = 1, default_val: float = 0.0) -> "pg.Profiledouble": """ Generate one profile from a dense array. :param gslv_profile: GSLV profile to fill. :param arr: Dense VeraGrid array. :param use_time_series: Whether time-series data is being exported. :param time_indices: Optional time-series selection. :param n_time: Number of exported time steps. :param default_val: Scalar fallback value for snapshot exports. :return: Filled GSLV profile. """ if use_time_series: if time_indices is None: # When there is no slicing request, the entire dense profile is exported. gslv_profile.init_dense(arr) else: assert len(time_indices) == n_time # The time-series export must preserve the selected VeraGrid slice only. gslv_profile.init_dense(arr[time_indices]) else: # Snapshot exports collapse the profile to a single scalar value. gslv_profile.fill(default_val) return gslv_profile
[docs] def set_injection_associations(gslv_elm: "pg.InjectionParent", elm: Load | Generator | StaticGenerator | Shunt | ControllableShunt, facility_dict: Dict[Facility, "pg.Facility"], technology_dict: Dict[Technology, "pg.Technology"]) -> None: """ Copy common VeraGrid injection associations into one GSLV injection. :param gslv_elm: Target GSLV injection. :param elm: Source VeraGrid injection. :param facility_dict: VeraGrid-to-GSLV facility lookup. :param technology_dict: VeraGrid-to-GSLV technology lookup. :return: None. """ if elm.facility is None: pass else: facility: pg.Facility | None = facility_dict.get(elm.facility, None) if facility is None: pass else: gslv_elm.facility = facility for association in elm.technologies: technology: pg.Technology | None = technology_dict.get(association.api_object, None) if technology is None: pass else: gslv_elm.technologies.add_object(api_obj=technology, val=float(association.value))
[docs] def set_generator_associations(gslv_elm: "pg.Generator", elm: Generator, facility_dict: Dict[Facility, "pg.Facility"], technology_dict: Dict[Technology, "pg.Technology"], fuel_dict: Dict[Fuel, "pg.Fuel"], emission_gas_dict: Dict[EmissionGas, "pg.EmissionGas"]) -> None: """ Copy VeraGrid generator associations into one GSLV generator-like injection. :param gslv_elm: Target GSLV generator or battery. :param elm: Source VeraGrid generator or battery. :param facility_dict: VeraGrid-to-GSLV facility lookup. :param technology_dict: VeraGrid-to-GSLV technology lookup. :param fuel_dict: VeraGrid-to-GSLV fuel lookup. :param emission_gas_dict: VeraGrid-to-GSLV emission gas lookup. :return: None. """ set_injection_associations( gslv_elm=gslv_elm, elm=elm, facility_dict=facility_dict, technology_dict=technology_dict, ) for association in elm.fuels: fuel: pg.Fuel | None = fuel_dict.get(association.api_object, None) if fuel is None: pass else: gslv_elm.fuels.add_object(api_obj=fuel, val=float(association.value)) for association in elm.emissions: emission_gas: pg.EmissionGas | None = emission_gas_dict.get(association.api_object, None) if emission_gas is None: pass else: gslv_elm.emissions.add_object(api_obj=emission_gas, val=float(association.value))
[docs] def get_single_three_phase_snapshot_index( use_time_series: bool, time_indices: Union[IntVec, None], n_time: int) -> int | None: """ Return the original VeraGrid snapshot index when one three-phase slice is being exported. :param use_time_series: Whether the conversion uses time-series slicing. :param time_indices: Requested time indices. :param n_time: Number of exported time steps. :return: Original time index when there is exactly one slice, else ``None``. """ if use_time_series: if time_indices is not None: if n_time == 1: return int(time_indices[0]) else: return None else: return None else: return None
[docs] def apply_three_phase_load_data(gslv_load: "pg.Load", elm: Load, time_index: int | None) -> None: """ Copy explicit three-phase load data into one GSLV load object. :param gslv_load: Target GSLV load. :param elm: Source VeraGrid load. :param time_index: Source time index when exporting one time slice. :return: None. """ pa: float pb: float pc: float qa: float qb: float qc: float g1: float g2: float g3: float b1: float b2: float b3: float ir1: float ir2: float ir3: float ii1: float ii2: float ii3: float # The GSLV wrapper only exposes scalar three-phase setters, so each export must # resolve the VeraGrid phase values at one concrete time slice. if time_index is None: pa = float(elm.Pa) pb = float(elm.Pb) pc = float(elm.Pc) qa = float(elm.Qa) qb = float(elm.Qb) qc = float(elm.Qc) g1 = float(elm.G1) g2 = float(elm.G2) g3 = float(elm.G3) b1 = float(elm.B1) b2 = float(elm.B2) b3 = float(elm.B3) ir1 = float(elm.Ir1) ir2 = float(elm.Ir2) ir3 = float(elm.Ir3) ii1 = float(elm.Ii1) ii2 = float(elm.Ii2) ii3 = float(elm.Ii3) else: pa = float(elm.get_Pa_at(time_index)) pb = float(elm.get_Pb_at(time_index)) pc = float(elm.get_Pc_at(time_index)) qa = float(elm.get_Qa_at(time_index)) qb = float(elm.get_Qb_at(time_index)) qc = float(elm.get_Qc_at(time_index)) g1 = float(elm.get_G1_at(time_index)) g2 = float(elm.get_G2_at(time_index)) g3 = float(elm.get_G3_at(time_index)) b1 = float(elm.get_B1_at(time_index)) b2 = float(elm.get_B2_at(time_index)) b3 = float(elm.get_B3_at(time_index)) ir1 = float(elm.get_Ir1_at(time_index)) ir2 = float(elm.get_Ir2_at(time_index)) ir3 = float(elm.get_Ir3_at(time_index)) ii1 = float(elm.get_Ii1_at(time_index)) ii2 = float(elm.get_Ii2_at(time_index)) ii3 = float(elm.get_Ii3_at(time_index)) # The connection enum changes how the solver maps the phase injections. gslv_load.conn = shunt_connection_type_dict[elm.conn] gslv_load.set_P1_val(pa) gslv_load.set_P2_val(pb) gslv_load.set_P3_val(pc) gslv_load.set_Q1_val(qa) gslv_load.set_Q2_val(qb) gslv_load.set_Q3_val(qc) gslv_load.set_G1_val(g1) gslv_load.set_G2_val(g2) gslv_load.set_G3_val(g3) gslv_load.set_B1_val(b1) gslv_load.set_B2_val(b2) gslv_load.set_B3_val(b3) gslv_load.set_Ir1_val(ir1) gslv_load.set_Ir2_val(ir2) gslv_load.set_Ir3_val(ir3) gslv_load.set_Ii1_val(ii1) gslv_load.set_Ii2_val(ii2) gslv_load.set_Ii3_val(ii3)
[docs] def apply_three_phase_shunt_data( gslv_shunt: "pg.Shunt | pg.ControllableShunt", elm: ShuntParent, time_index: int | None) -> None: """ Copy explicit three-phase shunt data into one GSLV shunt-like object. :param gslv_shunt: Target GSLV shunt-like object. :param elm: Source VeraGrid shunt-like object. :param time_index: Source time index when exporting one time slice. :return: None. """ ga: float gb: float gc: float ba: float bb: float bc: float # The shunt connection and per-phase admittances define the unbalanced branch to neutral. if time_index is None: ga = float(elm.Ga) gb = float(elm.Gb) gc = float(elm.Gc) ba = float(elm.Ba) bb = float(elm.Bb) bc = float(elm.Bc) else: ga = float(elm.get_Ga_at(time_index)) gb = float(elm.get_Gb_at(time_index)) gc = float(elm.get_Gc_at(time_index)) ba = float(elm.get_Ba_at(time_index)) bb = float(elm.get_Bb_at(time_index)) bc = float(elm.get_Bc_at(time_index)) gslv_shunt.conn = shunt_connection_type_dict[elm.conn] gslv_shunt.set_Ga_val(ga) gslv_shunt.set_Gb_val(gb) gslv_shunt.set_Gc_val(gc) gslv_shunt.set_Ba_val(ba) gslv_shunt.set_Bb_val(bb) gslv_shunt.set_Bc_val(bc)