|
23 | 23 | import com.powsybl.iidm.network.Network; |
24 | 24 | import com.powsybl.loadflow.validation.io.ValidationWriter; |
25 | 25 |
|
| 26 | +import static com.powsybl.loadflow.validation.ValidationUtils.*; |
| 27 | + |
26 | 28 | /** |
27 | 29 | * |
28 | 30 | * @author Massimo Ferraro {@literal <massimo.ferraro@techrain.eu>} |
@@ -129,15 +131,14 @@ public boolean checkGenerators(String id, double p, double q, double v, double t |
129 | 131 |
|
130 | 132 | double expectedP = getExpectedP(guesser, id, p, targetP, minP, maxP, config.getThreshold()); |
131 | 133 | if (connected && ValidationUtils.isMainComponent(config, mainComponent)) { |
132 | | - if (Double.isNaN(p) || Double.isNaN(q)) { |
133 | | - validated = checkGeneratorsNaNValues(id, p, q, targetP, targetQ); //Rule 1: |
134 | | - } else if (checkReactiveBoundInversion(minQ, maxQ, config)) { //Rule 2: when maxQ < minQ if noRequirementIfReactiveBoundInversion return true |
| 134 | + if (areNaN(p, q)) { |
| 135 | + validated = validateMissingPQRule(id, p, q, targetP, targetQ); |
| 136 | + } else if (isReactiveBoundInverted(minQ, maxQ, config.getThreshold(), config.isNoRequirementIfReactiveBoundInversion())) { |
135 | 137 | validated = true; |
136 | | - } else if (checkSetpointOutsidePowerBounds(targetP, minP, maxP, config)) { //Rule 3: when targetP < minP or targetP > maxP if noRequirementIfSetpointOutsidePowerBounds return true |
| 138 | + } else if (isSetpointOutsidePowerBounds(targetP, minP, maxP, config.getThreshold(), config.isNoRequirementIfSetpointOutsidePowerBounds())) { |
137 | 139 | validated = true; |
138 | 140 | } else { |
139 | | - //Rule 4, Rule 5, Rule 6 |
140 | | - validated = checkGeneratorsValues(id, p, q, v, expectedP, targetQ, targetV, voltageRegulatorOn, minQ, maxQ, config); |
| 141 | + validated = validateGeneratorNominalRules(id, p, q, v, expectedP, targetQ, targetV, voltageRegulatorOn, minQ, maxQ, config); |
141 | 142 | } |
142 | 143 | } |
143 | 144 | try { |
@@ -166,73 +167,41 @@ private static double getExpectedP(BalanceTypeGuesser guesser, String id, double |
166 | 167 | } |
167 | 168 | } |
168 | 169 |
|
169 | | - private static boolean checkGeneratorsNaNValues(String id, double p, double q, double targetP, double targetQ) { |
| 170 | + private static boolean validateMissingPQRule(String id, double p, double q, double targetP, double targetQ) { |
170 | 171 | // a validation error should be detected if there is both a voltage and a target but no p or q |
171 | | - if (!Double.isNaN(targetP) && targetP != 0 |
172 | | - || !Double.isNaN(targetQ) && targetQ != 0) { |
| 172 | + if (!Double.isNaN(targetP) && targetP != 0 || !Double.isNaN(targetQ) && targetQ != 0) { |
173 | 173 | LOGGER.warn("{} {}: {}: P={} targetP={} - Q={} targetQ={}", ValidationType.GENERATORS, ValidationUtils.VALIDATION_ERROR, id, p, targetP, q, targetQ); |
174 | 174 | return false; |
175 | 175 | } |
176 | 176 | return true; |
177 | 177 | } |
178 | 178 |
|
179 | | - /** |
180 | | - * Rule4: Active power (p) must match setpoint (expectedP) (within threshold) |
181 | | - * Rule5: if voltageRegulatorOn="false" then reactive power (Q) should match to setpoint (targetQ) (within threshold) |
182 | | - * Rule3: if voltageRegulatorOn="true" then either |
183 | | - * Rule6.1: (minQ/maxQ/targetV) must be defined |
184 | | - * Rule6.2: If V > targetV + threshold, generator (Qgen) must be at min reactive limit |
185 | | - * Rule6.3: If V < targetV - threshold, generator (Qgen) must be at max reactive limit |
186 | | - * Rule6.4: If |V-targetV| <= threshold, generator (Qgen) must be within [minQ, maxQ] |
187 | | - */ |
188 | | - private static boolean checkGeneratorsValues(String id, double p, double q, double v, double expectedP, double targetQ, double targetV, |
| 179 | + private static boolean validateGeneratorNominalRules(String id, double p, double q, double v, double expectedP, double targetQ, double targetV, |
189 | 180 | boolean voltageRegulatorOn, double minQ, double maxQ, ValidationConfig config) { |
190 | 181 | boolean validated = true; |
191 | 182 | double threshold = config.getThreshold(); |
192 | | - // Rule4: Active power (p) must match setpoint (expectedP) (within threshold) |
193 | | - if (ValidationUtils.areNaN(config, expectedP) || Math.abs(p + expectedP) > threshold) { |
194 | | - LOGGER.warn("{} {}: {}: P={} expectedP={}", ValidationType.GENERATORS, ValidationUtils.VALIDATION_ERROR, id, p, expectedP); |
| 183 | + // Rule: Active power p matches expected setpoint |
| 184 | + if (isActivePowerKo(p, expectedP, config, threshold)) { |
| 185 | + LOGGER.warn("{} {}: {}: P={} expectedP={}", |
| 186 | + ValidationType.GENERATORS, ValidationUtils.VALIDATION_ERROR, id, p, expectedP); |
195 | 187 | validated = false; |
196 | 188 | } |
197 | | - //Rule5: if voltageRegulatorOn="false" then reactive power (Q) should match to setpoint (targetQ) (within threshold) |
198 | | - if (!voltageRegulatorOn && (ValidationUtils.areNaN(config, targetQ) || Math.abs(q + targetQ) > threshold)) { |
| 189 | + |
| 190 | + //Rule: If voltage regulator is disabled, Reactive power Q matches targetQ |
| 191 | + if (!voltageRegulatorOn && (areNaN(config, targetQ) || isReactivePowerKo(q, targetQ, threshold))) { |
199 | 192 | LOGGER.warn("{} {}: {}: voltage regulator off - Q={} targetQ={}", ValidationType.GENERATORS, ValidationUtils.VALIDATION_ERROR, id, q, targetQ); |
200 | 193 | validated = false; |
201 | 194 | } |
202 | | - // Rule6, then |
203 | | - // either Rule6.1, Rule6.2, Rule6.3 or Rule6.4 |
204 | | - // |
205 | | - // if voltageRegulatorOn="true" then |
206 | | - // either if minQ/maxQ/targetV are not NaN, |
207 | | - // or q is equal to g.getReactiveLimits().getMinQ(p) and V is higher than g.getTargetV() |
208 | | - // or q is equal to g.getReactiveLimits().getMaxQ(p) and V is lower than g.getTargetV() |
209 | | - // or V at the connected bus is equal to g.getTargetV() and the reactive bounds are satisfied |
| 195 | + |
210 | 196 | double qGen = -q; |
211 | | - if (voltageRegulatorOn |
212 | | - && (ValidationUtils.areNaN(config, minQ, maxQ, targetV) |
213 | | - || v > targetV + threshold && Math.abs(qGen - getMinQ(minQ, maxQ)) > threshold |
214 | | - || v < targetV - threshold && Math.abs(qGen - getMaxQ(minQ, maxQ)) > threshold |
215 | | - || Math.abs(v - targetV) <= threshold && !ValidationUtils.boundedWithin(minQ, maxQ, qGen, threshold))) { |
| 197 | + // Rule: If voltage regulator ON, Reactive power q follow V/targetV logic |
| 198 | + // - qGen at minQ if V > targetV + threshold |
| 199 | + // - qGen at maxQ if V < targetV - threshold |
| 200 | + // - else qGen within [minQ, maxQ]) |
| 201 | + if (voltageRegulatorOn && (ValidationUtils.areNaN(config, minQ, maxQ, targetV) || isVoltageRegulationKo(qGen, v, targetV, minQ, maxQ, threshold))) { |
216 | 202 | LOGGER.warn("{} {}: {}: voltage regulator on - Q={} minQ={} maxQ={} - V={} targetV={}", ValidationType.GENERATORS, ValidationUtils.VALIDATION_ERROR, id, qGen, minQ, maxQ, v, targetV); |
217 | 203 | validated = false; |
218 | 204 | } |
219 | 205 | return validated; |
220 | 206 | } |
221 | | - |
222 | | - private static double getMaxQ(double minQ, double maxQ) { |
223 | | - return Math.max(maxQ, minQ); |
224 | | - } |
225 | | - |
226 | | - private static double getMinQ(double minQ, double maxQ) { |
227 | | - return Math.min(maxQ, minQ); |
228 | | - } |
229 | | - |
230 | | - private static boolean checkReactiveBoundInversion(double minQ, double maxQ, ValidationConfig config) { |
231 | | - return maxQ < minQ - config.getThreshold() && config.isNoRequirementIfReactiveBoundInversion(); |
232 | | - } |
233 | | - |
234 | | - private static boolean checkSetpointOutsidePowerBounds(double targetP, double minP, double maxP, ValidationConfig config) { |
235 | | - return (targetP < minP - config.getThreshold() || targetP > maxP + config.getThreshold()) && config.isNoRequirementIfSetpointOutsidePowerBounds(); |
236 | | - } |
237 | | - |
238 | 207 | } |
0 commit comments