Open Sustainable Technology

A curated list of open technology projects to sustain a stable climate, energy supply, biodiversity and natural resources.

Browse accepted projects | Review proposed projects | Propose new project | Open Issues

GridCal

Aims to be a complete platform for power systems research and simulation.
https://github.com/sanpen/gridcal

acdc cim comon-information-model electrical electrical-engineering helm latin-hypercube monte-carlo-simulation multi-terminal newton-raphson optimization power-systems powerflow python stochastic-power-flow

Last synced: about 6 hours ago
JSON representation

Repository metadata

GridCal, a cross-platform power systems software written in Python with user interface, used in academia and industry.

README

        

# GridCal

GridCal is a top tier power systems planning and simulation software.
As such it has all the static analysis studies that you can think of, plus
linear and non-linear optimization functions. Some of these functions are
well known, while others you may have never heard of as they are a
product of cutting-edge research.

![](pics/GridCal.png)

[![Codacy Badge](https://api.codacy.com/project/badge/Grade/75e794c9bcfd49bda1721b9ba8f6c790)](https://app.codacy.com/app/SanPen/GridCal?utm_source=github.com&utm_medium=referral&utm_content=SanPen/GridCal&utm_campaign=Badge_Grade_Dashboard)
[![Documentation Status](https://readthedocs.org/projects/gridcal/badge/?version=latest)](https://gridcal.readthedocs.io/en/latest/?badge=latest) [![Build Status](https://travis-ci.org/SanPen/GridCal.svg?branch=master)](https://travis-ci.org/SanPen/GridCal)
[![DOI](https://www.zenodo.org/badge/49583206.svg)](https://www.zenodo.org/badge/latestdoi/49583206)
[![Downloads](https://static.pepy.tech/personalized-badge/gridcal?period=total&units=abbreviation&left_color=grey&right_color=green&left_text=Downloads)](https://pepy.tech/project/gridcal)

GridCal started in 2015 with a clear objective: create a solid programming library and a user-friendly interface.
This straightforward approach sparked many innovations — some driven by the necessity
for commercial use, and others fueled by curiosity and research.

Whether you're a pro needing free tools, a researcher wanting a real-world tested platform,
a teacher sharing commercial-grade software insights, or a student diving into practical algorithms,
GridCal's got your back. It's a high quality product made for all of us now and
for the future generations.

## Installation

GridCal is a software made in the Python programming language.
Therefore, it needs a Python interpreter installed in your operative system.

### Standalone setup

If you don't know what is this Python thing, we offer a windows installation:

[Windows setup](https://www.advancedgridinsights.com/gridcal)

This will install GridCal as a normal windows program and you need not to worry
about any of the previous instructions. Still, if you need some guidance, the
following video might be of assistance: [Setup tutorial (video)](https://youtu.be/SY66WgLGo54).

### Package installation

We recommend to install the latest version of [Python](www.python.org) and then,
install GridCal with the following terminal command:

```
pip install GridCal
```

You may need to use `pip3` if you are under Linux or MacOS, both of which
come with Python pre-installed already.

### Install into an environment

```bash
python3 -m venv gc5venv
source gc5venv/bin/activate
pip install GridCal
gridcal
```

### Run the graphical user interface

Once you install GridCal in your local Python distribution, you can run the
graphical user interface with the following terminal command:

```
gridcal
```

If this doesn't work, try:

```
python -c "from GridCal.ExecuteGridCal import runGridCal; runGridCal()"
```

You may save this command in a shortcut for easy future access.

### Install only the engine

Some of you may only need GridCal as a library for some other purpose
like batch calculations, AI training or simple scripting. Whatever it may be,
you can get the GridCal engine with the following terminal command:

```
pip install GridCalEngine
```

This will install the `GridCalEngine` package that is a dependency of `GridCal`.

Again, you may need to use `pip3` if you are under Linux or MacOS.

## Features

GridCal is packed with feautures:

- Large collection of devices to model electricity grids
- AC/DC multi-grid power flow
- AC/DC multi-grid linear optimal power flow
- AC linear analysis (PTDF & LODF)
- AC linear net transfer capacity calculation
- AC+HVDC optimal net transfer capacity calculation
- AC/DC Stochastic power flow
- AC Short circuit
- AC Continuation power flow
- Contingency analysis (Power flow and LODF variants)
- Sigma analysis (one-shot stability analysis)
- Investments analysis
- Bus-branch schematic
- Substation-line map diagram
- Time series and snapshot for most simulations
- Overhead tower designer
- Inputs analysis
- Model bug report and repair
- Import many formats (PSSe .raw/rawx, epc, dgs, matpower, pypsa, json, cim, cgmes)
- Export in many formats (gridcal .xlsx/.gridcal/.json, cgmes, psse .raw/.rawx)

All of these are industry tested algoriths, some of which surpass most comemercially available software.
The aim is to be a drop-in replacement for the expensive and less usable commercial
software, so that you can work, research and learn with it.

### Resources

In an effort to ease the simulation and construction of grids,
We have included extra materials to work with. These are included in the standalone setups.

- [Load profiles](https://github.com/SanPen/GridCal/tree/master/Grids_and_profiles/equipment) for your projects.
- [Grids](https://github.com/SanPen/GridCal/tree/master/Grids_and_profiles/grids) from IEEE and other open projects.
- [Equipment catalogue](https://gridcal.readthedocs.io/en/latest/data_sheets.html) (Wires, Cables and Transformers) ready to use in GridCal.

### Tutorials and examples

- [Tutorials](https://gridcal.readthedocs.io/en/latest/rst_source/tutorials/tutorials_module.html)

- [Cloning the repository (video)](https://youtu.be/59W_rqimB6w)

- [Making a grid with profiles (video)](https://youtu.be/H2d_2bMsIS0)

- [GridCal PlayGround repository](https://github.com/yasirroni/GridCalPlayground) with some notebooks and examples.

- [The tests](https://github.com/SanPen/GridCal/tree/master/src/tests) may serve as a valuable source of examples.

## API

Since day one, GridCal was meant to be used as a library as much as it was meant
to be used from the user interface. Following, we include some usage examples, but
feel free to check the [documentation](https://gridcal.readthedocs.io) out where you will find a complete
description of the theory, the models and the objects.

### Understanding the program structure

All simulations in GridCal are handled by the simulation drivers. The structure is as follows:

Any driver is fed with the data model (`MultiCircuit` object), the respective driver options, and often another
object relative to specific inputs for that driver. The driver is run, storing the driver results object.
Although this may seem overly complicated, it has proven to be maintainable and very convenient.

### Snapshot vs. time series
GridCal has dual structure to handle legacy cases (snapshot), as well as cases with many variations (time series)

- A **snapshot** is the grid for a particular moment in time.
This includes the infrastructure plus the variable values of that infraestructure
such as the load, the generation, the rating, etc.

- The **time series** record the variations of the magnitudes that can vary. These are aplied along with
the infrastructure definition.

In GridCal, the inputs do not get modified by the simulation results. This very important concept, helps
maintaining the independence of the inputs and outputs, allowing the replicability of the results.
This key feature is not true for other open-source of comercial programs.

A snapshot or any point of the time series, may be compiled to a `NumericalCircuit`. This object holds the
numerical arrays and matrices of a time step, ready for the numerical methods.
For those simulations that require many time steps, a collection of `NumericalCircuit` is compiled and used.

It may seem that this extra step is redundant. However the compilation step is composed by mere copy operations,
which are fast. This steps benefits greatly the efficiency of the numerical calculations since the arrays are
aligned in memory. The GridCal data model is object-oriented, while the numerical circuit is array-oriented
(despite beign packed into objects)

### Loading a grid

```python
import GridCalEngine.api as gce

# load a grid
my_grid = gce.open_file("my_file.gridcal")
```

GridCal supports a plethora of file formats:

- CIM 16 (.zip and .xml)
- CGMES 2.4.15 (.zip and .xml)
- PSS/e raw and rawx versions 29 to 35, including USA market excahnge RAW-30 specifics.
- Matpower .m files directly.
- DigSilent .DGS (not fully compatible)
- PowerWorld .EPC (not fully compatible, supports substation coordinates)

### Save a grid

```python
import GridCalEngine.api as gce

# load a grid
my_grid = gce.open_file("my_file.gridcal")

# save
gce.save_file(my_grid, "my_file_2.gridcal")
```

### Creating a Grid using the API objects

We are going to create a very simple 5-node grid from the excellent book
*Power System Load Flow Analysis by Lynn Powell*.

```python
import GridCalEngine.api as gce

# declare a circuit object
grid = gce.MultiCircuit()

# Add the buses and the generators and loads attached
bus1 = gce.Bus('Bus 1', vnom=20)
# bus1.is_slack = True # we may mark the bus a slack
grid.add_bus(bus1)

# add a generator to the bus 1
gen1 = gce.Generator('Slack Generator', vset=1.0)
grid.add_generator(bus1, gen1)

# add bus 2 with a load attached
bus2 = gce.Bus('Bus 2', vnom=20)
grid.add_bus(bus2)
grid.add_load(bus2, gce.Load('load 2', P=40, Q=20))

# add bus 3 with a load attached
bus3 = gce.Bus('Bus 3', vnom=20)
grid.add_bus(bus3)
grid.add_load(bus3, gce.Load('load 3', P=25, Q=15))

# add bus 4 with a load attached
bus4 = gce.Bus('Bus 4', vnom=20)
grid.add_bus(bus4)
grid.add_load(bus4, gce.Load('load 4', P=40, Q=20))

# add bus 5 with a load attached
bus5 = gce.Bus('Bus 5', vnom=20)
grid.add_bus(bus5)
grid.add_load(bus5, gce.Load('load 5', P=50, Q=20))

# add Lines connecting the buses
grid.add_line(gce.Line(bus1, bus2, name='line 1-2', r=0.05, x=0.11, b=0.02))
grid.add_line(gce.Line(bus1, bus3, name='line 1-3', r=0.05, x=0.11, b=0.02))
grid.add_line(gce.Line(bus1, bus5, name='line 1-5', r=0.03, x=0.08, b=0.02))
grid.add_line(gce.Line(bus2, bus3, name='line 2-3', r=0.04, x=0.09, b=0.02))
grid.add_line(gce.Line(bus2, bus5, name='line 2-5', r=0.04, x=0.09, b=0.02))
grid.add_line(gce.Line(bus3, bus4, name='line 3-4', r=0.06, x=0.13, b=0.03))
grid.add_line(gce.Line(bus4, bus5, name='line 4-5', r=0.04, x=0.09, b=0.02))
```

### Power Flow

Using the simplified API:

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE39_1W.gridcal')
main_circuit = gce.open_file(fname)

results = gce.power_flow(main_circuit)

print(main_circuit.name)
print('Converged:', results.converged, 'error:', results.error)
print(results.get_bus_df())
print(results.get_branch_df())
```

Using the more complex library objects:

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE14_from_raw.gridcal')
main_circuit = gce.open_file(fname)

options = gce.PowerFlowOptions(gce.SolverType.NR, verbose=False)
power_flow = gce.PowerFlowDriver(main_circuit, options)
power_flow.run()

print(main_circuit.name)
print('Converged:', power_flow.results.converged, 'error:', power_flow.results.error)
print(power_flow.results.get_bus_df())
print(power_flow.results.get_branch_df())
```

Output:
```text
IEEE14_from_raw

Converged: True error: 5.98e-08

Bus resuts:
Vm Va P Q
BUS 1 1.06 0.00 232.39 -16.55
BUS 2 1.04 -4.98 18.30 30.86
BUS 3 1.01 -12.73 -94.20 6.08
BUS 4 1.02 -10.31 -47.80 3.90
BUS 5 1.02 -8.77 -7.60 -1.60
BUS 6 1.07 -14.22 -11.20 5.23
BUS 7 1.06 -13.36 0.00 0.00
BUS 8 1.09 -13.36 0.00 17.62
BUS 9 1.06 -14.94 -29.50 -16.60
BUS 10 1.05 -15.10 -9.00 -5.80
BUS 11 1.06 -14.79 -3.50 -1.80
BUS 12 1.06 -15.08 -6.10 -1.60
BUS 13 1.05 -15.16 -13.50 -5.80
BUS 14 1.04 -16.03 -14.90 -5.00

Branch results:
Pf Qf Pt Qt loading
1_2_1 156.88 -20.40 -152.59 27.68 -2,040,429,074,673.33
1_5_1 75.51 3.85 -72.75 2.23 385,498,944,321.99
2_3_1 73.24 3.56 -70.91 1.60 356,020,306,394.25
2_4_1 56.13 -1.55 -54.45 3.02 -155,035,233,483.95
2_5_1 41.52 1.17 -40.61 -2.10 117,099,586,051.68
3_4_1 -23.29 4.47 23.66 -4.84 447,311,351,720.93
4_5_1 -61.16 15.82 61.67 -14.20 1,582,364,180,487.11
6_11_1 7.35 3.56 -7.30 -3.44 356,047,085,671.01
6_12_1 7.79 2.50 -7.71 -2.35 250,341,387,213.42
6_13_1 17.75 7.22 -17.54 -6.80 721,657,405,311.13
7_8_1 -0.00 -17.16 0.00 17.62 -1,716,296,745,837.05
7_9_1 28.07 5.78 -28.07 -4.98 577,869,015,291.12
9_10_1 5.23 4.22 -5.21 -4.18 421,913,877,670.92
9_14_1 9.43 3.61 -9.31 -3.36 361,000,694,981.35
10_11_1 -3.79 -1.62 3.80 1.64 -161,506,127,162.22
12_13_1 1.61 0.75 -1.61 -0.75 75,395,885,855.71
13_14_1 5.64 1.75 -5.59 -1.64 174,717,248,747.17
4_7_1 28.07 -9.68 -28.07 11.38 -968,106,634,094.39
4_9_1 16.08 -0.43 -16.08 1.73 -42,761,145,748.20
5_6_1 44.09 12.47 -44.09 -8.05 1,247,068,151,943.25
```

### Inputs analysis

GridCal can perform a summary of the inputs with the `InputsAnalysisDriver`:

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE 118 Bus - ntc_areas.gridcal')

main_circuit = gce.open_file(fname)

drv = gce.InputsAnalysisDriver(grid=main_circuit)
mdl = drv.results.mdl(gce.ResultTypes.AreaAnalysis)
df = mdl.to_df()

print(df)
```

The results per area:
```text
P Pgen Pload Pbatt Pstagen Pmin Pmax Q Qmin Qmax
IEEE118-3 -57.0 906.0 963.0 0.0 0.0 -150000.0 150000.0 -345.0 -2595.0 3071.0
IEEE118-2 -117.0 1369.0 1486.0 0.0 0.0 -140000.0 140000.0 -477.0 -1431.0 2196.0
IEEE118-1 174.0 1967.0 1793.0 0.0 0.0 -250000.0 250000.0 -616.0 -3319.0 6510.0
```

### Linear analysis

We can run an PTDF equivalent of the power flow with the linear analysys drivers:

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE 5 Bus.xlsx')

main_circuit = gce.open_file(fname)

options_ = gce.LinearAnalysisOptions(distribute_slack=False, correct_values=True)

# snapshot
sn_driver = gce.LinearAnalysisDriver(grid=main_circuit, options=options_)
sn_driver.run()

print("Bus results:\n", sn_driver.results.get_bus_df())
print("Branch results:\n", sn_driver.results.get_branch_df())
print("PTDF:\n", sn_driver.results.mdl(gce.ResultTypes.PTDF).to_df())
print("LODF:\n", sn_driver.results.mdl(gce.ResultTypes.LODF).to_df())
```

Output:

```text
Bus results:
Vm Va P Q
Bus 0 1.0 0.0 2.1000 0.0
Bus 1 1.0 0.0 -3.0000 0.0
Bus 2 1.0 0.0 0.2349 0.0
Bus 3 1.0 0.0 -0.9999 0.0
Bus 4 1.0 0.0 4.6651 0.0

Branch results:
Pf loading
Branch 0-1 2.497192 0.624298
Branch 0-3 1.867892 0.832394
Branch 0-4 -2.265084 -0.828791
Branch 1-2 -0.502808 -0.391900
Branch 2-3 -0.267908 -0.774300
Branch 3-4 -2.400016 -1.000006

PTDF:
Bus 0 Bus 1 Bus 2 Bus 3 Bus 4
Branch 0-1 0.193917 -0.475895 -0.348989 0.0 0.159538
Branch 0-3 0.437588 0.258343 0.189451 0.0 0.360010
Branch 0-4 0.368495 0.217552 0.159538 0.0 -0.519548
Branch 1-2 0.193917 0.524105 -0.348989 0.0 0.159538
Branch 2-3 0.193917 0.524105 0.651011 0.0 0.159538
Branch 3-4 -0.368495 -0.217552 -0.159538 0.0 -0.480452

LODF:
Branch 0-1 Branch 0-3 Branch 0-4 Branch 1-2 Branch 2-3 Branch 3-4
Branch 0-1 -1.000000 0.344795 0.307071 -1.000000 -1.000000 -0.307071
Branch 0-3 0.542857 -1.000000 0.692929 0.542857 0.542857 -0.692929
Branch 0-4 0.457143 0.655205 -1.000000 0.457143 0.457143 1.000000
Branch 1-2 -1.000000 0.344795 0.307071 -1.000000 -1.000000 -0.307071
Branch 2-3 -1.000000 0.344795 0.307071 -1.000000 -1.000000 -0.307071
Branch 3-4 -0.457143 -0.655205 1.000000 -0.457143 -0.457143 -1.000000
```

Now let's make a comparison between the linear flows and the non-linear flows from Newton-Raphson:

```python
import os
from matplotlib import pyplot as plt
import GridCalEngine.api as gce

plt.style.use('fivethirtyeight')

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE39_1W.gridcal')
main_circuit = gce.open_file(fname)

ptdf_driver = gce.LinearAnalysisTimeSeriesDriver(grid=main_circuit)
ptdf_driver.run()

pf_options_ = gce.PowerFlowOptions(solver_type=gce.SolverType.NR)
ts_driver = gce.PowerFlowTimeSeriesDriver(grid=main_circuit, options=pf_options_)
ts_driver.run()

fig = plt.figure(figsize=(30, 6))
ax1 = fig.add_subplot(131)
ax1.set_title('Newton-Raphson based flow')
ax1.plot(ts_driver.results.Sf.real)
ax1.set_ylabel('MW')
ax1.set_xlabel('Time')

ax2 = fig.add_subplot(132)
ax2.set_title('PTDF based flow')
ax2.plot(ptdf_driver.results.Sf.real)
ax2.set_ylabel('MW')
ax2.set_xlabel('Time')

ax3 = fig.add_subplot(133)
ax3.set_title('Difference')
diff = ts_driver.results.Sf.real - ptdf_driver.results.Sf.real
ax3.plot(diff)
ax3.set_ylabel('MW')
ax3.set_xlabel('Time')

fig.set_tight_layout(tight=True)

plt.show()
```

![PTDF flows comparison.png](pics%2FPTDF%20flows%20comparison.png)

### Linear optimization

```python
import os
import numpy as np
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE39_1W.gridcal')

main_circuit = gce.open_file(fname)

# declare the snapshot opf
opf_driver = gce.OptimalPowerFlowDriver(grid=main_circuit)

print('Solving...')
opf_driver.run()

print("Status:", opf_driver.results.converged)
print('Angles\n', np.angle(opf_driver.results.voltage))
print('Branch loading\n', opf_driver.results.loading)
print('Gen power\n', opf_driver.results.generator_power)
print('Nodal prices \n', opf_driver.results.bus_shadow_prices)

# declare the time series opf
opf_ts_driver = gce.OptimalPowerFlowTimeSeriesDriver(grid=main_circuit)

print('Solving...')
opf_ts_driver.run()

print("Status:", opf_ts_driver.results.converged)
print('Angles\n', np.angle(opf_ts_driver.results.voltage))
print('Branch loading\n', opf_ts_driver.results.loading)
print('Gen power\n', opf_ts_driver.results.generator_power)
print('Nodal prices \n', opf_ts_driver.results.bus_shadow_prices)
```

#### Run a linear optimization and verify with power flow

Often ties, you want to dispatch the generation using a linear optimization, to then _verify_ the
results using the power exact power flow. With GridCal, to do so is as easy as passing the results of the OPF into the
PowerFlowDriver:

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE39_1W.gridcal')

main_circuit = gce.open_file(fname)

# declare the snapshot opf
opf_driver = gce.OptimalPowerFlowDriver(grid=main_circuit)
opf_driver.run()

# create the power flow driver, with the OPF results
pf_options = gce.PowerFlowOptions(solver_type=gce.SolverType.NR)
pf_driver = gce.PowerFlowDriver(grid=main_circuit,
options=pf_options,
opf_results=opf_driver.results)
pf_driver.run()

# Print results
print('Converged:', pf_driver.results.converged, '\nError:', pf_driver.results.error)
print(pf_driver.results.get_bus_df())
print(pf_driver.results.get_branch_df())
```

Output:
```text
OPF results:

Va P Shadow price
Bus 1 0.00 0.0 0.0
Bus 2 -2.22 0.0 0.0
Bus 3 -1.98 0.0 0.0
Bus 4 -2.12 0.0 0.0
Bus 5 -2.21 0.0 0.0

Pf Pt Tap angle Loading
Branch 1 -31.46 31.46 0.0 -44.94
Branch 1 -1.84 1.84 0.0 -10.20
Branch 1 -1.84 1.84 0.0 -9.18
Branch 1 0.14 -0.14 0.0 1.37
Branch 1 -48.30 48.30 0.0 -53.67
Branch 1 -35.24 35.24 0.0 -58.73
Branch 1 -4.62 4.62 0.0 -23.11

Power flow results:
Converged: True
Error: 3.13e-11

Vm Va P Q
Bus 1 1.00 0.00 1.17e+02 12.90
Bus 2 0.97 -2.09 -4.00e+01 -20.00
Bus 3 0.98 -1.96 -2.50e+01 -15.00
Bus 4 1.00 -2.61 2.12e-09 32.83
Bus 5 0.98 -2.22 -5.00e+01 -20.00

Pf Qf Pt Qt Loading
Branch 1 -31.37 -2.77 31.88 1.93 -44.81
Branch 2 -1.61 13.59 1.74 -16.24 -8.92
Branch 3 -1.44 -20.83 1.61 19.24 -7.21
Branch 4 0.46 5.59 -0.44 -7.46 4.62
Branch 5 -49.02 -4.76 49.77 4.80 -54.47
Branch 6 -34.95 -6.66 35.61 6.16 -58.25
Branch 7 -4.60 -5.88 4.62 4.01 -23.02
```

### Hydro linear OPF
The following example loads and runs the linear optimization for a system that integrates fluid elements into a regular electrical grid.

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'hydro_simple.gridcal')
grid = gce.open_file(fname)

# Run the simulation
opf_driver = gce.OptimalPowerFlowTimeSeriesDriver(grid=grid)

print('Solving...')
opf_driver.run()

print('Gen power\n', opf_driver.results.generator_power)
print('Branch loading\n', opf_driver.results.loading)
print('Reservoir level\n', opf_driver.results.fluid_node_current_level)
```

Output:
```text
OPF results:

time | p2x_1_gen | pump_1_gen | turbine_1_gen | slack_gen
------------------- | --------- | ---------- | ------------- | ---------
2023-01-01 00:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 01:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 02:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 03:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 04:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 05:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 06:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 07:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 08:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782
2023-01-01 09:00:00 | 0.0 | -6.8237821 | 6.0 | 11.823782

time | line1 | line2 | line3 | line4
------------------- | ------ | ----- | --------- | -----
2023-01-01 00:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 01:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 02:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 03:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 04:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 05:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 06:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 07:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 08:00:00 | 100.0 | 0.0 | 68.237821 | 40.0
2023-01-01 09:00:00 | 100.0 | 0.0 | 68.237821 | 40.0

time | f1 | f2 | f3 | f4
------------------- | ---------- | --- | --- | ----------
2023-01-01 00:00:00 | 49.998977 | 0.0 | 0.0 | 50.001022
2023-01-01 01:00:00 | 49.997954 | 0.0 | 0.0 | 50.002046
2023-01-01 02:00:00 | 49.996931 | 0.0 | 0.0 | 50.003068
2023-01-01 03:00:00 | 49.995906 | 0.0 | 0.0 | 50.004093
2023-01-01 04:00:00 | 49.994884 | 0.0 | 0.0 | 50.005116
2023-01-01 05:00:00 | 49.993860 | 0.0 | 0.0 | 50.006139
2023-01-01 06:00:00 | 49.992838 | 0.0 | 0.0 | 50.007162
2023-01-01 07:00:00 | 49.991814 | 0.0 | 0.0 | 50.008185
2023-01-01 08:00:00 | 49.990792 | 0.0 | 0.0 | 50.009208
2023-01-01 09:00:00 | 49.989768 | 0.0 | 0.0 | 50.010231
```

### Short circuit

GridCal has unbalanced short circuit calculations. Now let's run a line-ground short circuit in the third bus of
the South island of New Zealand grid example from reference book
_Computer Analysis of Power Systems by J. Arrillaga and C.P. Arnold_

```python
import os
import GridCalEngine.api as gce

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'South Island of New Zealand.gridcal')

grid = gce.open_file(filename=fname)

pf_options = gce.PowerFlowOptions()
pf = gce.PowerFlowDriver(grid, pf_options)
pf.run()

fault_index = 2
sc_options = gce.ShortCircuitOptions(bus_index=fault_index,
fault_type=gce.FaultType.LG)

sc = gce.ShortCircuitDriver(grid, options=sc_options,
pf_options=pf_options,
pf_results=pf.results)
sc.run()

print("Short circuit power: ", sc.results.SCpower[fault_index])
```

Output:

```text
Short circuit power: -217.00 MW - 680.35j MVAr
```

Sequence voltage, currents and powers are also available.

### Continuation power flow

```python
import os
from matplotlib import pyplot as plt
import GridCalEngine.api as gce

plt.style.use('fivethirtyeight')

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'South Island of New Zealand.gridcal')

# open the grid file
main_circuit = gce.FileOpen(fname).open()

# we need to initialize with a power flow solution
pf_options = gce.PowerFlowOptions()
power_flow = gce.PowerFlowDriver(grid=main_circuit, options=pf_options)
power_flow.run()

# declare the CPF options
vc_options = gce.ContinuationPowerFlowOptions(step=0.001,
approximation_order=gce.CpfParametrization.ArcLength,
adapt_step=True,
step_min=0.00001,
step_max=0.2,
error_tol=1e-3,
tol=1e-6,
max_it=20,
stop_at=gce.CpfStopAt.Full,
verbose=False)

# We compose the target direction
base_power = power_flow.results.Sbus / main_circuit.Sbase
vc_inputs = gce.ContinuationPowerFlowInput(Sbase=base_power,
Vbase=power_flow.results.voltage,
Starget=base_power * 2)

# declare the CPF driver and run
vc = gce.ContinuationPowerFlowDriver(grid=main_circuit,
options=vc_options,
inputs=vc_inputs,
pf_options=pf_options)
vc.run()

# plot the results
fig = plt.figure(figsize=(18, 6))

ax1 = fig.add_subplot(121)
res = vc.results.mdl(gce.ResultTypes.BusActivePower)
res.plot(ax=ax1)

ax2 = fig.add_subplot(122)
res = vc.results.mdl(gce.ResultTypes.BusVoltage)
res.plot(ax=ax2)

plt.tight_layout()
```

![cpf_south_island_new_zealand.png](pics%2Fcpf_south_island_new_zealand.png)

### Contingency analysis

GriCal has contingency simulations, and it features a quite flexible way of defining contingencies.
Firs you define a contingency group, and then define individual events that are assigned to that contingency group.
THe simulation then tries all the contingency groups and apply the events registered in each group:

```python
import os
from GridCalEngine.api import *
from GridCalEngine.enumerations import ContingencyMethod

folder = os.path.join('..', 'Grids_and_profiles', 'grids')
fname = os.path.join(folder, 'IEEE 5 Bus.xlsx')

main_circuit = FileOpen(fname).open()

branches = main_circuit.get_branches()

# manually generate the contingencies
for i, br in enumerate(branches):
# add a contingency group
group = ContingencyGroup(name="contingency {}".format(i+1))
main_circuit.add_contingency_group(group)

# add the branch contingency to the groups, only groups are failed at once
con = Contingency(device_idtag=br.idtag, name=br.name, group=group)
main_circuit.add_contingency(con)

# add a special contingency
group = ContingencyGroup(name="Special contingency")
main_circuit.add_contingency_group(group)
main_circuit.add_contingency(Contingency(device_idtag=branches[3].idtag,
name=branches[3].name, group=group))
main_circuit.add_contingency(Contingency(device_idtag=branches[5].idtag,
name=branches[5].name, group=group))

pf_options = PowerFlowOptions(solver_type=SolverType.NR)

# declare the contingency options
options_ = ContingencyAnalysisOptions(use_provided_flows=False,
Pf=None,
engine=ContingencyMethod.PowerFlow,
# if no power flow options are provided
# a linear power flow is used
pf_options=pf_options)

linear_multiple_contingencies = LinearMultiContingencies(grid=main_circuit)

simulation = ContingencyAnalysisDriver(grid=main_circuit,
options=options_,
linear_multiple_contingencies=linear_multiple_contingencies)

simulation.run()

# print results
df = simulation.results.mdl(ResultTypes.BranchActivePowerFrom).to_df()
print("Contingency flows:\n", df)
```

Output:

```text
Contingency flows:
Branch 0-1 Branch 0-3 Branch 0-4 Branch 1-2 Branch 2-3 Branch 3-4
# contingency 1 0.000000 322.256814 -112.256814 -300.000000 -277.616985 -350.438026
# contingency 2 314.174885 0.000000 -104.174887 11.387545 34.758624 -358.359122
# contingency 3 180.382705 29.617295 0.000000 -120.547317 -97.293581 -460.040537
# contingency 4 303.046401 157.540574 -250.586975 0.000000 23.490000 -214.130663
# contingency 5 278.818887 170.710914 -239.529801 -23.378976 0.000000 -225.076976
# contingency 6 323.104522 352.002620 -465.107139 20.157096 43.521763 0.000000
# Special contingency 303.046401 372.060738 -465.107139 0.000000 23.490000 0.000000
```

This simulation can also be done for time series.

### State estimation

Now lets program the example from the state estimation reference book
_State Estimation in Electric Power Systems by A. Monticelli_.

```python
from GridCalEngine.api import *

m_circuit = MultiCircuit()

b1 = Bus('B1', is_slack=True)
b2 = Bus('B2')
b3 = Bus('B3')

br1 = Line(b1, b2, name='Br1', r=0.01, x=0.03, rate=100.0)
br2 = Line(b1, b3, name='Br2', r=0.02, x=0.05, rate=100.0)
br3 = Line(b2, b3, name='Br3', r=0.03, x=0.08, rate=100.0)

# add measurements
m_circuit.add_pf_measurement(PfMeasurement(0.888, 0.008, br1))
m_circuit.add_pf_measurement(PfMeasurement(1.173, 0.008, br2))

m_circuit.add_qf_measurement(QfMeasurement(0.568, 0.008, br1))
m_circuit.add_qf_measurement(QfMeasurement(0.663, 0.008, br2))

m_circuit.add_pi_measurement(PiMeasurement(-0.501, 0.01, b2))
m_circuit.add_qi_measurement(QiMeasurement(-0.286, 0.01, b2))

m_circuit.add_vm_measurement(VmMeasurement(1.006, 0.004, b1))
m_circuit.add_vm_measurement(VmMeasurement(0.968, 0.004, b2))

m_circuit.add_bus(b1)
m_circuit.add_bus(b2)
m_circuit.add_bus(b3)

m_circuit.add_branch(br1)
m_circuit.add_branch(br2)
m_circuit.add_branch(br3)

# Declare the simulation driver and run
se = StateEstimation(circuit=m_circuit)
se.run()

print(se.results.get_bus_df())
print(se.results.get_branch_df())
```

Output:

```text
Vm Va P Q
B1 0.999629 0.000000 2.064016 1.22644
B2 0.974156 -1.247547 0.000000 0.00000
B3 0.943890 -2.745717 0.000000 0.00000

Pf Qf Pt Qt loading
Br1 89.299199 55.882169 0.0 0.0 55.882169
Br2 117.102446 66.761871 0.0 0.0 66.761871
Br3 38.591163 22.775597 0.0 0.0 22.775597
```

## Contact

- Join the [Discord GridCal channel](https://discord.com/invite/dzxctaNbvu) for a friendly chat, or quick question.
- Submit questions or comments to our [form](https://forms.gle/MpjJAntAwZiLwE6B6).
- Submit bugs or requests in the [Issues](https://github.com/SanPen/GridCal/issues) section.
- Simply email [[email protected]]([email protected])

## License

GridCal is licensed under the [Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html) (LGPL)

In practical terms this means that:

- You can use GridCal for commercial work.
- You can sell commercial services based on GridCal.
- If you distrubute GridCal, you must distribute GridCal's source code as well.
That is always achieved in practice with python code.
- GridCal license does not propagate to works that are not a derivative of GridCal.
An example of a derivative work is if you write a module of the program, the the license
of the modue must be LGPL too. An example of a non-derivative work is if you use
GridCal's API for something else without modifying the API itself, for instance,
using it as a library for another program.

Nonetheless, read the license carefully.

## Disclaimer

All trademarks mentioned in the documentation or the source code belong to their respective owners.


Owner metadata


GitHub Events

Total
Last Year

Committers metadata

Last synced: 1 day ago

Total Commits: 3,988
Total Committers: 39
Avg Commits per committer: 102.256
Development Distribution Score (DDS): 0.379

Commits in past year: 1,533
Committers in past year: 21
Avg Commits per committer in past year: 73.0
Development Distribution Score (DDS) in past year: 0.428

Name Email Commits
santi s****a@g****m 2475
RAMFERAN a****o@r****s 186
Santiago Peñate Vera s****e@i****s 163
Josep Fanals i Batllori j****s@e****h 162
penversa s****e@r****s 123
Michel Lavoie l****l@g****m 117
Santi s****o@g****m 104
Carlos-Alegre c****e@e****h 100
Mate Zsebehazi m****i@g****u 63
Bengt Lüers b****s@g****m 56
josepfanals j****3@g****m 47
ManuelNvro m****o@g****m 45
posmarfe f****o@r****s 43
JanaSoler j****r@e****h 38
ramferan r****n@g****m 35
benceszirbik b****k@g****u 31
Santiago s****e@a****m 27
Jozsef Gorcs j****s@n****m 26
jag0nzalez j****z@i****m 22
santi k****0 21
rcarmona_ingelectus r****a@i****s 20
QuimMoya q****a@h****m 20
dependabot[bot] 4****] 15
cggaray c****a@i****m 7
Peter Kulik p****k@n****m 6
Hugo Sanz González h****z@i****m 6
Muhammad Yasirroni 4****i 6
krimen k****n@g****t 5
Santiago Peñate Vera s****a@g****s 5
poypoyan s****3@g****m 4
and 9 more...

Committer domains:


Issue and Pull Request metadata

Last synced: 1 day ago

Total issues: 182
Total pull requests: 115
Average time to close issues: 2 months
Average time to close pull requests: 24 days
Total issue authors: 45
Total pull request authors: 18
Average comments per issue: 2.93
Average comments per pull request: 0.76
Merged pull request: 97
Bot issues: 0
Bot pull requests: 18

Past year issues: 107
Past year pull requests: 18
Past year average time to close issues: 3 months
Past year average time to close pull requests: 11 days
Past year issue authors: 13
Past year pull request authors: 5
Past year average comments per issue: 0.87
Past year average comments per pull request: 0.17
Past year merged pull request: 18
Past year bot issues: 0
Past year bot pull requests: 10

More stats: https://issues.ecosyste.ms/repositories/lookup?url=https://github.com/sanpen/gridcal

Top Issue Authors

  • SanPen (85)
  • miek770 (23)
  • fernpos (13)
  • Bengt (4)
  • JosepFanals (4)
  • yasirroni (3)
  • anmold-07 (3)
  • mzy2240 (3)
  • robbiemorrison (2)
  • PIC-Lab (2)
  • toddtail (2)
  • mrsilvabr (2)
  • hinfsynz (2)
  • SodinD (2)
  • sergio-dorado (2)

Top Pull Request Authors

  • miek770 (54)
  • dependabot[bot] (18)
  • SanPen (12)
  • yasirroni (7)
  • Bengt (4)
  • hsanzg (3)
  • poypoyan (3)
  • JosepFanals (2)
  • leeraiyan (2)
  • jahanbani (2)
  • RanjeetHambire (1)
  • eyllanesc (1)
  • ramferan (1)
  • atmurray (1)
  • rishabhabhinav (1)

Top Issue Labels

  • enhancement (76)
  • bug (7)
  • Support (2)
  • question (1)

Top Pull Request Labels

  • dependencies (18)

Package metadata

pypi.org: gridcal

GridCal is a Power Systems simulation program intended for professional use and research

  • Homepage: https://github.com/SanPen/GridCal
  • Documentation: https://gridcal.readthedocs.io/
  • Licenses: LGPL
  • Latest release: 5.1.8 (published 24 days ago)
  • Last Synced: 2024-05-10T09:04:29.487Z (1 day ago)
  • Versions: 285
  • Dependent Packages: 0
  • Dependent Repositories: 1
  • Downloads: 1,300 Last month
  • Rankings:
    • Stargazers count: 3.436%
    • Forks count: 4.991%
    • Downloads: 5.556%
    • Dependent packages count: 7.382%
    • Average: 8.723%
    • Dependent repos count: 22.248%
  • Maintainers (1)
pypi.org: gridcalengine

GridCal is a Power Systems simulation program intended for professional use and research

  • Homepage: https://github.com/SanPen/GridCal
  • Documentation: https://gridcalengine.readthedocs.io/
  • Licenses: LGPL
  • Latest release: 5.1.8 (published 24 days ago)
  • Last Synced: 2024-05-10T09:04:29.420Z (1 day ago)
  • Versions: 61
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Downloads: 567 Last month
  • Rankings:
    • Stargazers count: 3.449%
    • Forks count: 4.981%
    • Dependent packages count: 7.409%
    • Average: 21.244%
    • Dependent repos count: 69.138%
  • Maintainers (2)

Dependencies

.github/workflows/pylint.yml actions
  • actions/checkout v3 composite
  • actions/setup-python v3 composite
doc/requirements.txt pypi
  • GridCalEngine >=5.0.0
  • pandas *
  • pytablewriter *
  • sphinx *
  • sphinx_rtd_theme *
requirements-dev.txt pypi
  • Cython * development
  • mpi4py >=3.0.1 development
  • nptyping * development
  • sphinx >=2.0.1 development
  • sphinx-rtd-theme >=0.4.3 development
requirements.txt pypi
  • Cython *
  • PySide6 >=6.5.0
  • chardet >=3.0.4
  • darkdetect *
  • fastparquet *
  • geopy >=1.16
  • h5py >=2.9.0
  • hyperopt *
  • matplotlib >=2.1.1
  • networkx >=2.1
  • nose >=1.3
  • nptyping *
  • numba >=0.46
  • numpy >=1.14.0
  • openpyxl >=2.4.9
  • ortools >=9.8.0
  • pandas >=1.1
  • pvlib *
  • pyDOE >=0.3.8
  • pySOT >=0.2.1
  • pyarrow *
  • pyproj *
  • pyqtdarktheme *
  • pytest *
  • qtconsole >=4.3.1
  • requests *
  • scikit-learn >=0.18
  • scipy >=1.0.0
  • windpowerlib *
  • xlrd >=1.1.0
  • xlwt >=1.3.0
requirements_nose.txt pypi
  • Jinja2 ==2.11.3
  • MarkupSafe ==1.1.1
  • POAP ==0.1.26
  • Pillow >=8.1.2
  • PySide2 ==5.15.2
  • Pygments ==2.15.0
  • QtPy ==1.9.0
  • attrs ==20.3.0
  • backcall ==0.2.0
  • branca ==0.4.2
  • certifi ==2023.7.22
  • chardet ==4.0.0
  • cycler ==0.10.0
  • decorator ==4.4.2
  • dill ==0.3.3
  • et-xmlfile ==1.0.1
  • folium ==0.12.1
  • geographiclib ==1.50
  • geopy ==2.1.0
  • h5py ==3.1.0
  • idna ==2.10
  • iniconfig ==1.1.1
  • ipykernel ==5.4.3
  • ipython >=7.16
  • ipython-genutils ==0.2.0
  • jdcal ==1.4.1
  • jedi ==0.18.0
  • joblib ==1.2.0
  • jupyter-client ==6.1.11
  • jupyter-core ==4.11.2
  • kiwisolver ==1.3.1
  • llvmlite ==0.35.0
  • matplotlib ==3.3.4
  • networkx ==2.5
  • nose ==1.3.7
  • nptyping ==2.5.0
  • numba ==0.52.0
  • numpy >=1.19
  • openpyxl ==3.0.6
  • ortools >=9.0.0
  • packaging ==20.9
  • pandas >=1.1
  • parso ==0.8.1
  • pexpect ==4.8.0
  • pickleshare ==0.7.5
  • pluggy ==0.13.1
  • prompt-toolkit ==3.0.16
  • ptyprocess ==0.7.0
  • pvlib *
  • py ==1.10.0
  • pyDOE ==0.3.8
  • pyDOE2 ==1.3.0
  • pySOT ==0.3.3
  • pyparsing ==2.4.7
  • pyproj ==3.0.0.post1
  • pytest ==6.2.2
  • python-dateutil ==2.8.1
  • pytz ==2021.1
  • pyzmq ==22.0.3
  • qtconsole ==5.0.2
  • requests ==2.31.0
  • scikit-learn ==0.24.1
  • scipy >=1.5
  • shiboken2 ==5.15.2
  • six ==1.15.0
  • smopy ==0.0.7
  • threadpoolctl ==2.1.0
  • toml ==0.10.2
  • tornado ==6.3.3
  • traitlets >=4.3
  • urllib3 >=1.26.4
  • wcwidth ==0.2.5
  • windpowerlib *
  • xlrd ==2.0.1
  • xlwt ==1.3.0
requirements_venv.txt pypi
  • Cython *
  • atomicwrites ==1.3.0
  • attrs ==19.1.0
  • filelock ==3.0.12
  • importlib-metadata ==0.18
  • more-itertools ==7.2.0
  • nptyping *
  • numba >=0.46
  • ortools >=9.0.0
  • packaging ==19.0
  • pip ==23.3
  • pluggy ==0.12.0
  • pvlib *
  • py ==1.10.0
  • pyparsing ==2.4.1.1
  • pytest ==5.0.1
  • setuptools ==65.5.1
  • six ==1.12.0
  • toml ==0.10.0
  • tox ==3.13.2
  • virtualenv ==16.7.2
  • wcwidth ==0.1.7
  • windpowerlib *
  • zipp ==0.5.2
setup.py pypi
src/GridCal/setup.py pypi
src/GridCalEngine/setup.py pypi

Score: 17.25115979985458