Code Monkey home page Code Monkey logo

roseau_load_flow's People

Contributors

alihamdan avatar benoit9126 avatar dependabot[bot] avatar mullerlo avatar saelyos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

roseau_load_flow's Issues

ENH: Improve networks in the catalogue

Some improvements that can be made to the catalogue of networks:

  1. Add voltage limits to buses and current limits to lines. Use French voltage limits and IEC ampacities.
  2. Make the LV loads single-phase (they are currently 3-phase by mistake)
  3. Rename the MV source bus of LV networks from "VoltageSource" to "MVLV<SUBSTATION ID>" ("VoltageSource" was used in an old version of the software when a voltage source was defined as a bus)
  4. Remove the neutral of MV buses and MV sources to have phase-phase MV voltage instead (?)
  5. Round the powers of the loads

DOC: Conda-forge

Location of the document
Installation page

Documentation problem
Our repository is now available on conda-forge. The installation procedure can now be simplified using a reference to this page https://anaconda.org/conda-forge/roseau-load-flow. It contains the single line command to install roseau-load-flow for conda users: conda install -c conda-forge roseau-load-flow

BUG: License error

A license error we got in github actions recently. I am keeping a record of this error here in case we need it in the future and the github action logs get deleted. Note that the failure was only triggered on Python 3.10 and not on Python 3.11.

Logs

__________________________ test_single_phase_network ___________________________
[gw3] linux -- Python 3.10.13 /home/runner/.cache/pypoetry/virtualenvs/roseau-load-flow-OJvREwE6-py3.10/bin/python

key = '***'

    def activate_license(key: str | None = None) -> None:
        """Activate a license in the current process.
    
        Args:
            key:
                The key of the license to activate. If ``None`` is provided (default), the environment
                variable `ROSEAU_LOAD_FLOW_LICENSE_KEY` is used. If this variable is not set, an error
                is raised.
        """
        if key is None:
            key = os.getenv("ROSEAU_LOAD_FLOW_LICENSE_KEY", "")
        try:
>           cy_activate_license(key=key, cacert_filepath=certifi.where(), cache_folderpath=user_cache_dir())

roseau/load_flow/license.py:88: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
roseau/load_flow_engine/cy_engine.pyx:318: in roseau.load_flow_engine.cy_engine.cy_activate_license
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>   ???
E   RuntimeError: 0 A malformed request has been sent to the license server. The status code is "422" and the reason is "Unprocessable Entity".

roseau/load_flow_engine/cy_engine.pyx:319: RuntimeError

The above exception was the direct cause of the following exception:

single_phase_network = <ElectricalNetwork: 2 buses, 1 branch, 1 load, 1 source, 1 ground, 1 potential ref>

    def test_single_phase_network(single_phase_network: ElectricalNetwork):
        # Test dict conversion
        # ====================
        net_dict = single_phase_network.to_dict()
        new_net = ElectricalNetwork.from_dict(net_dict)
        assert_frame_equal(single_phase_network.buses_frame, new_net.buses_frame)
        assert_frame_equal(single_phase_network.branches_frame, new_net.branches_frame)
        assert_frame_equal(single_phase_network.transformers_frame, new_net.transformers_frame)
        assert_frame_equal(single_phase_network.lines_frame, new_net.lines_frame)
        assert_frame_equal(single_phase_network.switches_frame, new_net.switches_frame)
        assert_frame_equal(single_phase_network.loads_frame, new_net.loads_frame)
        assert_frame_equal(single_phase_network.sources_frame, new_net.sources_frame)
    
        # Test load flow results
        # ======================
        source_bus = single_phase_network.buses["bus0"]
        load_bus = single_phase_network.buses["bus1"]
        line = single_phase_network.branches["line"]
        load = single_phase_network.loads["load"]
    
>       single_phase_network.solve_load_flow()

roseau/load_flow/tests/test_electrical_network.py:701: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
roseau/load_flow/network.py:520: in solve_load_flow
    iterations, residual = self._solver.solve_load_flow(max_iterations=max_iterations, tolerance=tolerance)
