Skip to content

Commit

Permalink
Load Flow API updates (#709)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Damien Jeandemange <[email protected]>
  • Loading branch information
jeandemanged authored Feb 16, 2024
1 parent d4d6f5c commit 9283001
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 83 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ print(results)
```

```bash
[LoadFlowComponentResult(component_num=0, status=CONVERGED, iteration_count=3, slack_bus_id='VL4_0', slack_bus_active_power_mismatch=-0.006081)]
[ComponentResult(connected_component_num=0, synchronous_component_num=0, status=CONVERGED, status_text=CONVERGED, iteration_count=3, reference_bus_id='VL1_0', slack_bus_results=[SlackBusResult(id='VL1_0', active_power_mismatch=-0.006730108618313579)], distributed_active_power=0.0)]
```

We can now get buses data (like any other network elements) as a [Pandas](https://pandas.pydata.org/) dataframe:
Expand Down
27 changes: 20 additions & 7 deletions cpp/src/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ PYBIND11_MODULE(_pypowsybl, m) {
.value("CONVERGED", pypowsybl::LoadFlowComponentStatus::CONVERGED, "The loadflow has converged.")
.value("FAILED", pypowsybl::LoadFlowComponentStatus::FAILED, "The loadflow has failed.")
.value("MAX_ITERATION_REACHED", pypowsybl::LoadFlowComponentStatus::MAX_ITERATION_REACHED, "The loadflow has reached its maximum iterations count.")
.value("SOLVER_FAILED", pypowsybl::LoadFlowComponentStatus::SOLVER_FAILED, "The loadflow numerical solver has failed.")
.value("NO_CALCULATION", pypowsybl::LoadFlowComponentStatus::NO_CALCULATION, "The component was not calculated.")
.def("__bool__", [](const pypowsybl::LoadFlowComponentStatus& status) {
return status == pypowsybl::LoadFlowComponentStatus::CONVERGED;
});
Expand All @@ -359,6 +359,16 @@ PYBIND11_MODULE(_pypowsybl, m) {
.value("SOLVER_FAILED", pypowsybl::PostContingencyComputationStatus::SOLVER_FAILED, "The loadflow numerical solver has failed.")
.value("NO_IMPACT", pypowsybl::PostContingencyComputationStatus::NO_IMPACT, "The contingency has no impact.");

py::class_<slack_bus_result>(m, "SlackBusResult")
.def_property_readonly("id", [](const slack_bus_result& v) {
return v.id;
})
.def_property_readonly("active_power_mismatch", [](const slack_bus_result& v) {
return v.active_power_mismatch;
});

bindArray<pypowsybl::SlackBusResultArray>(m, "SlackBusResultArray");

py::class_<loadflow_component_result>(m, "LoadFlowComponentResult", "Loadflow result for one connected component of the network.")
.def_property_readonly("connected_component_num", [](const loadflow_component_result& r) {
return r.connected_component_num;
Expand All @@ -369,14 +379,17 @@ PYBIND11_MODULE(_pypowsybl, m) {
.def_property_readonly("status", [](const loadflow_component_result& r) {
return static_cast<pypowsybl::LoadFlowComponentStatus>(r.status);
})
.def_property_readonly("status_text", [](const loadflow_component_result& r) {
return r.status_text;
})
.def_property_readonly("iteration_count", [](const loadflow_component_result& r) {
return r.iteration_count;
})
.def_property_readonly("slack_bus_id", [](const loadflow_component_result& r) {
return r.slack_bus_id;
.def_property_readonly("slack_bus_results", [](const loadflow_component_result& r) {
return pypowsybl::SlackBusResultArray((array *) & r.slack_bus_results);
})
.def_property_readonly("slack_bus_active_power_mismatch", [](const loadflow_component_result& r) {
return r.slack_bus_active_power_mismatch;
.def_property_readonly("reference_bus_id", [](const loadflow_component_result& r) {
return r.reference_bus_id;
})
.def_property_readonly("distributed_active_power", [](const loadflow_component_result& r) {
return r.distributed_active_power;
Expand Down Expand Up @@ -416,10 +429,10 @@ PYBIND11_MODULE(_pypowsybl, m) {
.def(py::init(&pypowsybl::createLoadFlowParameters))
.def_readwrite("voltage_init_mode", &pypowsybl::LoadFlowParameters::voltage_init_mode)
.def_readwrite("transformer_voltage_control_on", &pypowsybl::LoadFlowParameters::transformer_voltage_control_on)
.def_readwrite("no_generator_reactive_limits", &pypowsybl::LoadFlowParameters::no_generator_reactive_limits)
.def_readwrite("use_reactive_limits", &pypowsybl::LoadFlowParameters::use_reactive_limits)
.def_readwrite("phase_shifter_regulation_on", &pypowsybl::LoadFlowParameters::phase_shifter_regulation_on)
.def_readwrite("twt_split_shunt_admittance", &pypowsybl::LoadFlowParameters::twt_split_shunt_admittance)
.def_readwrite("simul_shunt", &pypowsybl::LoadFlowParameters::simul_shunt)
.def_readwrite("shunt_compensator_voltage_control_on", &pypowsybl::LoadFlowParameters::shunt_compensator_voltage_control_on)
.def_readwrite("read_slack_bus", &pypowsybl::LoadFlowParameters::read_slack_bus)
.def_readwrite("write_slack_bus", &pypowsybl::LoadFlowParameters::write_slack_bus)
.def_readwrite("distributed_slack", &pypowsybl::LoadFlowParameters::distributed_slack)
Expand Down
14 changes: 10 additions & 4 deletions cpp/src/pypowsybl-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,29 @@ typedef struct network_metadata_struct {
int forecast_distance;
} network_metadata;

typedef struct slack_bus_result_struct {
char* id;
double active_power_mismatch;
} slack_bus_result;

typedef struct loadflow_component_result_struct {
int connected_component_num;
int synchronous_component_num;
int status;
char* status_text;
int iteration_count;
char* slack_bus_id;
double slack_bus_active_power_mismatch;
char* reference_bus_id;
array slack_bus_results;
double distributed_active_power;
} loadflow_component_result;

typedef struct loadflow_parameters_struct {
int voltage_init_mode;
unsigned char transformer_voltage_control_on;
unsigned char no_generator_reactive_limits;
unsigned char use_reactive_limits;
unsigned char phase_shifter_regulation_on;
unsigned char twt_split_shunt_admittance;
unsigned char simul_shunt;
unsigned char shunt_compensator_voltage_control_on;
unsigned char read_slack_bus;
unsigned char write_slack_bus;
unsigned char distributed_slack;
Expand Down
13 changes: 9 additions & 4 deletions cpp/src/pypowsybl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ Array<loadflow_component_result>::~Array() {
callJava<>(::freeLoadFlowComponentResultPointer, delegate_);
}

template<>
Array<slack_bus_result>::~Array() {
// already freed by loadflow_component_result
}

template<>
Array<post_contingency_result>::~Array() {
callJava<>(::freeContingencyResultArrayPointer, delegate_);
Expand Down Expand Up @@ -319,10 +324,10 @@ void deleteLoadFlowParameters(loadflow_parameters* ptr) {
LoadFlowParameters::LoadFlowParameters(loadflow_parameters* src) {
voltage_init_mode = static_cast<VoltageInitMode>(src->voltage_init_mode);
transformer_voltage_control_on = (bool) src->transformer_voltage_control_on;
no_generator_reactive_limits = (bool) src->no_generator_reactive_limits;
use_reactive_limits = (bool) src->use_reactive_limits;
phase_shifter_regulation_on = (bool) src->phase_shifter_regulation_on;
twt_split_shunt_admittance = (bool) src->twt_split_shunt_admittance;
simul_shunt = (bool) src->simul_shunt;
shunt_compensator_voltage_control_on = (bool) src->shunt_compensator_voltage_control_on;
read_slack_bus = (bool) src->read_slack_bus;
write_slack_bus = (bool) src->write_slack_bus;
distributed_slack = (bool) src->distributed_slack;
Expand All @@ -337,10 +342,10 @@ LoadFlowParameters::LoadFlowParameters(loadflow_parameters* src) {
void LoadFlowParameters::load_to_c_struct(loadflow_parameters& res) const {
res.voltage_init_mode = voltage_init_mode;
res.transformer_voltage_control_on = (unsigned char) transformer_voltage_control_on;
res.no_generator_reactive_limits = (unsigned char) no_generator_reactive_limits;
res.use_reactive_limits = (unsigned char) use_reactive_limits;
res.phase_shifter_regulation_on = (unsigned char) phase_shifter_regulation_on;
res.twt_split_shunt_admittance = (unsigned char) twt_split_shunt_admittance;
res.simul_shunt = (unsigned char) simul_shunt;
res.shunt_compensator_voltage_control_on = (unsigned char) shunt_compensator_voltage_control_on;
res.read_slack_bus = (unsigned char) read_slack_bus;
res.write_slack_bus = (unsigned char) write_slack_bus;
res.distributed_slack = (unsigned char) distributed_slack;
Expand Down
7 changes: 4 additions & 3 deletions cpp/src/pypowsybl.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Array {
array* delegate_;
};

typedef Array<slack_bus_result> SlackBusResultArray;
typedef Array<loadflow_component_result> LoadFlowComponentResultArray;
typedef Array<post_contingency_result> PostContingencyResultArray;
typedef Array<operator_strategy_result> OperatorStrategyResultArray;
Expand Down Expand Up @@ -99,8 +100,8 @@ std::map<std::string, std::string> convertMapStructToStdMap(string_map* map);
enum class LoadFlowComponentStatus {
CONVERGED = 0,
MAX_ITERATION_REACHED,
SOLVER_FAILED,
FAILED,
NO_CALCULATION,
};

enum class PostContingencyComputationStatus {
Expand Down Expand Up @@ -199,10 +200,10 @@ class LoadFlowParameters {

VoltageInitMode voltage_init_mode;
bool transformer_voltage_control_on;
bool no_generator_reactive_limits;
bool use_reactive_limits;
bool phase_shifter_regulation_on;
bool twt_split_shunt_admittance;
bool simul_shunt;
bool shunt_compensator_voltage_control_on;
bool read_slack_bus;
bool write_slack_bus;
bool distributed_slack;
Expand Down
9 changes: 8 additions & 1 deletion docs/reference/loadflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ The loadflow result is actually a list of results, one for each component of the

loadflow/componentresult

Some enum classes are used in results:
Some classes and enum classes are used in results:

.. autosummary::
:nosignatures:
:toctree: api/
:template: autosummary/class.rst

SlackBusResult

.. autosummary::
:toctree: api/
Expand Down
6 changes: 3 additions & 3 deletions docs/user_guide/loadflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Let's have a look at the default ones:
.. doctest::

>>> lf.Parameters()
Parameters(voltage_init_mode=UNIFORM_VALUES, transformer_voltage_control_on=False, no_generator_reactive_limits=False, phase_shifter_regulation_on=False, twt_split_shunt_admittance=False, simul_shunt=False, read_slack_bus=True, write_slack_bus=False, distributed_slack=True, balance_type=PROPORTIONAL_TO_GENERATION_P_MAX, dc_use_transformer_ratio=True, countries_to_balance=[], connected_component_mode=<ConnectedComponentMode.MAIN: 0>, provider_parameters={})
Parameters(voltage_init_mode=UNIFORM_VALUES, transformer_voltage_control_on=False, use_reactive_limits=True, phase_shifter_regulation_on=False, twt_split_shunt_admittance=False, shunt_compensator_voltage_control_on=False, read_slack_bus=True, write_slack_bus=False, distributed_slack=True, balance_type=PROPORTIONAL_TO_GENERATION_P_MAX, dc_use_transformer_ratio=True, countries_to_balance=[], connected_component_mode=<ConnectedComponentMode.MAIN: 0>, provider_parameters={})

For more details on each parameter, please refer to the :doc:`API reference </reference/loadflow/parameters>`.

Expand Down Expand Up @@ -107,15 +107,15 @@ included in the computation:
.. doctest::

>>> results
[ComponentResult(connected_component_num=0, synchronous_component_num=0, status=CONVERGED, iteration_count=3, slack_bus_id='VLHV1_0', slack_bus_active_power_mismatch=-606.5596837558763, distributed_active_power=0.0)]
[ComponentResult(connected_component_num=0, synchronous_component_num=0, status=CONVERGED, status_text=CONVERGED, iteration_count=3, reference_bus_id='VLHV1_0', slack_bus_results=[SlackBusResult(id='VLHV1_0', active_power_mismatch=-606.5596837558763)], distributed_active_power=0.0)]

Component results provides general information about the loadflow execution: was it successful? How many iterations did
it need? What is the remaining active power imbalance? For example, let's have a look at the imbalance
on the main component of the network:

.. doctest::

>>> results[0].slack_bus_active_power_mismatch
>>> results[0].slack_bus_results[0].active_power_mismatch
-606.5596837558763

Then, the main output of the loadflow is actually the updated data in the network itself:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ public interface NetworkMetadataPointer extends PointerBase {
void setForecastDistance(int forecastDistance);
}

@CStruct("slack_bus_result")
public interface SlackBusResultPointer extends PointerBase {

@CField("id")
CCharPointer getId();

@CField("id")
void setId(CCharPointer id);

@CField("active_power_mismatch")
double getActivePowerMismatch();

@CField("active_power_mismatch")
void setActivePowerMismatch(double activePowerMismatch);

SlackBusResultPointer addressOf(int index);
}

@CStruct("loadflow_component_result")
public interface LoadFlowComponentResultPointer extends PointerBase {

Expand All @@ -148,23 +166,26 @@ public interface LoadFlowComponentResultPointer extends PointerBase {
@CField("status")
void setStatus(int status);

@CField("status_text")
CCharPointer getStatusText();

@CField("status_text")
void setStatusText(CCharPointer statusText);

@CField("iteration_count")
int getIterationCount();

@CField("iteration_count")
void setIterationCount(int iterationCount);

@CField("slack_bus_id")
CCharPointer getSlackBusId();

@CField("slack_bus_id")
void setSlackBusId(CCharPointer slackBusId);
@CField("reference_bus_id")
CCharPointer getReferenceBusId();

@CField("slack_bus_active_power_mismatch")
double getSlackBusActivePowerMismatch();
@CField("reference_bus_id")
void setReferenceBusId(CCharPointer referenceBusId);

@CField("slack_bus_active_power_mismatch")
void setSlackBusActivePowerMismatch(double slackBusActivePowerMismatch);
@CFieldAddress("slack_bus_results")
ArrayPointer<SlackBusResultPointer> slackBusResults();

@CField("distributed_active_power")
double getDistributedActivePower();
Expand All @@ -190,11 +211,11 @@ public interface LoadFlowParametersPointer extends PointerBase {
@CField("transformer_voltage_control_on")
void setTransformerVoltageControlOn(boolean transformerVoltageControlOn);

@CField("no_generator_reactive_limits")
boolean isNoGeneratorReactiveLimits();
@CField("use_reactive_limits")
boolean isUseReactiveLimits();

@CField("no_generator_reactive_limits")
void setNoGeneratorReactiveLimits(boolean noGeneratorReactiveLimits);
@CField("use_reactive_limits")
void setUseReactiveLimits(boolean useReactiveLimits);

@CField("phase_shifter_regulation_on")
boolean isPhaseShifterRegulationOn();
Expand All @@ -208,11 +229,11 @@ public interface LoadFlowParametersPointer extends PointerBase {
@CField("twt_split_shunt_admittance")
void setTwtSplitShuntAdmittance(boolean twtSplitShuntAdmittance);

@CField("simul_shunt")
boolean isSimulShunt();
@CField("shunt_compensator_voltage_control_on")
boolean isShuntCompensatorVoltageControlOn();

@CField("simul_shunt")
void setSimulShunt(boolean simulShunt);
@CField("shunt_compensator_voltage_control_on")
void setShuntCompensatorVoltageControlOn(boolean shuntCompensatorVoltageControlOn);

@CField("read_slack_bus")
boolean isReadSlackBus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ public static void freeLoadFlowComponentResultPointer(IsolateThread thread, PyPo
PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) {
doCatch(exceptionHandlerPtr, () -> {
for (int i = 0; i < componentResultArrayPtr.getLength(); i++) {
UnmanagedMemory.free(componentResultArrayPtr.getPtr().addressOf(i).getSlackBusId());
PyPowsyblApiHeader.LoadFlowComponentResultPointer loadFlowComponentResultPointer = componentResultArrayPtr.getPtr().addressOf(i);
UnmanagedMemory.free(loadFlowComponentResultPointer.getStatusText());
UnmanagedMemory.free(loadFlowComponentResultPointer.getReferenceBusId());
for (int j = 0; j < loadFlowComponentResultPointer.slackBusResults().getLength(); j++) {
PyPowsyblApiHeader.SlackBusResultPointer slackBusResultPointer = loadFlowComponentResultPointer.slackBusResults().getPtr().addressOf(j);
UnmanagedMemory.free(slackBusResultPointer.getId());
}
}
freeArrayPointer(componentResultArrayPtr);
});
Expand Down Expand Up @@ -126,21 +132,34 @@ private static PyPowsyblApiHeader.ArrayPointer<PyPowsyblApiHeader.LoadFlowCompon
ptr.setConnectedComponentNum(componentResult.getConnectedComponentNum());
ptr.setSynchronousComponentNum(componentResult.getSynchronousComponentNum());
ptr.setStatus(componentResult.getStatus().ordinal());
ptr.setStatusText(CTypeUtil.toCharPtr(componentResult.getStatusText()));
ptr.setIterationCount(componentResult.getIterationCount());
ptr.setSlackBusId(CTypeUtil.toCharPtr(componentResult.getSlackBusId()));
ptr.setSlackBusActivePowerMismatch(componentResult.getSlackBusActivePowerMismatch());
ptr.setReferenceBusId(CTypeUtil.toCharPtr(componentResult.getReferenceBusId()));
createSlackBusResultPtr(ptr, componentResult.getSlackBusResults());
ptr.setDistributedActivePower(componentResult.getDistributedActivePower());
}
return allocArrayPointer(componentResultPtr, componentResults.size());
}

private static void createSlackBusResultPtr(PyPowsyblApiHeader.LoadFlowComponentResultPointer ptr, List<LoadFlowResult.SlackBusResult> slackBusResults) {
PyPowsyblApiHeader.SlackBusResultPointer slackBusResultPointer = UnmanagedMemory.calloc(slackBusResults.size() * SizeOf.get(PyPowsyblApiHeader.SlackBusResultPointer.class));
for (int i = 0; i < slackBusResults.size(); i++) {
LoadFlowResult.SlackBusResult slackBusResult = slackBusResults.get(i);
PyPowsyblApiHeader.SlackBusResultPointer slackBusResultPtrPlus = slackBusResultPointer.addressOf(i);
slackBusResultPtrPlus.setId(CTypeUtil.toCharPtr(slackBusResult.getId()));
slackBusResultPtrPlus.setActivePowerMismatch(slackBusResult.getActivePowerMismatch());
}
ptr.slackBusResults().setLength(slackBusResults.size());
ptr.slackBusResults().setPtr(slackBusResultPointer);
}

public static void copyToCLoadFlowParameters(LoadFlowParameters parameters, LoadFlowParametersPointer cParameters) {
cParameters.setVoltageInitMode(parameters.getVoltageInitMode().ordinal());
cParameters.setTransformerVoltageControlOn(parameters.isTransformerVoltageControlOn());
cParameters.setNoGeneratorReactiveLimits(parameters.isNoGeneratorReactiveLimits());
cParameters.setUseReactiveLimits(parameters.isUseReactiveLimits());
cParameters.setPhaseShifterRegulationOn(parameters.isPhaseShifterRegulationOn());
cParameters.setTwtSplitShuntAdmittance(parameters.isTwtSplitShuntAdmittance());
cParameters.setSimulShunt(parameters.isSimulShunt());
cParameters.setShuntCompensatorVoltageControlOn(parameters.isShuntCompensatorVoltageControlOn());
cParameters.setReadSlackBus(parameters.isReadSlackBus());
cParameters.setWriteSlackBus(parameters.isWriteSlackBus());
cParameters.setDistributedSlack(parameters.isDistributedSlack());
Expand Down
Loading

0 comments on commit 9283001

Please sign in to comment.