Support voltage-regulator Q limits and PV→PQ switching #1452
Support voltage-regulator Q limits and PV→PQ switching #1452scud-soptim wants to merge 5 commits into
Conversation
- 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>
|
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 :)
| - Voltage controlled bus: a bus with known $P$ and $U$. | ||
| Note: this bus is not supported by power-grid-model yet. | ||
| - Voltage-controlled bus: a bus with known active power $P$ and voltage magnitude $U$. In power-grid-model this is | ||
| modeled by an active `voltage_regulator` on a regulated load or generator and is supported by the Newton-Raphson |
There was a problem hiding this comment.
Maybe add a hyperlink for voltage_regulator here.
| }, | ||
| { | ||
| "data_type": "IntS", | ||
| "names": ["bus_type"], | ||
| "description": "effective bus type after calculation" |
There was a problem hiding this comment.
I'm not sure about this new output attribute. As you describe in the documentation below, doesn't the voltage_regulator.limit_violated output attribute already give enough information to determine if its bus is in PQ or PV mode? Or if multiple regulators are attached to the same node, just "loop" over such attributes?
The reason I'm hesitant is because this would now be an output attribute present for all calculation types and methods, but it's only useful for Newton Raphson Power Flow.
| ``` | ||
|
|
||
| ```{warning} | ||
| `bus_type` output is available only for the [Newton-Raphson power flow](./calculations.md#newton-raphson-power-flow) method; for other methods, `bus_type` is set to `0` (PQ) by default. |
There was a problem hiding this comment.
This is what I meant in https://github.com/PowerGridModel/power-grid-model/pull/1452/changes#r3504924406. How important is this attribute for you? Rather than bus_type only being available for Newton-Raphson Power Flow, the case is that it's only relevant for that method, but it's "available", albeit useless, for the rest.
| Voltage regulation is supported only by the [Newton-Raphson power flow](./calculations.md#newton-raphson-power-flow) | ||
| method. |
There was a problem hiding this comment.
Can you leave this as a warning or note please?
| return node.template get_null_output<sym>(); | ||
| } | ||
| auto bus_type = BusType::pq; | ||
| if (solver_output[math_id.group].bus.size() == solver_output[math_id.group].u.size()) { |
| } | ||
|
|
||
| // apply distributed Q to load_gens | ||
| for (auto const& [idx, load_gen] : enumerate(load_gens)) { |
There was a problem hiding this comment.
I think regulating_gens already contains the relevant load_gens. That would also remove the need for the status check below.
| for (auto const& [idx, load_gen] : enumerate(load_gens)) { | ||
| if (!loadgen_to_regulator.contains(load_gen)) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
You can also pass as argument to this function the list of relevant ones.
| if (std::abs(base_total) > numerical_tolerance) { | ||
| double const scale = q_scalar / base_total; | ||
| return RealValue<asymmetric_t>{base_distribution(0) * scale, base_distribution(1) * scale, | ||
| base_distribution(2) * scale}; | ||
| } | ||
| return RealValue<asymmetric_t>{q_scalar / 3.0, q_scalar / 3.0, q_scalar / 3.0}; |
There was a problem hiding this comment.
This logic looks very similar to what's done in allocate_q_bus_limit_violated when distributing. With some minor refactoring it can probably be made into a helper function.
|
|
||
| // auto num_regulating_gens = static_cast<double>(std::ranges::distance(regulating_gens)); // <- fails in windows | ||
| // build | ||
| double num_regulating_gens = 0.0; |
There was a problem hiding this comment.
I just realized, why is this a double?
| // When a regulator hits its limit, it is removed from the active set and the unallocated Q is | ||
| // redistributed among the remaining regulators. Unallocated Q remaining after all regulators hit | ||
| // their limits should not occur, as then the bus would have been switched to a PQ bus. | ||
| while (std::abs(total_q(q_remaining)) > numerical_tolerance && num_regulating_gens > 0.0) { |
There was a problem hiding this comment.
Could this get into an infinite loop?
figueroa1395
left a comment
There was a problem hiding this comment.
Some additional comments
| // keep copy, as reference might break batching | ||
| auto derived_solver = static_cast<DerivedSolver&>(*this); |
There was a problem hiding this comment.
The comment changed but the line below didn't. Also, this is still a reference to the BaseSolver which is then casted to a reference to the DerivedSolver.
Or is this to reinforce the absence of auto& instead of auto there? Can you try with auto&? If I recall correctly we create a solver per thread/set of batches and per independent grid, so I imagine this shouldn't be an issue.
| // finalize | ||
| { | ||
| // Timer const sub_timer{log, LogEvent::calculate_math_result}; // TODO(mgovers): need new event ?!? | ||
| derived_solver.finalize_derived_result(input, output); | ||
| } | ||
|
|
There was a problem hiding this comment.
Is the TODO a question to us?
Also, maybe doing something along the lines of
template <typename DerivedSolver>
void finalize(DerivedSolver& derived_solver, PowerFlowInput<sym> const& input, SolverOutput<sym> const& output) {
if constexpr (requires { derived_solver.finalize_derived_result(input, output); }) {
derived_solver.finalize_derived_result(input, output);
}
}makes sense so we don't have to include no-op finalize_derived_result in other solvers other than the Newton-Raphson one.
|
|
||
| void parameters_changed(bool changed) { parameters_changed_ = parameters_changed_ || changed; } | ||
|
|
||
| void finalize_derived_result(PowerFlowInput<sym> const& /*input*/, SolverOutput<sym>& /*output*/) { |
There was a problem hiding this comment.
| // initialize with BusType::pq, set calculated value in newton_raphson solver | ||
| BusType bus_type{BusType::pq}; | ||
| // 0: no violation, -1: q_min violated, 1: q_max violated | ||
| IntS q_limit_violated{0}; |
There was a problem hiding this comment.
Let's make this use a named variable or enum please.
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.
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.