roseau/load_flow/_solvers.py:80: in solve_load_flow
    activate_license(key=None)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key = '***'

    def activate_license(key: str | None = None) -> None:
        """Activate a license in the current process.
    
        Args:
            key:
                The key of the license to activate. If ``None`` is provided (default), the environment
                variable `ROSEAU_LOAD_FLOW_LICENSE_KEY` is used. If this variable is not set, an error
                is raised.
        """
        if key is None:
            key = os.getenv("ROSEAU_LOAD_FLOW_LICENSE_KEY", "")
        try:
            cy_activate_license(key=key, cacert_filepath=certifi.where(), cache_folderpath=user_cache_dir())
        except RuntimeError as e:
            msg = f"The license cannot be activated. The detailed error message is {e.args[0][2:]!r}."
            logger.error(msg)
>           raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.LICENSE_ERROR) from e
E           roseau.load_flow.exceptions.RoseauLoadFlowException: The license cannot be activated. The detailed error message is 'A malformed request has been sent to the license server. The status code is "422" and the reason is "Unprocessable Entity".'. [license_error]

roseau/load_flow/license.py:92: RoseauLoadFlowException

BUG: res_transformers.power1 contains NaN for neutral

Describe the bug
The real value of res_transformers.power1 is NaN for the neutral branch.

To Reproduce
Minimal Working Example to understand the problem

en = ElectricalNetwork.from_catalogue(name="LVFeeder38211", load_point_name="Winter")
for name, branch in en.branches.items():
    if name == 'Transformer':
        branch.parameters.max_power = 160. * 1e3 * 1.1

en.solve_load_flow(auth=auth)
print(en.res_transformers.power1.isna().sum())

Expected behavior
The real value of res_transformers.power1 is not NaN for the neutral branch.

Version details
roseau-load-flow 0.6.0

System Information
------------------
python               3.10.8.final.0
os                   linux
os_name              posix
machine              x86_64
Installed Dependencies
----------------------
pandas               2.1.0
numpy                1.23.5
geopandas            0.13.2
shapely              2.0.1
regex                2022.10.31
pint                 0.22
requests             2.31.0

Id management

The following elements must be modified for a good management of id:

  • The attribute id must be defined at the top of the class architecture. It could be in the JsonMixin class or preferably in a new class (eg. IdentifiableObject). This class must be above the Element class.
  • The classes LineCharacteristics and TransformerCharacteristics must inherit from this class (and from JsonMixin). Their parameter type_name can be renamed id and integer must be allowed as id.
  • Add an id to Ground and PotentielRef elements
  • The method remove_element must take elements and not id of elements in order to allow id to be unique per element type.

Make `connected_elements` private

The Element.connected_elements list should not be public. Direct changes to the connected elements might render the network configuration invalid. I am thinking of making the attribute private.

It is currently used in two public places:

  • AbstractBranch to get the connected buses. I will add bus1 and bus2 attributes which store the connected buses. This is more clear and cannot mess up network configuration validity.
  • PotentialRef to get the connected element (bus or ground). I'll add element attribute instead.

ENH: Add a catalogue of LineParameters (similar to TransformerParameters)

Describe the feature you want. What problem does it solve?
Make a catalogue of LineParameters available to the user (ie add methods from_catalogue and print_catalogue to the LineParameters class). The values stored in the catalogue could be the inputs to the from_geometry method.

Describe alternatives you've considered
Maintaining my own catalogue of LineParameters

Additional context
This would be similar to the basic standard types in pandapower

Flexible loads

The class PowerLoad can be used to model a FlexibleLoad with an additional argument in its constructor (flexible_parameters, None by default). It must be done after #18

Exit "special elements"

