Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/user_manual/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,9 @@ The state estimator uses the data to evaluate the state of the grid with the hig

A sensor only has output for state estimation.
For other calculation types, sensor output is undefined.
The sensor output attribute `energized` is derived from whether the measured object is energized at the measured
terminal, regardless of whether the measurement is included in state estimation.
If the measured object is not energized at the measured terminal, all residual values are zero.

### Generic Voltage Sensor

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,49 @@ namespace detail {
template <typename T, typename U>
concept assignable_to = std::assignable_from<U, T>;

template <typename MeasuredComponent, class ComponentContainer, typename... StatusArgs>
constexpr bool measured_component_active(MainModelState<ComponentContainer> const& state, Idx const obj_seq,
StatusArgs... status_args) {
if constexpr (common::component_container_c<ComponentContainer, MeasuredComponent>) {
return get_component_by_sequence<MeasuredComponent>(state.components, obj_seq).status(status_args...);
} else {
// A missing component type makes its terminal type unreachable in a valid model. Keep reduced containers
// compatible with the previous topology-only behavior.
return true;
}
}

template <class ComponentContainer>
constexpr bool measured_terminal_active(MeasuredTerminalType const terminal_type,
MainModelState<ComponentContainer> const& state, Idx const obj_seq) {
switch (terminal_type) {
using enum MeasuredTerminalType;

case branch_from:
return measured_component_active<Branch>(state, obj_seq, BranchSide::from);
case branch_to:
return measured_component_active<Branch>(state, obj_seq, BranchSide::to);
case source:
return measured_component_active<Source>(state, obj_seq);
case shunt:
return measured_component_active<Shunt>(state, obj_seq);
case load:
[[fallthrough]];
case generator:
return measured_component_active<GenericLoadGen>(state, obj_seq);
case branch3_1:
return measured_component_active<Branch3>(state, obj_seq, Branch3Side::side_1);
case branch3_2:
return measured_component_active<Branch3>(state, obj_seq, Branch3Side::side_2);
case branch3_3:
return measured_component_active<Branch3>(state, obj_seq, Branch3Side::side_3);
case node:
return true;
default:
throw MissingCaseForEnumError{"measured_terminal_active()", terminal_type};
}
}

template <typename Component, typename IndexType, class ComponentContainer, non_owning_view_c ComponentOutput,
functor_c ResFunc>
requires model_component_state_c<MainModelState, ComponentContainer, Component> &&
Expand Down Expand Up @@ -247,7 +290,7 @@ constexpr auto output_result(Component const& power_sensor, MainModelState<Compo
}
}();

if (obj_math_id.group == disconnected) {
if (obj_math_id.group == disconnected || !detail::measured_terminal_active(terminal_type, state, obj_seq)) {
return power_sensor.template get_null_output<sym>();
}

Expand Down Expand Up @@ -317,7 +360,7 @@ constexpr auto output_result(Component const& current_sensor, MainModelState<Com
}
}();

if (obj_math_id.group == disconnected) {
if (obj_math_id.group == disconnected || !detail::measured_terminal_active(terminal_type, state, obj_seq)) {
return current_sensor.template get_null_output<sym>();
}

Expand Down
Loading
Loading