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 network elements #259

Merged
merged 34 commits into from Feb 17, 2022
Merged

Create network elements #259

merged 34 commits into from Feb 17, 2022

Conversation

EtienneLt
Copy link
Contributor

@EtienneLt EtienneLt commented Oct 19, 2021

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)

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

Feature.

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

Network elements cannot be created through python methods.

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

We can now create network elements, for example:

def test_create_network_and_run_loadflow():
    n = pn.create_empty()
    stations = pd.DataFrame.from_records(index='id', data=[
        {'id': 'S1', 'country': 'BE'},
        {'id': 'S2', 'country': 'DE'}
    ])
    n.create_substations(stations)

    voltage_levels = pd.DataFrame.from_records(index='id', data=[
        {'substation_id': 'S1', 'id': 'VL1', 'topology_kind': 'BUS_BREAKER', 'nominal_v': 400},
        {'substation_id': 'S2', 'id': 'VL2', 'topology_kind': 'BUS_BREAKER', 'nominal_v': 400},
    ])
    n.create_voltage_levels(voltage_levels)

    buses = pd.DataFrame.from_records(index='id', data=[
        {'voltage_level_id': 'VL1', 'id': 'B1'},
        {'voltage_level_id': 'VL2', 'id': 'B2'},
    ])
    n.create_buses(buses)

    lines = pd.DataFrame.from_records(index='id', data=[
        {'id': 'LINE', 'voltage_level1_id': 'VL1', 'voltage_level2_id': 'VL2', 'bus1_id': 'B1', 'bus2_id': 'B2',
         'r': 0.1, 'x': 1.0, 'g1': 0, 'b1': 1e-6, 'g2': 0, 'b2': 1e-6}
    ])
    n.create_lines(lines)

    loads = pd.DataFrame.from_records(index='id', data=[
        {'voltage_level_id': 'VL2', 'id': 'LOAD', 'bus_id': 'B2', 'p0': 100, 'q0': 10}
    ])
    n.create_loads(loads)

    generators = pd.DataFrame.from_records(index='id', data=[
        {'voltage_level_id': 'VL1', 'id': 'GEN', 'bus_id': 'B1', 'target_p': 100, 'min_p': 0, 'max_p': 200,
         'target_v': 400, 'voltage_regulator_on': True}
    ])
    n.create_generators(generators)

    import pypowsybl.loadflow as lf
    lf.run_ac(n)

    line = n.get_lines().loc['LINE']
    assert line.p2 == pytest.approx(-100, abs=1e-2)
    assert line.q2 == pytest.approx(-10, abs=1e-2)
    assert line.p1 == pytest.approx(100, abs=1e-1)
    assert line.q1 == pytest.approx(9.7, abs=1e-1)

@EtienneLt EtienneLt changed the title WIP Create network equipment Create network equipment Oct 28, 2021
@sonarcloud
Copy link

sonarcloud bot commented Oct 28, 2021

SonarCloud Quality Gate failed.    Quality Gate failed

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

97.7% 97.7% Coverage
3.6% 3.6% Duplication

@annetill
Copy link
Member

annetill commented Dec 2, 2021

@EtienneLt it seems that your PR is needed for long-term studies. Do you think that you can rework on that in the next following months? It would be great!

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
@EtienneLt
Copy link
Contributor Author

yes no problem

@sylvlecl sylvlecl changed the title Create network equipment Create network elements Jan 19, 2022
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
- expose list of series metadata for element creation dataframes
- isolate elements creation tests
- add some missing creation (HVDC lines, substations)

Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
@sylvlecl
Copy link
Contributor

sylvlecl commented Feb 1, 2022

Actually something is missing for node breaker topologies:
we cannot create "internal connections". Still a todo.

Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
BatteryAdder batteryAdder = network.getVoltageLevel(dataframe.getStringValue("voltage_level_id", indexElement)
.orElseThrow(() -> new PowsyblException("voltage_level_id is missing")))
.newBattery();
NetworkElementCreationUtils.createInjection(batteryAdder, dataframe, indexElement);
Copy link
Member

Choose a reason for hiding this comment

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

Be careful here: if not present, I think that you will have NaN values.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.


