Skip to content

Commit

Permalink
Secondary voltage control: add voltage sensi epsilon parameter (#796)
Browse files Browse the repository at this point in the history
Signed-off-by: Geoffroy Jamgotchian <geoffroy.jamgotchian@gmail.com>
(cherry picked from commit 08c7a75)
  • Loading branch information
geofjamg authored and jeandemanged committed Jun 16, 2023
1 parent e1ee283 commit d5975a7
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ public List<AcOuterLoop> configure(LoadFlowParameters parameters, OpenLoadFlowPa
}
// secondary voltage control
if (parametersExt.isSecondaryVoltageControl()) {
outerLoops.add(new SecondaryVoltageControlOuterLoop());
outerLoops.add(new SecondaryVoltageControlOuterLoop(parametersExt.getControllerToPilotPointVoltageSensiEpsilon(),
parametersExt.getMinPlausibleTargetVoltage(),
parametersExt.getMaxPlausibleTargetVoltage()));
}
return outerLoops;
}
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.powsybl.openloadflow.ac.outerloop.AcOuterLoop;
import com.powsybl.openloadflow.ac.outerloop.IncrementalTransformerVoltageControlOuterLoop;
import com.powsybl.openloadflow.ac.outerloop.ReactiveLimitsOuterLoop;
import com.powsybl.openloadflow.ac.outerloop.SecondaryVoltageControlOuterLoop;
import com.powsybl.openloadflow.dc.DcLoadFlowParameters;
import com.powsybl.openloadflow.dc.DcValueVoltageInitializer;
import com.powsybl.openloadflow.dc.equations.DcEquationSystemCreationParameters;
Expand Down Expand Up @@ -171,6 +172,8 @@ public class OpenLoadFlowParameters extends AbstractExtension<LoadFlowParameters

public static final String SECONDARY_VOLTAGE_CONTROL_PARAM_NAME = "secondaryVoltageControl";

public static final String CONTROLLER_TO_PILOT_POINT_VOLTAGE_SENSI_EPSILON_PARAM_NAME = "controllerToPilotPointVoltageSensiEpsilon";

public static final String REACTIVE_LIMITS_MAX_SWITCH_PQ_PV_PARAM_NAME = "reactiveLimitsMaxPqPvSwitch";

