pub struct ObservabilityReport {
pub n_unknowns: usize,
pub rank: usize,
pub structurally_unmeasured: Vec<Unknown>,
pub unobservable: Vec<Unknown>,
pub skipped_numerical: bool,
pub de_energized: Vec<Unknown>,
pub global_current_without_angle_reference: bool,
}Expand description
What the measurements do and do not determine.
Fields§
§n_unknowns: usize§rank: usizeRank of the gain matrix. Equal to n_unknowns exactly when the state is
fully observable.
structurally_unmeasured: Vec<Unknown>Unknowns no measurement function mentions at all.
unobservable: Vec<Unknown>Unknowns left undetermined once linear dependence is accounted for.
A superset of structurally_unmeasured
in effect — a zero column is also rank-deficient — though the specific
variables reported can differ, since which member of a dependent group
gets blamed depends on the pivot order. The count is what is
meaningful; the names are representatives.
skipped_numerical: boolSet when the system was too large to analyze densely, in which case only the structural half of the report is filled in.
de_energized: Vec<Unknown>Unknowns belonging to buses that carry no voltage at all.
These are not a gap in the sensor plan and no sensor would close them.
A de-energized bus is reported at exactly zero by every estimator path
here — nr::estimate writes it, iterative::estimate skips its rows,
linear_start starts it there — so its columns are determined by the
network rather than by measurements, and they are zero columns of H by
construction. Counting them against the rank would report every CGMES
model containing one switched-out node as unobservable, which is the
same mistake, in the same function, that ignoring constraint rows was.
They are excluded from
structurally_unmeasured,
unobservable and
is_observable, and reported here instead.
global_current_without_angle_reference: boolA global-angle current sensor is present with nothing to measure its angle against.
Such a sensor reports I = i·e^{jθ} with θ absolute, which is only
meaningful if something else fixes what that reference is. With no
voltage angle in the set, StateLayout pins a reference bus instead —
and that pin is then a false constraint, rotating the estimate away from
the angle the sensor actually measured. The rank check will not catch it,
because the system is perfectly determined; it is determined to the wrong
thing.
power-grid-model refuses to run at all here, raising NotObservableError
with “Global angle current sensors require at least one voltage angle
measurement as a reference point”. gridoxide reports rather than refuses,
on the grounds that this analysis is the place that says what is wrong
and the estimator’s job is to answer the question it was asked.
Implementations§
Source§impl ObservabilityReport
impl ObservabilityReport
Sourcepub fn is_observable(&self) -> bool
pub fn is_observable(&self) -> bool
Whether every state variable is determined.
Deliberately does not fold in
global_current_without_angle_reference:
that state is fully determined, just to the wrong reference, and calling
it unobservable would misname it.
Trait Implementations§
Source§impl Clone for ObservabilityReport
impl Clone for ObservabilityReport
Auto Trait Implementations§
impl Freeze for ObservabilityReport
impl RefUnwindSafe for ObservabilityReport
impl Send for ObservabilityReport
impl Sync for ObservabilityReport
impl Unpin for ObservabilityReport
impl UnsafeUnpin for ObservabilityReport
impl UnwindSafe for ObservabilityReport
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T> DistributionExt for Twhere
T: ?Sized,
impl<T, U> Imply<T> for U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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