private static final List<SeriesMetadata> METADATA = List.of(
SeriesMetadata.stringIndex("id"),
SeriesMetadata.strings("voltage_level_id"),
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 general question: when you only fill the bus id, and not the connectable bus id, what is happening ? And if you want a disconnected battery, you will have to fill the connectable bus only ?

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

public void addElement(Network network, UpdatingDataframe dataframe, int indexElement) {
DanglingLineAdder adder = network.getVoltageLevel(dataframe.getStringValue("voltage_level_id", indexElement)
.orElseThrow(() -> new PowsyblException("voltage_level_id is missing"))).newDanglingLine();
NetworkElementCreationUtils.createInjection(adder, dataframe, indexElement);
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 that we have to think about g and a b default values equals to zero.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

GeneratorAdder generatorAdder = network.getVoltageLevel(dataframe.getStringValue("voltage_level_id", indexElement)
.orElseThrow(() -> new PowsyblException("voltage_level_id is missing")))
.newGenerator();
NetworkElementCreationUtils.createInjection(generatorAdder, dataframe, indexElement);
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 that we have to think about default values for minP and maxP (it seems to be NaN in powsybl-core), right?

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

NetworkElementCreationUtils.createIdentifiable(adder, dataframe, indexElement);
dataframe.getStringValue("converter_station1_id", indexElement).ifPresent(adder::setConverterStationId1);
dataframe.getStringValue("converter_station2_id", indexElement).ifPresent(adder::setConverterStationId2);
dataframe.getDoubleValue("max_p", indexElement).ifPresent(adder::setMaxP);
Copy link
Member

Choose a reason for hiding this comment

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

Default value for maxP ?

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

public void addElement(Network network, UpdatingDataframe dataframe, int indexElement) {
LineAdder lineAdder = network.newLine();
NetworkElementCreationUtils.createBranch(lineAdder, dataframe, indexElement);
dataframe.getDoubleValue("b1", indexElement).ifPresent(lineAdder::setB1);
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 that for line, we must think about by default having b1, b2, g1 and g2 equal to zero.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

private static final List<SeriesMetadata> METADATA = List.of(
SeriesMetadata.stringIndex("id"),
SeriesMetadata.strings("regulation_mode"),
SeriesMetadata.doubles("target_deadband"),
Copy link
Member

Choose a reason for hiding this comment

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

The target deadband could be at zero by default to ease understanding.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

SeriesMetadata.ints("tap")
);

private static final List<SeriesMetadata> STEPS_METADATA = List.of(
Copy link
Member

Choose a reason for hiding this comment

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

For phase tap changers, I think that we can provide a lot of default values and maybe allow only angles to be set.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

*/
public class RatioTapChangerDataframeAdder implements NetworkElementAdder {

private static final List<SeriesMetadata> METADATA = List.of(
Copy link
Member

Choose a reason for hiding this comment

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

On load can be to true by default, and the target deadband could be to zero. For the ratio tap changer, I think that it could be great to have the possibility to set only ratios.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

* @author Sylvain Leclerc <sylvain.leclerc@rte-france.com>
*/
public class ShuntDataframeAdder implements NetworkElementAdder {

Copy link
Member

Choose a reason for hiding this comment

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

In general, for shunt, it could be great to have a way to only set a B from a dataframe, that will create a shunt understandable by Powsybl.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

*/
public class SwitchDataframeAdder extends AbstractSimpleAdder {

private static final List<SeriesMetadata> METADATA = List.of(
Copy link
Member

Choose a reason for hiding this comment

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

In general: it always has been strange to be to give both voltage level id and bus id. It will be very great to have the possibility to give only busId to create equipments in the network, and to have a way to retrieve the voltage level. It is a bit confusing to expose the complexity of IIDM in python.

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

SeriesMetadata.doubles("rated_u1"),
SeriesMetadata.doubles("rated_u2"),
SeriesMetadata.doubles("rated_s"),
SeriesMetadata.doubles("b"),
Copy link
Member

Choose a reason for hiding this comment

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

Same remark as lines. And maybe rated U1 and U2 could also have default values equal to nominal voltage at ends. What do you think ?

Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion synthesis about default values in comment below.

@sylvlecl
Copy link
Contributor

sylvlecl commented Feb 16, 2022

Just writing down the outcome of the discussions about default values:

currently, we inherit the behaviour of powsybl-core adders.
The implementation of default values will be more valuable in powsybl-core than in pypowsybl, because it will benefit to all users.

Therefore, unless there is something specific that the python community asks for, and that we really don't want to implement in powsybl-core, we'll keep it this way.

Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Signed-off-by: Sylvain Leclerc <sylvain.leclerc@rte-france.com>
Copy link
Member

@annetill annetill left a comment

Choose a reason for hiding this comment

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

I am going to try to put some default values in the adders of powsybl-core, and we will see what remains for pypowsybl !

@sonarcloud
Copy link

sonarcloud bot commented Feb 17, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

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

97.5% 97.5% Coverage
0.0% 0.0% Duplication

@sylvlecl sylvlecl merged commit 23c7f8b into main Feb 17, 2022
@sylvlecl sylvlecl deleted the create-network-equipment branch February 17, 2022 17:04
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.

None yet

4 participants