PV node: Support voltage-regulator Q limits and PV→PQ switching - #1452
PV node: Support voltage-regulator Q limits and PV→PQ switching #1452scud-soptim wants to merge 16 commits into
Conversation
|
Hello @scud-soptim and @frie-soptim, Thank you once more for your contribution! Since this is a large and very involved PR, I'll give some initial comments:
Additionally, we notice that this PR only includes the Q-limit handling and not yet the voltage-setpoint controller as described in #1236. We agree with this decision. Let's keep the scope of this PR strictly to the Q-limit handling and leave the voltage-setpoint controller for a follow up discussion. That said, I'll add a few additional remarks about this in the issue itself. |
figueroa1395
left a comment
There was a problem hiding this comment.
@scud-soptim , @frie-soptim As always, great job!
Some additional comments:
- CI is broken, but it's just minor stuff.
- I see merge conflicts due to a recently merged documentation PR. Let us know if you want us to help resolving those.
- There's some TODOs in
power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/common_solver_functions.hpp, I agree that those should be implemented. - The logic looks good, no comment yet about that part.
- The rest of the review will follow tomorrow :)
figueroa1395
left a comment
There was a problem hiding this comment.
Some additional comments
There was a problem hiding this comment.
Thanks for the useful description of what's going on.
Additionally, would it make sense that for the node with two regulators, one violates q_max and the other violates q_min but they compensate each other? If this makes sense, can you add such a test? The same for the situation in which both violate opposite boundaries but they aren't able to compensate and the node becomes PQ instead.
There was a problem hiding this comment.
Added test with two regulators on a node where one has the limis [-50, 0] and the other [0, 50]. Then, instead of distributing the calculated Q of 10 to both generators (5 each), all of it lands on one generator.
figueroa1395
left a comment
There was a problem hiding this comment.
To be continued. I'm almost done now :)
| auto& bus_control = bus_control_[bus_idx]; | ||
| if (!sources.empty()) { | ||
| bus_types_[bus_idx] = BusType::slack; | ||
| bus_control.type = BusType::slack; |
There was a problem hiding this comment.
Reinforcing on earlier comments. slack type is only here for convenience but can be easily deduced as well starting from your comment. In addition, unless we would also make such changes in other solvers, I don't think we should add BusType to output types.
In addition, we had added such a check at construction time already, right? As in PGM would throw if a regulator is added where there is a source, right?
There was a problem hiding this comment.
I extended SolverOutput with
template <symmetry_tag sym_type> struct SolverOutput {
....
std::vector<ComplexValue<sym>> u;
std::vector<ComplexValue<sym>> bus_injection;
std::vector<BusSolverOutput> bus; // <-- NEW
....
};
struct BusSolverOutput {
BusType bus_type{BusType::pq};
LimitViolation q_limit_violated{LimitViolation::none};
};because I needed at least the q_limit_violated value on the bus to control the Q distribution in common_solver_functions::calculate_voltage_regulator_result.
The problem is that common_solver_functions::calculate_pf_result is called from the class IterativePFSolver and not from NewtonRaphsonPFSolver, and in the the base class I don't have immediate access to the NR's internal array.
Alternatively, I would have to update the generator/load output.S values in the NT solver instead of in the calculate_pf_result function. And I would have to make sure that calculate_pf_result does not overwrite the calculated Q with the specified values. And, at least to me, it feels wrong to do that in the solver.
Therefore, I extended the SolverOuput as the option with the presumably least impact. But if you have a better proposal how to pass on NR values to the common_solver_functions::calculate_voltage_regulator_result function, then I would do that instead.
There was a problem hiding this comment.
Also, we added a check that there cannot be an active Source AND and active VoltageRegulator in the same node in calculation_preparation::check_sources_and_voltage_regulators. If one of them is disabled, then it is OK.
There was a problem hiding this comment.
Let me see if I can come up with something else. I'll get back to you
There was a problem hiding this comment.
I'm a bit confused with the answer. I see you don't use output.bus_type on common_solver_functions::calculate_voltage_regulator_result, or did I miss it? How is that related with the question above? I think you have already access to q_limit_violated there, no?
On the other hand, maybe check by adding a small test, if not there yet, what happens when there is a regulator on a source. This should hard crash and also be added to the validator (I believe it is, but let's confirm).
There was a problem hiding this comment.
Yes, that is correct, I used bus_type only for the output of node data (output.hpp and node.hpp). But I used q_limit_violated in calculate_voltage_regulator_result, which is located in the same object as the bus_type in the extended SolverOutput / BusSolverOutput (see above).
So, I did not just had access to the q_limit_violated, I still had to extend the SolverOutput. And if I would remove the bus_type, then q_limit_violated would have to remain. And that was my problem or question, how to pass a value from the NR solver to the functions in common_solver_function.hpp.
There was a problem hiding this comment.
wouldn't using a function override solve this ambiguity? there is now a good reason why calculate_result for NRPF should be different from the one for the iterative current PF solver. The main reason that we didn't make the function virtual before was that there was no difference. Or do you foresee a world in which the iterative current PF solver will also have support for PV nodes?
|
Hi, we'll answer your questions when Frie returns from vacation. |
figueroa1395
left a comment
There was a problem hiding this comment.
Double checking, but is there any additional validation needed on the python side? Say anything regarding the q-limits for instance? What would happen if q_min is set to 1 and q_max to -1 by mistake? I imagine this should be catched by both the validator and a hard error thrown?
Can you add some unit testing handling sym and asym regulated loads on a node? one violating limits, both violating limits, and both not violating limits?
I think I should be done with the overall review. If I have anything else to comment on, I'll mention it once Frie is back so this doesn't grow even more.
| CHECK(imag(output.load_gen[0].s) > -1.0); | ||
| CHECK(imag(output.load_gen[0].s) < 1.0); |
There was a problem hiding this comment.
I imaging checking for the exact value is not practical? But double checking why a range instead? Also, why not check for q directly?
There was a problem hiding this comment.
q is an output parameter, i don't think it's available here.
There was a problem hiding this comment.
This is indeed checking the output though. In any case, if what I'm asking doesn't make sense, feel free to resolve this comment :)
There was a problem hiding this comment.
It was simply a test that the calculated Q is within boundaries. But I can check the actual value too.
There was a problem hiding this comment.
If it's easy to do, it'd be nice to check the value, yes.
|
Hello @scud-soptim and @frie-soptim, Once small remark for when you return. We have enabled mandatory cryptographic signing for the PGM-org. Could you please amend your commits and either This was announced during the last community meeting, where @scud-soptim was present. |
- add bus_type to node output
- implement feature in newton_raphson solver
- implmentet iterative equal distribution of Q at the end
- try distributing equally
- if individual regulator limit is violated, then save unallocated
amount and distribute again in next iteration
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: SCUD-SOPTIM <udo.schmitz@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
`cache_run` needed in Newton Raphson PF solver to determine that in case of batching the max deviation should not be artificially set to infinity (to trigger q limit checking) and then run into a IterationDiverge error, because with cache_run also max_iter is set to 1 Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
- cache relevant IDs of voltage_regualtors and load_gens initially - simplify distribution by iterating over cached regulators, instead of iterating over load_gens and looking up regulators over and over again - check for unallocated power after distribution and throw (should be purely technical, cases where distribution actually not possible are handled in another function) Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
- refactor q limit check triggering by using countdown variable, that starts limit check at iteration N, or earlier if solver converger before that - split bus type and bus limit determination from settung reference voltage for PV buses - remove noop call for function finalize_result() in non-NR solver - use enum values for limit violations Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
- mark node that it required recalculation after limit violation - in setup of jacobian only calculate row for marked buses Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
… load_gens - specified_q on regulated load_gens should have no effect on the solution - however, the initial linear solver considered it and calculated "wrong" start values for voltage. For PV buses, this voltage was replaced by the reference voltage of the regulator, but for PQ buses the "wrong" voltages remained - this can lead to larger mismatches in the first iterations and thus to a potential limit violation and a bus switch to PQ. - solution is to ignore specified_q values for regulated load_gens for the linear solver Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
8efbef8 to
153af49
Compare
@figueroa1395 I have pushed some new commits and rebased the branch on current main, in order to sign the older commits too. If you had checked out the branch before, then you will have to update it now. |
@frie-soptim Thanks for the heads up. I'll review the new updated PR next week + resolve any outdated comments that are no longer relevant. In addition, I've enabled the CI to run. |
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
Signed-off-by: Eduard Fried <eduard.fried@soptim.de>
…ath_solver/newton_raphson_pf_solver.hpp Co-authored-by: Santiago Figueroa Manrique <22895394+figueroa1395@users.noreply.github.com> Signed-off-by: Eduard Fried <146418093+frie-soptim@users.noreply.github.com>
|
|
||
| constexpr Idx no_limit_check = -1; | ||
| constexpr Idx do_limit_check = 0; | ||
| constexpr Idx limit_check_at_iteration = 2; // TODO(frie-soptim): maybe consider making this a parameter in the future |
There was a problem hiding this comment.
What do you mean with "a parameter"?
There was a problem hiding this comment.
I mean an option the user can specify. Currently limit checks start at iteration 2, but maybe someone wants it to start at iteration 3.
Or, in general, complex features might need a complex configuration. I don't think it is currently possible to provide such options. So, I added that todo as a reminder that this could be such a configuration option. In a way, it would be similar to a global option to disable Q-limit checks.
|
@figueroa1395 I believe I have addressed all comments or issues in my last commits. At the end, I also removed the the experimental feature check for Q limits. I don't think I have the option to resolve any discussions, you will have to do it yourself. If there are still any issues regarding naming or location of functions, feel free to change it as you see fit. |
Implements #1236
Summary
This PR adds reactive-power limit handling for voltage-regulated nodes in Newton-Raphson power-flow calculations.
An active
voltage_regulatornow models the regulated node as a PV node by enforcing the configured voltage magnitudeu_ref. If the reactive power required to maintain this voltage violatesq_minorq_max, the regulated object is clamped to the violated limit and the effective node type is switched from PV to PQ.The PR also exposes the effective node type in steady-state node output and documents the voltage-regulator behavior, including PV→PQ switching and reactive-power allocation when multiple regulators are connected to the same node.
Main changes
Add effective
bus_typeto steady-state node output.0: PQ1: PV2: Source/SlackExtend voltage-regulator steady-state output with a limit indicator.
-1: lower reactive-power limitq_minreached0: no limit violation1: upper reactive-power limitq_maxreachedImplement PV-node handling in Newton-Raphson power flow for active voltage regulators.
Implement PV→PQ switching when the required reactive power violates configured Q limits.
Clamp the regulated object to the violated Q limit after switching to PQ.
Propagate final bus type and Q-limit status to steady-state output.
Add and update tests for voltage-regulator Q-limit behavior, PV→PQ switching, batch updates, and output metadata.
Update user documentation for voltage-regulator behavior and power-flow algorithm details.
Reactive-power allocation with multiple voltage regulators
When multiple active voltage regulators are connected to the same node, they jointly regulate the same node voltage. The required reactive power is initially distributed equally over the active regulated objects. If one regulator reaches its individual
q_minorq_max, it is clamped at that limit and removed from the remaining allocation. The unallocated reactive power is redistributed over the remaining regulators until all required reactive power has been allocated or all available regulators have reached a limit.For asymmetric calculations, limit checks use the total three-phase reactive power. The phase distribution follows the available phase proportions where possible; if no usable phase proportion is available, the value is distributed equally over the three phases.
Documentation updates
The documentation now describes:
node.bus_type;voltage_regulator.limit_violated;voltage_regulatorcomponents in Newton-Raphson power flow.Testing
Added and updated tests cover:
bus_type;limit_violated;Notes for reviewers
q_minandq_max.voltage_regulatoroutput only reports the limit status.Related to #1236toCloses #1236.