Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create adders from existing objects: ReactiveCapabilityCurve, PhaseTapChanger, RatioTapChanger #3130

Merged
merged 7 commits into from
Dec 2, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.util.Optional;

/**
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
public interface PhaseTapChangerHolder {
Expand All @@ -21,6 +20,33 @@ public interface PhaseTapChangerHolder {
*/
PhaseTapChangerAdder newPhaseTapChanger();

/**
* Get a builder to create and associate a phase tap changer to the
* transformer, initialized with the values of an existing ratio tap changer.
*/
default PhaseTapChangerAdder newPhaseTapChanger(PhaseTapChanger phaseTapChanger) {
PhaseTapChangerAdder adder = this.newPhaseTapChanger()
.setRegulationTerminal(phaseTapChanger.getRegulationTerminal())
.setRegulationMode(phaseTapChanger.getRegulationMode())
.setRegulationValue(phaseTapChanger.getRegulationValue())
.setLowTapPosition(phaseTapChanger.getLowTapPosition())
.setTapPosition(phaseTapChanger.getTapPosition())
.setRegulating(phaseTapChanger.isRegulating())
.setTargetDeadband(phaseTapChanger.getTargetDeadband());
for (int tapPosition = phaseTapChanger.getLowTapPosition(); tapPosition <= phaseTapChanger.getHighTapPosition(); tapPosition++) {
PhaseTapChangerStep step = phaseTapChanger.getStep(tapPosition);
adder.beginStep()
.setAlpha(step.getAlpha())
.setRho(step.getRho())
.setB(step.getB())
.setG(step.getG())
.setX(step.getX())
.setR(step.getR())
.endStep();
}
return adder;
}

