Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e2498dc
[3618] Light init WIP
MatthieuSAUR Oct 27, 2025
68bc6a5
[3618] Add VoltageRegulationAdder and Builder
MatthieuSAUR Nov 28, 2025
3eb3f07
[3618] Separate VoltageRegulationAdder and VoltageRegulationBuilder
MatthieuSAUR Dec 3, 2025
91d7440
[3618] Use Interface and implementation for VoltageRegulation
MatthieuSAUR Dec 17, 2025
48d78da
[3618] Remove regulatingPoint from GeneratorImpl
MatthieuSAUR Dec 29, 2025
abbc693
[3618] Use NetworkRef instead of Network
MatthieuSAUR Jan 2, 2026
d37c0ab
[3618] Remove setVoltageRegulation from VoltageRegulationHolder
MatthieuSAUR Jan 2, 2026
110c1c7
[3618] Init VoltageRegulationSerDe
MatthieuSAUR Jan 2, 2026
2f73543
[3618] Add VoltageRegulation.targetValue in iidm for Generator
MatthieuSAUR Jan 5, 2026
aa56800
[3618] Complete xsd files with all attributes of the VoltageRegulatio…
MatthieuSAUR Jan 8, 2026
3018704
[3618] Add regulationMode setter in VoltageRegulation
MatthieuSAUR Jan 8, 2026
ca4329d
[3618] Add some attributes and fix unit test (some test are disabled)
MatthieuSAUR Jan 28, 2026
ead178c
[3618] Add 'targetDeadband' and 'slope' attributes
MatthieuSAUR Jan 28, 2026
6ce4451
[3618] Add 'terminal' into VoltageRegulation xsd and remove 'regulati…
MatthieuSAUR Jan 28, 2026
3401118
[3618] Allow invalid voltageRegulation (TODO add VoltageRegulation va…
MatthieuSAUR Feb 2, 2026
9fde0d9
[3618] Refacto newVoltageRegulation() from Generator itself
MatthieuSAUR Feb 2, 2026
c914d72
[3618] Refacto newVoltageRegulation() (use adderOrBuilder now)
MatthieuSAUR Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
import com.powsybl.iidm.network.GeneratorAdder;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.iidm.network.extensions.CoordinatedReactiveControlAdder;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControlAdder;
import com.powsybl.iidm.network.extensions.*;
import com.powsybl.iidm.network.regulation.RegulationMode;
import com.powsybl.triplestore.api.PropertyBag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -97,7 +97,11 @@ private boolean setRegulatingControlVoltage(String controlId, RegulatingControl
.mapForVoltageControl(control.cgmesTerminal, context)
.orElse(gen.getTerminal());

gen.setRegulatingTerminal(regulatingTerminal);
gen.newVoltageRegulation()
.withMode(RegulationMode.VOLTAGE)
.withTerminal(regulatingTerminal)
.withRegulating(false)
.add();

// add qPercent as an extension
if (!Double.isNaN(qPercent)) {
Expand All @@ -122,10 +126,11 @@ private boolean setRegulatingControlReactivePower(String controlId, RegulatingCo
return false;
}

gen.newExtension(RemoteReactivePowerControlAdder.class)
.withRegulatingTerminal(mappedRegulatingTerminal.getTerminal())
.withEnabled(false)
.add();
gen.newVoltageRegulation()
.withTerminal(mappedRegulatingTerminal.getTerminal())
.withMode(RegulationMode.REACTIVE_POWER)
.withRegulating(false)
.add();

// add qPercent as an extension
if (!Double.isNaN(qPercent)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import com.powsybl.cgmes.conversion.Conversion;
import com.powsybl.cgmes.model.CgmesNames;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl;
import com.powsybl.iidm.network.regulation.RegulationMode;
import com.powsybl.iidm.network.regulation.VoltageRegulation;
import com.powsybl.triplestore.api.PropertyBag;
import com.powsybl.triplestore.api.PropertyBags;

Expand Down Expand Up @@ -190,64 +191,66 @@ private static void updateRegulatingControlVoltage(Generator generator, Boolean
// to ensure consistency with the applied checks
private static void setVoltageRegulation(Generator generator, double targetV, boolean regulatingOn) {
if (regulatingOn) {
generator
.setTargetV(targetV)
.setVoltageRegulatorOn(true);
VoltageRegulation voltageRegulation = generator.getVoltageRegulation();
voltageRegulation.setTargetValue(targetV);
voltageRegulation.setMode(RegulationMode.VOLTAGE);
voltageRegulation.setRegulating(true);
} else {
generator
.setVoltageRegulatorOn(false)
.setTargetV(targetV);
VoltageRegulation voltageRegulation = generator.getVoltageRegulation();
voltageRegulation.setTargetValue(targetV);
voltageRegulation.setMode(RegulationMode.VOLTAGE);
voltageRegulation.setRegulating(false);
}
}

private static void updateRegulatingControlReactivePower(Generator generator, Boolean controlEnabled, Context context) {
RemoteReactivePowerControl remoteReactivePowerControl = generator.getExtension(RemoteReactivePowerControl.class);
if (remoteReactivePowerControl == null || remoteReactivePowerControl.getRegulatingTerminal() == null) {
VoltageRegulation voltageRegulation = generator.getVoltageRegulation();
if (voltageRegulation == null || voltageRegulation.getTerminal() == null || voltageRegulation.getMode() != RegulationMode.REACTIVE_POWER) {
return;
}
Optional<PropertyBag> cgmesRegulatingControl = findCgmesRegulatingControl(generator, context);
int terminalSign = findTerminalSign(generator);
double defaultTargetQ = getDefaultTargetQ(remoteReactivePowerControl, context);
boolean defaultRegulatingOn = getDefaultRegulatingOn(remoteReactivePowerControl, context);
double defaultTargetQ = getDefaultTargetQ(voltageRegulation.getTargetValue(), context);
boolean defaultRegulatingOn = getDefaultRegulatingOn(voltageRegulation.isRegulating(), context);
boolean updatedControlEnabled = controlEnabled != null ? controlEnabled : defaultRegulatingOn;

double targetQ = cgmesRegulatingControl.map(propertyBag -> findTargetQ(propertyBag, terminalSign, defaultTargetQ, DefaultValueUse.NOT_DEFINED)).orElse(defaultTargetQ);
boolean regulatingOn = cgmesRegulatingControl.map(propertyBag -> findRegulatingOn(propertyBag, defaultRegulatingOn, DefaultValueUse.NOT_DEFINED)).orElse(defaultRegulatingOn);

setReactivePowerRegulation(remoteReactivePowerControl, targetQ, regulatingOn && updatedControlEnabled && isValidTargetQ(targetQ));
setReactivePowerRegulation(voltageRegulation, targetQ, regulatingOn && updatedControlEnabled && isValidTargetQ(targetQ));
}

// TargetQ must be valid before the regulation is turned on,
// and the regulation must be turned off before assigning potentially invalid regulation values,
// to ensure consistency with the applied checks
private static void setReactivePowerRegulation(RemoteReactivePowerControl remoteReactivePowerControl, double targetQ, boolean regulatingOn) {
private static void setReactivePowerRegulation(VoltageRegulation voltageRegulation, double targetQ, boolean regulatingOn) {
if (regulatingOn) {
remoteReactivePowerControl
.setTargetQ(targetQ)
.setEnabled(true);
voltageRegulation.setTargetValue(targetQ);
voltageRegulation.setRegulating(true);
} else {
remoteReactivePowerControl
.setEnabled(false)
.setTargetQ(targetQ);
voltageRegulation.setTargetValue(targetQ);
voltageRegulation.setRegulating(false);
}
}

private static double getDefaultTargetV(Generator generator, Context context) {
double defaultTargetV = Optional.ofNullable(generator.getRegulatingTerminal())
.orElse(generator.getTerminal())
.getVoltageLevel().getNominalV();
return getDefaultValue(null, generator.getTargetV(), defaultTargetV, Double.NaN, context);
double targetVGenerator = generator.getVoltageRegulation() != null && generator.getVoltageRegulation().getMode() == RegulationMode.VOLTAGE ?
generator.getVoltageRegulation().getTargetValue() : generator.getTargetV();
return getDefaultValue(null, targetVGenerator, defaultTargetV, Double.NaN, context);
}

private static boolean getDefaultRegulatingOn(Generator generator, Context context) {
return getDefaultValue(false, generator.isVoltageRegulatorOn(), false, false, context);
}

private static double getDefaultTargetQ(RemoteReactivePowerControl remoteReactivePowerControl, Context context) {
return getDefaultValue(null, remoteReactivePowerControl.getTargetQ(), Double.NaN, Double.NaN, context);
private static double getDefaultTargetQ(double previousValue, Context context) {
return getDefaultValue(null, previousValue, Double.NaN, Double.NaN, context);
}

private static boolean getDefaultRegulatingOn(RemoteReactivePowerControl remoteReactivePowerControl, Context context) {
return getDefaultValue(false, remoteReactivePowerControl.isEnabled(), false, false, context);
private static boolean getDefaultRegulatingOn(boolean previousValue, Context context) {
return getDefaultValue(false, previousValue, false, false, context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.powsybl.cgmes.model.CgmesTerminal;
import com.powsybl.cgmes.model.PowerFlow;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.regulation.RegulationMode;
import com.powsybl.iidm.network.regulation.VoltageRegulation;
import com.powsybl.triplestore.api.PropertyBag;

import java.util.Optional;
Expand Down Expand Up @@ -129,9 +131,18 @@ public static void update(Generator generator, PropertyBag cgmesData, Context co
boolean regulatingOn = findRegulatingOn(cgmesData, CgmesNames.REGULATION_STATUS, defaultRegulatingOn, DefaultValueUse.NOT_DEFINED);

generator.setTargetP(getTargetP(updatedPowerFlow, generator, context))
.setTargetQ(getTargetQ(updatedPowerFlow, generator, context))
.setTargetV(targetV)
.setVoltageRegulatorOn(regulatingOn && regulationCapability && isValidTargetV(targetV));
.setTargetQ(getTargetQ(updatedPowerFlow, generator, context));
// .setTargetV(targetV)
// TODO MSA how to regulate with reactive mode
// .getVoltageRegulation().setRegulating(regulatingOn && regulationCapability && isValidTargetV(targetV))
// .setVoltageRegulatorOn(regulatingOn && regulationCapability && isValidTargetV(targetV));
VoltageRegulation voltageRegulation = generator.getVoltageRegulation();
if (voltageRegulation == null) {
voltageRegulation = generator.newVoltageRegulation().add().getVoltageRegulation();
}
voltageRegulation.setMode(RegulationMode.VOLTAGE);
voltageRegulation.setTargetValue(targetV);
voltageRegulation.setRegulating(regulatingOn && regulationCapability && isValidTargetV(targetV));
}

private static double getTargetP(PowerFlow updatedPowerFlow, Generator generator, Context context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl;
import com.powsybl.triplestore.api.PropertyBag;
import org.apache.commons.lang3.tuple.Pair;

Expand Down Expand Up @@ -478,8 +477,9 @@ private void addIidmMappingsGenerators(Network network) {
}

private static boolean hasRegulatingControlCapability(Generator generator) {
return generator.getExtension(RemoteReactivePowerControl.class) != null
|| !Double.isNaN(generator.getTargetV()) && hasReactiveCapability(generator);
return generator.getVoltageRegulation() != null
&& generator.getVoltageRegulation().isRegulating()
&& hasReactiveCapability(generator);
}

private static boolean hasReactiveCapability(Generator generator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.LoadDetail;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -540,25 +539,37 @@ public static boolean isValidReactivePowerSetpoint(double q) {
return Double.isFinite(q);
}

public static String getGeneratorRegulatingControlMode(Generator generator, RemoteReactivePowerControl rrpc) {
if (rrpc == null) {
return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
}
boolean enabledVoltageControl = generator.isVoltageRegulatorOn();
boolean enabledReactivePowerControl = rrpc.isEnabled();

if (enabledVoltageControl) {
return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
} else if (enabledReactivePowerControl) {
return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER;
} else {
boolean validVoltageSetpoint = isValidVoltageSetpoint(generator.getTargetV());
boolean validReactiveSetpoint = isValidReactivePowerSetpoint(rrpc.getTargetQ());
if (validReactiveSetpoint && !validVoltageSetpoint) {
return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER;
}
return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
public static String getGeneratorRegulatingControlMode(Generator generator) {
// TODO MSA add isValidVoltageSetpoint and isValidReactivePowerSetpoint
if (generator.getVoltageRegulation() != null) {
return switch (generator.getVoltageRegulation().getMode()) {
case REACTIVE_POWER ->
RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER;
case VOLTAGE, REACTIVE_POWER_PER_ACTIVE_POWER, VOLTAGE_PER_REACTIVE_POWER ->
RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
};
}
return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;

//
// if (rrpc == null) {
// return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
// }
// boolean enabledVoltageControl = generator.isVoltageRegulatorOn();
// boolean enabledReactivePowerControl = rrpc.isEnabled();
//
// if (enabledVoltageControl) {
// return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
// } else if (enabledReactivePowerControl) {
// return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER;
// } else {
// boolean validVoltageSetpoint = isValidVoltageSetpoint(generator.getTargetV());
// boolean validReactiveSetpoint = isValidReactivePowerSetpoint(rrpc.getTargetQ());
// if (validReactiveSetpoint && !validVoltageSetpoint) {
// return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER;
// }
// return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE;
// }
}

public static String getSvcMode(StaticVarCompensator svc) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import com.powsybl.commons.exceptions.UncheckedXmlStreamException;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl;
import com.powsybl.iidm.network.extensions.VoltagePerReactivePowerControl;

import com.powsybl.math.graph.TraverseResult;
Expand Down Expand Up @@ -361,11 +360,10 @@ private static void writeGenerators(Network network, Map<Terminal, String> mapTe
Set<String> generatingUnitsWritten = new HashSet<>();
for (Generator generator : network.getGenerators()) {
String cgmesOriginalClass = generator.getProperty(Conversion.PROPERTY_CGMES_ORIGINAL_CLASS, CgmesNames.SYNCHRONOUS_MACHINE);
RemoteReactivePowerControl rrpc = generator.getExtension(RemoteReactivePowerControl.class);
String mode = CgmesExportUtil.getGeneratorRegulatingControlMode(generator, rrpc);
String mode = CgmesExportUtil.getGeneratorRegulatingControlMode(generator);
Terminal regulatingTerminal;
if (mode.equals(RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER)) {
regulatingTerminal = rrpc.getRegulatingTerminal();
regulatingTerminal = generator.getRegulatingTerminal();
} else if (context.isExportGeneratorsInLocalRegulationMode()) {
regulatingTerminal = generator.getTerminal();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.ActivePowerControl;
import com.powsybl.iidm.network.extensions.ReferencePriority;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl;
import com.powsybl.iidm.network.extensions.VoltageRegulation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -413,14 +412,13 @@ private static void addRegulatingControlView(Generator g, Map<String, List<Regul
double target;
String targetValueUnitMultiplier;
boolean enabled;
RemoteReactivePowerControl rrpc = g.getExtension(RemoteReactivePowerControl.class);
String generatorMode = CgmesExportUtil.getGeneratorRegulatingControlMode(g, rrpc);
String generatorMode = CgmesExportUtil.getGeneratorRegulatingControlMode(g);
if (generatorMode.equals(RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER)) {
target = rrpc.getTargetQ();
target = g.getVoltageRegulation().getTargetValue();
targetValueUnitMultiplier = "M";
enabled = rrpc.isEnabled();
enabled = g.getVoltageRegulation().isRegulating();
} else {
target = g.getTargetV();
target = g.getVoltageRegulation().getTargetValue();
if (context.isExportGeneratorsInLocalRegulationMode()) {
double remoteNominalV = g.getRegulatingTerminal().getVoltageLevel().getNominalV();
double localNominalV = g.getTerminal().getVoltageLevel().getNominalV();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ private static void assertSsh(Generator generator, double targetP, double target
double tol = 0.0000001;
assertEquals(targetP, generator.getTargetP(), tol);
assertEquals(targetQ, generator.getTargetQ(), tol);
assertEquals(targetV, generator.getTargetV(), tol);
double targetValue = generator.getVoltageRegulation() != null ? generator.getVoltageRegulation().getTargetValue() : generator.getTargetV();
assertEquals(targetV, targetValue, tol);
assertEquals(isRegulatingOn, generator.isVoltageRegulatorOn());

ActivePowerControl<Generator> activePowerControl = generator.getExtension(ActivePowerControl.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.powsybl.cgmes.conversion.Conversion;
import com.powsybl.cgmes.model.GridModelReference;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.regulation.RegulationMode;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
Expand Down Expand Up @@ -51,7 +52,8 @@ void microGridBaseCaseRegulatingTerminalsDefinedOnSwitches() {
terminal = gen.getTerminal();
assertEquals(terminal, regulatingTerminal);

regulationValue = gen.getTargetV();
assertEquals(RegulationMode.VOLTAGE, gen.getVoltageRegulation().getMode());
regulationValue = gen.getVoltageRegulation().getTargetValue();
assertEquals(21.987, regulationValue, 0.0);
}

Expand Down Expand Up @@ -82,7 +84,8 @@ void microGridBaseBECaseRegulatingTerminalsDefinedOnSwitches() {
terminal = gen.getTerminal();
assertEquals(terminal, regulatingTerminal);

regulationValue = gen.getTargetV();
assertEquals(RegulationMode.VOLTAGE, gen.getVoltageRegulation().getMode());
regulationValue = gen.getVoltageRegulation().getTargetValue();
assertEquals(21.987, regulationValue, 0.0);
}

Expand Down
Loading