Source code for VeraGridEngine.Compilers.circuit_to_data

# 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
import numpy as np
from cmath import rect
from typing import Dict, Union, TYPE_CHECKING

from VeraGridEngine.basic_structures import Logger
import VeraGridEngine.Devices as dev
from VeraGridEngine.Devices.Substation.bus import Bus
from VeraGridEngine.Devices.Aggregation.area import Area
from VeraGridEngine.enumerations import (BusMode, BranchImpedanceMode, ExternalGridMode, DeviceType,
                                         TapModuleControl, TapPhaseControl, HvdcControlType, ConverterControlType,
                                         ShuntConnectionType, ShuntControlMode, GeneratorControlMode)
from VeraGridEngine.basic_structures import BoolVec, IntVec
from VeraGridEngine.Devices.types import BRANCH_TYPES
from VeraGridEngine.DataStructures.battery_data import BatteryData
from VeraGridEngine.DataStructures.passive_branch_data import PassiveBranchData
from VeraGridEngine.DataStructures.active_branch_data import ActiveBranchData
from VeraGridEngine.DataStructures.bus_data import BusData
from VeraGridEngine.DataStructures.generator_data import GeneratorData
from VeraGridEngine.DataStructures.hvdc_data import HvdcData
from VeraGridEngine.DataStructures.vsc_data import VscData
from VeraGridEngine.DataStructures.load_data import LoadData
from VeraGridEngine.DataStructures.shunt_data import ShuntData
from VeraGridEngine.DataStructures.fluid_node_data import FluidNodeData
from VeraGridEngine.DataStructures.fluid_turbine_data import FluidTurbineData
from VeraGridEngine.DataStructures.fluid_pump_data import FluidPumpData
from VeraGridEngine.DataStructures.fluid_p2x_data import FluidP2XData
from VeraGridEngine.DataStructures.fluid_path_data import FluidPathData
from VeraGridEngine.DataStructures.numerical_circuit import NumericalCircuit

if TYPE_CHECKING:  # Only imports the below statements during type checking
    from VeraGridEngine.Devices.multi_circuit import MultiCircuit
    from VeraGridEngine.Simulations.OPF.opf_results import OptimalPowerFlowResults
    from VeraGridEngine.Simulations.OPF.opf_ts_results import OptimalPowerFlowTimeSeriesResults
    from VeraGridEngine.Simulations.NTC.ntc_results import OptimalNetTransferCapacityResults
    from VeraGridEngine.Simulations.NTC.ntc_ts_results import OptimalNetTransferCapacityTimeSeriesResults
    from VeraGridEngine.Simulations import OptimalNetTransferCapacityResults

    VALID_OPF_RESULTS = Union[
        OptimalPowerFlowResults,
        OptimalPowerFlowTimeSeriesResults,
        OptimalNetTransferCapacityResults,
        OptimalNetTransferCapacityTimeSeriesResults
    ]