/**
* Get the phase tap changer.
* <p>Could return <code>null</code> if the transfomer is not associated to
Expand All @@ -36,7 +62,7 @@ default Optional<PhaseTapChanger> getOptionalPhaseTapChanger() {
}

/**
* Check if a phase tap changer is present
* Check if a phase tap changer is present
*/
default boolean hasPhaseTapChanger() {
return getPhaseTapChanger() != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,34 @@ public interface RatioTapChangerHolder {
*/
RatioTapChangerAdder newRatioTapChanger();

/**
* Get a builder to create and associate a ratio tap changer to the
* transformer, initialized with the values of an existing ratio tap changer.
*/
default RatioTapChangerAdder newRatioTapChanger(RatioTapChanger ratioTapChanger) {
RatioTapChangerAdder adder = this.newRatioTapChanger()
.setRegulationTerminal(ratioTapChanger.getRegulationTerminal())
.setRegulationMode(ratioTapChanger.getRegulationMode())
.setRegulationValue(ratioTapChanger.getRegulationValue())
.setLoadTapChangingCapabilities(ratioTapChanger.hasLoadTapChangingCapabilities())
.setTargetV(ratioTapChanger.getTargetV())
.setLowTapPosition(ratioTapChanger.getLowTapPosition())
.setTapPosition(ratioTapChanger.getTapPosition())
.setRegulating(ratioTapChanger.isRegulating())
.setTargetDeadband(ratioTapChanger.getTargetDeadband());
for (int tapPosition = ratioTapChanger.getLowTapPosition(); tapPosition <= ratioTapChanger.getHighTapPosition(); tapPosition++) {
RatioTapChangerStep step = ratioTapChanger.getStep(tapPosition);
adder.beginStep()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be interesting to add a A beginStep(D step) in TapChangerAdder interface? It's probably not very useful, but in that way each level would have its copy method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to stick to the perimeter you defined previously.

If the need for copy-adder on steps solidifies, they could be added in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if you consider the code duplication in PhaseTapChangerHolder and RatioTapChangerHolder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After looking back into it, the issue I have with it is that beginStep carries a state that would be unrelated to existing step: the order in which steps are created currently encodes the tap position. Being able to initialize beginStep by copying a step with a different tap position may be confusing for users.

A helper class could be used to mutualize the code, but it is unclear to me whether this would be better than the current situation.

.setRho(step.getRho())
.setB(step.getB())
.setG(step.getG())
.setX(step.getX())
.setR(step.getR())
.endStep();
}
return adder;
}

/**
* Get the ratio tap changer.
* <p>Could return <code>null</code> if the leg is not associated to a ratio
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,19 @@ public interface ReactiveLimitsHolder {
* to this generator.
*/
MinMaxReactiveLimitsAdder newMinMaxReactiveLimits();

/**
* Get a builder to create and associate a new reactive capability curve
* to this generator based on an existing curve.
*/
default ReactiveCapabilityCurveAdder newReactiveCapabilityCurve(ReactiveCapabilityCurve copiedCurve) {
ReactiveCapabilityCurveAdder adder = newReactiveCapabilityCurve();
copiedCurve.getPoints().forEach(copiedPoint ->
adder.beginPoint()
.setP(copiedPoint.getP())
.setMinQ(copiedPoint.getMinQ())
.setMaxQ(copiedPoint.getMaxQ())
.endPoint());
return adder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.powsybl.iidm.network.impl.tck;
flo-dup marked this conversation as resolved.
Show resolved Hide resolved

import com.powsybl.iidm.network.tck.AbstractTapChangerHolderTest;

public class TapChangerHolderTest extends AbstractTapChangerHolderTest {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.ReactiveCapabilityCurve;
import com.powsybl.iidm.network.ReactiveLimitsKind;
import com.powsybl.iidm.network.ValidationException;
import com.powsybl.iidm.network.test.FictitiousSwitchFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static com.powsybl.iidm.network.ReactiveLimitsKind.CURVE;
import static org.junit.jupiter.api.Assertions.*;

public abstract class AbstractReactiveCapabilityCurveTest {
Expand Down Expand Up @@ -48,7 +50,7 @@ public void testAdder() {
.setMinQ(2.0)
.endPoint()
.add();
assertEquals(ReactiveLimitsKind.CURVE, reactiveCapabilityCurve.getKind());
assertEquals(CURVE, reactiveCapabilityCurve.getKind());
assertEquals(100.0, reactiveCapabilityCurve.getMaxP(), 0.0);
assertEquals(1.0, reactiveCapabilityCurve.getMinP(), 0.0);
assertEquals(3, reactiveCapabilityCurve.getPoints().size());
Expand Down Expand Up @@ -134,4 +136,33 @@ public void invalidMinQGreaterThanMaxQ() {
assertTrue(e.getMessage().contains("maximum reactive power is expected to be greater than or equal to minimum reactive power"));
}

@Test
void shouldCopyDataWhenCreatingReactiveCapabilityCurveFromTemplate() {
ReactiveCapabilityCurve copiedCurve = generator.newReactiveCapabilityCurve()
.beginPoint()
.setP(1)
.setMinQ(5)
.setMaxQ(50)
.endPoint()
.beginPoint()
.setP(2)
.setMinQ(7)
.setMaxQ(70)
.endPoint()
.add();
Generator anotherGenerator = generator.getNetwork().getGenerator("CC");

anotherGenerator.newReactiveCapabilityCurve(copiedCurve)
.add();

copiedCurve.getPoints().forEach(copiedPoint -> {
Optional<ReactiveCapabilityCurve.Point> pastedPoint = anotherGenerator.getReactiveLimits(ReactiveCapabilityCurve.class).getPoints().stream()
.filter(point -> point.getP() == copiedPoint.getP())
.findFirst();
assertTrue(pastedPoint.isPresent());
assertEquals(copiedPoint.getMinQ(), pastedPoint.get().getMinQ());
assertEquals(copiedPoint.getMaxQ(), pastedPoint.get().getMaxQ());
});

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* Copyright (c) 2024, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
bc-rte marked this conversation as resolved.
Show resolved Hide resolved
* 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.iidm.network.tck;

import com.powsybl.iidm.network.*;
import org.junit.jupiter.api.Test;

import static com.powsybl.iidm.network.PhaseTapChanger.RegulationMode.FIXED_TAP;
import static com.powsybl.iidm.network.RatioTapChanger.RegulationMode.VOLTAGE;
import static com.powsybl.iidm.network.TopologyKind.BUS_BREAKER;
import static com.powsybl.iidm.network.TwoSides.ONE;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Benoît Chiquet {@literal <benoit.chiquet at rte-france.com>}
*/
public abstract class AbstractTapChangerHolderTest {

@Test
void shouldReuseCopiedPhaseTapChangerPropertiesFixedTapExample() {
Network network = exampleNetwork();
PhaseTapChanger existingPhaseTapChanger = network.getTwoWindingsTransformer("transformer")
.newPhaseTapChanger()
.setTapPosition(1)
.setRegulationValue(12)
.setRegulationMode(FIXED_TAP)
.setLowTapPosition(0)
.setRegulating(false)
.setTargetDeadband(3)
.beginStep().setAlpha(1).setRho(2).setR(3).setG(4).setB(5).setX(6)
.endStep()
.beginStep().setAlpha(2).setRho(3).setR(4).setG(5).setB(6).setX(7)
.endStep()
.beginStep().setAlpha(3).setRho(4).setR(5).setG(6).setB(7).setX(8)
.endStep()
.add();

PhaseTapChanger newPhaseTapChanger = network.getTwoWindingsTransformer("transformer2")
.newPhaseTapChanger(existingPhaseTapChanger)
.add();

assertEquals(existingPhaseTapChanger.getTapPosition(), newPhaseTapChanger.getTapPosition());
assertEquals(existingPhaseTapChanger.getLowTapPosition(), newPhaseTapChanger.getLowTapPosition());
assertEquals(existingPhaseTapChanger.getRegulationValue(), newPhaseTapChanger.getRegulationValue());
assertEquals(existingPhaseTapChanger.getRegulationMode(), newPhaseTapChanger.getRegulationMode());
assertEquals(existingPhaseTapChanger.isRegulating(), newPhaseTapChanger.isRegulating());
assertEquals(existingPhaseTapChanger.getTargetDeadband(), newPhaseTapChanger.getTargetDeadband());

newPhaseTapChanger.getAllSteps().forEach((tap, newStep) -> {
PhaseTapChangerStep existingStep = existingPhaseTapChanger.getStep(tap);
assertEquals(existingStep.getAlpha(), newStep.getAlpha());
assertEquals(existingStep.getRho(), newStep.getRho());
assertEquals(existingStep.getR(), newStep.getR());
assertEquals(existingStep.getG(), newStep.getG());
assertEquals(existingStep.getB(), newStep.getB());
assertEquals(existingStep.getX(), newStep.getX());
});
}

@Test
void shouldReuseCopiedPhaseTapChangerPropertiesActivePowerControlExample() {
Network network = exampleNetwork();
PhaseTapChanger existingPhaseTapChanger = network.getTwoWindingsTransformer("transformer")
.newPhaseTapChanger()
.setTapPosition(1)
.setRegulationValue(12)
.setRegulationMode(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL)
.setRegulationTerminal(network.getTwoWindingsTransformer("transformer").getTerminal(ONE))
.setLowTapPosition(0)
.setRegulating(false)
.setTargetDeadband(3)
.beginStep().setAlpha(1).setRho(2).setR(3).setG(4).setB(5).setX(6)
.endStep()
.beginStep().setAlpha(2).setRho(3).setR(4).setG(5).setB(6).setX(7)
.endStep()
.beginStep().setAlpha(3).setRho(4).setR(5).setG(6).setB(7).setX(8)
.endStep()
.add();

PhaseTapChanger newPhaseTapChanger = network.getTwoWindingsTransformer("transformer2")
.newPhaseTapChanger(existingPhaseTapChanger)
.add();

assertEquals(existingPhaseTapChanger.getTapPosition(), newPhaseTapChanger.getTapPosition());
assertEquals(existingPhaseTapChanger.getLowTapPosition(), newPhaseTapChanger.getLowTapPosition());
assertEquals(existingPhaseTapChanger.getRegulationValue(), newPhaseTapChanger.getRegulationValue());
assertEquals(existingPhaseTapChanger.getRegulationMode(), newPhaseTapChanger.getRegulationMode());
assertEquals(existingPhaseTapChanger.isRegulating(), newPhaseTapChanger.isRegulating());
assertEquals(existingPhaseTapChanger.getTargetDeadband(), newPhaseTapChanger.getTargetDeadband());
assertEquals(existingPhaseTapChanger.getRegulationTerminal(), newPhaseTapChanger.getRegulationTerminal());

newPhaseTapChanger.getAllSteps().forEach((tap, newStep) -> {
PhaseTapChangerStep existingStep = existingPhaseTapChanger.getStep(tap);
assertEquals(existingStep.getAlpha(), newStep.getAlpha());
assertEquals(existingStep.getRho(), newStep.getRho());
assertEquals(existingStep.getR(), newStep.getR());
assertEquals(existingStep.getG(), newStep.getG());
assertEquals(existingStep.getB(), newStep.getB());
assertEquals(existingStep.getX(), newStep.getX());
});
}

@Test
void shouldReuseCopiedRatioTapChangerProperties() {
Network network = exampleNetwork();
RatioTapChanger existingRatioTapChanger = network.getTwoWindingsTransformer("transformer").newRatioTapChanger()
.setTapPosition(1)
.setTargetV(400)
.setRegulationValue(12)
.setRegulationMode(VOLTAGE)
.setLowTapPosition(0)
.setRegulating(false)
.setLoadTapChangingCapabilities(true)
.setTargetDeadband(3)
.beginStep().setRho(2).setR(3).setG(4).setB(5).setX(6)
.endStep()
.beginStep().setRho(3).setR(4).setG(5).setB(6).setX(7)
.endStep()
.beginStep().setRho(4).setR(5).setG(6).setB(7).setX(8)
.endStep()
.add();

RatioTapChanger newRatioTapChanger = network.getTwoWindingsTransformer("transformer2")
.newRatioTapChanger(existingRatioTapChanger)
.add();

assertEquals(existingRatioTapChanger.getTapPosition(), newRatioTapChanger.getTapPosition());
assertEquals(existingRatioTapChanger.getLowTapPosition(), newRatioTapChanger.getLowTapPosition());
assertEquals(existingRatioTapChanger.getRegulationValue(), newRatioTapChanger.getRegulationValue());
assertEquals(existingRatioTapChanger.getRegulationMode(), newRatioTapChanger.getRegulationMode());
assertEquals(existingRatioTapChanger.isRegulating(), newRatioTapChanger.isRegulating());
assertEquals(existingRatioTapChanger.getTargetDeadband(), newRatioTapChanger.getTargetDeadband());
assertEquals(existingRatioTapChanger.getRegulationTerminal(), newRatioTapChanger.getRegulationTerminal());
assertEquals(existingRatioTapChanger.getTargetV(), newRatioTapChanger.getTargetV());
assertEquals(existingRatioTapChanger.hasLoadTapChangingCapabilities(), newRatioTapChanger.hasLoadTapChangingCapabilities());

newRatioTapChanger.getAllSteps().forEach((tap, newStep) -> {
RatioTapChangerStep existingStep = existingRatioTapChanger.getStep(tap);
assertEquals(existingStep.getRho(), newStep.getRho());
assertEquals(existingStep.getR(), newStep.getR());
assertEquals(existingStep.getG(), newStep.getG());
assertEquals(existingStep.getB(), newStep.getB());
assertEquals(existingStep.getX(), newStep.getX());
});
}

Network exampleNetwork() {
Network network = Network.create("test", "test");
Substation substation = network.newSubstation()
.setId("substation")
.setCountry(Country.AD)
.add();
VoltageLevel vl1 = substation.newVoltageLevel()
.setId("vl1")
.setTopologyKind(BUS_BREAKER)
.setName("name")
.setNominalV(225)
.setLowVoltageLimit(200)
.setHighVoltageLimit(250)
.add();
vl1.getBusBreakerView().newBus().setId("bus1").add();
VoltageLevel vl2 = substation.newVoltageLevel()
.setId("vl2")
.setTopologyKind(BUS_BREAKER)
.setName("name")
.setNominalV(90)
.setLowVoltageLimit(80)
.setHighVoltageLimit(100)
.add();
vl2.getBusBreakerView().newBus().setId("bus2").add();

substation.newTwoWindingsTransformer()
.setId("transformer")
.setR(17)
.setX(10)
.setBus1("bus1")
.setBus2("bus2")
.add();

substation.newTwoWindingsTransformer()
.setId("transformer2")
.setR(12)
.setX(15)
.setBus1("bus1")
.setBus2("bus2")
.add();

return network;
}
}