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

Introduce voltage sensitivity #253

Merged
merged 29 commits into from
Apr 8, 2021
Merged

Introduce voltage sensitivity #253

merged 29 commits into from
Apr 8, 2021

Conversation

Djazouli
Copy link
Contributor

Signed-off-by: Gael Macherel [email protected]

Please check if the PR fulfills these requirements (please use '[x]' to check the checkboxes, or submit the PR and then click the checkboxes)

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

Does this PR already have an issue describing the problem ? If so, link to this issue using '#XXX' and skip the rest

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

What is the current behavior? (You can also link to an open issue here)

What is the new behavior (if this is a feature change)?

Does this PR introduce a breaking change or deprecate an API? If yes, check the following:

  • The Breaking Change or Deprecated label has been added
  • The migration guide has been updated in the github wiki (What changes might users need to make in their application due to this PR?)

Other information:

(if any of the questions/checkboxes don't apply, please delete them entirely)

Signed-off-by: Gael Macherel <[email protected]>
@Djazouli Djazouli requested a review from annetill March 23, 2021 17:14
@Djazouli
Copy link
Contributor Author

We will need to update the core version once the sensitivity is released on core's side

@@ -55,6 +54,27 @@ protected AbstractSensitivityAnalysis(MatrixFactory matrixFactory) {
this.matrixFactory = Objects.requireNonNull(matrixFactory);
}

protected static Terminal getEquipmentRegulatingTerminal(Network network, String equipmentId) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@annetill Here I am trying to get the terminal that corresponds to the equipmentId, but I am not sure of the way to do it

Copy link
Member

Choose a reason for hiding this comment

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

I have pushed a commit to help you (but the code could be improved if you want). Indeed it was what I have tried to avoid with another design of the factor, without any success from my colleagues :-)

functionLfBranch = null;
Bus bus = ((BusVoltagePerTargetV) factor).getFunction().getBusRef().resolve(network).orElseThrow();
functionLfBus = lfNetwork.getBusById(bus.getId());
equationTerm = equationSystem.getEquationTerm(ElementType.BUS, functionLfBus.getNum(), EquationTerm.VariableEquationTerm.class);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@geofjamg @annetill I would like to do the same thing as the branches (like using functionLfBus.getV() to get an Evaluable), but I am not sure if the Evaluable should be the bus_V equation, or the bus_V equation term ? Moreover, currently, getV() returns v / nominalV, so I am not sure whether the nominal V should be included in the Evaluable or not

Copy link
Member

Choose a reason for hiding this comment

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

You can use the BUS_V equation term.
No the / nominalV should not be included and just be careful to add it when updating the IIDM network (LfBusImpl.updateState method)

functionLfBranch = null;
Bus bus = ((BusVoltagePerTargetV) factor).getFunction().getBusRef().resolve(network).orElseThrow();
functionLfBus = lfNetwork.getBusById(bus.getId());
equationTerm = equationSystem.getEquationTerm(ElementType.BUS, functionLfBus.getNum(), EquationTerm.VariableEquationTerm.class);
Copy link
Member

Choose a reason for hiding this comment

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

You can use the BUS_V equation term.
No the / nominalV should not be included and just be careful to add it when updating the IIDM network (LfBusImpl.updateState method)