This issue requires #15
The name "special elements" is not really meaningful.

  • Define a "grounds" section in the network and in the JSON export.
  • Define a "potential_refs" section in the network and in the JSON export.
  • Enforce ID uniqueness of grounds and potential refs (Requires #15).

BUG: Bad transformer parameters can induce NaN values in the solver

Describe the bug
When bad parameters values are given to TransformerParameters, it can induce NaN values for z2, that will cause singular matrix. This is because of the line :

l2_omega = np.sqrt((self._vsc * self._ulv**2 / self._sn) ** 2 - r2**2)

To Reproduce

from roseau.load_flow import *

parameters = TransformerParameters(
    id="test",
    windings="Dyn11",
    sn=50000.0,
    uhv=20000.0,
    ulv=400.0,
    i0=0.027,
    p0=210.0,
    psc=2150.0,
    vsc=0.04,
)
print(parameters.to_zyk())

Expected behavior
An exception should be raised before running the load flow.

Version details

Please copy/paste here the output of the function show_versions

from roseau.load_flow import show_versions

show_versions()
# Paste here the output of the function `show_versions`

ENH: Revisit the deprecated results serialization methods

Since version 0.7, the results_to_dict/json and results_from_dict/json method have been deprecated in favor of including the results in the network or element dictionary.
It could be useful to keep the results_to_dict/json methods as they could be used to store the results of a timeseries study where storing the whole network dictionary might not be desirable for performance and saving disk space reasons. I propose the following:

  1. Un-deprecate the results_to_dict/json methods and remove the results_from_dict/json methods. The users are free to use the exported results for analysis with their preferred tools but cannot load them back to the network.
  2. Document the sturcture of the results dict/JSON artifact. We should try to keep this structure stable over time. Maybe add a version key in the produced dictionary?

ENH: Add `type` attributes to loads and branches and `flexible` column to loads frames

  • Add a type attribute to loads that can take the values: "power", "current", "impedance". The attribute already exists as _type, it needs to be renamed and changed to a property.
  • Add a column type in the loads dataframes on the electrical network including loads_frame, res_loads, res_loads_...
  • Add a column flexible to the loads dataframes in the electrical network. This corresponds to the attribute is_flexible on the loads classes.
  • Rename the branch_type attribute on branches and the branch_type column in the branches dataframes to type to be consistent with the loads.

ENH: `PowerLoad` modifications

Describe the feature you want. What problem does it solve?
Currently, a PowerLoad is a real load when the provided active powers are positive and a static generator when the provided active powers are negative.

Describe alternatives you've considered
An alternative could be to create a StaticGenerator class accepting production powers and to change the PowerLoad to only accept consumption powers.

ENH: Transformers

Currently, only three-phase transformers can be modelled with the solver.

ENH: NetworkX

Add a method to export a network to a NetworkX directed graph. This dependency can be an extra dependency as matplotlib.

DOC: Flexible loads feasible domains

Location of the document
Flexible load section

Documentation problem
It seems that several elements are not clear in the documentation:

  • Add more explanations to the feasible domains section.
  • Maybe add some plot with some (P, Q) points per voltage in a wide range of voltages to see them in the (P,Q) disc. (for another issue: maybe allow the user to plot such figures by adding a plot method to FlexibleLoad class)
    Trajectoire_Controle_PmaxU_QU
  • Emphasize the fact that the projection may reduce P or Q or both values. Maybe add some control curves (P(V) or Q(V)) with the effect of the projection. Currently, there are only theoretical curves.
  • Change the word circle in favour of disc. When the (P, Q) point lies in the disc, no projection is performed.
  • Emphasize that s_max defines the maximum possible consumed or produced reactive power. Later, we plan to allow the user to define his own values. See #126

@YassineAbdelouadoud

ENH: Voltage Unbalance Factor (VUF)

Add a property to get the Voltage Unbalance Factor (VUF) defined as the ratio of the negative sequence voltage phasors by the positive one. Add it in the data frame of buses' results.

See this paper for ore details on the subject

BUG: Non-physical power flow solution returned when simulating MV/LV network

Describe the bug
When simulating a MV/LV network (MV feeder to which is connected a MV/LV transformer, a LV line and a constant power load), it seems that a non-physical power flow solution can be returned for specific combination of inputs.

To Reproduce
Minimal Working Example to understand the problem

import pandas as pd
from roseau.load_flow import (ElectricalNetwork, Line, Bus, Transformer, LineParameters, LineType, ConductorType,
                              InsulatorType, Q_, PowerLoad, CurrentLoad, TransformerParameters)

results = []

line_parameters = LineParameters.from_geometry(
    "T_AL_70",
    line_type=LineType.TWISTED,
    conductor_type=ConductorType.AL,
    insulator_type=InsulatorType.PEX,
    section=70,  # mm²
    section_neutral=54,  # mm²
    height=10,  # m
    external_diameter=Q_(3.9, "cm"),
    max_current=150.
)

transformer_parameters = TransformerParameters.from_catalogue(id='FT_Standard_Standard_250kVA')
transformer_parameters.max_power = transformer_parameters.sn * 1.1

for lv_load in tqdm(np.arange(55., 70., 1.)):

    en = ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
    mv_lv_bus_id = 'MVLV02676'

    lv_side_bus = Bus(id=mv_lv_bus_id + '_LV',
                      phases="abcn",
                      min_voltage=230. * 0.915,
                      max_voltage=230. * 1.085)
    # create transformer
    Transformer(id=mv_lv_bus_id + '_Transformer',
                bus1=en.buses[mv_lv_bus_id],
                bus2=lv_side_bus,
                parameters=transformer_parameters,
                phases1='abc',
                phases2='abcn')

    lv_bus_0 = Bus(id=mv_lv_bus_id + '_LV_0',
                   phases="abcn",
                   min_voltage=230. * 0.915,
                   max_voltage=230. * 1.085)

    new_line = Line(id=mv_lv_bus_id + '_line',
                    bus1=lv_side_bus,
                    bus2=lv_bus_0,
                    parameters=line_parameters,
                    phases='abcn',
                    ground=en.grounds['ground'],
                    length=0.1)
    load = PowerLoad(id=mv_lv_bus_id + '_load', bus=lv_bus_0,
                       powers=[complex(lv_load * 1.e3, 0.3 * lv_load), 0., 0.])
  
    en.solve_load_flow(auth=auth)
    v_mag = en.res_buses_voltages['voltage'].abs()
    lv_v_mag = v_mag[v_mag < 1000.]

    results.append(
        {
            'load': lv_load,
            'transfo_load1': en.res_transformers['power1'].abs().sum() * 1.e-3,
            'min_voltage': lv_v_mag.min(),
            'max_lv_voltage': lv_v_mag.max()
        }
    )

res_df = pd.DataFrame.from_records(results)
res_df['trafo_power_ratio'] = res_df['transfo_load1'] / res_df['load']
print(res_df)
    load  transfo_load1  min_voltage  max_lv_voltage  trafo_power_ratio
0   55.0      79.510371   200.891534      236.545121           1.445643
1   56.0      81.041662   200.293016      236.703442           1.447173
2   57.0      82.584240   199.690045      236.862680           1.448846
3   58.0      84.138325   199.082522      237.022856           1.450661
4   59.0     492.998626    32.109242      303.108455           8.355909
5   60.0     491.491145    32.755285      302.807896           8.191519
6   61.0      88.871999   197.231602      237.509223           1.456918
7   62.0     488.440114    34.062084      302.200464           7.878066
8   63.0      92.089861   195.972916      237.838563           1.461744
9   64.0      93.718248   195.335794      238.004836           1.464348
10  65.0      95.360005   194.693316      238.172212           1.467077
11  66.0      97.015451   194.045349      238.340720           1.469931
12  67.0      98.684915   193.391752      238.510388           1.472909
13  68.0     100.368746   192.732379      238.681246           1.476011
14  69.0     102.067307   192.067078      238.853326           1.479236

The same simulation, but using CurrentLoad instead of PowerLoad yields coherent results :

load = CurrentLoad(id=mv_lv_bus_id + '_LV_0_load', bus=lv_bus_0,
                           currents=[complex(lv_load * 1.e3, 0.3 * lv_load) / 230., 0., 0.] )

    load  transfo_load1  min_voltage  max_lv_voltage  trafo_power_ratio
0   55.0      74.132574   203.418299      239.287685           1.347865
1   56.0      75.272632   202.956715      239.479149           1.344154
2   57.0      76.412936   202.495167      239.670675           1.340578
3   58.0      77.553474   202.033657      239.862261           1.337129
4   59.0      78.694233   201.572184      240.053908           1.333801
5   60.0      79.835203   201.110749      240.245616           1.330587
6   61.0      80.976373   200.649352      240.437384           1.327482
7   62.0      82.117732   200.187994      240.629213           1.324480
8   63.0      83.259271   199.726673      240.821102           1.321576
9   64.0      84.400982   199.265392      241.013051           1.318765
10  65.0      85.542856   198.804149      241.205061           1.316044
11  66.0      86.684885   198.342946      241.397130           1.313407
12  67.0      87.827062   197.881783      241.589258           1.310852
13  68.0      88.969380   197.420659      241.781447           1.308373
14  69.0      90.111832   196.959575      241.973694           1.305969

The same simulation as the first one, but replacing the MV feeder by a voltage source also yields coherent results :

    load  transfo_load1  min_voltage  max_lv_voltage  trafo_power_ratio
0   55.0      79.415539   203.461498      238.598885           1.443919
1   56.0      80.934474   202.876465      238.756005           1.445258
2   57.0      82.464217   202.287252      238.914001           1.446741
3   58.0      84.004967   201.693773      239.072891           1.448362
4   59.0      85.556934   201.095934      239.232694           1.450118
5   60.0      87.120337   200.493641      239.393431           1.452006
6   61.0      88.695406   199.886795      239.555123           1.454023
7   62.0      90.282379   199.275294      239.717790           1.456167
8   63.0      91.881507   198.659032      239.881455           1.458437
9   64.0      93.493051   198.037898      240.046142           1.460829
10  65.0      95.117283   197.411778      240.211874           1.463343
11  66.0      96.754490   196.780553      240.378677           1.465977
12  67.0      98.404968   196.144098      240.546577           1.468731
13  68.0     100.069028   195.502286      240.715600           1.471603
14  69.0     101.746997   194.854980      240.885776           1.474594

Expected behavior
In the first simulation, we can observe that the lowest voltage magnitude drops from 199 V to 32 V when increasing the load from 58 to 59 kW, while the voltage drop from 57 kW to 58 kW was only 0.6 V. The same happens for 60 kW and 62 kW. This voltage drop causes huge current (and thus losses) in the line, which translates into overloading of the transformer.

I expect the lowest voltage to be between 197 and 199 V for the 59 and 60 kW.

Version details
roseau-load-flow 0.6.0

System Information
------------------
python               3.10.8.final.0
os                   linux
os_name              posix
machine              x86_64
Installed Dependencies
----------------------
pandas               2.1.0
numpy                1.23.5
geopandas            0.13.2
shapely              2.0.1
regex                2022.10.31
pint                 0.22
requests             2.31.0

ENH: Transformer catalogue

I have done a small catalogue of three-phase transformers for another project. I can add this catalogue to the data of the repository.

  • Add a from_catalogue(cls, manufacturer: str, **kwargs) class method to the TransformerParameters class.
  • Add a print_catalogue(cls, manufacturer: str |None=None, **kwargs) to print the catalogue of transformers. Optional arguments allow the user to print a subpart of the catalogue only. TransformerParameters.print_catalogue(manufacturer="SE", product_type="Minera")

ENH: `LineParameters` alternative constructors

In #121, I realized several things that should be done regarding the LineParameters class. Here is a small list of remaining tasks

  • In the class method from_geometry, there is a geometric configuration for a twisted line and for an underground line. There is no configuration for overhead line (flag configuration for example). Currently, the overhead is linked to the twisted configuration.
  • In the class method from_geometry, some checks should be done:
    • with an underground line, the height parameter should be negative. For overhead and twisted lines, height should be positive
    • when the sections become large, there is some geometric infeasibility because of the d_{ext}/4 constraint (especially in the twisted configuration) => Maybe modify the provided lengths/dimensions to ensure that no impossible configurations can be generated.
  • In the class method from_geometry, if no neutral data are provided, maybe consider generating configuration without neutral wire (3x3 matrices). Or take a new "phases" argument to define the number of conductors in the geometric configuration.
  • The class method from_name_mv uses the Coiffier's coefficients to generate the impedance and shunt admittance matrices. It needs to be refactor in a more interesting name. The documentation doesn't exist...
  • The class method from_name_lv is a wrapper around the from_geometry class method with default values. I think it should be removed.

BUG: DimensionalityError when voltage of the source is Quantity

Describe the bug
Passing a list of Quantity objects to the source voltages errors with this traceback

/tmp/rlf/.venv/lib/python3.12/site-packages/roseau/load_flow/models/sources.py:103: UnitStrippedWarning: The unit of the quantity is stripped when downcasting to ndarray.
  self._voltages = np.asarray(voltages, dtype=complex)
Traceback (most recent call last):
  File "/tmp/rlf/main.py", line 21, in <module>
    vs = lf.VoltageSource(id="vs", bus=source_bus, voltages=source_voltages)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/rlf/.venv/lib/python3.12/site-packages/roseau/load_flow/models/sources.py", line 78, in __init__
    self.voltages = voltages
    ^^^^^^^^^^^^^
  File "/tmp/rlf/.venv/lib/python3.12/site-packages/pint/registry_helpers.py", line 279, in wrapper
    result = func(*new_values, **kw)
             ^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/rlf/.venv/lib/python3.12/site-packages/roseau/load_flow/models/sources.py", line 103, in voltages
    self._voltages = np.asarray(voltages, dtype=complex)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/rlf/.venv/lib/python3.12/site-packages/pint/facets/plain/quantity.py", line 581, in __complex__
    raise DimensionalityError(self._units, "dimensionless")
pint.errors.DimensionalityError: Cannot convert from 'kilovolt' to 'dimensionless'

To Reproduce

Take the example network from the getting started page and make this change

-un = 400 / np.sqrt(3)
+un = Q_(0.4 / np.sqrt(3), "kV")

Expected behavior
No error

DOC: Models section

A "Models" section is missing in the documentation. Currently, each model has its equations in the API section only. A detailed page per model should be added:

  • VoltageSource
  • Loads: PowerLoad, CurrentLoad, ImpedanceLoad
  • Flexibility in power loads
  • Transformers
  • Lines/Switch
  • Ground
  • PotentialRef

For each model, present the underlying equations and a minimal example of usage. If possible, give a link to some example networks which use this model (or to an existing tutorial).

For the flexible power loads, create a simple example in which the effect of the projection can be seen. A single line, a single flexible load and we plot the theoretical control function and the real control function (different because of a projection)

Show versions

In order to help debugging clients, a show_versions method should be implemented. I suggest using the function show_versions of pandas as a starting point.

ENH: make the limit on bus number a parameter

Describe the feature you want. What problem does it solve?
Currently, the limit of bus number for a simulation is set to 1000 and, to the best of my knowledge, cannot be configured by the user. When modelling MV and LV networks together (example : one MV feeder and the connected LV feeders), this limit is frequently surpassed. I propose to make this limit a parameter that the user can set, at least for the future local version if not the SaaS one.

Describe alternatives you've considered
I am currently only working with networks with fewer than 1000 buses for testing purposes.

Additional context
For a random sample of 100 MV feeders, there are around 2/3 of networks with more than 1000 buses. The biggest one is close to 10000 buses.

Merging `to_dict` and `results_to_dict` methods

I am working on merging the to_dict and results_to_dict methods (also ..._to_json methods) and I have a question about the new design.
The current in progress work adds a include_results=True parameter to these methods. This means that the results will be included by default when converting to a dict or to json. This however would fail if the results are invalid or if no load flow is run yet. Another option is to make the default False and error when the user set it to True when results are invalid. I see four options to move forward:

  1. Include the results by default (include_results=True) and ignore them if they are invalid. Warn the user when trying to convert a network with invalid results.
  2. Include the results by default (include_results=True) and ignore them if they are invalid silently (no warning).
  3. Include the results by default (include_results=True) and raise an error when trying to convert a network with invalid results.
  4. Do not include the results by default (include_results=False). Raise an error when trying to convert a network with explicit include_results=True passed by the user and invalid results.

I tend to prefer option 1. I think including the results is a good default and I don't like raising an error on freshly created networks with no load flow results.

What do you think would be the best option?

Add methods to get the power results of loads and lines

  • The AbstractLoad class must add a res_powers property (in addition to the existing (res_)currents)
  • The AbstractBranch class must add a res_powers_losses property (in addition to the existing (res_)currents). 0VA for switches.

Phases management

In order to remove the n argument to constructor of classes, we will use phases as strings. Here is the definition of these new arguments:

  • Bus:
    • the allowed phases are "abc", "abcn", "ab", "bc", "ca", "an", "bn", "cn", "abn", "can", "bcn".
    • This argument of the constructor is mandatory.
  • VoltageSource:
    • the allowed phases are the same as for the buses.
    • Nevertheless, it is not required. By default, the phases of the bus are taken
    • The phases, except "n", must be present in the bus it is connected to.
  • AbstractLoad: idem as voltage source. NB: When the phase contains a "n", the load will be a star-load, otherwise a delta-load.
  • Line:
    • Only a single phases argument to the constructor. The allowed values are "abc", "abcn", "ab", "bc", "ca", "an", "bn", "cn", "abn", "can", "bcn", "a", "b", "c", "n".
    • Nevertheless, it is not required. The intersection of the phases of the two buses at the extremities of the line are taken by default.
    • The phases of the line must be present in the upstream and in the downstream buses.
    • The size of the matrices of the line characteristics must be equal to the number of phases of the line.
  • Switch ⇾ Same as Line
  • Transformers
    • The arguments phases1 and phases2 must be added in the constructor of the class.
    • For the moment, only the phases "abc" and "abcn" are accepted
    • It is not mandatory to define them. The windings of the transformer characteristics define the default: "D" ⇾ "abc", "Y" ⇾ "abcn". (modification: if "D" or "Y" or "Z" -> "abc", if "YN" or "ZN" -> "abcn")
    • The phases1 must be present in the upstream bus.
    • The phases2 must be present in the downstream bus.
    • The winding of the transformer characteristics must be compliant with the selected phases1 and phases2.
  • Ground
    • The constructor only have an id argument
    • The Ground class has a method `connect which takes two arguments: a) the bus to connect to; b) the phase to connect to
    • The phase argument of the connect method only accepts "a", "b", "c" or "n" as argument.
    • Its default value is "n".
    • The chosen phase must be available in the bus.
  • PotentialRef
    • Its constructor takes an element which is a bus or a ground instance.
    • Its constructor takes a phase argument which can be a, b, c or n or None.
    • The default value of the phase argument is None
    • If the provided element is a ground, its potential is fixed at 0V.
    • If the provided phase is None and if the bus' phases contains "n", we fix the potential of the neutral of the bus to zero. Otherwise, we define the sum of potentials of the bus to 0V
    • If the provided phase is not None, we check that this value is present in the bus' phase, and we fix it to 0V
    • In the JSON export, the value provided in the constructor must be serialized (Nonenull) (to be added in #19)

Input and output data and methods

In order to avoid confusions, a lot of methods and arguments must be renamed:

  • In the Line, Transformer, *Load and VoltageSource, the update_* methods will be modified by property setter
  • In the constructor of PowerLoad (resp. CurrentLoad and ImpedanceLoad), the s (resp. i and z) parameters will be renamed powers (resp. currents and impedances)
  • All the results of element classes and ElectricalNetwork class must be prefixed with res_ (res_potentials, res_voltages, res_currents) and changed into properties

DOC: Clarify the installation instructions for beginners

  • Add a note explaining that the pip/conda commands are executed in a terminal ($ or C:> ), not in a Python console (>>> )
  • Add instructions for installing RLF in a jupyter notebook using the %pip install jupyter cell magic. If people are not able to find the notebook's kernel environment, this would unblock them.
  • Add a note for beginners to Python with a link to Python's tutorial
  • Add a link back to the github repository as a banner or in the footer
  • Communicate better what Python versions we support

JSON format

Some modifications to the JSON format:

  • Make all objects top-levels. Require #17
  • Add a version number to the file
  • Ensure that the ground connections and potentials references connection are well saved and restored. Require #16

BUG: res_transformers attribute is an empty Dataframe

Describe the bug
When running a powerflow with a network that includes a transformer (without setting the max_power attribute of the TransformerParameters instance), the returned res_transformers is an empty Dataframe.

To Reproduce
Can be reproduced with the documentation example
Three_Phase_Transformer

Expected behavior
I expect the res_transformers Dataframe to be populated whether I set the max_power attribute or not.

Version details

roseau-load-flow version is 0.6.0

System Information
------------------
python               3.10.8.final.0
os                   linux
os_name              posix
machine              x86_64
Installed Dependencies
----------------------
pandas               2.1.0
numpy                1.23.5
geopandas            0.13.2
shapely              2.0.1
regex                2022.10.31
pint                 0.22
requests             2.31.0

ENH: Maximum produced/consumed reactive power

Describe the feature you want. What problem does it solve?
For flexible loads, the value s_max defines the maximum reactive power which can be consumed or produced for the Q(U) control. It would be great to define in this control two optional values to define the q_min and q_max to limit the maximum reactive power which can be consumed or produced.

Example:

from roseau.load_flow import FlexibleParameter, Control, Projection, Q_

fp = FlexibleParameter(
    control_p=Control.constant(),
    control_q=Control.q_u(
        u_min=Q_(210, "V"), u_down=Q_(220, "V"), u_up=Q_(240, "V"), u_max=Q_(250, "V"),
        q_max=Q(2.5, "kVAr"), # <----- New value
        q_min=Q(-2, "kVAr"), # <----- New value
    ),
    projection=Projection(type="keep_p"),
    s_max=Q_(5, "kVA"),
)

In the Q(U) theoretical curve (see below), it will q_max/s_max would replace the "1" on the Y axis and q_min/s_max the "-1" on the Y axis. At any time, the value Q provided by the user must lie between q_min and q_max. Obviously, abs(q_min)<s_max and abs(q_max)<s_max and q_min<q_max

@YassineAbdelouadoud

BUG: JSON export with LineParameters created from catalogue

Describe the bug
The to_json method fails when LineParameters created using the from_catalogue method is present in the network.

TypeError: Object of type int64 is not JSON serializable

the problem comes from the max_current and section parameters

To Reproduce

from roseau.load_flow import *

  line_parameters = LineParameters.from_catalogue(
      "T_AL_70",
  )
  en = ElectricalNetwork.from_catalogue(name="MVFeeder210", load_point_name="Winter")
  list(en.branches.values())[1].parameters = line_parameters
  en.to_json('tmp.json')

Expected behavior
Working JSON export

System Information
------------------
python               3.10.8.final.0
os                   linux
os_name              posix
machine              x86_64
Installed Dependencies
----------------------
pandas               2.1.0
numpy                1.23.5
geopandas            0.13.2
shapely              2.0.1
regex                2022.10.31
pint                 0.22
platformdirs         4.1.0
certifi              2023.7.22
roseau-load-flow-engine 0.12.0a0

Connection simplification

Add a connect method to element to avoid the repetition of lines like

self.connected_elements = [bus]
bus.connected_elements.append(self)

ENH: Expose the line series and shunt currents

For a line with shunt elements, there is no public API that exposes the currents that flow in the series components and those in the shunt components. We have these values internally but we don't expose them yet.

To make them public we need to:

  • Add res_shunt_currents and res_series_currents properties to the Line class
  • Modify the res_lines_losses data frame on the electrical network to include the currents

For a simplified line with no shunt components, the series currents are equal to the currents from buses 1 & 2 and the shunt currents are zero.

ENH: Detect invalid element overrides

We should detect when multiple elements with same type and ID are being created and raise an error. For example, this code should raise an error:

import roseau.load_flow as lf
bus = lf.Bus("bus1", phases="an")
load1 = lf.PowerLoad("load1", bus, powers=[1000])
load2 = lf.PowerLoad("load1", bus, powers=[1000])  # <- oops, forgot to change the ID

The same problem could happen when working in notebooks if a cell that creates a load is called multiple times.

Preventing this helps avoid problems that are difficult to debug because the duplicate elements will not appear in the dictionaries of the ElectricalNetwork class, but they exist in the internal structure representing the low level network.

Implementation details
To do this, we can replace the _connected_elements private attribute on the Element class by separate dictionaries, one per element type (similar to the electrical network loads, buses, etc dictionaries). We can also add an iterator method _iter_connected_elements that yields from these dictionaries for easy access to all elements.

Documentation updates

  • Update the tutorials to work with the new models
  • Simplify the API documentation.
    • Avoid showing private attributes like logger
    • Avoid deep nesting of modules
    • Add more information about the controller, projections, control types, etc.
  • Explain more how to save a network and recreate it, save results etc.
  • Document the models of all the elements. If possible, always include the electrical diagrams.
  • Explain the matrices z_line and y_shunt of the lines; include an example.
  • Explain how the ground and potential reference work; write an example of a "simple" network with one of each.

BUG: Transformer Yz

Describe the bug
The transformers Yz11 and Yz5 do not work as intended, they are producing power instead of consuming it. There is a problem in the transformation matrices.

DOC: Describe the proper way to replace a Transformer or a Line

Location of the document
disconnecting-an-element

Documentation problem
The documentation explains how to disconnect a Load, but not the proper way to replace a Transformer or Line with a new one with different parameters. The use case would be : a constraint is violated in a transformer or line, the user wants to replace it with a larger one, without having to recreate the network from scratch.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.