Source code for pyflow_acdc.constants

"""Domain constants used across the pyflow_acdc package."""

from enum import Enum


# ── String enums ────────────────────────────────────────────────────────

class NodeType(str, Enum):
    SLACK = 'Slack'
    PV = 'PV'
    PQ = 'PQ'

class ConverterDCType(str, Enum):
    P = 'P'
    PAC = 'PAC'
    SLACK = 'Slack'
    DROOP = 'Droop'

class DataInput(str, Enum):
    REAL = 'Real'
    PU = 'pu'
    OHM = 'Ohm'

class Polarity(str, Enum):
    MONOPOLAR = 'm'
    BIPOLAR = 'b'
    SYMMETRIC_MONOPOLAR = 'sm'

class PowerLossModel(str, Enum):
    QUADRATIC = 'quadratic'
    MMC = 'MMC'

class CableType(str, Enum):
    """Cable catalogue selection for AC/DC lines.

    Only the ``CUSTOM`` sentinel is listed here: it means “use explicit R/X
    (or ``Cable_parameters`` from ``grid_analysis``), not a row from the
    bundled cable database”.

    All other ``Cable_type`` values are **arbitrary strings** that must match
    the index of the YAML/DataFrame cable library (e.g. ``'NKT …'``). Do not
    add one enum member per catalogue entry.
    """
    CUSTOM = 'Custom'

class ConverterOpfFxType(str, Enum):
    """How converter AC quantities are fixed in OPF (see AC_DC_converter in Classes)."""
    PDC = 'PDC'
    PQ = 'PQ'
    PV = 'PV'
    NONE = 'None'
    Q = 'Q'


class AcDcSide(str, Enum):
    """Which subsystem a generator / renewable source is attached to."""
    AC = 'AC'
    DC = 'DC'


class PriceZoneCategory(str, Enum):
    """Price-zone kind in export/import dicts and `generate_add_price_zone_code`."""
    MAIN = 'main'
    OFFSHORE = 'offshore'
    MTDC = 'MTDC'


