@@ -16,6 +16,7 @@ struct ObservabilitySensorsResult {
1616 std::vector<int8_t > flow_sensors; // power sensor and current sensor
1717 std::vector<int8_t > voltage_phasor_sensors; // voltage phasor sensors
1818 std::vector<int8_t > bus_injections; // bus injections, zero injection and power sensors at buses
19+ int8_t total_injections{0 }; // total number of injections at buses
1920 bool is_possibly_ill_conditioned{false };
2021};
2122enum class ConnectivityStatus : std::int8_t {
@@ -75,7 +76,7 @@ ObservabilitySensorsResult scan_network_sensors(MeasuredValues<sym> const& measu
7576 // diagonal for bus injection measurement
7677 if (measured_values.has_bus_injection (bus)) {
7778 result.bus_injections [bus] = 1 ;
78- result.bus_injections . back () += 1 ;
79+ result.total_injections += 1 ;
7980 result.flow_sensors [current_bus_entry] = 1 ;
8081 has_at_least_one_sensor = true ;
8182 bus_neighbourhood_info[bus].status = ConnectivityStatus::node_measured; // only treat power/0 injection
@@ -351,13 +352,12 @@ inline bool find_spanning_tree_from_node(Idx start_bus, Idx n_bus,
351352 return false ;
352353 };
353354
354- auto try_general_connection_rules = [&local_neighbour_list, &visited, &discovered_edges, &edge_track, ¤t_bus,
355- &downwind ](bool & step_success, bool current_bus_no_measurement) {
355+ auto try_general_connection_rules = [&local_neighbour_list, &visited, &discovered_edges, &edge_track,
356+ ¤t_bus ](bool & step_success, bool current_bus_no_measurement) {
356357 // Helper lambda to handle common edge processing logic
357- auto process_edge = [&local_neighbour_list, &visited, &discovered_edges, &edge_track, ¤t_bus, &downwind,
358+ auto process_edge = [&local_neighbour_list, &visited, &discovered_edges, &edge_track, ¤t_bus,
358359 &step_success](auto & neighbour, ConnectivityStatus neighbour_status,
359- ConnectivityStatus reverse_status, bool use_current_node,
360- bool set_upwind = false ) {
360+ ConnectivityStatus reverse_status, bool use_current_node) {
361361 discovered_edges.emplace_back (current_bus, neighbour.bus );
362362 edge_track.emplace_back (current_bus, neighbour.bus );
363363 visited[current_bus] = BusVisited::Visited;
@@ -380,11 +380,6 @@ inline bool find_spanning_tree_from_node(Idx start_bus, Idx n_bus,
380380 local_neighbour_list[neighbour.bus ].status = ConnectivityStatus::has_no_measurement;
381381 }
382382
383- // Set direction flag if needed
384- if (set_upwind) {
385- downwind = false ; // upwind
386- }
387-
388383 current_bus = neighbour.bus ;
389384 step_success = true ;
390385 return true ;
@@ -398,20 +393,15 @@ inline bool find_spanning_tree_from_node(Idx start_bus, Idx n_bus,
398393 bool const neighbour_bus_has_no_measurement =
399394 local_neighbour_list[neighbour.bus ].status == ConnectivityStatus::has_no_measurement;
400395
401- if (!current_bus_no_measurement && (downwind && neighbour_bus_has_no_measurement)) {
402- // Case: current has measurement, downwind, neighbour empty
403- return process_edge (neighbour, ConnectivityStatus::branch_discovered_with_to_node_sensor,
404- ConnectivityStatus::branch_discovered_with_from_node_sensor, true );
405- }
406- if (!current_bus_no_measurement && (downwind || neighbour_bus_has_no_measurement)) {
407- // Case: current has measurement, (downwind OR neighbour empty)
396+ if (!current_bus_no_measurement && neighbour_bus_has_no_measurement) {
397+ // Case: current has measurement, neighbour empty (not in downwind mode)
408398 return process_edge (neighbour, ConnectivityStatus::branch_discovered_with_from_node_sensor,
409399 ConnectivityStatus::branch_discovered_with_to_node_sensor, true );
410400 }
411401 if (!neighbour_bus_has_no_measurement) {
412402 // Case: neighbour has measurement
413403 return process_edge (neighbour, ConnectivityStatus::branch_discovered_with_from_node_sensor,
414- ConnectivityStatus::branch_discovered_with_to_node_sensor, false , true );
404+ ConnectivityStatus::branch_discovered_with_to_node_sensor, false );
415405 }
416406 }
417407 return false ;
@@ -420,10 +410,10 @@ inline bool find_spanning_tree_from_node(Idx start_bus, Idx n_bus,
420410 // Helper function to reassign nodal measurement between two connected nodes
421411 auto reassign_nodal_measurement = [&local_neighbour_list](Idx from_node, Idx to_node) {
422412 // no reassignment possible if reached via edge measurement
423- auto const branch_it =
424- std::ranges::find_if (local_neighbour_list[from_node].direct_neighbours ,
425- [to_node](auto const & neighbour) { return neighbour.bus == to_node; });
426- if ( branch_it != local_neighbour_list[from_node].direct_neighbours .end () &&
413+ if ( auto const branch_it =
414+ std::ranges::find_if (local_neighbour_list[from_node].direct_neighbours ,
415+ [to_node](auto const & neighbour) { return neighbour.bus == to_node; });
416+ branch_it != local_neighbour_list[from_node].direct_neighbours .end () &&
427417 branch_it->status == ConnectivityStatus::branch_native_measurement_consumed) {
428418 return ;
429419 }
@@ -447,7 +437,7 @@ inline bool find_spanning_tree_from_node(Idx start_bus, Idx n_bus,
447437 // Find and update the reverse connection from to_node to from_node
448438 for (auto & neighbour : local_neighbour_list[to_node].direct_neighbours ) {
449439 if (neighbour.bus == from_node) {
450- // Change to downstream connection ( from from_node to to_node perspective)
440+ // Change from from_node to to_node perspective
451441 neighbour.status = ConnectivityStatus::branch_discovered_with_from_node_sensor;
452442 break ;
453443 }
@@ -509,10 +499,7 @@ inline bool find_spanning_tree_from_node(Idx start_bus, Idx n_bus,
509499}
510500
511501inline bool
512- sufficient_condition_meshed_without_voltage_phasor (std::vector<detail::BusNeighbourhoodInfo> const & _neighbour_list) {
513- // make a copy of the neighbour list
514- std::vector<detail::BusNeighbourhoodInfo> const neighbour_list = _neighbour_list;
515-
502+ sufficient_condition_meshed_without_voltage_phasor (std::vector<detail::BusNeighbourhoodInfo> const & neighbour_list) {
516503 auto const n_bus = static_cast <Idx>(neighbour_list.size ());
517504 std::vector<Idx> starting_candidates;
518505 prepare_starting_nodes (neighbour_list, n_bus, starting_candidates);
@@ -563,7 +550,7 @@ inline ObservabilityResult observability_check(MeasuredValues<sym> const& measur
563550 }
564551
565552 // Sufficient early out, enough nodal measurement equals observable
566- if (observability_sensors.bus_injections . back () > n_bus - 2 ) {
553+ if (observability_sensors.total_injections > n_bus - 2 ) {
567554 return ObservabilityResult{.is_observable = true ,
568555 .is_possibly_ill_conditioned = observability_sensors.is_possibly_ill_conditioned };
569556 }
0 commit comments