[docs] def delta2StarAdmittance(Yab: complex, Ybc: complex, Yca: complex): """ Converts Delta to Star in admittances :param Yab: :param Ybc: :param Yca: :return: Ya, Yb, Yc """ return 1 / 3 * np.array([ [0, 0, 0, 0], [0, Yab + Yca, -Yab, -Yca], [0, -Yab, Yab + Ybc, -Ybc], [0, -Yca, -Ybc, Ybc + Yca] ])
[docs] def set_bus_control_voltage(i: int, j: int, remote_control: bool, bus_name: str, bus_voltage_used: BoolVec, bus_data: BusData, candidate_Vm: float, logger: Logger) -> None: """ Set the bus control voltage :param i: Bus index :param j: Remote Bus index :param remote_control: Using remote control? :param bus_name: Bus name :param bus_voltage_used: Array of flags indicating if a bus voltage has been modified before :param bus_data: BusData :param candidate_Vm: Voltage set point that you want to set :param logger: Logger """ if bus_data.bus_types[i] != BusMode.Slack_tpe.value: # if it is not Slack if remote_control and j > -1 and j != i: # delete voltage control # bus_data.bus_types[j] = BusMode.PQV_tpe.value # remote bus to PQV type bus_data.set_bus_mode(j, BusMode.PQV_tpe) # bus_data.bus_types[i] = BusMode.P_tpe.value # local bus to P type bus_data.set_bus_mode(i, BusMode.P_tpe) else: # local voltage control # bus_data.bus_types[i] = BusMode.PV_tpe.value # set as PV bus_data.set_bus_mode(i, BusMode.PV_tpe) if not bus_voltage_used[i]: if remote_control and j > -1 and j != i: # initialize the remote bus voltage to the control value but preserve angle while updating magnitude existing_angle = np.angle(bus_data.Vbus[j]) bus_data.Vbus[j] = rect(candidate_Vm, existing_angle) bus_voltage_used[j] = True else: # initialize the local bus voltage to the control value but preserve angle while updating magnitude existing_angle = np.angle(bus_data.Vbus[i]) bus_data.Vbus[i] = rect(candidate_Vm, existing_angle) bus_voltage_used[i] = True elif candidate_Vm != bus_data.Vbus[i]: logger.add_error(msg='Different control voltage set points', device=bus_name, value=candidate_Vm, expected_value=bus_data.Vbus[i])
[docs] def set_bus_control_voltage_vsc(i: int, j: int, remote_control: bool, bus_name: str, bus_voltage_used: BoolVec, bus_data: BusData, candidate_Vm: float, use_stored_guess: bool, logger: Logger) -> None: """ Set the bus control voltage :param i: Bus index :param j: Remote Bus index :param remote_control: Using remote control? :param bus_name: Bus name :param bus_voltage_used: Array of flags indicating if a bus voltage has been modified before :param bus_data: BusData :param candidate_Vm: Voltage set point that you want to set :param use_stored_guess: Use the stored seed values? :param logger: Logger """ if bus_data.bus_types[i] != BusMode.Slack_tpe.value: # if it is not Slack if remote_control and j > -1 and j != i: # delete voltage control # bus_data.bus_types[j] = BusMode.PQV_tpe.value # remote bus to PQV type # bus_data.set_bus_mode(j, BusMode.PQV_tpe) bus_data.is_p_controlled[j] = True bus_data.is_q_controlled[j] = True bus_data.is_vm_controlled[j] = True # bus_data.bus_types[i] = BusMode.P_tpe.value # local bus to P type # bus_data.set_bus_mode(i, BusMode.P_tpe) bus_data.is_p_controlled[i] = True else: # local voltage control # bus_data.bus_types[i] = BusMode.PV_tpe.value # set as PV # bus_data.set_bus_mode(i, BusMode.PV_tpe) bus_data.is_p_controlled[i] = True bus_data.is_vm_controlled[i] = True if not use_stored_guess: if not bus_voltage_used[i]: if remote_control and j > -1 and j != i: # initialize the remote bus voltage to the control value but preserve angle while updating magnitude existing_angle = np.angle(bus_data.Vbus[j]) bus_data.Vbus[j] = complex(candidate_Vm, 0) * np.exp(1j * existing_angle) bus_voltage_used[j] = True else: # initialize the local bus voltage to the control value but preserve angle while updating magnitude existing_angle = np.angle(bus_data.Vbus[i]) bus_data.Vbus[i] = complex(candidate_Vm, 0) * np.exp(1j * existing_angle) bus_voltage_used[i] = True elif candidate_Vm != bus_data.Vbus[i]: logger.add_error(msg='Different control voltage set points', device=bus_name, value=candidate_Vm, expected_value=bus_data.Vbus[i])
[docs] def set_bus_control_angle_vsc(i: int, j: int, remote_control: bool, bus_name: str, bus_angle_used: BoolVec, bus_data: BusData, candidate_Va: float, use_stored_guess: bool, logger: Logger) -> None: """ Set the bus control angle :param i: Bus index :param j: Remote Bus index :param remote_control: Using remote control? :param bus_name: Bus name :param bus_angle_used: Array of flags indicating if a bus angle has been modified before :param bus_data: BusData :param candidate_Va: Angle set point that you want to set in degrees :param use_stored_guess: Use the stored seed values? :param logger: Logger """ if bus_data.bus_types[i] != BusMode.Slack_tpe.value: # if it is not Slack if remote_control and j > -1 and j != i: # delete angle control # bus_data.bus_types[j] = BusMode.PQV_tpe.value # remote bus to PQV type # bus_data.set_bus_mode(j, BusMode.PQV_tpe) bus_data.is_p_controlled[j] = True bus_data.is_q_controlled[j] = True bus_data.is_va_controlled[j] = True # bus_data.bus_types[i] = BusMode.P_tpe.value # local bus to P type # bus_data.set_bus_mode(i, BusMode.P_tpe) bus_data.is_p_controlled[i] = True else: # local angle control controlling q as well # bus_data.bus_types[i] = BusMode.PV_tpe.value # set as PV # bus_data.set_bus_mode(i, BusMode.PV_tpe) bus_data.is_q_controlled[i] = True bus_data.is_va_controlled[i] = True if not use_stored_guess: if not bus_angle_used[i]: if remote_control and j > -1 and j != i: # initialize the remote bus angle to the control value but preserve magnitude while updating angle existing_magnitude = np.abs(bus_data.Vbus[j]) bus_data.Vbus[j] = existing_magnitude * np.exp(1j * candidate_Va * np.pi / 180) bus_angle_used[j] = True else: # initialize the local bus angle to the control value but preserve magnitude while updating angle existing_magnitude = np.abs(bus_data.Vbus[i]) bus_data.Vbus[i] = existing_magnitude * np.exp(1j * candidate_Va * np.pi / 180) bus_angle_used[i] = True else: existing_angle = np.angle(bus_data.Vbus[i]) if not np.isclose(candidate_Va, existing_angle): logger.add_error(msg='Different control angle set points', device=bus_name, value=candidate_Va, expected_value=existing_angle)
[docs] def set_bus_control_voltage_hvdc(i: int, j: int, remote_control: bool, bus_name: str, bus_voltage_used: BoolVec, bus_data: BusData, candidate_Vm: float, use_stored_guess: bool, logger: Logger) -> None: """ Set the bus control voltage :param i: Bus index :param j: Remote Bus index :param remote_control: Using remote control? :param bus_name: Bus name :param bus_voltage_used: Array of flags indicating if a bus voltage has been modified before :param bus_data: BusData :param candidate_Vm: Voltage set point that you want to set :param use_stored_guess: Use the stored seed values? :param logger: Logger """ if bus_data.bus_types[i] != BusMode.Slack_tpe.value: # if it is not Slack # local voltage control bus_data.bus_types[i] = BusMode.PV_tpe.value # set as PV # bus_data.set_bus_mode(i, BusMode.PV_tpe) bus_data.is_p_controlled[i] = True bus_data.is_vm_controlled[i] = True bus_data.is_q_controlled[i] = True if not use_stored_guess: if not bus_voltage_used[i]: if remote_control and j > -1 and j != i: # initialize the remote bus voltage to the control value bus_data.Vbus[j] = complex(candidate_Vm, 0) bus_voltage_used[j] = True else: # initialize the local bus voltage to the control value bus_data.Vbus[i] = complex(candidate_Vm, 0) bus_voltage_used[i] = True elif candidate_Vm != bus_data.Vbus[i]: logger.add_error(msg='Different control voltage set points', device=bus_name, value=candidate_Vm, expected_value=bus_data.Vbus[i])
[docs] def get_bus_data(bus_data: BusData, circuit: MultiCircuit, areas_dict: Dict[Area, int], t_idx: int | None, use_stored_guess=False, consider_grounded_buses: bool = False) -> None: """ :param bus_data: BusData :param circuit: :param areas_dict: :param t_idx: :param use_stored_guess: :param consider_grounded_buses: :return: """ substation_dict = {sub: i for i, sub in enumerate(circuit.substations)} for i, bus in enumerate(circuit.buses): # bus parameters bus_data.original_idx[i] = i bus_data.names[i] = bus.name bus_data.idtag[i] = bus.idtag bus_data.Vnom[i] = bus.Vnom bus_data.cost_v[i] = bus.Vm_cost bus_data.Vbus[i] = bus.get_voltage_guess(use_stored_guess=use_stored_guess) bus_data.is_dc[i] = bus.is_dc # Grounded buses go to zero if bus.is_grounded and consider_grounded_buses and bus.is_dc: bus_data.Vbus[i] = 1e-20 * np.exp(1j * 1e-20) # effectively zero bus_data.is_grounded[i] = bus.is_grounded else: bus_data.is_grounded[i] = False bus_data.angle_min[i] = bus.angle_min bus_data.angle_max[i] = bus.angle_max if bus.is_slack: # bus_data.bus_types[i] = BusMode.Slack_tpe.value # VD bus_data.set_bus_mode(i, BusMode.Slack_tpe) else: # PQ by default, later it is modified by generators and batteries # bus_data.bus_types[i] = BusMode.PQ_tpe.value bus_data.set_bus_mode(i, BusMode.PQ_tpe) bus_data.substations[i] = substation_dict.get(bus.substation, 0) bus_data.areas[i] = areas_dict.get(bus.area, 0) bus_data.active[i] = bus.get_active_at(t_idx) bus_data.Vmin[i] = bus.get_Vmin_at(t_idx) bus_data.Vmax[i] = bus.get_Vmax_at(t_idx) return None
[docs] def get_load_data(data: LoadData, circuit: MultiCircuit, bus_dict: Dict[Bus, int], bus_voltage_used: BoolVec, bus_data: BusData, t_idx: int | None, logger: Logger, opf_results: Union[OptimalPowerFlowResults, None] = None, fill_three_phase: bool = False) -> LoadData: """ :param data: :param circuit: :param bus_dict: :param bus_voltage_used: :param bus_data: :param logger: :param t_idx: :param opf_results: :param use_stored_guess: :param fill_three_phase: Fill the tree phase info? :return: """ ii = 0 for elm in circuit.get_loads(): if elm.bus is None: data.bus_idx[ii] = -1 data.active[ii] = False else: i = bus_dict[elm.bus] data.names[ii] = elm.name data.idtag[ii] = elm.idtag data.bus_idx[ii] = i data.original_idx[ii] = ii data.mttf[ii] = elm.mttf data.mttr[ii] = elm.mttr data.scalable[ii] = elm.scalable if opf_results is not None: if opf_results.load_shedding.ndim == 1: data.S[ii] = elm.get_S_at(t_idx) - opf_results.load_shedding[ii] elif opf_results.load_shedding.ndim == 2: data.S[ii] = elm.get_S_at(t_idx) - opf_results.load_shedding[t_idx, ii] else: data.S[ii] = elm.get_S_at(t_idx) # ??? else: data.S[ii] = elm.get_S_at(t_idx) if fill_three_phase: if elm.conn == ShuntConnectionType.GroundedStar: S_abc = any([elm.get_Sa_at(t_idx), elm.get_Sb_at(t_idx), elm.get_Sc_at(t_idx)]) if not S_abc: data.S3_star[4 * ii + 1] = elm.get_S_at(t_idx) / 3 data.S3_star[4 * ii + 2] = elm.get_S_at(t_idx) / 3 data.S3_star[4 * ii + 3] = elm.get_S_at(t_idx) / 3 else: data.S3_star[4 * ii + 1] = elm.get_Sa_at(t_idx) data.S3_star[4 * ii + 2] = elm.get_Sb_at(t_idx) data.S3_star[4 * ii + 3] = elm.get_Sc_at(t_idx) I_abc = any( [np.conj(elm.get_I1_at(t_idx)), np.conj(elm.get_I2_at(t_idx)), np.conj(elm.get_I3_at(t_idx))]) if not I_abc: data.I3_star[4 * ii + 1] = np.conj(elm.get_I_at(t_idx)) / 3 data.I3_star[4 * ii + 2] = np.conj(elm.get_I_at(t_idx)) / 3 data.I3_star[4 * ii + 3] = np.conj(elm.get_I_at(t_idx)) / 3 else: data.I3_star[4 * ii + 1] = np.conj(elm.get_I1_at(t_idx)) data.I3_star[4 * ii + 2] = np.conj(elm.get_I2_at(t_idx)) data.I3_star[4 * ii + 3] = np.conj(elm.get_I3_at(t_idx)) Y_abc = any([elm.get_Y1_at(t_idx), elm.get_Y2_at(t_idx), elm.get_Y3_at(t_idx)]) if not Y_abc: data.Y3_star[4 * ii + 0, 1] = -1 * elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 0, 2] = -1 * elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 0, 3] = -1 * elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 1, 1] = elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 2, 2] = elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 3, 3] = elm.get_Y_at(t_idx) / 3 else: data.Y3_star[4 * ii + 0, 1] = -1 * elm.get_Y1_at(t_idx) data.Y3_star[4 * ii + 0, 2] = -1 * elm.get_Y2_at(t_idx) data.Y3_star[4 * ii + 0, 3] = -1 * elm.get_Y3_at(t_idx) data.Y3_star[4 * ii + 1, 1] = elm.get_Y1_at(t_idx) data.Y3_star[4 * ii + 2, 2] = elm.get_Y2_at(t_idx) data.Y3_star[4 * ii + 3, 3] = elm.get_Y3_at(t_idx) elif elm.conn == ShuntConnectionType.FloatingStar: # Admittances Ya = elm.get_Y1_at(t_idx) Yb = elm.get_Y2_at(t_idx) Yc = elm.get_Y3_at(t_idx) if Ya != 0.0 + 0.0j and Yb != 0.0 + 0.0j and Yc != 0.0 + 0.0j: data.A_floatingstar[ii] = Ya / (Ya + Yb + Yc) data.B_floatingstar[ii] = Yb / (Ya + Yb + Yc) data.C_floatingstar[ii] = Yc / (Ya + Yb + Yc) A = Ya / (Ya + Yb + Yc) B = Yb / (Ya + Yb + Yc) C = Yc / (Ya + Yb + Yc) # First row data.Y3_star[4 * ii + 1, 1] = (1 - A) * Ya data.Y3_star[4 * ii + 1, 2] = -B * Ya data.Y3_star[4 * ii + 1, 3] = -C * Ya # Second row data.Y3_star[4 * ii + 2, 1] = -A * Yb data.Y3_star[4 * ii + 2, 2] = (1 - B) * Yb data.Y3_star[4 * ii + 2, 3] = -C * Yb # Third row data.Y3_star[4 * ii + 3, 1] = -A * Yc data.Y3_star[4 * ii + 3, 2] = -B * Yc data.Y3_star[4 * ii + 3, 3] = (1 - C) * Yc # Currents Ia = np.conj(elm.get_I1_at(t_idx)) Ib = np.conj(elm.get_I2_at(t_idx)) Ic = np.conj(elm.get_I3_at(t_idx)) if Ia != 0.0 + 0.0j and Ib != 0.0 + 0.0j and Ic != 0.0 + 0.0j: data.I3_floatingstar[4 * ii + 1] = Ia data.I3_floatingstar[4 * ii + 2] = Ib data.I3_floatingstar[4 * ii + 3] = Ic # Powers Sa = elm.get_Sa_at(t_idx) Sb = elm.get_Sb_at(t_idx) Sc = elm.get_Sc_at(t_idx) if Sa != 0.0 + 0.0j and Sb != 0.0 + 0.0j and Sc != 0.0 + 0.0j: data.S3_floatingstar[4 * ii + 1] = Sa data.S3_floatingstar[4 * ii + 2] = Sb data.S3_floatingstar[4 * ii + 3] = Sc elif elm.conn == ShuntConnectionType.NeutralStar: # Admittance Ya = elm.get_Y1_at(t_idx) Yb = elm.get_Y2_at(t_idx) Yc = elm.get_Y3_at(t_idx) # First row data.Y3_star[4 * ii + 0, 0] = Ya + Yb + Yc + 1e-4 data.Y3_star[4 * ii + 0, 1] = -Ya data.Y3_star[4 * ii + 0, 2] = -Yb data.Y3_star[4 * ii + 0, 3] = -Yc # Second row data.Y3_star[4 * ii + 1, 0] = -Ya data.Y3_star[4 * ii + 1, 1] = Ya # Third row data.Y3_star[4 * ii + 2, 0] = -Yb data.Y3_star[4 * ii + 2, 2] = Yb # Fourth row data.Y3_star[4 * ii + 3, 0] = -Yc data.Y3_star[4 * ii + 3, 3] = Yc # Current data.I3_star[4 * ii + 0] = -np.conj(elm.get_I1_at(t_idx)) - np.conj(elm.get_I2_at(t_idx)) - np.conj( elm.get_I3_at(t_idx)) data.I3_star[4 * ii + 1] = np.conj(elm.get_I1_at(t_idx)) data.I3_star[4 * ii + 2] = np.conj(elm.get_I2_at(t_idx)) data.I3_star[4 * ii + 3] = np.conj(elm.get_I3_at(t_idx)) # Current data.S3_star[4 * ii + 0] = -elm.get_Sa_at(t_idx) - elm.get_Sb_at(t_idx) - elm.get_Sc_at(t_idx) data.S3_star[4 * ii + 1] = elm.get_Sa_at(t_idx) data.S3_star[4 * ii + 2] = elm.get_Sb_at(t_idx) data.S3_star[4 * ii + 3] = elm.get_Sc_at(t_idx) elif elm.conn == ShuntConnectionType.Delta: data.S3_delta[4 * ii + 1] = elm.get_Sa_at(t_idx) data.S3_delta[4 * ii + 2] = elm.get_Sb_at(t_idx) data.S3_delta[4 * ii + 3] = elm.get_Sc_at(t_idx) data.I3_delta[4 * ii + 1] = np.conj(elm.get_I1_at(t_idx)) data.I3_delta[4 * ii + 2] = np.conj(elm.get_I2_at(t_idx)) data.I3_delta[4 * ii + 3] = np.conj(elm.get_I3_at(t_idx)) data.Y3_star[4 * ii:4 * ii + 4, [0, 1, 2, 3]] = delta2StarAdmittance( Yab=elm.get_Y1_at(t_idx), Ybc=elm.get_Y2_at(t_idx), Yca=elm.get_Y3_at(t_idx) ) else: raise Exception(f"Unhandled connection type {elm.conn}") data.I[ii] = elm.get_I_at(t_idx) data.Y[ii] = elm.get_Y_at(t_idx) data.active[ii] = elm.get_active_at(t_idx) data.cost[ii] = elm.get_Cost_at(t_idx) data.shift_key[ii] = elm.get_shift_key_at(t_idx) if elm.use_kw: # pass kW to MW data.S[ii] /= 1000.0 data.I[ii] /= 1000.0 data.Y[ii] /= 1000.0 data.cost[ii] /= 1000.0 # reactive power sharing data if data.active[ii]: bus_data.q_fixed[i] -= data.S[ii].imag bus_data.ii_fixed[i] -= data.I[ii].imag bus_data.b_fixed[i] -= data.Y[ii].imag # data.C_bus_elm[i, ii] = 1 ii += 1 for elm in circuit.get_static_generators(): if elm.bus is None: data.bus_idx[ii] = -1 data.active[ii] = False else: i = bus_dict[elm.bus] data.bus_idx[ii] = i data.names[ii] = elm.name data.idtag[ii] = elm.idtag data.original_idx[ii] = ii data.scalable[ii] = elm.scalable data.S[ii] -= elm.get_S_at(t_idx) data.active[ii] = elm.get_active_at(t_idx) data.cost[ii] = elm.get_Cost_at(t_idx) data.shift_key[ii] = elm.get_shift_key_at(t_idx) if fill_three_phase: if elm.conn == ShuntConnectionType.GroundedStar: data.S3_star[3 * ii + 1] -= elm.get_Sa_at(t_idx) data.S3_star[3 * ii + 2] -= elm.get_Sb_at(t_idx) data.S3_star[3 * ii + 3] -= elm.get_Sc_at(t_idx) elif elm.conn == ShuntConnectionType.Delta: data.S3_delta[3 * ii + 1] -= elm.get_Sa_at(t_idx) data.S3_delta[3 * ii + 2] -= elm.get_Sb_at(t_idx) data.S3_delta[3 * ii + 3] -= elm.get_Sc_at(t_idx) else: raise Exception(f"Unhandled connection type {elm.conn}") if elm.use_kw: # pass kW to MW data.S[ii] /= 1000.0 data.cost[ii] /= 1000.0 # reactive power sharing data if data.active[ii]: bus_data.q_fixed[i] += data.S[ii].imag # bus_data.ii_fixed[i] += data.I[ii].imag # bus_data.b_fixed[i] += data.Y[ii].imag # data.C_bus_elm[i, ii] = 1 ii += 1 for elm in circuit.get_external_grids(): if elm.bus is None: data.bus_idx[ii] = -1 data.active[ii] = False else: i = bus_dict[elm.bus] data.bus_idx[ii] = i data.names[ii] = elm.name data.idtag[ii] = elm.idtag data.scalable[ii] = elm.scalable data.original_idx[ii] = ii # change stuff depending on the modes if elm.mode == ExternalGridMode.VD: # bus_data.bus_types[i] = BusMode.Slack_tpe.value # set as Slack bus_data.set_bus_mode(i, BusMode.Slack_tpe) set_bus_control_voltage(i=i, j=-1, remote_control=False, bus_name=elm.bus.name, bus_data=bus_data, bus_voltage_used=bus_voltage_used, candidate_Vm=elm.get_Vm_at(t_idx), logger=logger) elif elm.mode == ExternalGridMode.PV: set_bus_control_voltage(i=i, j=-1, remote_control=False, bus_name=elm.bus.name, bus_data=bus_data, bus_voltage_used=bus_voltage_used, candidate_Vm=elm.get_Vm_at(t_idx), logger=logger) data.S[ii] += elm.get_S_at(t_idx) if fill_three_phase: if elm.conn == ShuntConnectionType.GroundedStar: data.S3_star[3 * ii + 1] += elm.get_Sa_at(t_idx) data.S3_star[3 * ii + 2] += elm.get_Sb_at(t_idx) data.S3_star[3 * ii + 3] += elm.get_Sc_at(t_idx) elif elm.conn == ShuntConnectionType.Delta: data.S3_delta[3 * ii + 1] += elm.get_Sa_at(t_idx) data.S3_delta[3 * ii + 2] += elm.get_Sb_at(t_idx) data.S3_delta[3 * ii + 3] += elm.get_Sc_at(t_idx) else: raise Exception(f"Unhandled connection type {elm.conn}") data.active[ii] = elm.get_active_at(t_idx) data.shift_key[ii] = elm.get_shift_key_at(t_idx) if elm.use_kw: # pass kW to MW data.S[ii] /= 1000.0 data.cost[ii] /= 1000.0 # reactive power sharing data if data.active[ii]: bus_data.q_fixed[i] += data.S[ii].imag bus_data.ii_fixed[i] += data.I[ii].imag bus_data.b_fixed[i] += data.Y[ii].imag # data.C_bus_elm[i, ii] = 1 ii += 1 for elm in circuit.get_current_injections(): if elm.bus is None: data.bus_idx[ii] = -1 data.active[ii] = False else: i = bus_dict[elm.bus] data.bus_idx[ii] = i data.names[ii] = elm.name data.idtag[ii] = elm.idtag data.original_idx[ii] = ii data.mttf[ii] = elm.mttf data.mttr[ii] = elm.mttr data.scalable[ii] = elm.scalable data.I[ii] += elm.get_I_at(t_idx) if fill_three_phase: if elm.conn == ShuntConnectionType.GroundedStar: data.I3_star[3 * ii + 1] += np.conj(elm.get_I1_at(t_idx)) data.I3_star[3 * ii + 2] += np.conj(elm.get_I2_at(t_idx)) data.I3_star[3 * ii + 3] += np.conj(elm.get_I3_at(t_idx)) elif elm.conn == ShuntConnectionType.Delta: data.I3_delta[3 * ii + 1] += np.conj(elm.get_I1_at(t_idx)) data.I3_delta[3 * ii + 2] += np.conj(elm.get_I2_at(t_idx)) data.I3_delta[3 * ii + 3] += np.conj(elm.get_I3_at(t_idx)) else: raise Exception(f"Unhandled connection type {elm.conn}") data.active[ii] = elm.get_active_at(t_idx) data.cost[ii] = elm.get_Cost_at(t_idx) data.shift_key[ii] = elm.get_shift_key_at(t_idx) if elm.use_kw: # pass kW to MW data.I[ii] /= 1000.0 data.cost[ii] /= 1000.0 # reactive power sharing data if data.active[ii]: bus_data.q_fixed[i] += data.S[ii].imag bus_data.ii_fixed[i] += data.I[ii].imag bus_data.b_fixed[i] += data.Y[ii].imag # data.C_bus_elm[i, ii] = 1 ii += 1 return data
[docs] def get_shunt_data( data: ShuntData, circuit: MultiCircuit, bus_dict, bus_voltage_used: BoolVec, bus_data: BusData, t_idx: int | None, logger: Logger, control_remote_voltage: bool = True, fill_three_phase: bool = False ) -> None: """ :param data: :param circuit: :param bus_dict: :param bus_voltage_used: :param bus_data: :param logger: :param t_idx: :param use_stored_guess: :param control_remote_voltage: :param fill_three_phase: :return: """ ii = 0 for k, elm in enumerate(circuit.shunts): if elm.bus is None: data.bus_idx[k] = -1 data.active[k] = False else: i = bus_dict[elm.bus] data.bus_idx[k] = i data.names[k] = elm.name data.idtag[k] = elm.idtag data.original_idx[ii] = ii data.mttf[k] = elm.mttf data.mttr[k] = elm.mttr data.active[k] = elm.get_active_at(t_idx) data.Y[k] = elm.get_Y_at(t_idx) if fill_three_phase: if elm.conn == ShuntConnectionType.GroundedStar: Y_abc = any([elm.get_Ya_at(t_idx), elm.get_Yb_at(t_idx), elm.get_Yc_at(t_idx)]) if not Y_abc: data.Y3_star[4 * ii + 0, 1] = -1 * elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 0, 2] = -1 * elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 0, 3] = -1 * elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 1, 1] = elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 2, 2] = elm.get_Y_at(t_idx) / 3 data.Y3_star[4 * ii + 3, 3] = elm.get_Y_at(t_idx) / 3 else: data.Y3_star[4 * ii + 0, 1] = -1 * elm.get_Ya_at(t_idx) data.Y3_star[4 * ii + 0, 2] = -1 * elm.get_Yb_at(t_idx) data.Y3_star[4 * ii + 0, 3] = -1 * elm.get_Yc_at(t_idx) data.Y3_star[4 * ii + 1, 1] = elm.get_Ya_at(t_idx) data.Y3_star[4 * ii + 2, 2] = elm.get_Yb_at(t_idx) data.Y3_star[4 * ii + 3, 3] = elm.get_Yc_at(t_idx) elif elm.conn == ShuntConnectionType.FloatingStar: # Admittances Ya = elm.get_Ya_at(t_idx) Yb = elm.get_Yb_at(t_idx) Yc = elm.get_Yc_at(t_idx) if Ya != 0.0 + 0.0j and Yb != 0.0 + 0.0j and Yc != 0.0 + 0.0j: data.A_floating_star[ii] = Ya / (Ya + Yb + Yc) data.B_floating_star[ii] = Yb / (Ya + Yb + Yc) data.C_floating_star[ii] = Yc / (Ya + Yb + Yc) A = Ya / (Ya + Yb + Yc) B = Yb / (Ya + Yb + Yc) C = Yc / (Ya + Yb + Yc) # First row data.Y3_star[4 * ii + 1, 1] = (1 - A) * Ya data.Y3_star[4 * ii + 1, 2] = -B * Ya data.Y3_star[4 * ii + 1, 3] = -C * Ya # Second row data.Y3_star[4 * ii + 2, 1] = -A * Yb data.Y3_star[4 * ii + 2, 2] = (1 - B) * Yb data.Y3_star[4 * ii + 2, 3] = -C * Yb # Third row data.Y3_star[4 * ii + 3, 1] = -A * Yc data.Y3_star[4 * ii + 3, 2] = -B * Yc data.Y3_star[4 * ii + 3, 3] = (1 - C) * Yc elif elm.conn == ShuntConnectionType.NeutralStar: # Admittance Ya = elm.get_Ya_at(t_idx) Yb = elm.get_Yb_at(t_idx) Yc = elm.get_Yc_at(t_idx) # First row data.Y3_star[4 * ii + 0, 0] = Ya + Yb + Yc + 1e-10j data.Y3_star[4 * ii + 0, 1] = -Ya data.Y3_star[4 * ii + 0, 2] = -Yb data.Y3_star[4 * ii + 0, 3] = -Yc # Second row data.Y3_star[4 * ii + 1, 0] = -Ya data.Y3_star[4 * ii + 1, 1] = Ya # Third row data.Y3_star[4 * ii + 2, 0] = -Yb data.Y3_star[4 * ii + 2, 2] = Yb # Fourth row data.Y3_star[4 * ii + 3, 0] = -Yc data.Y3_star[4 * ii + 3, 3] = Yc elif elm.conn == ShuntConnectionType.Delta: data.Y3_star[4 * ii:4 * ii + 4, [0, 1, 2, 3]] = delta2StarAdmittance( Yab=elm.get_Ya_at(t_idx), Ybc=elm.get_Yb_at(t_idx), Yca=elm.get_Yc_at(t_idx) ) else: raise Exception(f"Unhandled connection type {elm.conn}") if elm.use_kw: # pass kW to MW data.Y[ii] /= 1000.0 # reactive power sharing data if data.active[ii]: bus_data.b_fixed[i] += data.Y[ii].imag # data.C_bus_elm[i, k] = 1 ii += 1 for elm in circuit.controllable_shunts: if elm.bus is None: data.bus_idx[ii] = -1 data.active[ii] = False else: i = bus_dict[elm.bus] data.bus_idx[ii] = i data.names[ii] = elm.name data.idtag[ii] = elm.idtag data.original_idx[ii] = ii data.mttf[ii] = elm.mttf data.mttr[ii] = elm.mttr data.control_mode_int[ii] = elm.control_mode.idx() data.vset[ii] = elm.Vset data.vmin[ii] = elm.Vmin data.vmax[ii] = elm.Vmax data.qmin[ii] = elm.Bmin data.qmax[ii] = elm.Bmax data.step[ii] = elm.step data.g_steps.insert(i=ii, x=np.insert(np.cumsum(elm.g_steps), 0, 0)) data.b_steps.insert(i=ii, x=np.insert(np.cumsum(elm.b_steps), 0, 0)) data.active[ii] = elm.get_active_at(t_idx) data.cost[ii] = elm.get_Cost_at(t_idx) if elm.control_mode == ShuntControlMode.Continuous: data.Y[ii] += elm.get_Y_at(t_idx) if elm.control_mode == ShuntControlMode.Discrete: if elm.step < len(elm.g_steps): data.Y[ii] += elm.step * complex(elm.g_steps[elm.step], elm.b_steps[elm.step]) else: data.Y[ii] += complex(elm.g_steps[0], elm.b_steps[0]) if fill_three_phase: if elm.conn == ShuntConnectionType.GroundedStar: data.Y3_star[3 * ii + 0, 1] = elm.get_Ya_at(t_idx) data.Y3_star[3 * ii + 1, 2] = elm.get_Yb_at(t_idx) data.Y3_star[3 * ii + 2, 3] = elm.get_Yc_at(t_idx) elif elm.conn == ShuntConnectionType.Delta: data.Y3_star[4 * ii:4 * ii + 4, [0, 1, 2, 3]] = delta2StarAdmittance( Yab=elm.get_Ya_at(t_idx), Ybc=elm.get_Yb_at(t_idx), Yca=elm.get_Yc_at(t_idx) ) else: raise Exception(f"Unhandled connection type {elm.conn}") if elm.control_mode == ShuntControlMode.Continuous and elm.active: if elm.control_bus is not None: remote_control = True j = bus_dict[elm.control_bus] else: remote_control = False j = -1 data.controllable_bus_idx[ii] = j set_bus_control_voltage(i=i, j=j, remote_control=remote_control and control_remote_voltage, bus_name=elm.bus.name, bus_data=bus_data, bus_voltage_used=bus_voltage_used, candidate_Vm=elm.Vset, logger=logger) if elm.use_kw: # pass kW to MW data.Y[ii] /= 1000.0 # reactive power sharing data if data.active[ii]: if data.control_mode_int[ii] == ShuntControlMode.Locked.idx(): bus_data.q_shared_total[i] += data.Y[ii].imag data.q_share[ii] = data.Y[ii].imag else: bus_data.b_fixed[i] += data.Y[ii].imag # data.C_bus_elm[i, ii] = 1 ii += 1
[docs] def fill_generator_parent( k: int, data: GeneratorData | BatteryData, elm: dev.Generator | dev.Battery, bus_dict: Dict[dev.Bus, int], bus_voltage_used: BoolVec, logger: Logger, bus_data: BusData, t_idx: int | None = None, control_remote_voltage: bool = True, fill_three_phase: bool = False ) -> None: """ Fill the common ancestor of generation and batteries :param k: :param data: :param elm: :param bus_dict: :param bus_voltage_used: :param logger: :param bus_data: :param t_idx: :param control_remote_voltage: :param fill_three_phase: :return: """ if elm.bus is None: data.bus_idx[k] = -1 data.active[k] = False return idx3 = np.arange(3) i = bus_dict[elm.bus] data.bus_idx[k] = i data.names[k] = elm.name data.idtag[k] = elm.idtag data.original_idx[k] = k data.mttf[k] = elm.mttf data.mttr[k] = elm.mttr data.control_mode_int[k] = elm.control_mode.idx() data.installed_p[k] = elm.Snom bus_data.installed_power[i] += elm.Snom # r0, r1, r2, x0, x1, x2 data.r0[k] = elm.R0 data.r1[k] = elm.R1 data.r2[k] = elm.R2 data.x0[k] = elm.X0 data.x1[k] = elm.X1 data.x2[k] = elm.X2 # asynchronous generator impedance data.Rs[k] = elm.Rs data.Xs[k] = elm.Xs data.Xm[k] = elm.Xm data.Rr[k] = elm.Rr data.Xr[k] = elm.Xr data.tpe_int[k] = elm.tpe.idx() data.startup_cost[k] = elm.startup_cost data.shut_down_cost[k] = elm.shutdown_cost data.ramp_up[k] = elm.ramp_up data.ramp_down[k] = elm.ramp_down data.min_time_up[k] = elm.min_time_up data.min_time_down[k] = elm.min_time_down data.dispatchable[k] = elm.get_enabled_dispatch_at(t_idx) data.must_run[k] = elm.get_must_run_at(t_idx) data.capex[k] = elm.capex data.snom[k] = elm.Snom data.scalable[k] = elm.scalable data.p[k] = elm.get_P_at(t_idx) data.active[k] = elm.get_active_at(t_idx) data.v[k] = elm.get_Vset_at(t_idx) data.k_droop[k] = elm.k_droop data.dead_band[k] = elm.dead_band data.pmax[k] = elm.get_Pmax_at(t_idx) data.pmin[k] = elm.get_Pmin_at(t_idx) if elm.use_reactive_power_curve: data.qmin[k] = elm.q_curve.get_qmin(data.p[k]) data.qmax[k] = elm.q_curve.get_qmax(data.p[k]) else: data.qmin[k] = elm.get_Qmin_at(t_idx) data.qmax[k] = elm.get_Qmax_at(t_idx) data.cost_0[k] = elm.get_Cost0_at(t_idx) data.cost_1[k] = elm.get_Cost_at(t_idx) data.cost_2[k] = elm.get_Cost2_at(t_idx) data.shift_key[k] = elm.get_shift_key_at(t_idx) if data.active[k]: if elm.get_srap_enabled_at(t_idx) and data.p[k] > 0.0: bus_data.srap_available_power[i] += data.p[k] if elm.control_mode == GeneratorControlMode.V: if elm.control_bus is not None: remote_control = True j = bus_dict[elm.control_bus] else: remote_control = False j = -1 data.controllable_bus_idx[k] = j set_bus_control_voltage(i=i, j=j, remote_control=remote_control and control_remote_voltage, bus_name=elm.bus.name, bus_data=bus_data, bus_voltage_used=bus_voltage_used, candidate_Vm=elm.Vset, logger=logger) # we pick this value to initialize data.q[k] = elm.get_Q_at(t_idx) elif elm.control_mode == GeneratorControlMode.Q: data.q[k] = elm.get_Q_at(t_idx) elif elm.control_mode == GeneratorControlMode.QVDroop: # Q will be computed by the droop data.q[k] = 0.0 else: pass if elm.use_kw: # pass kW to MW data.p[k] /= 1000.0 data.pmax[k] /= 1000.0 data.pmin[k] /= 1000.0 data.qmax[k] /= 1000.0 data.qmin[k] /= 1000.0 data.snom[k] /= 1000.0 # data.cost_0[k] /= 1000.0 data.cost_1[k] /= 1000.0 data.cost_2[k] /= 1e6 # this is because of MW^2 if fill_three_phase: # Note: for a generator that is balanced, the delta and star configurations # translate to the same values in star data.p3_star[3 * k + idx3] = data.p[k] / 3.0 # reactive power-sharing data # We use P as a reference for scaling, hence issues may arise if P = 0.0 # Thus we add a small value to compensate for that # The small value cannot be 1e-20, as then the split of Q # would be half the value (1e-20/1e-20). # A value of 1e-14 seems a sweet compromise. if data.active[k]: if data.control_mode_int[k] == GeneratorControlMode.V.idx(): bus_data.q_shared_total[i] += data.p[k] + 1e-14 data.q_share[k] = data.p[k] + 1e-14 # bus_data.q_shared_total[i] += data.p[k] # data.q_share[k] = data.p[k] else: bus_data.q_fixed[i] += data.get_q_at(k)
[docs] def get_generator_data( data: GeneratorData, circuit: MultiCircuit, bus_dict, bus_voltage_used: BoolVec, logger: Logger, bus_data: BusData, t_idx: int | None, opf_results: VALID_OPF_RESULTS | None = None, time_series: bool = False, control_remote_voltage: bool = True, fill_three_phase: bool = False ) -> Dict[str, int]: """ :param data: :param circuit: :param bus_dict: :param bus_voltage_used: :param logger: :param bus_data: :param opf_results: :param t_idx: :param time_series: :param use_stored_guess: :param control_remote_voltage: :param fill_three_phase: :return: """ gen_index_dict: Dict[str, int] = dict() for k, elm in enumerate(circuit.get_generators()): gen_index_dict[elm.idtag] = k # associate the idtag to the index fill_generator_parent(k=k, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, logger=logger, t_idx=t_idx, control_remote_voltage=control_remote_voltage, fill_three_phase=fill_three_phase) if opf_results is not None: # overwrite P with the OPF results if time_series: data.p[k] = opf_results.generator_power[t_idx, k] - opf_results.generator_shedding[t_idx, k] else: data.p[k] = opf_results.generator_power[k] - opf_results.generator_shedding[k] return gen_index_dict
[docs] def get_battery_data( data: BatteryData, circuit: MultiCircuit, bus_dict: Dict[Bus, int], bus_voltage_used: BoolVec, logger: Logger, bus_data: BusData, t_idx: int | None, opf_results: VALID_OPF_RESULTS | None = None, time_series=False, control_remote_voltage: bool = True, fill_three_phase: bool = False ) -> None: """ :param data: :param circuit: :param bus_dict: :param bus_voltage_used: :param logger: :param bus_data: :param opf_results: :param t_idx: :param time_series: :param use_stored_guess: :param control_remote_voltage: :param fill_three_phase: :return: """ for k, elm in enumerate(circuit.get_batteries()): fill_generator_parent(k=k, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, logger=logger, t_idx=t_idx, control_remote_voltage=control_remote_voltage, fill_three_phase=fill_three_phase) data.enom[k] = elm.Enom data.min_soc[k] = elm.min_soc data.max_soc[k] = elm.max_soc data.soc_0[k] = elm.soc_0 data.e_min[k] = elm.Enom * elm.min_soc data.e_max[k] = elm.Enom * elm.max_soc data.discharge_efficiency[k] = elm.discharge_efficiency data.charge_efficiency[k] = elm.charge_efficiency if opf_results is not None: # overwrite P with the OPF results if time_series: data.p[k] = opf_results.battery_power[t_idx, k] else: data.p[k] = opf_results.battery_power[k]
[docs] def fill_parent_branch(i: int, elm: BRANCH_TYPES, data: PassiveBranchData, bus_data: BusData, bus_dict: Dict[Bus, int], bus_voltage_used: BoolVec, use_stored_guess=False, t_idx: int | None = None, ): """ :param i: :param elm: :param data: :param bus_data: :param bus_dict: :param bus_voltage_used: :param use_stored_guess: :param t_idx: :return: """ data.names[i] = elm.name data.idtag[i] = elm.idtag data.mttf[i] = elm.mttf data.mttr[i] = elm.mttr data.active[i] = elm.get_active_at(t_idx) data.rates[i] = elm.get_rate_at(t_idx) data.contingency_rates[i] = data.rates[i] * elm.get_contingency_factor_at(t_idx) data.protection_rates[i] = data.rates[i] * elm.get_protection_rating_factor_at(t_idx) data.overload_cost[i] = elm.get_Cost_at(t_idx) f = bus_dict[elm.bus_from] t = bus_dict[elm.bus_to] data.F[i] = f data.T[i] = t data.original_idx[i] = i data.reducible[i] = int(elm.reducible) data.contingency_enabled[i] = int(elm.contingency_enabled) data.monitor_loading[i] = int(elm.monitor_loading) data.virtual_tap_f[i], data.virtual_tap_t[i] = elm.get_virtual_taps() # This is to initialize the bus voltages for branches # that do have a significant virtual tap difference. # i.e. transformers for distribution systems # Grounded buses carry a pinned ground-reference Vbus from get_bus_data # and must not be overwritten by the virtual-tap default. if not bus_voltage_used[f] and not use_stored_guess and not bus_data.is_grounded[f]: bus_data.Vbus[f] = data.virtual_tap_f[i] if not bus_voltage_used[t] and not use_stored_guess and not bus_data.is_grounded[t]: bus_data.Vbus[t] = data.virtual_tap_t[i] return f, t
[docs] def fill_controllable_branch( ii: int, elm: Union[dev.Transformer2W, dev.Winding, dev.UPFC], data: PassiveBranchData, ctrl_data: ActiveBranchData, bus_data: BusData, bus_dict: Dict[Bus, int], t_idx: int | None, opf_results: VALID_OPF_RESULTS | None, use_stored_guess: bool, bus_voltage_used: BoolVec, Sbase: float, control_taps_modules: bool, control_taps_phase: bool, logger: Logger): """ :param ii: :param elm: :param data: :param ctrl_data: :param bus_data: :param bus_dict: :param t_idx: :param opf_results: :param use_stored_guess: :param bus_voltage_used: :param Sbase: :param control_taps_modules: :param control_taps_phase: :param logger: :return: """ fill_parent_branch(i=ii, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, use_stored_guess=use_stored_guess, t_idx=t_idx) if control_taps_phase: ctrl_data.tap_phase_control_mode[ii] = elm.get_tap_phase_control_mode_at(t_idx).idx() if control_taps_modules: ctrl_data.tap_module_control_mode[ii] = elm.get_tap_module_control_mode_at(t_idx).idx() if elm.regulation_bus is None: reg_bus = elm.bus_from if ctrl_data.tap_module_control_mode[ii] == TapModuleControl.Vm.idx(): logger.add_warning("Unspecified regulation bus", device_class=elm.device_type.value, device=elm.name) else: reg_bus = elm.regulation_bus ctrl_data.tap_controlled_buses[ii] = bus_dict[reg_bus] ctrl_data.Pset[ii] = elm.get_Pset_at(t_idx) / Sbase ctrl_data.Qset[ii] = elm.get_Qset_at(t_idx) / Sbase ctrl_data.vset[ii] = elm.get_vset_at(t_idx) if opf_results is not None: ctrl_data.tap_angle[ii] = opf_results.tap_angle[t_idx, ii] ctrl_data.tap_module[ii] = elm.get_tap_module_at(t_idx) else: ctrl_data.tap_angle[ii] = elm.get_tap_phase_at(t_idx) ctrl_data.tap_module[ii] = elm.get_tap_module_at(t_idx) ctrl_data.is_controlled[ii] = 1 ctrl_data.tap_module_min[ii] = elm.tap_module_min ctrl_data.tap_module_max[ii] = elm.tap_module_max ctrl_data.tap_angle_min[ii] = elm.tap_phase_min ctrl_data.tap_angle_max[ii] = elm.tap_phase_max if ctrl_data.tap_module_control_mode[ii] != TapModuleControl.fixed.idx(): ctrl_data.any_pf_control = True if not ctrl_data.any_pf_control: # if true, we can skip this step if ctrl_data.tap_phase_control_mode[ii] != TapPhaseControl.fixed.idx(): ctrl_data.any_pf_control = True if not use_stored_guess: if ctrl_data.tap_module_control_mode[ii] == TapModuleControl.Vm.idx(): ctrl_data.any_pf_control = True bus_idx = ctrl_data.tap_controlled_buses[ii] if not bus_voltage_used[bus_idx]: if elm.vset > 0.0: bus_data.Vbus[bus_idx] = elm.vset else: logger.add_warning("Branch control voltage out of bounds", device_class=str(elm.device_type.value), device=elm.name, value=elm.vset) elif elm.vset != bus_data.Vbus[bus_idx]: logger.add_error(msg='Different control voltage set points', device=bus_data.names[bus_idx], value=elm.vset, expected_value=bus_data.Vbus[bus_idx]) # modify the voltage angle guess using the phase if ctrl_data.tap_angle[ii] != 0: f = bus_dict[elm.bus_from] t = bus_dict[elm.bus_to] Vm = abs(bus_data.Vbus[f]) Va = np.angle(bus_data.Vbus[f], deg=False) bus_data.Vbus[f] = Vm * np.exp(1j * (Va + ctrl_data.tap_angle[ii]))
[docs] def get_branch_data( data: PassiveBranchData, ctrl_data: ActiveBranchData, circuit: MultiCircuit, bus_dict: Dict[Bus, int], bus_data: BusData, bus_voltage_used: BoolVec, apply_temperature: bool, branch_tolerance_mode: BranchImpedanceMode, t_idx: int | None, opf_results: VALID_OPF_RESULTS | None = None, use_stored_guess: bool = False, control_taps_modules: bool = True, control_taps_phase: bool = True, logger: Logger = Logger(), fill_three_phase: bool = False ) -> Dict[BRANCH_TYPES, int]: """ Compile BranchData for a time step or the snapshot :param data: BranchData :param ctrl_data: ControllableBranchData :param circuit: MultiCircuit :param bus_dict: Dictionary of buses to compute the indices :param bus_data: BusData :param bus_voltage_used: :param apply_temperature: apply the temperature correction? :param branch_tolerance_mode: BranchImpedanceMode :param t_idx: time index (-1 is useless) :param opf_results: OptimalPowerFlowResults :param use_stored_guess: use the stored voltage ? :param control_taps_modules: Control TapsModules :param control_taps_phase: Control TapsPhase :param logger: Logger :param fill_three_phase: Fill the tree phase info? :return: BranchData """ branch_dict: Dict[BRANCH_TYPES, int] = dict() idx4 = np.array(range(4)) ii = 0 # Compile the lines for i, elm in enumerate(circuit.lines): # generic stuff fill_parent_branch(i=ii, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, use_stored_guess=use_stored_guess, t_idx=t_idx) data.R[ii] = elm.R_corrected if apply_temperature else elm.R if branch_tolerance_mode == BranchImpedanceMode.Lower: data.R[ii] *= (1 - elm.tolerance / 100.0) elif branch_tolerance_mode == BranchImpedanceMode.Upper: data.R[ii] *= (1 + elm.tolerance / 100.0) data.X[ii] = elm.X data.B[ii] = elm.B data.R0[ii] = elm.R0 data.X0[ii] = elm.X0 data.B0[ii] = elm.B0 data.R2[ii] = elm.R2 data.X2[ii] = elm.X2 data.B2[ii] = elm.B2 if fill_three_phase: """ yff = ys_abc + ysh_abc / 2 yft = - ys_abc ytf = - ys_abc ytt = ys_abc + ysh_abc / 2 """ k4 = 4 * ii + idx4 y1 = elm.ys.values + elm.ysh.values / 2.0 * 1e-6 y2 = - elm.ys.values data.Yff3[k4, :] = y1 data.Yft3[k4, :] = y2 data.Ytf3[k4, :] = y2 data.Ytt3[k4, :] = y1 """ Save the phases of each line """ data.phN[ii] = elm.ys.phN data.phA[ii] = elm.ys.phA data.phB[ii] = elm.ys.phB data.phC[ii] = elm.ys.phC # store for later branch_dict[elm] = ii # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 # DC-lines for i, elm in enumerate(circuit.dc_lines): # generic stuff fill_parent_branch(i=ii, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, use_stored_guess=use_stored_guess, t_idx=t_idx) data.R[ii] = elm.R_corrected if apply_temperature else elm.R if branch_tolerance_mode == BranchImpedanceMode.Lower: data.R[ii] *= (1 - elm.tolerance / 100.0) elif branch_tolerance_mode == BranchImpedanceMode.Upper: data.R[ii] *= (1 + elm.tolerance / 100.0) # store for later branch_dict[elm] = ii data.dc[ii] = 1 # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 # 2-winding transformers for i, elm in enumerate(circuit.transformers2w): fill_controllable_branch(ii=ii, elm=elm, data=data, ctrl_data=ctrl_data, bus_data=bus_data, bus_dict=bus_dict, t_idx=t_idx, opf_results=opf_results, use_stored_guess=use_stored_guess, bus_voltage_used=bus_voltage_used, Sbase=circuit.Sbase, control_taps_modules=control_taps_modules, control_taps_phase=control_taps_phase, logger=logger) data.R[ii] = elm.R_corrected if apply_temperature else elm.R if branch_tolerance_mode == BranchImpedanceMode.Lower: data.R[ii] *= (1 - elm.tolerance / 100.0) elif branch_tolerance_mode == BranchImpedanceMode.Upper: data.R[ii] *= (1 + elm.tolerance / 100.0) data.X[ii] = elm.X data.G[ii] = elm.G data.B[ii] = elm.B data.R0[ii] = elm.R0 data.X0[ii] = elm.X0 data.G0[ii] = elm.G0 data.B0[ii] = elm.B0 data.R2[ii] = elm.R2 data.X2[ii] = elm.X2 data.G2[ii] = elm.G2 data.B2[ii] = elm.B2 if fill_three_phase: k4 = 4 * ii + idx4 (data.Yff3[k4, :], data.Yft3[k4, :], data.Ytf3[k4, :], data.Ytt3[k4, :]) = elm.transformer_admittance(vtap_f=data.virtual_tap_f[ii], vtap_t=data.virtual_tap_t[ii], logger=logger) (data.phN[ii], data.phA[ii], data.phB[ii], data.phC[ii]) = elm.transformer_phases(logger=logger) data.conn[ii] = elm.conn.idx() data.conn_f[ii] = elm.conn_f.idx() data.conn_t[ii] = elm.conn_t.idx() data.m_taps[ii] = elm.tap_changer.tap_modules_array data.tau_taps[ii] = elm.tap_changer.tap_angles_array # store for later branch_dict[elm] = ii # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 # windings for i, elm in enumerate(circuit.windings): if elm.bus_from is not None and elm.bus_to is not None: # generic stuff fill_controllable_branch(ii=ii, elm=elm, data=data, ctrl_data=ctrl_data, bus_data=bus_data, bus_dict=bus_dict, t_idx=t_idx, opf_results=opf_results, use_stored_guess=use_stored_guess, bus_voltage_used=bus_voltage_used, Sbase=circuit.Sbase, control_taps_modules=control_taps_modules, control_taps_phase=control_taps_phase, logger=logger) data.R[ii] = elm.R_corrected if apply_temperature else elm.R if branch_tolerance_mode == BranchImpedanceMode.Lower: data.R[ii] *= (1 - elm.tolerance / 100.0) elif branch_tolerance_mode == BranchImpedanceMode.Upper: data.R[ii] *= (1 + elm.tolerance / 100.0) data.X[ii] = elm.X data.G[ii] = elm.G data.B[ii] = elm.B data.R0[ii] = elm.R0 data.X0[ii] = elm.X0 data.G0[ii] = elm.G0 data.B0[ii] = elm.B0 data.R2[ii] = elm.R2 data.X2[ii] = elm.X2 data.G2[ii] = elm.G2 data.B2[ii] = elm.B2 data.conn[ii] = elm.conn.idx() data.conn_f[ii] = elm.conn_f.idx() data.conn_t[ii] = elm.conn_t.idx() data.m_taps[ii] = elm.tap_changer.tap_modules_array data.tau_taps[ii] = elm.tap_changer.tap_angles_array # store for later branch_dict[elm] = ii # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 else: logger.add_error("Ill connected winding", device=elm.idtag) # UPFC for i, elm in enumerate(circuit.upfc_devices): # generic stuff fill_controllable_branch(ii=ii, elm=elm, data=data, ctrl_data=ctrl_data, bus_data=bus_data, bus_dict=bus_dict, t_idx=t_idx, opf_results=opf_results, use_stored_guess=use_stored_guess, bus_voltage_used=bus_voltage_used, Sbase=circuit.Sbase, control_taps_modules=control_taps_modules, control_taps_phase=control_taps_phase, logger=logger) ysh1 = elm.get_ysh1() data.R[ii] = elm.R data.X[ii] = elm.X data.G[ii] = ysh1.real data.B[ii] = ysh1.imag ysh0 = elm.get_ysh0() data.R0[ii] = elm.R0 data.X0[ii] = elm.X0 data.G0[ii] = ysh0.real data.B0[ii] = ysh0.imag ysh2 = elm.get_ysh2() data.R2[ii] = elm.R2 data.X2[ii] = elm.X2 data.G2[ii] = ysh2.real data.B2[ii] = ysh2.imag # store for later branch_dict[elm] = ii # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 # Series reactance for i, elm in enumerate(circuit.series_reactances): # generic stuff fill_parent_branch(i=ii, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, use_stored_guess=use_stored_guess, t_idx=t_idx) data.R[ii] = elm.R_corrected if apply_temperature else elm.R if branch_tolerance_mode == BranchImpedanceMode.Lower: data.R[ii] *= (1 - elm.tolerance / 100.0) elif branch_tolerance_mode == BranchImpedanceMode.Upper: data.R[ii] *= (1 + elm.tolerance / 100.0) data.X[ii] = elm.X data.R0[ii] = elm.R0 data.X0[ii] = elm.X0 data.R2[ii] = elm.R2 data.X2[ii] = elm.X2 # store for later branch_dict[elm] = ii # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 # Switches for i, elm in enumerate(circuit.switch_devices): # generic stuff fill_parent_branch(i=ii, elm=elm, data=data, bus_data=bus_data, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, use_stored_guess=use_stored_guess, t_idx=t_idx) if fill_three_phase: data.phN[ii] = 1 data.phA[ii] = 1 data.phB[ii] = 1 data.phC[ii] = 1 data.R[ii] = elm.R data.X[ii] = elm.X # store for later branch_dict[elm] = ii # handle """superconductor branches""" data.detect_superconductor_at(ii) ii += 1 return branch_dict
[docs] def set_control_dev(k: int, f: int, t: int, control_int: int, control_dev: Bus | BRANCH_TYPES | None, control_val: float, control_bus_idx: IntVec, control_branch_idx: IntVec, bus_dict: Dict[Bus, int], bus_data: BusData, bus_voltage_used: BoolVec, bus_angle_used: BoolVec, use_stored_guess: bool, logger: Logger): """ :param k: device index :param f: :param t: :param control_int: ConverterControlType :param control_dev: control device :param control_val: control value :param control_bus_idx: array to be filled in :param control_branch_idx: array to be filled in :param bus_dict: dictionary to be filled in :param bus_data: bus data :param bus_voltage_used: used bus voltage :param bus_angle_used: used bus angle :param use_stored_guess: :param logger: """ if control_dev is not None: if control_dev.device_type == DeviceType.BusDevice: bus_idx = bus_dict[control_dev] control_bus_idx[k] = bus_idx if control_int == ConverterControlType.Vm_ac.idx(): set_bus_control_voltage_vsc(i=bus_idx, j=-1, remote_control=False, bus_name=str(bus_data.names[bus_idx]), bus_voltage_used=bus_voltage_used, bus_data=bus_data, candidate_Vm=control_val, use_stored_guess=use_stored_guess, logger=logger) elif control_int == ConverterControlType.Vm_dc.idx(): set_bus_control_voltage_vsc(i=bus_idx, j=-1, remote_control=False, bus_name=str(bus_data.names[bus_idx]), bus_voltage_used=bus_voltage_used, bus_data=bus_data, candidate_Vm=control_val, use_stored_guess=use_stored_guess, logger=logger) elif control_int == ConverterControlType.Va_ac.idx(): set_bus_control_angle_vsc(i=bus_idx, j=-1, remote_control=False, bus_name=str(bus_data.names[bus_idx]), bus_angle_used=bus_angle_used, bus_data=bus_data, candidate_Va=control_val, use_stored_guess=use_stored_guess, logger=logger) else: # TODO: the formulation does not allow for VSC remote control yet # control_branch_idx[k] = branch_dict[control_dev] control_branch_idx[k] = k else: if control_int == ConverterControlType.Vm_ac.idx(): control_bus_idx[k] = t set_bus_control_voltage_vsc(i=t, j=-1, remote_control=False, bus_name=str(bus_data.names[t]), bus_voltage_used=bus_voltage_used, bus_data=bus_data, candidate_Vm=control_val, use_stored_guess=use_stored_guess, logger=logger) elif control_int == ConverterControlType.Vm_dc.idx(): control_bus_idx[k] = f set_bus_control_voltage_vsc(i=f, j=-1, remote_control=False, bus_name=str(bus_data.names[f]), bus_voltage_used=bus_voltage_used, bus_data=bus_data, candidate_Vm=control_val, use_stored_guess=use_stored_guess, logger=logger) elif control_int == ConverterControlType.Va_ac.idx(): control_bus_idx[k] = t set_bus_control_angle_vsc(i=t, j=-1, remote_control=False, bus_name=str(bus_data.names[t]), bus_angle_used=bus_angle_used, bus_data=bus_data, candidate_Va=control_val, use_stored_guess=use_stored_guess, logger=logger) else: # control_branch_idx[k] = len(branch_dict) + k # TODO: why? control_branch_idx[k] = k
[docs] def get_vsc_data( data: VscData, circuit: MultiCircuit, bus_dict: Dict[Bus, int], branch_dict: Dict[BRANCH_TYPES, int], bus_data: BusData, bus_voltage_used: BoolVec, t_idx: int | None, opf_results: VALID_OPF_RESULTS | None = None, use_stored_guess: bool = False, control_remote_voltage: bool = True, logger: Logger = Logger() ) -> None: """ Compile VscData for a time step or the snapshot :param data: VscData :param circuit: MultiCircuit :param bus_dict: Dictionary of buses to compute the indices :param branch_dict: Dictionary of branches to compute the indices :param bus_data: BusData :param bus_voltage_used: :param t_idx: time index (-1 is useless) :param opf_results: OptimalPowerFlowResults :param use_stored_guess: use the stored voltage ? :param control_remote_voltage: Control RemoteVoltage :param logger: Logger :return: VscData """ bus_angle_used = np.array(bus_data.bus_types == BusMode.Slack_tpe.value).astype(bool) ii = 0 # VSC for i, elm in enumerate(circuit.vsc_devices): # generic stuff data.names[i] = elm.name data.idtag[i] = elm.idtag data.mttf[i] = elm.mttf data.mttr[i] = elm.mttr f = bus_dict[elm.bus_from] t = bus_dict[elm.bus_to] data.original_idx[i] = i data.F[i] = f # TODO SANPEN: Handle the -1 everywhere for this data.F_dcn[i] = -1 if elm.bus_dc_n is None else bus_dict[elm.bus_dc_n] data.T[i] = t data.active[i] = elm.get_active_at(t_idx) data.rates[i] = elm.get_rate_at(t_idx) data.contingency_rates[i] = data.rates[i] * elm.get_contingency_factor_at(t_idx) data.protection_rates[i] = data.rates[i] * elm.get_protection_rating_factor_at(t_idx) data.overload_cost[i] = elm.get_Cost_at(t_idx) data.control1_int[ii] = elm.get_control1_at(t_idx).idx() data.control2_int[ii] = elm.get_control2_at(t_idx).idx() data.fault_control_int[ii] = elm.get_fault_control_at(t_idx).idx() data.control1_val[ii] = elm.get_control1_val_at(t_idx) data.control2_val[ii] = elm.get_control2_val_at(t_idx) data.control1_val_min[ii] = elm.control1_val_min data.control1_val_max[ii] = elm.control1_val_max data.control1_val_droop[ii] = elm.get_control1_val_droop_at(t_idx) data.control1_droop_val[ii] = elm.get_control1_droop_val_at(t_idx) data.control1_droop_val_min[ii] = elm.control1_droop_val_min data.control1_droop_val_max[ii] = elm.control1_droop_val_max data.control2_val_min[ii] = elm.control2_val_min data.control2_val_max[ii] = elm.control2_val_max data.control2_val_droop[ii] = elm.get_control2_val_droop_at(t_idx) data.control2_droop_val[ii] = elm.get_control2_droop_val_at(t_idx) data.control2_droop_val_min[ii] = elm.control2_droop_val_min data.control2_droop_val_max[ii] = elm.control2_droop_val_max # Using DC_positive to set the controls, may need to also pass DC_negative set_control_dev(k=ii, f=f, t=t, control_int=data.control1_int[ii], control_dev=elm.get_control1_dev_at(t_idx), control_val=data.control1_val[ii], control_bus_idx=data.control1_bus_idx, control_branch_idx=data.control1_branch_idx, bus_dict=bus_dict, bus_data=bus_data, bus_voltage_used=bus_voltage_used, bus_angle_used=bus_angle_used, use_stored_guess=use_stored_guess, logger=logger) set_control_dev(k=ii, f=f, t=t, control_int=data.control2_int[ii], control_dev=elm.get_control2_dev_at(t_idx), control_val=data.control2_val[ii], control_bus_idx=data.control2_bus_idx, control_branch_idx=data.control2_branch_idx, bus_dict=bus_dict, bus_data=bus_data, bus_voltage_used=bus_voltage_used, bus_angle_used=bus_angle_used, use_stored_guess=use_stored_guess, logger=logger) data.contingency_enabled[i] = int(elm.contingency_enabled) data.monitor_loading[i] = int(elm.monitor_loading) # data.Kdp[ii] = elm.kdp data.alpha1[ii] = elm.alpha1 data.alpha2[ii] = elm.alpha2 data.alpha3[ii] = elm.alpha3 data.min_ac_voltage[ii] = elm.min_ac_voltage data.ysvs[ii] = elm.ysvs ii += 1
[docs] def get_hvdc_data(data: HvdcData, circuit: MultiCircuit, bus_dict, bus_data: BusData, bus_voltage_used: BoolVec, t_idx: int | None, opf_results: Union[OptimalPowerFlowResults, OptimalNetTransferCapacityResults, None] = None, use_stored_guess: bool = False, logger: Logger = Logger()): """ :param data: :param circuit: :param bus_dict: :param bus_data: :param bus_voltage_used: :param t_idx: :param opf_results: :param use_stored_guess: :param logger: :return: """ # HVDC for i, elm in enumerate(circuit.hvdc_lines): # generic stuff f = bus_dict[elm.bus_from] t = bus_dict[elm.bus_to] data.original_idx[i] = i data.dispatchable[i] = int(elm.dispatchable) data.F[i] = f data.T[i] = t # hvdc values data.names[i] = elm.name data.idtag[i] = elm.idtag data.active[i] = elm.get_active_at(t_idx) data.rates[i] = elm.get_rate_at(t_idx) data.contingency_rates[i] = data.rates[i] * elm.get_contingency_factor_at(t_idx) data.protection_rates[i] = data.rates[i] * elm.get_protection_rating_factor_at(t_idx) data.angle_droop[i] = elm.get_angle_droop_at(t_idx) data.r[i] = elm.r if opf_results is not None: # if we are taking the values from the OPF, do not allow the free mode data.control_mode_int[i] = HvdcControlType.type_1_Pset.idx() data.Pset[i] = opf_results.hvdc_Pf[t_idx, i] else: data.control_mode_int[i] = elm.control_mode.idx() data.Pset[i] = elm.get_Pset_at(t_idx) data.Vset_f[i] = elm.get_Vset_f_at(t_idx) data.Vset_t[i] = elm.get_Vset_t_at(t_idx) # hack the bus types to believe they are PV if data.active[i] != 0: set_bus_control_voltage_hvdc(i=f, j=-1, remote_control=False, bus_name=elm.bus_from.name, bus_data=bus_data, bus_voltage_used=bus_voltage_used, candidate_Vm=data.Vset_f[i], use_stored_guess=use_stored_guess, logger=logger) set_bus_control_voltage_hvdc(i=t, j=-1, remote_control=False, bus_name=elm.bus_to.name, bus_data=bus_data, bus_voltage_used=bus_voltage_used, candidate_Vm=data.Vset_t[i], use_stored_guess=use_stored_guess, logger=logger) data.Vnf[i] = elm.bus_from.Vnom data.Vnt[i] = elm.bus_to.Vnom data.Qmin_f[i], data.Qmax_f[i], data.Qmin_t[i], data.Qmax_t[i] = elm.get_q_limits(P=data.Pset[i])
[docs] def get_fluid_node_data(data: FluidNodeData, circuit: MultiCircuit, t_idx: int | None = None) -> Dict[str, int]: """ :param data: :param circuit: :param t_idx: :return: """ plant_dict: Dict[str, int] = dict() for k, elm in enumerate(circuit.get_fluid_nodes()): plant_dict[elm.idtag] = k data.names[k] = elm.name data.idtag[k] = elm.idtag # Convert input data in hm3 to m3 data.min_level[k] = 1e6 * elm.min_level data.max_level[k] = 1e6 * elm.max_level data.initial_level[k] = 1e6 * elm.initial_level data.inflow[k] = elm.get_inflow_at(t_idx) data.spillage_cost[k] = elm.get_spillage_cost_at(t_idx) data.max_soc[k] = elm.get_max_soc_at(t_idx) data.min_soc[k] = elm.get_min_soc_at(t_idx) return plant_dict
[docs] def get_fluid_turbine_data(data: FluidTurbineData, circuit: MultiCircuit, plant_dict: Dict[str, int], gen_dict: Dict[str, int]) -> FluidTurbineData: """ :param data: :param circuit: :param plant_dict: :param gen_dict: :return: """ for k, elm in enumerate(circuit.get_fluid_turbines()): data.plant_idx[k] = plant_dict[elm.plant.idtag] data.generator_idx[k] = gen_dict[elm.generator.idtag] data.names[k] = elm.name data.idtag[k] = elm.idtag data.efficiency[k] = elm.efficiency data.max_flow_rate[k] = elm.max_flow_rate return data
[docs] def get_fluid_pump_data(data: FluidPumpData, circuit: MultiCircuit, plant_dict: Dict[str, int], gen_dict: Dict[str, int]) -> FluidPumpData: """ :param data: :param circuit: :param plant_dict: :param gen_dict: :return: """ for k, elm in enumerate(circuit.get_fluid_pumps()): data.plant_idx[k] = plant_dict[elm.plant.idtag] data.generator_idx[k] = gen_dict[elm.generator.idtag] data.names[k] = elm.name data.idtag[k] = elm.idtag data.efficiency[k] = elm.efficiency data.max_flow_rate[k] = elm.max_flow_rate return data
[docs] def get_fluid_p2x_data(data: FluidP2XData, circuit: MultiCircuit, plant_dict: Dict[str, int], gen_dict: Dict[str, int]) -> FluidP2XData: """ :param data: :param circuit: :param plant_dict: :param gen_dict: :return: """ for k, elm in enumerate(circuit.get_fluid_p2xs()): data.plant_idx[k] = plant_dict[elm.plant.idtag] data.generator_idx[k] = gen_dict[elm.generator.idtag] data.names[k] = elm.name data.idtag[k] = elm.idtag data.efficiency[k] = elm.efficiency data.max_flow_rate[k] = elm.max_flow_rate return data
[docs] def get_fluid_path_data(data: FluidPathData, circuit: MultiCircuit, plant_dict: Dict[str, int]) -> FluidPathData: """ :param data: FluidPathData :param circuit: :param plant_dict: :return: """ for k, elm in enumerate(circuit.get_fluid_paths()): data.names[k] = elm.name data.idtag[k] = elm.idtag # pass idx, check data.source_idx[k] = plant_dict[elm.source.idtag] data.target_idx[k] = plant_dict[elm.target.idtag] data.min_flow[k] = elm.min_flow data.max_flow[k] = elm.max_flow return data
[docs] def compile_numerical_circuit_at(circuit: MultiCircuit, t_idx: Union[int, None] = None, apply_temperature: bool = False, branch_tolerance_mode=BranchImpedanceMode.Specified, opf_results: VALID_OPF_RESULTS | None = None, use_stored_guess: bool = False, bus_dict: Union[Dict[Bus, int], None] = None, areas_dict: Union[Dict[Area, int], None] = None, control_taps_modules: bool = True, control_taps_phase: bool = True, control_remote_voltage: bool = True, fill_gep: bool = False, fill_three_phase: bool = False, consider_grounded_buses: bool = False, logger: Logger = Logger()) -> NumericalCircuit: """ Compile a NumericalCircuit from a MultiCircuit :param circuit: MultiCircuit instance :param t_idx: time step from the time series to gather data from, if None the snapshot is used :param apply_temperature: apply the branch temperature correction :param branch_tolerance_mode: Branch tolerance mode :param opf_results:(optional) OptimalPowerFlowResults instance :param use_stored_guess: use the storage voltage guess? :param bus_dict (optional) Dict[Bus, int] dictionary :param areas_dict (optional) Dict[Area, int] dictionary :param control_taps_modules: control taps modules? :param control_taps_phase: control taps phase? :param control_remote_voltage: control remote voltage? :param fill_gep: fill generation expansion planning parameters? :param fill_three_phase: :param consider_grounded_buses: Consider the is_grounded bus state :param logger: Logger instance :return: NumericalCircuit instance """ # if any valid time index is specified, then the data is compiled from the time series time_series = t_idx is not None bus_voltage_used = np.zeros(circuit.get_bus_number(), dtype=bool) ngen = circuit.get_generators_number() nbatt = circuit.get_batteries_number() # declare the numerical circuit nc = NumericalCircuit( nbus=circuit.get_bus_number(), nbr=circuit.get_branch_number(add_vsc=False, add_hvdc=False, add_switch=True), nhvdc=circuit.get_hvdc_number(), nvsc=circuit.get_vsc_number(), nload=circuit.get_load_like_device_number(), ngen=ngen, nbatt=nbatt, nshunt=circuit.get_shunt_like_device_number(), nfluidnode=circuit.get_fluid_nodes_number(), nfluidturbine=circuit.get_fluid_turbines_number(), nfluidpump=circuit.get_fluid_pumps_number(), nfluidp2x=circuit.get_fluid_p2xs_number(), nfluidpath=circuit.get_fluid_paths_number(), sbase=circuit.Sbase, t_idx=t_idx ) if bus_dict is None: bus_dict = {bus: i for i, bus in enumerate(circuit.buses)} if areas_dict is None: areas_dict = {elm: i for i, elm in enumerate(circuit.areas)} get_bus_data( bus_data=nc.bus_data, # filled here circuit=circuit, t_idx=t_idx, areas_dict=areas_dict, use_stored_guess=use_stored_guess, consider_grounded_buses=consider_grounded_buses ) gen_dict = get_generator_data( data=nc.generator_data, # filled here circuit=circuit, bus_dict=bus_dict, bus_data=nc.bus_data, t_idx=t_idx, time_series=time_series, bus_voltage_used=bus_voltage_used, logger=logger, opf_results=opf_results, control_remote_voltage=control_remote_voltage, fill_three_phase=fill_three_phase ) get_battery_data( data=nc.battery_data, # filled here circuit=circuit, bus_dict=bus_dict, bus_data=nc.bus_data, t_idx=t_idx, time_series=time_series, bus_voltage_used=bus_voltage_used, logger=logger, opf_results=opf_results, control_remote_voltage=control_remote_voltage, fill_three_phase=fill_three_phase ) get_shunt_data( data=nc.shunt_data, # filled here circuit=circuit, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, bus_data=nc.bus_data, t_idx=t_idx, logger=logger, control_remote_voltage=control_remote_voltage, fill_three_phase=fill_three_phase ) get_load_data( data=nc.load_data, circuit=circuit, bus_dict=bus_dict, bus_voltage_used=bus_voltage_used, bus_data=nc.bus_data, t_idx=t_idx, logger=logger, opf_results=opf_results, fill_three_phase=fill_three_phase ) branch_dict = get_branch_data( data=nc.passive_branch_data, ctrl_data=nc.active_branch_data, circuit=circuit, t_idx=t_idx, bus_dict=bus_dict, bus_data=nc.bus_data, bus_voltage_used=bus_voltage_used, apply_temperature=apply_temperature, branch_tolerance_mode=branch_tolerance_mode, opf_results=opf_results, use_stored_guess=use_stored_guess, control_taps_modules=control_taps_modules, control_taps_phase=control_taps_phase, fill_three_phase=fill_three_phase ) get_vsc_data( data=nc.vsc_data, circuit=circuit, t_idx=t_idx, bus_dict=bus_dict, branch_dict=branch_dict, bus_data=nc.bus_data, bus_voltage_used=bus_voltage_used, opf_results=opf_results, use_stored_guess=use_stored_guess, control_remote_voltage=control_remote_voltage, ) get_hvdc_data( data=nc.hvdc_data, circuit=circuit, t_idx=t_idx, bus_dict=bus_dict, bus_data=nc.bus_data, bus_voltage_used=bus_voltage_used, opf_results=opf_results, use_stored_guess=use_stored_guess, logger=logger ) if len(circuit.fluid_nodes) > 0: plant_dict = get_fluid_node_data( data=nc.fluid_node_data, circuit=circuit, t_idx=t_idx ) get_fluid_turbine_data( data=nc.fluid_turbine_data, circuit=circuit, plant_dict=plant_dict, gen_dict=gen_dict, ) get_fluid_pump_data( data=nc.fluid_pump_data, circuit=circuit, plant_dict=plant_dict, gen_dict=gen_dict ) get_fluid_p2x_data( data=nc.fluid_p2x_data, circuit=circuit, plant_dict=plant_dict, gen_dict=gen_dict, ) get_fluid_path_data( data=nc.fluid_path_data, circuit=circuit, plant_dict=plant_dict ) if fill_gep: # formulate the investment gen_dict = {elm.idtag: (idx, elm) for idx, elm in enumerate(circuit.generators)} batt_dict = {elm.idtag: (idx, elm) for idx, elm in enumerate(circuit.batteries)} for investment in circuit.investments: # search in generators data = gen_dict.get(investment.device_idtag, None) if data is not None: idx, elm = data if investment.CAPEX != 0.0: # overwrite the base capex nc.generator_data.capex[idx] = investment.CAPEX nc.generator_data.is_candidate[idx] = True nc.generator_data.discount_rate[idx] = investment.group.discount_rate else: # search in batteries data = batt_dict.get(investment.device_idtag, None) if data is not None: idx, elm = data if investment.CAPEX != 0.0: # overwrite the base capex nc.battery_data.capex[idx] = investment.CAPEX nc.battery_data.is_candidate[idx] = True nc.battery_data.discount_rate[idx] = investment.group.discount_rate else: logger.add_error("Could not find investment device", value=investment.device_idtag) nc.bus_dict = bus_dict nc.consolidate_information() if nc.active_branch_data.any_pf_control is False: if nc.vsc_data.nelm > 0: nc.active_branch_data.any_pf_control = True return nc