public static final String PHASE_SHIFTER_CONTROL_MODE_PARAM_NAME = "phaseShifterControlMode";
Expand Down Expand Up @@ -221,6 +224,7 @@ private static <E extends Enum<E>> List<Object> getEnumPossibleValues(Class<E> e
new Parameter(DEBUG_DIR_PARAM_NAME, ParameterType.STRING, "Directory to dump debug files", LfNetworkParameters.DEBUG_DIR_DEFAULT_VALUE, Collections.emptyList(), ParameterScope.TECHNICAL),
new Parameter(INCREMENTAL_TRANSFORMER_VOLTAGE_CONTROL_OUTER_LOOP_MAX_TAP_SHIFT_PARAM_NAME, ParameterType.INTEGER, "Incremental transformer voltage control maximum tap shift per outer loop", IncrementalTransformerVoltageControlOuterLoop.DEFAULT_MAX_TAP_SHIFT),
new Parameter(SECONDARY_VOLTAGE_CONTROL_PARAM_NAME, ParameterType.BOOLEAN, "Secondary voltage control simulation", LfNetworkParameters.SECONDARY_VOLTAGE_CONTROL_DEFAULT_VALUE),
new Parameter(CONTROLLER_TO_PILOT_POINT_VOLTAGE_SENSI_EPSILON_PARAM_NAME, ParameterType.DOUBLE, "Secondary voltage control, controller unit to pilot point voltage minimal sensitivity", SecondaryVoltageControlOuterLoop.SENSI_V_V_EPS_DEFAULT_VALUE),
new Parameter(REACTIVE_LIMITS_MAX_SWITCH_PQ_PV_PARAM_NAME, ParameterType.INTEGER, "Reactive limits maximum Pq Pv switch", ReactiveLimitsOuterLoop.MAX_SWITCH_PQ_PV),
new Parameter(NEWTONRAPHSON_STOPPING_CRITERIA_TYPE_PARAM_NAME, ParameterType.STRING, "Newton raphson stopping criteria type", NEWTONRAPHSON_STOPPING_CRITERIA_TYPE_DEFAULT_VALUE.name(), getEnumPossibleValues(NewtonRaphsonStoppingCriteriaType.class)),
new Parameter(MAX_ACTIVE_POWER_MISMATCH_PARAM_NAME, ParameterType.DOUBLE, "Maximum active power for per equation stopping criteria", MAX_ACTIVE_POWER_MISMATCH_DEFAULT_VALUE),
Expand Down Expand Up @@ -351,6 +355,8 @@ public enum ReactiveRangeCheckMode {

private boolean secondaryVoltageControl = LfNetworkParameters.SECONDARY_VOLTAGE_CONTROL_DEFAULT_VALUE;

private double controllerToPilotPointVoltageSensiEpsilon = SecondaryVoltageControlOuterLoop.SENSI_V_V_EPS_DEFAULT_VALUE;

private int reactiveLimitsMaxPqPvSwitch = ReactiveLimitsOuterLoop.MAX_SWITCH_PQ_PV;

private PhaseShifterControlMode phaseShifterControlMode = PHASE_SHIFTER_CONTROL_MODE_DEFAULT_VALUE;
Expand Down Expand Up @@ -716,6 +722,15 @@ public OpenLoadFlowParameters setSecondaryVoltageControl(boolean secondaryVoltag
return this;
}

public double getControllerToPilotPointVoltageSensiEpsilon() {
return controllerToPilotPointVoltageSensiEpsilon;
}

public OpenLoadFlowParameters setControllerToPilotPointVoltageSensiEpsilon(double controllerToPilotPointVoltageSensiEpsilon) {
this.controllerToPilotPointVoltageSensiEpsilon = controllerToPilotPointVoltageSensiEpsilon;
return this;
}

public String getDebugDir() {
return debugDir;
}
Expand Down Expand Up @@ -866,6 +881,7 @@ public static OpenLoadFlowParameters load(PlatformConfig platformConfig) {
.setDebugDir(config.getStringProperty(DEBUG_DIR_PARAM_NAME, LfNetworkParameters.DEBUG_DIR_DEFAULT_VALUE))
.setIncrementalTransformerVoltageControlOuterLoopMaxTapShift(config.getIntProperty(INCREMENTAL_TRANSFORMER_VOLTAGE_CONTROL_OUTER_LOOP_MAX_TAP_SHIFT_PARAM_NAME, IncrementalTransformerVoltageControlOuterLoop.DEFAULT_MAX_TAP_SHIFT))
.setSecondaryVoltageControl(config.getBooleanProperty(SECONDARY_VOLTAGE_CONTROL_PARAM_NAME, LfNetworkParameters.SECONDARY_VOLTAGE_CONTROL_DEFAULT_VALUE))
.setControllerToPilotPointVoltageSensiEpsilon(config.getDoubleProperty(CONTROLLER_TO_PILOT_POINT_VOLTAGE_SENSI_EPSILON_PARAM_NAME, SecondaryVoltageControlOuterLoop.SENSI_V_V_EPS_DEFAULT_VALUE))
.setReactiveLimitsMaxPqPvSwitch(config.getIntProperty(REACTIVE_LIMITS_MAX_SWITCH_PQ_PV_PARAM_NAME, ReactiveLimitsOuterLoop.MAX_SWITCH_PQ_PV))
.setPhaseShifterControlMode(config.getEnumProperty(PHASE_SHIFTER_CONTROL_MODE_PARAM_NAME, PhaseShifterControlMode.class, PHASE_SHIFTER_CONTROL_MODE_DEFAULT_VALUE))
.setAlwaysUpdateNetwork(config.getBooleanProperty(ALWAYS_UPDATE_NETWORK_PARAM_NAME, NewtonRaphsonParameters.ALWAYS_UPDATE_NETWORK_DEFAULT_VALUE))
Expand Down Expand Up @@ -960,6 +976,8 @@ public OpenLoadFlowParameters update(Map<String, String> properties) {
.ifPresent(prop -> this.setIncrementalTransformerVoltageControlOuterLoopMaxTapShift(Integer.parseInt(prop)));
Optional.ofNullable(properties.get(SECONDARY_VOLTAGE_CONTROL_PARAM_NAME))
.ifPresent(prop -> this.setSecondaryVoltageControl(Boolean.parseBoolean(prop)));
Optional.ofNullable(properties.get(CONTROLLER_TO_PILOT_POINT_VOLTAGE_SENSI_EPSILON_PARAM_NAME))
.ifPresent(prop -> this.setControllerToPilotPointVoltageSensiEpsilon(Double.parseDouble(prop)));
Optional.ofNullable(properties.get(REACTIVE_LIMITS_MAX_SWITCH_PQ_PV_PARAM_NAME))
.ifPresent(prop -> this.setReactiveLimitsMaxPqPvSwitch(Integer.parseInt(prop)));
Optional.ofNullable(properties.get(PHASE_SHIFTER_CONTROL_MODE_PARAM_NAME))
Expand Down Expand Up @@ -1022,6 +1040,7 @@ public Map<String, Object> toMap() {
map.put(DEBUG_DIR_PARAM_NAME, debugDir);
map.put(INCREMENTAL_TRANSFORMER_VOLTAGE_CONTROL_OUTER_LOOP_MAX_TAP_SHIFT_PARAM_NAME, incrementalTransformerVoltageControlOuterLoopMaxTapShift);
map.put(SECONDARY_VOLTAGE_CONTROL_PARAM_NAME, secondaryVoltageControl);
map.put(CONTROLLER_TO_PILOT_POINT_VOLTAGE_SENSI_EPSILON_PARAM_NAME, controllerToPilotPointVoltageSensiEpsilon);
map.put(REACTIVE_LIMITS_MAX_SWITCH_PQ_PV_PARAM_NAME, reactiveLimitsMaxPqPvSwitch);
map.put(PHASE_SHIFTER_CONTROL_MODE_PARAM_NAME, phaseShifterControlMode);
map.put(ALWAYS_UPDATE_NETWORK_PARAM_NAME, alwaysUpdateNetwork);
Expand Down Expand Up @@ -1330,6 +1349,7 @@ public static boolean equals(LoadFlowParameters parameters1, LoadFlowParameters
Objects.equals(extension1.getDebugDir(), extension2.getDebugDir()) &&
extension1.getIncrementalTransformerVoltageControlOuterLoopMaxTapShift() == extension2.getIncrementalTransformerVoltageControlOuterLoopMaxTapShift() &&
extension1.isSecondaryVoltageControl() == extension2.isSecondaryVoltageControl() &&
extension1.getControllerToPilotPointVoltageSensiEpsilon() == extension2.getControllerToPilotPointVoltageSensiEpsilon() &&
extension1.getReactiveLimitsMaxPqPvSwitch() == extension2.getReactiveLimitsMaxPqPvSwitch() &&
extension1.getPhaseShifterControlMode() == extension2.getPhaseShifterControlMode() &&
extension1.isAlwaysUpdateNetwork() == extension2.isAlwaysUpdateNetwork() &&
Expand Down Expand Up @@ -1393,6 +1413,7 @@ public static LoadFlowParameters clone(LoadFlowParameters parameters) {
.setDebugDir(extension.getDebugDir())
.setIncrementalTransformerVoltageControlOuterLoopMaxTapShift(extension.getIncrementalTransformerVoltageControlOuterLoopMaxTapShift())
.setSecondaryVoltageControl(extension.isSecondaryVoltageControl())
.setControllerToPilotPointVoltageSensiEpsilon(extension.getControllerToPilotPointVoltageSensiEpsilon())
.setReactiveLimitsMaxPqPvSwitch(extension.getReactiveLimitsMaxPqPvSwitch())
.setPhaseShifterControlMode(extension.getPhaseShifterControlMode())
.setAlwaysUpdateNetwork(extension.isAlwaysUpdateNetwork())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,22 @@ public class SecondaryVoltageControlOuterLoop implements AcOuterLoop {

private static final Logger LOGGER = LoggerFactory.getLogger(SecondaryVoltageControlOuterLoop.class);

public static final double SENSI_V_V_EPS_DEFAULT_VALUE = 1e-2;

private static final double TARGET_V_DIFF_EPS = 1e-1; // in Kv
private static final double SENSI_V_EPS = 1e-3;
private static final double DQ_PU_EPS = 1d / PerUnit.SB; // in PU

private final double sensiVvEps;

private final double minPlausibleTargetVoltage;

private final double maxPlausibleTargetVoltage;

public SecondaryVoltageControlOuterLoop(double sensiVvEps, double minPlausibleTargetVoltage, double maxPlausibleTargetVoltage) {
this.sensiVvEps = sensiVvEps;
this.minPlausibleTargetVoltage = minPlausibleTargetVoltage;
this.maxPlausibleTargetVoltage = maxPlausibleTargetVoltage;
}

@Override
public String getType() {
Expand All @@ -53,14 +67,18 @@ private static List<LfBus> getControllerBuses(LfBus controlledBus) {
.collect(Collectors.toList());
}

private static void findActiveSecondaryVoltageControls(LfNetwork network, Map<LfSecondaryVoltageControl, List<LfBus>> activeSecondaryVoltageControls,
Set<LfBus> allBusSet) {
private void findActiveSecondaryVoltageControls(LfNetwork network, Map<LfSecondaryVoltageControl, List<LfBus>> activeSecondaryVoltageControls,
Set<LfBus> allBusSet) {
List<LfSecondaryVoltageControl> secondaryVoltageControls = network.getSecondaryVoltageControls().stream()
.filter(control -> !control.getPilotBus().isDisabled())
.collect(Collectors.toList());
for (LfSecondaryVoltageControl secondaryVoltageControl : secondaryVoltageControls) {
List<LfBus> activeControlledBuses = secondaryVoltageControl.getControlledBuses().stream()
.filter(SecondaryVoltageControlOuterLoop::isValid)
.filter(controlledBus -> {
double targetV = controlledBus.getGeneratorVoltageControl().orElseThrow().getTargetValue();
return targetV > minPlausibleTargetVoltage && targetV < maxPlausibleTargetVoltage;
})
.collect(Collectors.toList());
if (!activeControlledBuses.isEmpty()) {
activeSecondaryVoltageControls.put(secondaryVoltageControl, activeControlledBuses);
Expand Down Expand Up @@ -197,7 +215,7 @@ static class SensiVq {
* - controlled bus voltage to controller bus reactive power injection sensitivity.
* - pilot bus voltage to controller bus reactive power injection sensitivity.
*/
private static SensiVq calculateSensiVq(SensitivityContext sensitivityContext, List<LfBus> controlledBuses, double[] svi) {
private SensiVq calculateSensiVq(String zoneName, SensitivityContext sensitivityContext, List<LfBus> controlledBuses, double[] svi) {
// get list of all controllers of the zone
List<LfBus> allControllerBuses = controlledBuses.stream()
.flatMap(controlledBus -> getControllerBuses(controlledBus).stream())
Expand All @@ -206,10 +224,11 @@ private static SensiVq calculateSensiVq(SensitivityContext sensitivityContext, L

double[] si = new double[allControllerBuses.size()];
double[] sqi = new double[allControllerBuses.size()];
List<String> filteredControlledBusIds = new ArrayList<>();
for (int i = 0; i < controlledBuses.size(); i++) {
LfBus controlledBus = controlledBuses.get(i);
// we need to filter very small sensitivity to avoid large target v shift
if (Math.abs(svi[i]) > SENSI_V_EPS) {
if (Math.abs(svi[i]) > sensiVvEps) {
for (LfBus controllerBus : getControllerBuses(controlledBus)) {
double sq = sensitivityContext.calculateSensiVq(controllerBus);
int j = controllerBusIndex.get(controllerBus.getNum());
Expand All @@ -218,9 +237,16 @@ private static SensiVq calculateSensiVq(SensitivityContext sensitivityContext, L
si[j] = svi[i] / sq;
}
}
} else {
filteredControlledBusIds.add(controlledBus.getId());
}
}

if (!filteredControlledBusIds.isEmpty()) {
LOGGER.trace("Target voltage of controlled buses {} of zone '{}' won't be adjusted because sensitivity on pilot point voltage is too small",
filteredControlledBusIds, zoneName);
}

return new SensiVq(sqi, si, controllerBusIndex);
}

Expand Down Expand Up @@ -248,37 +274,55 @@ private static SensiVq calculateSensiVq(SensitivityContext sensitivityContext, L
* dvi = dq / sqi
* </pre>
*/
private static void adjustPrimaryVoltageControlTargets(String zoneName, SensitivityContext sensitivityContext,
List<LfBus> controlledBuses, LfBus pilotBus, double pilotDv) {
private boolean adjustPrimaryVoltageControlTargets(String zoneName, SensitivityContext sensitivityContext,
List<LfBus> controlledBuses, LfBus pilotBus, double pilotDv) {
boolean adjusted = false;

// calculate sensitivity of controlled buses voltage to pilot bus voltage
double[] svi = sensitivityContext.calculateSensiVv(controlledBuses, pilotBus);

// calculate sensitivity of controlled buses voltage to controller buses reactive power injection
// and sensitivity of controller bus reactive power injection to pilot bus voltage
var sensiVq = calculateSensiVq(sensitivityContext, controlledBuses, svi);

// supposing we want all controllers to shift to same amount of reactive power
double dq = pilotDv / Arrays.stream(sensiVq.si).sum();

LOGGER.trace("Control units of zone '{}' need to be adjusted of {} MVar", zoneName, dq * PerUnit.SB);

for (LfBus controlledBus : controlledBuses) {
double pvcDv = 0;
for (LfBus controllerBus : getControllerBuses(controlledBus)) {
double sqi = sensiVq.getSqi(controllerBus);
if (sqi != 0) {
pvcDv += dq / sqi;
var sensiVq = calculateSensiVq(zoneName, sensitivityContext, controlledBuses, svi);

double siSum = Arrays.stream(sensiVq.si).sum();
if (siSum != 0) {
// supposing we want all controllers to shift to same amount of reactive power
double dq = pilotDv / siSum;
if (Math.abs(dq) > DQ_PU_EPS) {
LOGGER.trace("Each of the controller buses of zone '{}' need to be adjusted of {} MVar", zoneName, dq * PerUnit.SB);

for (LfBus controlledBus : controlledBuses) {
double pvcDv = 0;
for (LfBus controllerBus : getControllerBuses(controlledBus)) {
double sqi = sensiVq.getSqi(controllerBus);
if (sqi != 0) {
pvcDv += dq / sqi;
}
}
if (pvcDv != 0) {
var pvc = controlledBus.getGeneratorVoltageControl().orElseThrow();
double newPvcTargetV = pvc.getTargetValue() + pvcDv;
String plausibleTargetInfos = "";
if (newPvcTargetV > maxPlausibleTargetVoltage) {
newPvcTargetV = maxPlausibleTargetVoltage;
plausibleTargetInfos = " (cut to max plausible target voltage)";
}
if (newPvcTargetV < minPlausibleTargetVoltage) {
newPvcTargetV = minPlausibleTargetVoltage;
plausibleTargetInfos = " (cut to min plausible target voltage)";
}
LOGGER.trace("Adjust target voltage of controlled bus '{}': {} -> {}{}",
controlledBus.getId(), pvc.getTargetValue() * controlledBus.getNominalV(),
newPvcTargetV * controlledBus.getNominalV(), plausibleTargetInfos);
pvc.setTargetValue(newPvcTargetV);
adjusted = true;
}
}
}
if (pvcDv != 0) {
var pvc = controlledBus.getGeneratorVoltageControl().orElseThrow();
double newPvcTargetV = pvc.getTargetValue() + pvcDv;
LOGGER.trace("Adjust primary voltage control target of bus '{}': {} -> {}",
controlledBus.getId(), pvc.getTargetValue() * controlledBus.getNominalV(),
newPvcTargetV * controlledBus.getNominalV());
pvc.setTargetValue(newPvcTargetV);
}
}

return adjusted;
}

@Override
Expand Down Expand Up @@ -312,10 +356,12 @@ public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
LOGGER.debug("Secondary voltage control of zone '{}' needs a pilot point voltage adjustment: {} -> {}",
secondaryVoltageControl.getZoneName(), pilotBus.getV() * pilotBus.getNominalV(),
secondaryVoltageControl.getTargetValue() * pilotBus.getNominalV());
adjustPrimaryVoltageControlTargets(secondaryVoltageControl.getZoneName(), sensitivityContext, controlledBuses,
pilotBus, svcTargetDv);
adjustedZoneNames.add(secondaryVoltageControl.getZoneName());
status = OuterLoopStatus.UNSTABLE;
boolean adjusted = adjustPrimaryVoltageControlTargets(secondaryVoltageControl.getZoneName(), sensitivityContext, controlledBuses,
pilotBus, svcTargetDv);
if (adjusted) {
adjustedZoneNames.add(secondaryVoltageControl.getZoneName());
status = OuterLoopStatus.UNSTABLE;
}
}
}
if (!adjustedZoneNames.isEmpty()) {
Expand Down
Loading

0 comments on commit d5975a7

Please sign in to comment.