Source code for geopulse.network.base

"""Abstract base class for grounded conductor networks.

Infrastructure-agnostic interface: a submarine cable, a power grid, a
pipeline, and a railway all implement the same ABC. The
:mod:`~geopulse.solver` module never knows which kind of infrastructure it is
solving — only nodes, branches, admittances, and Thévenin voltages.

References
----------
.. [1] Boteler, D. H. (2014). Methodology for simulation of geomagnetically
   induced currents in power systems. J. Space Weather Space Clim., 4, A21.
"""

from __future__ import annotations

import abc
from collections.abc import Sequence
from dataclasses import dataclass

import numpy as np

__all__ = ["Branch", "ConductorNetwork", "Node"]


[docs] @dataclass(frozen=True) class Node: """A node (grounding point) in the conductor network. Attributes ---------- node_id : str Unique identifier (e.g. ``"SUB_01"`` for a substation, ``"KP_100"`` for a kilometer post on a pipeline). latitude_deg : float Geographic latitude, degrees north. longitude_deg : float Geographic longitude, degrees east. earthing_impedance_Ohm : float, optional Impedance to remote earth in Ohms. Default: 0.0 (perfectly grounded). Use ``numpy.inf`` for ungrounded (floating) nodes. """ node_id: str latitude_deg: float longitude_deg: float earthing_impedance_Ohm: float = 0.0
[docs] @dataclass(frozen=True) class Branch: """A branch (conductor segment) between two nodes. Attributes ---------- branch_id : str Unique identifier. from_node : str ``node_id`` of the start node. to_node : str ``node_id`` of the end node. resistance_Ohm : float Total DC resistance of the branch in Ohms. length_m : float Physical length of the branch in meters. """ branch_id: str from_node: str to_node: str resistance_Ohm: float length_m: float
[docs] class ConductorNetwork(abc.ABC): """Abstract base class for grounded conductor networks. Subclasses (all deferred to later phases): * ``CableNetwork`` — submarine cables (SCUBAS refactor). * ``PowerGridNetwork`` — substations + transmission lines. * ``PipelineNetwork`` — DSTL (Boteler 1997). * ``RailwayNetwork`` — track circuits + traction substations. """
[docs] @abc.abstractmethod def get_nodes(self) -> Sequence[Node]: """Return all nodes in the network."""
[docs] @abc.abstractmethod def get_branches(self) -> Sequence[Branch]: """Return all branches in the network."""
[docs] @abc.abstractmethod def assemble_network_admittance(self) -> np.ndarray: """Assemble the network admittance matrix ``Y_n``. Returns ------- numpy.ndarray Symmetric, real-valued matrix. Shape ``(n_nodes, n_nodes)``. Units: Siemens. Notes ----- ``Y_n[i, i]`` = sum of admittances of branches connected to node ``i``. ``Y_n[i, j]`` = ``-admittance`` of the branch between ``i`` and ``j``. """
[docs] @abc.abstractmethod def assemble_earthing_impedance(self) -> np.ndarray: """Assemble the diagonal earthing-impedance matrix ``Z_e``. Returns ------- numpy.ndarray Diagonal matrix. Shape ``(n_nodes, n_nodes)``. Units: Ohms. """
[docs] @abc.abstractmethod def compute_thevenin_voltages( self, ex_Vm: np.ndarray, ey_Vm: np.ndarray, lat_deg: np.ndarray, lon_deg: np.ndarray, ) -> np.ndarray: """Compute the Thévenin equivalent voltage for each branch. ``V_th(ab) = ∫_a^b E(r) · dl``. For a uniform E-field over the branch length this reduces to ``V_th = Ex · Δx + Ey · Δy``. Parameters ---------- ex_Vm, ey_Vm : numpy.ndarray North-south and east-west E-field components in V/m. lat_deg, lon_deg : numpy.ndarray Coordinates of the E-field evaluation points. Returns ------- numpy.ndarray Thévenin voltage per branch. Shape ``(n_branches,)``. Units: V. """
[docs] @classmethod @abc.abstractmethod def from_file(cls, path: str) -> "ConductorNetwork": """Load a network from disk (JSON, GeoJSON, or YAML). Parameters ---------- path : str Path to the network definition file. Returns ------- ConductorNetwork The loaded network. """