[docs] class ObjComponent(str, Enum): """OPF objective components (keys of the `ObjRule` / `weights_def` dict). Values are the public string keys users pass via ``ObjRule`` (e.g. ``Optimal_PF(grid, ObjRule={'Energy_cost': 1})``). Being a ``str`` Enum, members compare and hash equal to their string value, so they can be used interchangeably as dict keys/lookups while giving fail-fast typo protection on comparisons. """ EXT_GEN = 'Ext_Gen' ENERGY_COST = 'Energy_cost' CURTAILMENT_RED = 'Curtailment_Red' AC_LOSSES = 'AC_losses' DC_LOSSES = 'DC_losses' CONVERTER_LOSSES = 'Converter_Losses' GENERAL_LOSSES = 'General_Losses' ARRAY_LOSSES = 'Array_losses' PZ_COST_OF_GENERATION = 'PZ_cost_of_generation' RENEWABLE_PROFIT = 'Renewable_profit' GEN_SET_DEV = 'Gen_set_dev'
[docs] class CssMode(str, Enum): """Cable-string-sizing solve mode in `Array_OPT` (`NL` argument). ``NL=False`` selects the linear model; ``NL=True`` is normalized to ``OPF``. Only the non-linear string values are enumerated here. """ OPF = 'OPF' PF = 'PF'
[docs] class MIPBackend(str, Enum): """Solver backend for the array-MIP path problem in `Array_OPT`.""" PYOMO = 'pyomo' ORTOOLS = 'ortools'
# OR-Tools ``linear_solver`` backends (CSS MILP and optional Pyomo path-MIP fallback). ORTOOLS_LINEAR_SOLVERS = ('GUROBI', 'SCIP', 'CBC') PYOMO_LINEAR_SOLVERS = ('gurobi', 'highs','cbc','glpk') class PricingStrategy(str, Enum): """How an `MTDCPrice_Zone` derives its price from linked price zones.""" MIN = 'min' MAX = 'max' AVG = 'avg' class TSType(str, Enum): """`TimeSeries.type` labels recognised by the TS dispatch logic. These are the built-in time-series categories matched in `Time_series.update_grid_data`, `grid_modifications.time_series_dict` and `ACDC_Static_TEP`. ``ts.type`` is user-set, so unknown labels simply match no branch (unchanged behaviour); this enum only centralises the labels the package itself acts on. """ A_CG = 'a_CG' B_CG = 'b_CG' C_CG = 'c_CG' PGL_MIN = 'PGL_min' PGL_MAX = 'PGL_max' PRICE = 'price' LOAD = 'Load' # Renewable availability series (see TS_RENEWABLE_TYPES) WPP = 'WPP' OWPP = 'OWPP' SF = 'SF' REN = 'REN' SOLAR = 'Solar' # Renewable-availability TS labels handled together as one group. TS_RENEWABLE_TYPES = (TSType.WPP, TSType.OWPP, TSType.SF, TSType.REN, TSType.SOLAR) # Default fuel / technology labels for Grid.gen_ac_types (ENTSO-E-like; grids may extend). DEFAULT_GENERATION_TYPES = ( 'nuclear', 'hard coal', 'hydro', 'oil', 'lignite', 'natural gas', 'solid biomass', 'other', 'waste', 'biogas', 'geothermal', 'ccgt', 'diesel', 'shunt reactor', ) DEFAULT_RENEWABLE_TYPES = ( 'wind', 'solar', 'offshore wind', 'onshore wind', ) # Default Gen_AC / Gen_DC / add_gen fuel_type (capital O; normalized to lowercase in lookups). DEFAULT_GEN_TYPE = 'Other' # ── General ────────────────────────────────────────────────────────────── """ Classes, grid_creator, grid_modifications, Results_class, grid_analysis """ import math SQRT_3 = math.sqrt(3) """ Classes, Time_series, Results_class, AC_L_CSS_*, ACDC_Static_TEP, ACDC_MultiPeriod_TEP, Array_OPT, ACDC_TEP_pymoo """ HOURS_PER_YEAR = 8760 # ── Economics (TEP / planning) ─────────────────────────────────────────── """ Classes, ACDC_Static_TEP, ACDC_MultiPeriod_TEP, Array_OPT """ DEFAULT_N_YEARS = 25 """ Classes, AC_L_CSS_*, ACDC_Static_TEP, ACDC_MultiPeriod_TEP, Array_OPT, ACDC_TEP_pymoo """ DEFAULT_DISCOUNT_RATE = 0.02 # ── Solver defaults ────────────────────────────────────────────────────── """ AC_L_CSS_*, ACDC_Static_TEP, Array_OPT, ACDC_TEP_pymoo """ DEFAULT_TIME_LIMIT = 300 # ── Power-flow tolerances ──────────────────────────────────────────────── """ ACDC_PF (Power_flow, AC_PowerFlow, DC_PowerFlow), Time_series (TS_ACDC_PF), ACDC_OPF_NL_model, Array_OPT, ACDC_OPF, Results_class """ DEFAULT_TOLERANCE = 1e-10 """ ACDC_PF (ACDC_sequential outer loop), ACDC_OPF """ PF_OUTER_TOLERANCE = 1e-4 """ ACDC_PF (:func:`power_flow` hybrid path): outer sequential tol = inner * factor """ PF_SEQ_TOL_FACTOR = 1e4 """ ACDC_PF (load_flow_dc, load_flow_ac, acdc_sequential internal_tol), ACDC_MultiPeriod_TEP """ PF_INNER_TOLERANCE = PF_OUTER_TOLERANCE / PF_SEQ_TOL_FACTOR """ ACDC_PF (flow_conv — converter inner iterations) """ CONV_TOLERANCE = PF_OUTER_TOLERANCE / PF_SEQ_TOL_FACTOR**2 # ── Iteration caps ─────────────────────────────────────────────────────── """ ACDC_PF, Time_series """ DEFAULT_PF_MAX_ITER = 100 """ ACDC_PF (flow_conv) """ DEFAULT_CONV_MAX_ITER = 20 """ Time_series_clustering """ DEFAULT_CLUSTERING_MAX_ITER = 300 # ── Voltage limits (per-unit) ──────────────────────────────────────────── """ Classes (Node_DC), grid_creator, grid_modifications """ DEFAULT_V_MIN_DC = 0.95 DEFAULT_V_MAX_DC = 1.05 # ── Placeholders / thresholds ──────────────────────────────────────────── """ grid_creator, grid_modifications, Time_series, ACDC_OPF_NL_model, ACDC_Static_TEP, ACDC_MultiPeriod_TEP, AC_OPF_L_model """ MAX_RATING_PLACEHOLDER = 99999 """ ACDC_OPF_NL_model, ACDC_Static_TEP, AC_OPF_L_model """ CT_SELECTION_THRESHOLD = 0.90 """ Time_series, ACDC_OPF_NL_model, ACDC_Static_TEP, AC_OPF_L_model (binary variable rounding: >= threshold → treat as 1) """ BINARY_THRESHOLD = 0.99999 # ── OPF objective defaults ───────────────────────────────────────────────
[docs] def default_obj_weights(): """Return a fresh OPF objective-weights dict with every component at ``w=0``. Single source of truth for the ``weights_def`` / ``Grid.OPF_obj`` layout (used by ``obj_w_rule``, ``Grid.__init__`` and ``TS_ACDC_OPF``). A new dict with fresh inner dicts is built on every call so each caller can mutate its own copy. Key order follows ``ObjComponent`` declaration order; keys are plain strings (``.value``) so existing serialization/display is unchanged. """ return {component.value: {'w': 0} for component in ObjComponent}
# ── Shared economics helpers ────────────────────────────────────────────
[docs] def present_value_factor(Hy=HOURS_PER_YEAR, discount_rate=DEFAULT_DISCOUNT_RATE, n_years=25): """Annuity factor scaled by hours-per-year. Returns Hy * (1 - (1 + r)^{-n}) / r which converts a per-hour operational cost into its net-present-value over *n_years* at a constant annual *discount_rate*. """ return Hy * (1 - (1 + discount_rate) ** -n_years) / discount_rate