AbstractLfBus.java
/**
* Copyright (c) 2019, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.openloadflow.network.impl;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.util.Evaluable;
import com.powsybl.openloadflow.util.PerUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.function.ToDoubleFunction;
import static com.powsybl.openloadflow.util.EvaluableConstants.NAN;
/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
public abstract class AbstractLfBus extends AbstractElement implements LfBus {
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractLfBus.class);
private static final double Q_DISPATCH_EPSILON = 1e-3;
private static final double PLAUSIBLE_REACTIVE_LIMITS = 1000 / PerUnit.SB;
protected boolean slack = false;
protected boolean reference = false;
protected double v;
protected Evaluable calculatedV = NAN;
protected double angle;
private boolean hasGeneratorsWithSlope;
protected boolean generatorVoltageControlEnabled = false;
protected boolean generatorReactivePowerControlEnabled = false;
protected Double generationTargetP;
protected double generationTargetQ = 0;
protected QLimitType qLimitType;
protected final List<LfGenerator> generators = new ArrayList<>();
protected LfShunt shunt;
protected LfShunt controllerShunt;
protected LfShunt svcShunt;
protected boolean distributedOnConformLoad;
protected final List<LfLoad> loads = new ArrayList<>();
protected Double loadTargetP;
protected Double loadTargetQ;
protected final List<LfBranch> branches = new ArrayList<>();
protected final List<LfHvdc> hvdcs = new ArrayList<>();
private GeneratorVoltageControl generatorVoltageControl;
private GeneratorReactivePowerControl generatorReactivePowerControl;
protected TransformerVoltageControl transformerVoltageControl;
protected ShuntVoltageControl shuntVoltageControl;
protected Evaluable p = NAN;
protected Evaluable q = NAN;
protected double remoteControlReactivePercent = Double.NaN;
protected final Map<LoadFlowModel, LfZeroImpedanceNetwork> zeroImpedanceNetwork = new EnumMap<>(LoadFlowModel.class);
protected LfAsymBus asym;
private LfArea area = null;
protected AbstractLfBus(LfNetwork network, double v, double angle, boolean distributedOnConformLoad) {
super(network);
this.v = v;
this.angle = angle;
this.distributedOnConformLoad = distributedOnConformLoad;
}
@Override
public ElementType getType() {
return ElementType.BUS;
}
@Override
public boolean isSlack() {
network.updateSlackBusesAndReferenceBus();
return slack;
}
@Override
public void setSlack(boolean slack) {
if (slack != this.slack) {
this.slack = slack;
for (LfNetworkListener listener : network.getListeners()) {
listener.onSlackBusChange(this, slack);
}
}
}
@Override
public boolean isReference() {
network.updateSlackBusesAndReferenceBus();
return reference;
}
@Override
public void setReference(boolean reference) {
if (reference != this.reference) {
this.reference = reference;
for (LfNetworkListener listener : network.getListeners()) {
listener.onReferenceBusChange(this, reference);
}
}
}
@Override
public double getTargetP() {
return getGenerationTargetP() - getLoadTargetP();
}
@Override
public double getTargetQ() {
return getGenerationTargetQ() - getLoadTargetQ();
}
@Override
public List<VoltageControl<?>> getVoltageControls() {
List<VoltageControl<?>> voltageControls = new ArrayList<>(3);
getGeneratorVoltageControl().ifPresent(voltageControls::add);
getTransformerVoltageControl().ifPresent(voltageControls::add);
getShuntVoltageControl().ifPresent(voltageControls::add);
return voltageControls;
}
@Override
public boolean isVoltageControlled() {
return isGeneratorVoltageControlled() || isShuntVoltageControlled() || isTransformerVoltageControlled();
}
@Override
public boolean isVoltageControlled(VoltageControl.Type type) {
return switch (type) {
case GENERATOR -> isGeneratorVoltageControlled();
case TRANSFORMER -> isTransformerVoltageControlled();
case SHUNT -> isShuntVoltageControlled();
};
}
@Override
public Optional<VoltageControl<?>> getVoltageControl(VoltageControl.Type type) {
return getVoltageControls().stream().filter(vc -> vc.getType() == type).findAny();
}
@Override
public OptionalDouble getHighestPriorityTargetV() {
return VoltageControl.getHighestPriorityTargetV(this);
}
@Override
public Optional<GeneratorVoltageControl> getGeneratorVoltageControl() {
return Optional.ofNullable(generatorVoltageControl);
}
@Override
public void setGeneratorVoltageControl(GeneratorVoltageControl generatorVoltageControl) {
this.generatorVoltageControl = generatorVoltageControl;
if (generatorVoltageControl != null) {
if (hasGeneratorVoltageControllerCapability()) {
this.generatorVoltageControlEnabled = true;
} else if (!isGeneratorVoltageControlled()) {
throw new PowsyblException("Setting inconsistent voltage control to bus " + getId());
}
} else {
this.generatorVoltageControlEnabled = false;
}
}
private boolean hasGeneratorVoltageControllerCapability() {
return generatorVoltageControl != null && generatorVoltageControl.getControllerElements().contains(this);
}
@Override
public Optional<GeneratorReactivePowerControl> getGeneratorReactivePowerControl() {
return Optional.ofNullable(generatorReactivePowerControl);
}
@Override
public void setGeneratorReactivePowerControl(GeneratorReactivePowerControl generatorReactivePowerControl) {
this.generatorReactivePowerControl = Objects.requireNonNull(generatorReactivePowerControl);
}
@Override
public boolean hasGeneratorReactivePowerControl() {
return generatorReactivePowerControl != null;
}
@Override
public boolean isGeneratorReactivePowerControlEnabled() {
return generatorReactivePowerControlEnabled;
}
@Override
public void setGeneratorReactivePowerControlEnabled(boolean generatorReactivePowerControlEnabled) {
if (this.generatorReactivePowerControlEnabled != generatorReactivePowerControlEnabled) {
this.generatorReactivePowerControlEnabled = generatorReactivePowerControlEnabled;
for (LfNetworkListener listener : network.getListeners()) {
listener.onGeneratorReactivePowerControlChange(this, generatorReactivePowerControlEnabled);
}
}
}
@Override
public boolean isGeneratorVoltageControlled() {
return generatorVoltageControl != null && generatorVoltageControl.getControlledBus() == this;
}
@Override
public List<LfGenerator> getGeneratorsControllingVoltageWithSlope() {
return generators.stream().filter(gen -> gen.getGeneratorControlType() == LfGenerator.GeneratorControlType.VOLTAGE && gen.getSlope() != 0).toList();
}
@Override
public boolean hasGeneratorsWithSlope() {
return hasGeneratorsWithSlope;
}
@Override
public void removeGeneratorSlopes() {
hasGeneratorsWithSlope = false;
generators.forEach(g -> g.setSlope(0));
}
@Override
public boolean isGeneratorVoltageControlEnabled() {
return generatorVoltageControlEnabled;
}
@Override
public void setGeneratorVoltageControlEnabled(boolean generatorVoltageControlEnabled) {
if (this.generatorVoltageControlEnabled != generatorVoltageControlEnabled) {
this.generatorVoltageControlEnabled = generatorVoltageControlEnabled;
for (LfNetworkListener listener : network.getListeners()) {
listener.onGeneratorVoltageControlChange(this, generatorVoltageControlEnabled);
}
}
}
private static LfLoadModel createLfLoadModel(LoadModel loadModel, LfNetworkParameters parameters) {
if (!parameters.isUseLoadModel() || loadModel == null) {
return null;
}
if (loadModel.getType() == LoadModelType.ZIP) {
ZipLoadModel zipLoadModel = (ZipLoadModel) loadModel;
return new LfLoadModel(List.of(new LfLoadModel.ExpTerm(zipLoadModel.getC0p(), 0),
new LfLoadModel.ExpTerm(zipLoadModel.getC1p(), 1),
new LfLoadModel.ExpTerm(zipLoadModel.getC2p(), 2)),
List.of(new LfLoadModel.ExpTerm(zipLoadModel.getC0q(), 0),
new LfLoadModel.ExpTerm(zipLoadModel.getC1q(), 1),
new LfLoadModel.ExpTerm(zipLoadModel.getC2q(), 2)));
} else if (loadModel.getType() == LoadModelType.EXPONENTIAL) {
ExponentialLoadModel expoLoadModel = (ExponentialLoadModel) loadModel;
return new LfLoadModel(List.of(new LfLoadModel.ExpTerm(1, expoLoadModel.getNp())),
List.of(new LfLoadModel.ExpTerm(1, expoLoadModel.getNq())));
} else {
throw new PowsyblException("Unsupported load model: " + loadModel.getType());
}
}
protected LfLoadImpl getOrCreateLfLoad(LoadModel loadModel, LfNetworkParameters parameters) {
LfLoadModel lfLoadModel = createLfLoadModel(loadModel, parameters);
return (LfLoadImpl) loads.stream().filter(l -> Objects.equals(l.getLoadModel().orElse(null), lfLoadModel)).findFirst()
.orElseGet(() -> {
LfLoadImpl l = new LfLoadImpl(AbstractLfBus.this, distributedOnConformLoad, lfLoadModel);
loads.add(l);
return l;
});
}
void addLoad(Load load, LfNetworkParameters parameters) {
getOrCreateLfLoad(load.getModel().orElse(null), parameters).add(load, parameters);
}
void addLccConverterStation(LccConverterStation lccCs, LfNetworkParameters parameters) {
if (!HvdcConverterStations.isHvdcDanglingInIidm(lccCs)) {
// Note: Load is determined statically - contingencies or actions that change an LCC Station connectivity
// will continue to give incorrect result
getOrCreateLfLoad(null, parameters).add(lccCs, parameters);
}
}
protected void add(LfGenerator generator) {
generators.add(generator);
generator.setBus(this);
if (generator.getGeneratorControlType() != LfGenerator.GeneratorControlType.VOLTAGE && !Double.isNaN(generator.getTargetQ())) {
generationTargetQ += generator.getTargetQ();
}
}
void addGenerator(Generator generator, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
add(LfGeneratorImpl.create(generator, network, parameters, report));
}
void addStaticVarCompensator(StaticVarCompensator staticVarCompensator, LfNetworkParameters parameters,
LfNetworkLoadingReport report) {
LfStaticVarCompensatorImpl lfSvc = LfStaticVarCompensatorImpl.create(staticVarCompensator, network, this, parameters, report);
add(lfSvc);
if (lfSvc.getSlope() != 0) {
hasGeneratorsWithSlope = true;
}
if (lfSvc.getB0() != 0) {
svcShunt = LfStandbyAutomatonShunt.create(lfSvc);
lfSvc.setStandByAutomatonShunt(svcShunt);
}
}
void addVscConverterStation(VscConverterStation vscCs, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
add(LfVscConverterStationImpl.create(vscCs, network, parameters, report));
}
void addBattery(Battery generator, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
add(LfBatteryImpl.create(generator, network, parameters, report));
}
void setShuntCompensators(List<ShuntCompensator> shuntCompensators, LfNetworkParameters parameters, LfTopoConfig topoConfig, LfNetworkLoadingReport report) {
if (!parameters.isShuntVoltageControl() && !shuntCompensators.isEmpty()) {
shunt = new LfShuntImpl(shuntCompensators, network, this, false, parameters, topoConfig);
} else {
List<ShuntCompensator> controllerShuntCompensators = new ArrayList<>();
List<ShuntCompensator> fixedShuntCompensators = new ArrayList<>();
shuntCompensators.forEach(sc -> {
if (checkVoltageControl(sc, parameters, report)) {
controllerShuntCompensators.add(sc);
} else {
fixedShuntCompensators.add(sc);
}
});
if (!controllerShuntCompensators.isEmpty()) {
controllerShunt = new LfShuntImpl(controllerShuntCompensators, network, this, true, parameters, topoConfig);
}
if (!fixedShuntCompensators.isEmpty()) {
shunt = new LfShuntImpl(fixedShuntCompensators, network, this, false, parameters, topoConfig);
}
}
}
static boolean checkVoltageControl(ShuntCompensator shuntCompensator, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
double nominalV = shuntCompensator.getRegulatingTerminal().getVoltageLevel().getNominalV();
double targetV = shuntCompensator.getTargetV();
if (!shuntCompensator.isVoltageRegulatorOn()) {
return false;
}
if (!VoltageControl.checkTargetV(targetV / nominalV, nominalV, parameters)) {
LOGGER.trace("Shunt compensator '{}' has an inconsistent target voltage: {} pu: shunt voltage control discarded", shuntCompensator.getId(), targetV);
if (report != null) {
report.shuntsWithInconsistentTargetVoltage++;
}
return false;
}
return true;
}
@Override
public void invalidateGenerationTargetP() {
generationTargetP = null;
}
@Override
public double getGenerationTargetP() {
if (generationTargetP == null) {
generationTargetP = 0.0;
for (LfGenerator generator : generators) {
generationTargetP += generator.getTargetP();
}
}
return generationTargetP;
}
@Override
public double getGenerationTargetQ() {
return generationTargetQ;
}
@Override
public void setGenerationTargetQ(double generationTargetQ) {
if (generationTargetQ != this.generationTargetQ) {
double oldGenerationTargetQ = this.generationTargetQ;
this.generationTargetQ = generationTargetQ;
for (LfNetworkListener listener : network.getListeners()) {
listener.onGenerationReactivePowerTargetChange(this, oldGenerationTargetQ, generationTargetQ);
}
}
}
@Override
public void invalidateLoadTargetP() {
loadTargetP = null;
}
@Override
public double getLoadTargetP() {
if (loadTargetP == null) {
loadTargetP = 0.0;
for (LfLoad load : loads) {
loadTargetP += load.getTargetP() * load.getLoadModel().flatMap(lm -> lm.getExpTermP(0).map(LfLoadModel.ExpTerm::c)).orElse(1d);
}
}
return loadTargetP;
}
@Override
public double getNonFictitiousLoadTargetP() {
return loads.stream()
.mapToDouble(load -> load.getNonFictitiousLoadTargetP() * load.getLoadModel().flatMap(lm -> lm.getExpTermP(0).map(LfLoadModel.ExpTerm::c)).orElse(1d))
.sum();
}
@Override
public void invalidateLoadTargetQ() {
loadTargetQ = null;
}
@Override
public double getLoadTargetQ() {
if (loadTargetQ == null) {
double sum = 0.0;
for (LfLoad load : loads) {
sum += load.getTargetQ() * load.getLoadModel().flatMap(lm -> lm.getExpTermQ(0).map(LfLoadModel.ExpTerm::c)).orElse(1d);
}
loadTargetQ = sum;
}
return loadTargetQ;
}
@Override
public double getMaxP() {
return generators.stream().mapToDouble(LfGenerator::getMaxTargetP).sum();
}
private double getLimitQ(ToDoubleFunction<LfGenerator> limitQ) {
return generators.stream()
.filter(g -> !g.isDisabled())
.mapToDouble(generator -> (generator.getGeneratorControlType() == LfGenerator.GeneratorControlType.VOLTAGE ||
generator.getGeneratorControlType() == LfGenerator.GeneratorControlType.REMOTE_REACTIVE_POWER) ?
limitQ.applyAsDouble(generator) : generator.getTargetQ()).sum();
}
@Override
public double getMinQ() {
return getLimitQ(LfGenerator::getMinQ);
}
@Override
public double getMaxQ() {
return getLimitQ(LfGenerator::getMaxQ);
}
@Override
public Optional<QLimitType> getQLimitType() {
return Optional.ofNullable(this.qLimitType);
}
@Override
public void setQLimitType(QLimitType qLimitType) {
this.qLimitType = qLimitType;
}
@Override
public double getV() {
return v / getNominalV();
}
@Override
public void setV(double v) {
this.v = v * getNominalV();
}
@Override
public Evaluable getCalculatedV() {
return calculatedV;
}
@Override
public void setCalculatedV(Evaluable calculatedV) {
this.calculatedV = Objects.requireNonNull(calculatedV);
}
@Override
public double getAngle() {
return angle;
}
@Override
public void setAngle(double angle) {
this.angle = angle;
}
@Override
public Optional<LfShunt> getShunt() {
return Optional.ofNullable(shunt);
}
@Override
public Optional<LfShunt> getControllerShunt() {
return Optional.ofNullable(controllerShunt);
}
@Override
public Optional<LfShunt> getSvcShunt() {
return Optional.ofNullable(svcShunt);
}
@Override
public List<LfGenerator> getGenerators() {
return generators;
}
@Override
public List<LfLoad> getLoads() {
return loads;
}
@Override
public List<LfBranch> getBranches() {
return branches;
}
@Override
public void addBranch(LfBranch branch) {
branches.add(Objects.requireNonNull(branch));
}
@Override
public List<LfHvdc> getHvdcs() {
return hvdcs;
}
@Override
public void addHvdc(LfHvdc hvdc) {
hvdcs.add(Objects.requireNonNull(hvdc));
}
private static ToDoubleFunction<String> splitDispatchQ(List<LfGenerator> generatorsWithControl, double qToDispatch) {
// proportional to reactive keys if possible,
// or else, fallback on dispatch q proportional to max reactive power range if possible,
// or else, fallback on dispatch q equally (always possible)
return splitDispatchQWithReactiveKeys(generatorsWithControl, qToDispatch)
.orElse(splitDispatchQFromMaxReactivePowerRange(generatorsWithControl, qToDispatch)
.orElse(splitDispatchQEqually(generatorsWithControl, qToDispatch))
);
}
private static ToDoubleFunction<String> splitDispatchQEqually(List<LfGenerator> generatorsWithControl, double qToDispatch) {
int size = generatorsWithControl.size();
return id -> qToDispatch / size;
}
/**
* Dispatch q ensuring a constant k value.
* qToDispatch = q1 + q2 + ...
* we have to find the k value for qToDispatch
* k = (2 * qToDispatch - qmax1 - qmin1 - qmax2 - qmin2 - ...) / (qmax1 - qmin1 + qmax2 - qmin2 + ...)
*/
private static ToDoubleFunction<String> splitDispatchQWithEqualProportionOfK(List<LfGenerator> generatorsWithControl, double qToDispatch) {
double k = 2 * qToDispatch;
double denom = 0;
for (LfGenerator generator : generatorsWithControl) {
k -= generator.getMaxQ() + generator.getMinQ();
denom += generator.getMaxQ() - generator.getMinQ();
}
if (denom != 0) {
k /= denom;
}
Map<String, Double> qToDispatchByGeneratorId = new HashMap<>(generatorsWithControl.size());
for (LfGenerator generator : generatorsWithControl) {
qToDispatchByGeneratorId.put(generator.getId(), LfGenerator.kToQ(k, generator));
}
return qToDispatchByGeneratorId::get;
}
private static Optional<ToDoubleFunction<String>> splitDispatchQWithReactiveKeys(List<LfGenerator> generatorsWithControl, double qToDispatch) {
double sumQkeys = 0;
for (LfGenerator generator : generatorsWithControl) {
double qKey = generator.getRemoteControlReactiveKey().orElse(Double.NaN);
sumQkeys += qKey;
}
if (Double.isNaN(sumQkeys) || sumQkeys == 0.0) {
return Optional.empty();
}
Map<String, Double> qToDispatchByGeneratorId = new HashMap<>(generatorsWithControl.size());
for (LfGenerator generator : generatorsWithControl) {
double qKey = generator.getRemoteControlReactiveKey().orElseThrow();
qToDispatchByGeneratorId.put(generator.getId(), (qKey / sumQkeys) * qToDispatch);
}
return Optional.of(qToDispatchByGeneratorId::get);
}
private static Optional<ToDoubleFunction<String>> splitDispatchQFromMaxReactivePowerRange(List<LfGenerator> generatorsWithControl, double qToDispatch) {
double sumMaxRanges = 0.0;
for (LfGenerator generator : generatorsWithControl) {
if (!generatorHasPlausibleReactiveLimits(generator)) {
return Optional.empty();
}
double maxRangeQ = generator.getRangeQ(LfGenerator.ReactiveRangeMode.MAX);
sumMaxRanges += maxRangeQ;
}
if (sumMaxRanges == 0.0) {
return Optional.empty();
}
Map<String, Double> qToDispatchByGeneratorId = new HashMap<>(generatorsWithControl.size());
for (LfGenerator generator : generatorsWithControl) {
double maxRangeQ = generator.getRangeQ(LfGenerator.ReactiveRangeMode.MAX);
qToDispatchByGeneratorId.put(generator.getId(), (maxRangeQ / sumMaxRanges) * qToDispatch);
}
return Optional.of(qToDispatchByGeneratorId::get);
}
private static boolean generatorHasPlausibleReactiveLimits(LfGenerator generator) {
double minQ = generator.getMinQ();
double maxQ = generator.getMaxQ();
double rangeQ = maxQ - minQ;
return Math.abs(minQ) < PLAUSIBLE_REACTIVE_LIMITS &&
Math.abs(maxQ) < PLAUSIBLE_REACTIVE_LIMITS &&
rangeQ > PlausibleValues.MIN_REACTIVE_RANGE / PerUnit.SB &&
rangeQ < PlausibleValues.MAX_REACTIVE_RANGE / PerUnit.SB;
}
private static boolean allGeneratorsHavePlausibleReactiveLimits(List<LfGenerator> generators) {
return generators.stream().allMatch(AbstractLfBus::generatorHasPlausibleReactiveLimits);
}
protected static double dispatchQ(List<LfGenerator> generatorsWithControl, boolean reactiveLimits,
ReactivePowerDispatchMode reactivePowerDispatchMode, double qToDispatch) {
double residueQ = 0;
if (generatorsWithControl.isEmpty()) {
throw new IllegalArgumentException("the generator list to dispatch Q can not be empty");
}
ToDoubleFunction<String> qToDispatchByGeneratorId = switch (reactivePowerDispatchMode) {
case Q_EQUAL_PROPORTION -> splitDispatchQ(generatorsWithControl, qToDispatch);
case K_EQUAL_PROPORTION -> allGeneratorsHavePlausibleReactiveLimits(generatorsWithControl)
? splitDispatchQWithEqualProportionOfK(generatorsWithControl, qToDispatch)
: splitDispatchQEqually(generatorsWithControl, qToDispatch); // fallback to dispatch q equally
};
Iterator<LfGenerator> itG = generatorsWithControl.iterator();
while (itG.hasNext()) {
LfGenerator generator = itG.next();
double generatorAlreadyCalculatedQ = generator.getCalculatedQ();
double qToDispatchForThisGenerator = qToDispatchByGeneratorId.applyAsDouble(generator.getId());
if (reactiveLimits && qToDispatchForThisGenerator + generatorAlreadyCalculatedQ < generator.getMinQ()) {
residueQ += qToDispatchForThisGenerator + generatorAlreadyCalculatedQ - generator.getMinQ();
generator.setCalculatedQ(generator.getMinQ());
itG.remove();
} else if (reactiveLimits && qToDispatchForThisGenerator + generatorAlreadyCalculatedQ > generator.getMaxQ()) {
residueQ += qToDispatchForThisGenerator + generatorAlreadyCalculatedQ - generator.getMaxQ();
generator.setCalculatedQ(generator.getMaxQ());
itG.remove();
} else {
generator.setCalculatedQ(generatorAlreadyCalculatedQ + qToDispatchForThisGenerator);
}
}
return residueQ;
}
void updateGeneratorsState(double generationQ, boolean reactiveLimits, ReactivePowerDispatchMode reactivePowerDispatchMode) {
double qToDispatch = generationQ;
List<LfGenerator> generatorsThatControlVoltage = new LinkedList<>();
List<LfGenerator> generatorsThatControlReactivePower = new LinkedList<>();
for (LfGenerator generator : generators) {
if (generator.getGeneratorControlType() == LfGenerator.GeneratorControlType.VOLTAGE) {
generatorsThatControlVoltage.add(generator);
} else if (generator.getGeneratorControlType() == LfGenerator.GeneratorControlType.REMOTE_REACTIVE_POWER) {
generatorsThatControlReactivePower.add(generator);
} else {
qToDispatch -= generator.getTargetQ();
}
}
List<LfGenerator> initialGeneratorsThatControlVoltage = new LinkedList<>(generatorsThatControlVoltage);
for (LfGenerator generator : generatorsThatControlVoltage) {
generator.setCalculatedQ(0);
}
while (!generatorsThatControlVoltage.isEmpty() && Math.abs(qToDispatch) > Q_DISPATCH_EPSILON) {
qToDispatch = dispatchQ(generatorsThatControlVoltage, reactiveLimits, reactivePowerDispatchMode, qToDispatch);
}
if (!initialGeneratorsThatControlVoltage.isEmpty() && Math.abs(qToDispatch) > Q_DISPATCH_EPSILON) {
// FIXME
// We have to much reactive power to dispatch, which is linked to a bus that has been forced to remain PV to
// ease the convergence. Updating a generator reactive power outside its reactive limits is a quick fix.
// It could be better to return a global failed status.
dispatchQ(initialGeneratorsThatControlVoltage, false, reactivePowerDispatchMode, qToDispatch);
}
for (LfGenerator generator : generatorsThatControlReactivePower) {
generator.setCalculatedQ(0);
}
while (!generatorsThatControlReactivePower.isEmpty() && Math.abs(qToDispatch) > Q_DISPATCH_EPSILON) {
qToDispatch = dispatchQ(generatorsThatControlReactivePower, reactiveLimits, reactivePowerDispatchMode, qToDispatch);
}
}
@Override
public void updateState(LfNetworkStateUpdateParameters parameters) {
// update generator reactive power
updateGeneratorsState(generatorVoltageControlEnabled || generatorReactivePowerControlEnabled ? (q.eval() + getLoadTargetQ()) : generationTargetQ,
parameters.isReactiveLimits(), parameters.getReactivePowerDispatchMode());
// update load power
for (LfLoad load : loads) {
load.updateState(parameters.isLoadPowerFactorConstant(),
parameters.isBreakers());
}
}
@Override
public Optional<TransformerVoltageControl> getTransformerVoltageControl() {
return Optional.ofNullable(transformerVoltageControl);
}
@Override
public boolean isTransformerVoltageControlled() {
return transformerVoltageControl != null && transformerVoltageControl.getControlledBus() == this;
}
@Override
public void setTransformerVoltageControl(TransformerVoltageControl transformerVoltageControl) {
this.transformerVoltageControl = transformerVoltageControl;
}
@Override
public Optional<ShuntVoltageControl> getShuntVoltageControl() {
return Optional.ofNullable(shuntVoltageControl);
}
@Override
public boolean isShuntVoltageControlled() {
return shuntVoltageControl != null && shuntVoltageControl.getControlledBus() == this;
}
@Override
public void setShuntVoltageControl(ShuntVoltageControl shuntVoltageControl) {
this.shuntVoltageControl = shuntVoltageControl;
}
@Override
public void setDisabled(boolean disabled) {
super.setDisabled(disabled);
if (shunt != null) {
shunt.setDisabled(disabled);
}
if (controllerShunt != null) {
controllerShunt.setDisabled(disabled);
}
for (LfHvdc hvdc : hvdcs) {
if (disabled) {
hvdc.setDisabled(true);
} else if (!hvdc.getOtherBus(this).isDisabled()) {
// if both buses enabled only
hvdc.setDisabled(false);
}
}
}
@Override
public void setP(Evaluable p) {
this.p = Objects.requireNonNull(p);
}
@Override
public Evaluable getP() {
return p;
}
@Override
public void setQ(Evaluable q) {
this.q = Objects.requireNonNull(q);
}
@Override
public Evaluable getQ() {
return q;
}
@Override
public Map<LfBus, List<LfBranch>> findNeighbors() {
Map<LfBus, List<LfBranch>> neighbors = new LinkedHashMap<>(branches.size());
for (LfBranch branch : branches) {
if (branch.isConnectedAtBothSides()) {
LfBus otherBus = branch.getBus1() == this ? branch.getBus2() : branch.getBus1();
neighbors.computeIfAbsent(otherBus, k -> new ArrayList<>())
.add(branch);
}
}
return neighbors;
}
@Override
public double getRemoteControlReactivePercent() {
return remoteControlReactivePercent;
}
@Override
public void setRemoteControlReactivePercent(double remoteControlReactivePercent) {
this.remoteControlReactivePercent = remoteControlReactivePercent;
}
@Override
public double getMismatchP() {
return p.eval() - getTargetP(); // slack bus can also have real injection connected
}
@Override
public void setZeroImpedanceNetwork(LoadFlowModel loadFlowModel, LfZeroImpedanceNetwork zeroImpedanceNetwork) {
Objects.requireNonNull(zeroImpedanceNetwork);
this.zeroImpedanceNetwork.put(loadFlowModel, zeroImpedanceNetwork);
}
@Override
public LfZeroImpedanceNetwork getZeroImpedanceNetwork(LoadFlowModel loadFlowModel) {
return zeroImpedanceNetwork.get(loadFlowModel);
}
@Override
public LfAsymBus getAsym() {
return asym;
}
@Override
public void setAsym(LfAsymBus asym) {
this.asym = asym;
asym.setBus(this);
}
@Override
public Optional<LfArea> getArea() {
return Optional.ofNullable(area);
}
@Override
public void setArea(LfArea area) {
this.area = area;
}
}