Unit test not observable network passed through no perturbation#1322
Unit test not observable network passed through no perturbation#1322Jerry-Jinfeng-Guo wants to merge 8 commits intomainfrom
Conversation
Signed-off-by: Jerry Jinfeng Guo <jerry.jinfeng.guo@alliander.com>
Signed-off-by: Jerry Jinfeng Guo <jerry.jinfeng.guo@alliander.com>
There was a problem hiding this comment.
Pull request overview
Adds C++ unit tests around math_solver::observability::ObservabilityResult::use_perturbation() to ensure non-observable networks don’t trigger perturbation, and to exercise the use_perturbation boolean logic.
Changes:
- Added a new
TEST_CASEcoveringuse_perturbation()for a non-observable meshed network with multiple voltage phasor sensors. - Added additional subcases attempting to cover an observable case and direct
ObservabilityResultlogic combinations.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Jerry Guo <6221579+Jerry-Jinfeng-Guo@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Jerry Guo <6221579+Jerry-Jinfeng-Guo@users.noreply.github.com>
Signed-off-by: Jerry Jinfeng Guo <jerry.jinfeng.guo@alliander.com>
Signed-off-by: Jerry Jinfeng Guo <jerry.jinfeng.guo@alliander.com>
| Test case `07-observable-2-voltage-sensors` represents a meshed network with two voltage phasor sensors | ||
| (voltage measurements with both magnitude and angle). This configuration is currently treated as | ||
| **non-observable** due to a known limitation: the current meshed network sufficient-condition | ||
| implementation cannot handle multiple voltage phasor sensors. |
There was a problem hiding this comment.
I think that this description is misleading. Because even though we deem it unobservable internally, we don't throw NotObservableError by design. Hence, if it's truly unobservable, in this case we would reach a SparseMatrixError, but since this specific case is actually observable even if it has two voltage phasor sensors, the calculation proceeds as expected.
To me this description doesn't reflect what I wrote above.
| This edge case is comprehensively covered by unit tests in `tests/cpp_unit_tests/test_observability.cpp` | ||
| (specifically the test case "Test ObservabilityResult - use_perturbation with non-observable network"). | ||
| The unit tests verify: | ||
|
|
||
| - Networks with `n_voltage_phasor_sensors > 1 && !is_radial` are correctly identified as non-observable | ||
| - The `ObservabilityResult.is_observable` flag is set to `false` | ||
| - The `use_perturbation()` method returns `false` (no perturbation applied to non-observable networks) | ||
| - The specific code path in `observability_check()` that triggers early return for this condition |
There was a problem hiding this comment.
This is true and already well described in the unit test. I don't think we need to explicitly write such a description here.
| When support for multiple voltage phasors in meshed networks is implemented, the unit tests can be | ||
| updated accordingly, and test case `07-observable-2-voltage-sensors` can be populated with expected | ||
| output values. |
There was a problem hiding this comment.
Actually, this test can already be populated with expected output values, even with current implementation (I described the reason in https://github.com/PowerGridModel/power-grid-model/pull/1322/changes#r2904270623). The reason we don't have output values here is because we just use it to test that we can process such a case that is actually observable even thought voltage phasor sensors are present (@nitbharambe correct me if I'm wrong please).
| topo.power_sensors_per_load_gen = {from_sparse, {0}}; | ||
| topo.power_sensors_per_shunt = {from_sparse, {0}}; | ||
| // Sufficient branch sensors to pass necessary condition | ||
| topo.power_sensors_per_branch_from = {from_sparse, {0, 1, 2, 3, 4}}; |
There was a problem hiding this comment.
Why are these sensors needed if there aren't any loads? Having 0 power sensors should satisfy the necessary condition in this scenario.
| CHECK(result.is_observable == false); | ||
|
|
||
| // Verify that use_perturbation() returns false when not observable | ||
| CHECK(result.use_perturbation() == false); |
There was a problem hiding this comment.
I think this behavioral check is good and valuable.
| An explicit integration test marked as "expected to fail" is **not necessary** because: | ||
|
|
||
| 1. **Unit test coverage is sufficient**: The unit tests provide precise, maintainable verification at the | ||
| appropriate level of granularity. | ||
| 2. **Maintenance burden**: "Expected to fail" tests require special infrastructure and documentation, | ||
| and risk being forgotten when the limitation is eventually addressed. | ||
| 3. **Rare use case**: Multiple voltage phasor sensors (with precise angle measurements) in meshed | ||
| networks are uncommon in real-world applications. |
There was a problem hiding this comment.
I don't think we need such a description here, but I also disagree with some things mentioned:
2. A test case that is expected to fail doesn't need special handling beyond the additional parameter. Moreover, they are good because they are hard to miss. If there is a behavioral change and the expected to fail test now passes, then such test would now fail, making it clear that we either have to change it according to the new behavior or that we messed up somewhere.
3. I don't think this is relevant here.
What I had envisioned when I suggested the new test in #1317 (comment), was different compared to 07-observable-2-voltage-sensors. The mentioned one, doesn't need a perturbation to perform a successful calculation, whereas what I intended to propose, would be a similar meshed test (probably with links involved), that even though marked as non-observable internally (because of the >2 voltage phasor voltages present), it would run the calculation but fail with a SparseMatrixError because perturbation would be needed (but we don't allow it because of .is_observable = false, not because it is not observable "in real life"). Nevertheless, this might be a hassle to come up with and merging links into nodes would probably deem it unnecessary, so I think it's not needed unless others think otherwise.
| 1. Observable system with branch measurement. | ||
| 2. Observable system without branch measurement (using only nodal measurements). | ||
| 3. Unobservable system with branch measurement (x2). | ||
| The following test cases are included: |
There was a problem hiding this comment.
Nice addition, easy to find out what's what. Maybe good to add such descriptions in other places as well. Out of scope, just food for thought.
| CHECK(result.use_perturbation() == (result.is_observable && result.is_possibly_ill_conditioned)); | ||
| } | ||
|
|
||
| SUBCASE("Test use_perturbation logic directly") { |
…sed-through-no-perturbation Signed-off-by: Jerry Guo <6221579+Jerry-Jinfeng-Guo@users.noreply.github.com>
|



Added unit tests for checking the possibility to use perturbation due to observability check render
.is_observablestate without exception thrown.PR comes from #1317