Skip to main content

SeNetwork

Struct SeNetwork 

Source
pub struct SeNetwork {
    pub ybus: YBusSparse,
    pub terminals: Vec<[CurrentFunctional; 2]>,
    pub shunt_y: Vec<Complex<f64>>,
    pub source_branches: Vec<Vec<usize>>,
    pub zero_injection: Vec<bool>,
    pub energized: Vec<bool>,
}
Expand description

Everything h(x) needs about the network, beyond the state itself.

Assembled once per topology. The per-bus shunt admittance and the per-bus list of source branches are kept explicitly because both are otherwise only recoverable by inspecting Y-bus entries, which cannot distinguish a shunt from a line’s charging susceptance.

Fields§

§ybus: YBusSparse§terminals: Vec<[CurrentFunctional; 2]>

Per branch, the current functional at each terminal: [0] is the from side, [1] the to side.

Stored resolved rather than as branch parameters, because a three-phase terminal is not a two-bus quantity: phase p of one end depends on all six phasors of the branch, through the 3×3 blocks. A coefficient list expresses both cases, and the estimator above it cannot tell them apart — which is what makes the phase domain a matter of longer lists rather than of a second Jacobian.

In the phase domain the index is 3·branch + phase, exactly as a bus index is 3·node + phase. That is what keeps Target unchanged between the two.

§shunt_y: Vec<Complex<f64>>

Total shunt admittance stamped at each bus, or zero.

§source_branches: Vec<Vec<usize>>

For each bus, the synthesized source branches delivering into it.

§zero_injection: Vec<bool>

Per-bus zero-injection flags, carried through from the conversion — see gridoxide’s pgm::PgmNetwork::zero_injection.

§energized: Vec<bool>

Whether each bus is reachable from any source.

A connected component containing no source carries no voltage that any measurement can determine: every quantity in it is identically zero, and the sensors that sit there — a network can perfectly well have a voltage sensor on a de-energized node — describe nothing. power-grid-model reports such nodes as energized: 0 with a state of exactly zero, and decides it topologically, ignoring whatever sensors are attached.

Without this the gain matrix comes back singular rather than merely uninformative, because those buses’ columns are touched — by their own sensors, or by a zero-injection constraint — while being determined by nothing. jacobian::mask_untouched only pins what nothing touches at all, which catches a fully isolated bus and not this.

solver::PersistentSolver has done the equivalent for power flow all along, via network::connected_components and mark_unreferenced_islands.

Implementations§

Source§

impl SeNetwork

Source

pub fn functional(&self, target: Target) -> Option<CurrentFunctional>

The current functional a measurement target names, or None if the target does not resolve in this network.

This is the single description of what each target measures. Everything downstream — h(x), the Jacobian, the iterative-linear row builder — reads it rather than restating it.

Source§

impl SeNetwork

Source

pub fn new( ybus: YBusSparse, lines: &[Line], transformers: &[Transformer], shunts: &[ShuntAdm], source_branch_idx: &HashMap<u64, usize>, zero_injection: &[bool], ) -> Self

Builds the model from a converted network plus the shunt list that was stamped into its Y-bus.

shunts must be the same slice passed to network::stamp_shunts — the shunt measurement function needs the admittance itself, and the Y-bus has already summed it together with everything else on that diagonal.

source_branch_idx maps each source to the branch synthesized for it, and zero_injection flags the buses known to inject nothing; the PGM converter records both, and gridoxide’s pgm::PgmNetwork::se_network passes them on.

Source

pub fn from_node_breaker(net: &NodeBreakerNetwork, ybus: YBusSparse) -> Self

The measurement model for state estimation over a node-breaker network’s bus view.

ybus must be the one built from its lines and transformers with stamp_shunts applied — the estimator reads the branch list a second time to build its functionals, and a disagreement produces an estimate that converges confidently to the wrong answer.

The retained switches are already in the branch list, so a measurement on a breaker is an ordinary branch-terminal measurement at branch_of_switch — as with power flow, the estimator never learns that any of its branches is a switch.

What is specific to this view is the constraint set. Almost every bus the node-breaker topology adds is a bay-internal node with nothing attached, so zero_injection is dense here in a way it never is on a bus-branch model, and those buses are observable only because of it.

Source

pub fn from_bus_network( buses: &[Bus], ybus: YBusSparse, lines: &[Line], transformers: &[Transformer], shunts: &[ShuntAdm], zero_injection: Vec<bool>, ) -> Self

Builds the model for a network whose sources are bus types rather than synthesized source branches — everything that does not come through gridoxide’s pgm, which for now means CGMES, including the node-breaker view.

The two differ in exactly one thing, and it is not the measurement model: power-grid-model gives a source its own virtual bus behind a Thévenin impedance, so “is this bus fed?” is answered by a branch list, while CGMES marks a reference bus as BusType::Slack and injects nothing extra. source_branches is therefore empty here, and every place that consulted it for a reference goes through SeNetwork::reference_bus instead.

zero_injection is the caller’s: which buses carry no appliance at all. It has to be structural, not a test on p_spec/q_spec — a load that happens to sit at zero in one snapshot is not a bus that injects nothing, and a hard constraint saying otherwise biases the estimate.

Source

pub fn reference_bus(&self, buses: &[Bus]) -> usize

The bus whose angle is held fixed when nothing measures phase.

With no angle measurement anywhere, the state is determined only up to a common rotation: one angle has to be pinned or the gain matrix is singular by construction. Which one is arbitrary mathematically, but it must be a bus that carries voltage at all — pinning a de-energized bus pins nothing and leaves the rotation free.

A source’s own bus is the natural choice and is what a power flow would use. The Slack fallback is what makes a CGMES network work, since it has no source branches; the final 0 is unreachable in practice and is there so this never panics on a degenerate network that the estimator will reject a moment later anyway.

Source

pub fn from_3ph( ybus: YBusSparse, lines: &[Line3Ph], transformers: &[Transformer3PhSeq], shunts: &[ShuntAdm3Ph], source_branch_idx: &HashMap<u64, usize>, zero_injection_per_node: &[bool], ) -> Self

Builds the model for a three-phase network.

Everything above this is unchanged by the phase domain. Bus k phase p is index 3k + p, as lib.rs documents for power flow, and a branch terminal is indexed 3b + p to match — so Target, StateLayout, the Jacobian, the constraints and both estimator methods carry over without knowing there are phases at all. What changes is the length of a coefficient list: six terms where the scalar case has two, because phase p of one end of a branch depends on all six phasors through the 3×3 blocks.

lines, transformers and shunts must be the same slices stamped into ybus — this reads their blocks a second time to build the measurement model, and a disagreement between the two would produce an estimate that converges confidently to the wrong answer.

Source

pub fn constrained_buses(&self) -> Vec<bool>

Buses a zero-injection constraint should apply to.

A de-energized bus’s injection is zero too, but asserting it adds a row that reaches columns nothing determines — which is how a constraint turns an uninformative island into a singular matrix.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ByRef<T> for T

Source§

fn by_ref(&self) -> &T

Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> DistributionExt for T
where T: ?Sized,

Source§

fn rand<T>(&self, rng: &mut (impl Rng + ?Sized)) -> T
where Self: Distribution<T>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V