@@ -111,7 +146,9 @@ protected JacobianMatrix createJacobianMatrix(EquationSystem equationSystem, Vol
// Wrap factors in specific class to have instant access to their branch, and their equation term
private final SensitivityFactor factor;

private final LfBranch functionLfBranch;
private final LfBranch functionLfBranch; // todo: create two classes: branch function and bus function ?
Copy link
Member

Choose a reason for hiding this comment

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

Ideally yes, if possible we should have another class with commons abstract for branch or bus monitored factor.


LfBusVoltagePerTargetV(SensitivityFactor factor, Network network, LfNetwork lfNetwork, EquationSystem equationSystem) {
super(factor, network, lfNetwork, equationSystem);
String targetVTerminalId = ((TargetV) factor.getVariable()).getEquipmentId();
Copy link
Member

Choose a reason for hiding this comment

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

This is not a terminal id, but an element id

LfBusVoltagePerTargetV(SensitivityFactor factor, Network network, LfNetwork lfNetwork, EquationSystem equationSystem) {
super(factor, network, lfNetwork, equationSystem);
String targetVTerminalId = ((TargetV) factor.getVariable()).getEquipmentId();
Terminal regulatingTerminal = getEquipmentRegulatingTerminal(network, targetVTerminalId);
Copy link
Member

Choose a reason for hiding this comment

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

The factor/bus ref is crappy, this why you are doing all this ugly code. Here factor should provide you a TerminaRef directly.

Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
@geofjamg
Copy link
Member

@Djazouli Now that we have a sensi internal API, I propose that for this PR, you don't care about powsybl sensi API and just make voltage sensi available from internal API. In Gridpy I made an evolution to directly connect to sensi internal API, so even without new powsybl API addition I will be able to use it.

@geofjamg
Copy link
Member

@Djazouli Now that we have a sensi internal API, I propose that for this PR, you don't care about powsybl sensi API and just make voltage sensi available from internal API. In Gridpy I made an evolution to directly connect to sensi internal API, so even without new powsybl API addition I will be able to use it.

The idea would be to merge this, before switching to next powsybl release. Adaptation to new factor from powsybl api would be done in another PR.

@Djazouli
Copy link
Contributor Author

@Djazouli Now that we have a sensi internal API, I propose that for this PR, you don't care about powsybl sensi API and just make voltage sensi available from internal API. In Gridpy I made an evolution to directly connect to sensi internal API, so even without new powsybl API addition I will be able to use it.

Sweet idea, I'll do it

Djazouli and others added 10 commits March 30, 2021 17:14
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
private final String variableId;

protected final LfBranch functionLfBranch;
ILfSensitivityFunction function;
Copy link
Member

Choose a reason for hiding this comment

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

LfBranch and LfBus has a common base class LfElement. Would it be possible to use it here in order to avoid adding ILfSensitivityFunction?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we could try in this PR to adapt LfFactor to internal API. To avoid LfFactor subclassing hell (with all function/variable combinations), we could try to only have one LfFactor class and one LfFactorGroup. So having LfElement type for variable and function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, we would to store a LfElement + a SensitivityFunctionType instead of having a class wrapping the element for each Function type ? And then, to get the equation term from that, we write a big switch statement, instead of overriding methods ? (and do the same thing for the variables)

Copy link
Member

Choose a reason for hiding this comment

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

We can try to see what it would look like.

Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
Signed-off-by: Gael Macherel <[email protected]>
protected JacobianMatrix createJacobianMatrix(EquationSystem equationSystem, VoltageInitializer voltageInitializer) {
double[] x = equationSystem.createStateVector(voltageInitializer);
equationSystem.updateEquations(x);
return new JacobianMatrix(equationSystem, matrixFactory);
}

static class LfSensitivityFactor {
abstract static class AbstractLfSensitivityFactor {
Copy link
Member

Choose a reason for hiding this comment

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

Thanks, for this great refactoring! Maybe we can keep a LfSensitivityFactor interface in addition to the AbstractLfSensitivityFactor to avoid using AbstractLfSensitivityFactor everywhere in the code (Abstract class are not supposed to be used as an API)?

@Djazouli Djazouli changed the title [WIP] introduce voltage sensitivity Introduce voltage sensitivity Apr 1, 2021
public EquationTerm getEquationTerm() {
throw new NotImplementedException("getEquationTerm should have an override");
switch (functionType) {
case BRANCH_ACTIVE_POWER:
Copy link
Member

Choose a reason for hiding this comment

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

Just a small remark here: maybe it could be better to use the terminology we already introduced: BRANCH_P, BRANCH_I (I have it on a working branch for transformer in current limiter mode) and BUS_V?

Copy link
Member

@geofjamg geofjamg Apr 1, 2021

Choose a reason for hiding this comment

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

I would keep it like this as it is supposed one day to move to core as API V2 and as an official API is it more meaningful to keep BRANCH_ACTIVE_POWER

if (staticVarCompensator != null) {
return staticVarCompensator.getRegulatingTerminal();
}
TwoWindingsTransformer t2wt = network.getTwoWindingsTransformer(equipmentId);
Copy link
Member

Choose a reason for hiding this comment

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

This PR is indeed not working with transformers as we don't have any PV bus linked to the transformer regulation. In your test, the PV bus is linked to the generator. For phase tap changers, we have activated the phase variable. Here, we have to active the rho variable and the linked bus_v equation I think.

@@ -263,7 +263,7 @@ private static void createBranchActivePowerTargetEquation(LfBranch branch, Discr

private static void createDiscreteVoltageControlEquation(LfBus bus, VariableSet variableSet, EquationSystem equationSystem) {
if (bus.isDiscreteVoltageControlled()) {
EquationTerm vTerm = EquationTerm.createVariableTerm(bus, VariableType.BUS_V, variableSet, bus.getV().eval());
EquationTerm vTerm = EquationTerm.createVariableTerm(bus, VariableType.BUS_V, variableSet, bus.getDiscreteVoltageControl().getTargetValue());
Copy link
Member

Choose a reason for hiding this comment

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

@geofjamg I think it is bus.getV().eval()

Copy link
Member

Choose a reason for hiding this comment

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

Using a numeric value of a state variable in the equation system is not correct.
bus.getDiscreteVoltageControl().getTargetValue() is better, but not correct too. It should be redesign to move it in the RHS.

Copy link
Member

Choose a reason for hiding this comment

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

After thinking again, I am not sure to understand why do we need to set an initial value for equation term at this place. Normally equation initial value should be computed from initial state vector. To investigate before merging this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Yes indeed I understand now the purpose of this new term and we have to investigate why we need it now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The initial value is only used if the voltage initializer type is set to PREVIOUS_VALUES. (because it causes a call to lfBus.getV().eval() ). Otherwise it is indeed replaced immediately by the value from the state vector.

Copy link
Member

Choose a reason for hiding this comment

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

But with PREVIOUS_VALUES the bus voltage is initialized from IIDM values (if defined). Why do you need to override this value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The PREVIOUS_VALUES was initialized with LfBus.getV() (now lfBus.getV().eval()). The issue is that after the creation of the system, lfBus.getV() returns the EquationTerm representing V in the system, and not the value found in the IIDM, so we need to pass the value from the IIDM to the EquationTerm so it does not get lost. (or we could also try to call the voltage initialization before the creation of the equation system)

Copy link
Member

Choose a reason for hiding this comment

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

Ok I understand, so @annetill is right in her comment, it should be bus.getV().eval() in that case to really init from IIDM network state as it was before.

@Override
public void write(Writer writer) throws IOException {
variables.get(0).write(writer);
}
}

static VariableEquationTerm createVariableTerm(LfElement element, VariableType variableType, VariableSet variableSet) {
return createVariableTerm(element, variableType, variableSet, 0d);
Copy link
Member

Choose a reason for hiding this comment

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

I think it will be better to put Double.NaN here.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Apr 7, 2021

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 14 Code Smells

90.3% 90.3% Coverage
0.0% 0.0% Duplication

@annetill annetill merged commit c2b0a30 into master Apr 8, 2021
@annetill annetill deleted the V_sensitivity branch April 8, 2021 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants