Security Analysis (doc in progress)
The documentation of this section is in progress. It is rather incomplete for the moment, and only expose the most basic features.
If you are interested in collaborating to improve this section, let us know.
Warning
This function might give wrong result for lightsim2grid version 0.5.5 were they were a bug : when some contingencies made the grid non connex, it made all the other contingencies diverge. This bug has been fixed in version 0.6.0 and this is why we do not recommend to use this feature with lightsim2grid version < 0.6.0 !
Goal
This class aims to make faster (and easier) the computations of a security analysis (which is the results of some powerflow after the disconnection of one or more powerlines)
This function is much (much) faster than its pure grid2op counterpart. For example, on the case 118, to simulate all n-1 contingencies you can expect a ~20x speed ups compared to using the grid2op obs.simulate(…, time_step=0) while obtaining the exact same results (see section Benchmarks)
It can be used as:
import grid2op
from lightsim2grid import SecurityAnalysis
from lightsim2grid import LightSimBackend
env_name = ...
env = grid2op.make(env_name, backend=LightSimBackend())
security_analysis = SecurityAnalysis(env)
security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...)
res_p, res_a, res_v = security_analysis.get_flows()
# in this results, then
# res_p[row_id] will be the active power flows (origin side), on all powerlines corresponding to the `row_id` contingency.
# res_a[row_id] will be the current flows, on all powerlines corresponding to step "row_id"
# res_v[row_id] will be the complex voltage, on all bus of the grid corresponding to the `row_id` contingency.
# you can retrieve which contingency is id'ed `row_id` with `security_analysis.contingency_order[row_id]`
For now this relies on grid2op, but we could imagine a version of this class that can read to / from other data sources.
Note
A more advanced usage is given in the examples\security_analysis.py file from the lightsim2grid package.
Benchmarks
Here are some benchmarks made with:
system: Linux 5.11.0-40-generic
OS: ubuntu 20.04
processor: Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
python version: 3.8.10.final.0 (64 bit)
numpy version: 1.18.5
pandas version: 1.1.4
pandapower version: 2.7.0
lightsim2grid version: 0.6.0
grid2op version: 1.6.4
Where lightsim2grid has been installed from source with all optimization enabled.
This benchmark is available by running, from the root of the lightsim2grid repository:
cd examples
python3 security_analysis.py
For this setting the outputs are:
For environment: l2rpn_neurips_2020_track2_small (177 n-1 simulated)
Total time spent in "computer" to solve everything: 18.5ms (9573 pf / s), 0.10 ms / pf)
- time to compute the coefficients to simulate line disconnection: 0.04ms
- time to pre process Ybus: 2.50ms
- time to perform powerflows: 15.84ms (11172 pf / s, 0.09 ms / pf)
In addition, it took 0.83 ms to retrieve the current from the complex voltages (in total 9160.4 pf /s, 0.11 ms / pf)
Comparison with raw grid2op timings
It took grid2op (with lightsim2grid, using obs.simulate): 0.42s to perform the same computation
This is a 21.6 speed up from SecurityAnalysis over raw grid2op (using obs.simulate and lightsim2grid)
It took grid2op (with pandapower, using obs.simulate): 6.39s to perform the same computation
This is a 330.9 speed up from SecurityAnalysis over raw grid2op (using obs.simulate and pandapower)
All results match !
In this case then, the SecurityAnalysis module is more than 22 times faster than raw grid2op ( with obs.simulate as a way to compute the outcome of a contingency)
Detailed usage
Classes:
|
This class allows to perform a "security analysis" from a given grid state. |
Allows the computation of "security analysis", that consists in computing the flows that would result from the disconnection of one or multiple disconnections of some powerlines. |
- class lightsim2grid.securityAnalysis.SecurityAnalysis(grid2op_env)[source]
This class allows to perform a “security analysis” from a given grid state.
For now, you cannot change the grid state, and it only computes the security analysis with current flows at origin of powerlines.
Feel free to post a feature request if you want to extend it.
This class is used in 4 phases:
you create it from a grid2op environment (the grid topology will not be modified from this environment)
you add some contingencies to simulate
you start the simulation
you read back the results
Examples
An example is given here
import grid2op from lightsim2grid import SecurityAnalysis from lightsim2grid import LightSimBackend env_name = ... env = grid2op.make(env_name, backend=LightSimBackend()) 0) you create security_analysis = SecurityAnalysis(env) 1) you add some contingencies to simulate security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...) 2) you start the simulation (done automatically) 3) you read back the results res_p, res_a, res_v = security_analysis.get_flows() # in this results, then # res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency. # you can retrieve it with `security_analysis.contingency_order[row_id]`
Notes
Sometimes, the behaviour might differ from grid2op. For example, if simulating a contingency leads to a non connected grid, then this function will return “Nan” for the flows and 0. for the voltages.
In grid2op, it would be, in this case, 0. for the flows and 0. for the voltages.
Methods:
This method registers as the contingencies that will be computed all the contingencies that disconnects 1 powerline
add_multiple_contingencies
(*args)This function will add multiple contingencies at the same time.
add_single_contingency
(*args)This function allows to add a single contingency specified by either the powerlines names (which should match env.name_line) or by their ID.
clear
()Clear the list of contingencies to simulate
close
()permanently close the object
This function returns the current flows (in Amps, A) at the origin / high voltage side
This function returns the active power flows (in MW) at the origin / high voltage side
This function allows to retrieve the complex voltage at each bus of the grid for each contingency.
get_flows
(*args)Retrieve the flows after each contingencies has been simulated.
- add_all_n1_contingencies()[source]
This method registers as the contingencies that will be computed all the contingencies that disconnects 1 powerline
This is equivalent to:
for single_cont_id in range(env.n_line): self.add_single_contingency(single_cont_id)
- add_multiple_contingencies(*args)[source]
This function will add multiple contingencies at the same time.
This code is equivalent to:
for single_cont in args: self.add_single_contingency(single_cont)
It does not accept any keword arguments.
Examples
import grid2op from lightsim2grid import SecurityAnalysis from lightsim2grid import LightSimBackend env_name = ... env = grid2op.make(env_name, backend=LightSimBackend()) security_anlysis = SecurityAnalysis(env) # add a single contingency that disconnect powerline 2 and 3 at the same time security_anlysis.add_single_contingency(env.name_line[2], 3) # add a multiple contingencies the first one disconnect powerline 2 and # and the second one disconnect powerline 3 security_anlysis.add_multiple_contingencies(env.name_line[2], 3)
- add_single_contingency(*args)[source]
This function allows to add a single contingency specified by either the powerlines names (which should match env.name_line) or by their ID.
The contingency added can be a “n-1” which will simulate a single powerline disconnection or a “n-k” which will simulate the disconnection of multiple powerlines.
It does not accept any keword arguments.
Examples
import grid2op from lightsim2grid import SecurityAnalysis from lightsim2grid import LightSimBackend env_name = ... env = grid2op.make(env_name, backend=LightSimBackend()) security_anlysis = SecurityAnalysis(env) # the single (n-1) contingency "disconnect powerline 0" is added security_anlysis.add_single_contingency(0) # add the single (n-1) contingency "disconnect line 1 security_anlysis.add_single_contingency(env.name_line[1]) # add a single contingency that disconnect powerline 2 and 3 at the same time security_anlysis.add_single_contingency(env.name_line[2], 3)
Notes
If it raises an error for a given contingency, the object might be not properly initialized. In this case, we recommend you to clear it (using the clear() method and to attempt to add contingencies again.)
- compute_A()[source]
This function returns the current flows (in Amps, A) at the origin / high voltage side
Warning
Order of the results
The order in which the results are returned is NOT necessarily the order in which the contingencies have been entered. Please use get_flows() method for easier reading back of the results !
- compute_P()[source]
This function returns the active power flows (in MW) at the origin / high voltage side
Warning
Order of the results
The order in which the results are returned is NOT necessarily the order in which the contingencies have been entered. Please use get_flows() method for easier reading back of the results !
- compute_V()[source]
This function allows to retrieve the complex voltage at each bus of the grid for each contingency.
Warning
Order of the results
The order in which the results are returned is NOT necessarily the order in which the contingencies have been entered. Please use get_flows() method for easier reading back of the results
- get_flows(*args)[source]
Retrieve the flows after each contingencies has been simulated.
Each row of the resulting flow matrix will correspond to a contingency simulated in the arguments.
You can require only the result on some contingencies with the args argument, but in each case, all the results will be computed. If you don’t specify anything, the results will be returned for all contingencies (which we recommend to do)
Examples
import grid2op from lightsim2grid import SecurityAnalysis from lightsim2grid import LightSimBackend env_name = ... env = grid2op.make(env_name, backend=LightSimBackend()) security_analysis = SecurityAnalysis(env) security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...) res_p, res_a, res_v = security_analysis.get_flows() # in this results, then # res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency. # you can retrieve it with `security_analysis.contingency_order[row_id]`
- class lightsim2grid.securityAnalysis.SecurityAnalysisCPP
Allows the computation of “security analysis”, that consists in computing the flows that would result from the disconnection of one or multiple disconnections of some powerlines.
This is a “raw” c++ class, for an easier to use interface, please refer to the python documentation of the
lightsim2grid.securityAnalysis.SecurityAnalysis
class.Warning
This function might give wrong result for lightsim2grid version 0.5.5 were they were a bug : when some contingencies made the grid non connex, it made all the other contingencies diverge. This bug has been fixed in version 0.6.0 and this is why we do not recommend to use this feature with lightsim2grid version < 0.6.0 !
Note
Even if you instruct it to simulate the same contingency multiple times, it will only do it once.
Note
You can only simulate disconnection of powerlines / transformers
At a glance, this class should be used in three steps:
Modify the list of contingencies to simulate, with the functions:
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_all_n1()
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_multiple_n1()
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.remove_n1()
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.remove_nk()
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.remove_multiple_n1()
2) Then you can start the computation of the security analysis with
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute()
then optionallylightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute_flows()
.3) And finally inspect the results with
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.get_flows()
andlightsim2grid.securityAnalysis.SecurityAnalysisCPP.get_voltages()
.Methods:
add_all_n1
(self)This allows to add all the "n-1" in the contingency list to simulate.
add_multiple_n1
(self, arg0)This allows to add a multiple "n-1" in the contingency list to simulate (it will add as many contingency as the size of the list) and is equivalent to call multiple times
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_n1()
add_n1
(self, arg0)This allows to add a single "n-1" in the contingency list to simulate.
add_nk
(self, arg0)This allows to add a single "n-k" in the contingency list to simulate (it will only add at most one contingency)
amps_computation_time
(self)Time spent in computing the flows (in amps) after the voltages have been computed at each nodes
available_solvers
(self)Return the list of solver available on the current lightsim2grid installation.
change_solver
(self, arg0)This function allows to control which solver is used during the powerflow.
clear
(self)Clear the list of all contingencies.
close
(self)Clear the solver and to as if the class never performed any powerflow.
compute
(self, arg0, arg1, arg2)Compute the voltages (at each bus of the grid model) for some time series of injections (productions, loads, storage units, etc.)
compute_flows
(self)Compute the current flows (in amps, at the origin of each powerlines / high voltage size of each transformers.
compute_power_flows
(self)Compute the current flows (in MW, at the origin of each powerlines / high voltage size of each transformers.
get_flows
(self)Get the flows (in kA) at the origin side / high voltage side of each transformers / powerlines.
get_power_flows
(self)Get the active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.
get_solver_type
(self)Return the type of the solver currently used.
get_voltages
(self)Get the complex voltage angles at each bus of the powergrid.
modif_Ybus_time
(self)Time spent to modify the Ybus matrix before simulating each contingency.
my_defaults
(self)Allows to inspect the contingency list that will be simulated.
nb_solved
(self)Total number of powerflows solved.
preprocessing_time
(self)Time spent in pre processing the data (this involves, the checking whether the grid would be still connex after the contingency for example)
remove_multiple_n1
(self, arg0)Remove multiple "n-1" contingency from the contingency list to simulate.
remove_n1
(self, arg0)Remove a single "n-1" contingency from the contingency list to simulate.
remove_nk
(self, arg0)Remove a single "n-k" contingency from the contingency list to simulate.
reset
(self)Clear the list of all contingencies.
solver_time
(self)Total time spent only in solving the powerflows (excluding pre processing the data, post processing them, initializing everything etc.)
total_time
(self)Total time spent in solving the powerflows, pre processing the data, post processing them, initializing everything etc.
- add_all_n1(self: lightsim2grid_cpp.SecurityAnalysisCPP) None
This allows to add all the “n-1” in the contingency list to simulate.
See also
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_n1()
to add only a single lineSee also
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_multiple_n1()
to add multiple single contingencies in the same call (but not necessarily all)
- add_multiple_n1(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: List[int]) None
This allows to add a multiple “n-1” in the contingency list to simulate (it will add as many contingency as the size of the list) and is equivalent to call multiple times
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_n1()
See also
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_all_n1()
to add all the “n-1” contingencies.Warning
A “n-k” will disconnect multiple powerlines at the same time. It’s not the same as adding muliple “n-1” contingencies, where powerlines will be disconnected one after the other.
- Parameters:
vect_n1 (
list
(ofint
)) – The lines id you want to add to the contingency list
- add_n1(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: int) None
This allows to add a single “n-1” in the contingency list to simulate.
See also
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_all_n1()
to add all contingencies at the same timeSee also
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.add_multiple_n1()
to add multiple single contingencies in the same call.- Parameters:
line_id (
int
) – The line id you would like to see disconnected
- add_nk(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: List[int]) None
This allows to add a single “n-k” in the contingency list to simulate (it will only add at most one contingency)
Warning
A “n-k” will disconnect multiple powerlines at the same time. It’s not the same as adding muliple “n-1” contingencies, where powerlines will be disconnected one after the other.
- Parameters:
vect_nk (
list
(ofint
)) – The lines id you want to add in the single contingency added.
- amps_computation_time(self: lightsim2grid_cpp.SecurityAnalysisCPP) float
Time spent in computing the flows (in amps) after the voltages have been computed at each nodes
It is given in seconds (
float
).
- available_solvers(self: lightsim2grid_cpp.SecurityAnalysisCPP) List[lightsim2grid_cpp.SolverType]
Return the list of solver available on the current lightsim2grid installation.
This is a list of
lightsim2grid.solver.SolverType
.
- change_solver(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: lightsim2grid_cpp.SolverType) None
This function allows to control which solver is used during the powerflow. See the section Even more advanced usage for more information about them.
See also
lightsim2grid.solver.SolverType
for a list of the available solver (NB: some solvers might not be available on all platform)Note
If the solver type entered is a DC solver (eg from
lightsim2grid.solver.SolverType
, DC, KLUDC or NICSLUDC), it will change the _dc_solver otherwise the regular _solver is modified.Examples
from lightsim2grid.solver import SolverType # init the grid model from lightsim2grid.gridmodel import init pp_net = ... # any pandapower grid lightsim_grid_model = init(pp_net) # some warnings might be issued as well as some warnings # change the solver used for the powerflow # to use internally a solver based on Newton Raphson algorithme using Eigen sparse LU lightsim_grid_model.change_solver(SolverType.SparseLUSolver)
- clear(self: lightsim2grid_cpp.SecurityAnalysisCPP) None
Clear the list of all contingencies. After a call to this method, you will need to re add some contingencies with
- close(self: lightsim2grid_cpp.SecurityAnalysisCPP) None
Clear the solver and to as if the class never performed any powerflow.
- compute(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: numpy.ndarray[numpy.complex128[m, 1]], arg1: int, arg2: float) None
Compute the voltages (at each bus of the grid model) for some time series of injections (productions, loads, storage units, etc.)
Note
This function must be called before
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute_flows()
andlightsim2grid.securityAnalysis.SecurityAnalysisCPP.get_flows()
orlightsim2grid.securityAnalysis.SecurityAnalysisCPP.get_voltages()
.Note
During this computation, the GIL is released, allowing easier parrallel computation
- Parameters:
Vinit (
numy.ndarray
, complex) – First voltage at each bus of the grid model (including the disconnected buses)max_iter (
int
) – Total number of iteration (>0 integer)tol (
float
) – Solver tolerance (> 0. float)
- compute_flows(self: lightsim2grid_cpp.SecurityAnalysisCPP) numpy.ndarray[numpy.float64[m, n], flags.c_contiguous]
Compute the current flows (in amps, at the origin of each powerlines / high voltage size of each transformers.
Warning
This function must be called after
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute()
has been called.Note
This function must be called before
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.get_flows()
Note
During this computation, the GIL is released, allowing easier parrallel computation
- compute_power_flows(self: lightsim2grid_cpp.SecurityAnalysisCPP) numpy.ndarray[numpy.float64[m, n], flags.c_contiguous]
Compute the current flows (in MW, at the origin of each powerlines / high voltage size of each transformers.
Warning
This function must be called after
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute()
has been called.Note
This function must be called before
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.get_flows()
Note
During this computation, the GIL is released, allowing easier parrallel computation
- get_flows(self: lightsim2grid_cpp.SecurityAnalysisCPP) numpy.ndarray[numpy.float64[m, n], flags.c_contiguous]
Get the flows (in kA) at the origin side / high voltage side of each transformers / powerlines.
Each rows correspond to a contingency, each column to a powerline / transformer
Warning
This function must be called after
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute_flows()
has been called. (compute_flows also requires thatlightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute()
has been caleed)Warning
The order in which the contingencies are computed is NOT (in this c++ class) the order in which you enter them. They are computed in the order given by
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.my_defaults()
. For an easier, more “human readable” please use thelightsim2grid.securityAnalysis.SecurityAnalysis.get_flows()
method.- Returns:
As – The flows (in kA) at the origin side / high voltage side of each transformers / powerlines.
- Return type:
numpy.ndarray
(matrix)
- get_power_flows(self: lightsim2grid_cpp.SecurityAnalysisCPP) numpy.ndarray[numpy.float64[m, n], flags.c_contiguous]
Get the active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.
Each rows correspond to a contingency, each column to a powerline / transformer
Warning
This function must be called after
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute_power_flows()
has been called. (compute_flows also requires thatlightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute()
has been caleed)Warning
The order in which the contingencies are computed is NOT (in this c++ class) the order in which you enter them. They are computed in the order given by
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.my_defaults()
. For an easier, more “human readable” please use thelightsim2grid.securityAnalysis.SecurityAnalysis.get_flows()
method.- Returns:
As – The flows (in kA) at the origin side / high voltage side of each transformers / powerlines.
- Return type:
numpy.ndarray
(matrix)
- get_solver_type(self: lightsim2grid_cpp.SecurityAnalysisCPP) lightsim2grid_cpp.SolverType
Return the type of the solver currently used.
This is equivalent to the get_type of the
lightsim2grid.solver.AnySolver.get_type()
of the solver used.
- get_voltages(self: lightsim2grid_cpp.SecurityAnalysisCPP) numpy.ndarray[numpy.complex128[m, n], flags.c_contiguous]
Get the complex voltage angles at each bus of the powergrid.
Each rows correspond to a contingency, each column to a bus.
Warning
This function must be called after
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.compute()
.Warning
The order in which the contingencies are computed is NOT (in this c++ class) the order in which you enter them. They are computed in the order given by
lightsim2grid.securityAnalysis.SecurityAnalysisCPP.my_defaults()
. For an easier, more “human readable” please use thelightsim2grid.securityAnalysis.SecurityAnalysis.get_flows()
method.- Returns:
Vs – The complex voltage angles at each bus of the powergrid.
- Return type:
numpy.ndarray
(matrix)
- modif_Ybus_time(self: lightsim2grid_cpp.SecurityAnalysisCPP) float
Time spent to modify the Ybus matrix before simulating each contingency.
It is given in seconds (
float
).
- my_defaults(self: lightsim2grid_cpp.SecurityAnalysisCPP) List[List[int]]
Allows to inspect the contingency list that will be simulated.
- Returns:
my_defaults_vect – The list (of list) of all the current contingencies. Its length corresponds to the number of contingencies simulated. For each contingency, it gives which powerline will be disconnected.
- Return type:
list
- nb_solved(self: lightsim2grid_cpp.SecurityAnalysisCPP) int
Total number of powerflows solved.
- preprocessing_time(self: lightsim2grid_cpp.SecurityAnalysisCPP) float
Time spent in pre processing the data (this involves, the checking whether the grid would be still connex after the contingency for example)
It is given in seconds (
float
).
- remove_multiple_n1(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: List[int]) int
Remove multiple “n-1” contingency from the contingency list to simulate. This can remove up to len(vect_n1) single contingencies from the contingency list.
- Parameters:
vect_n1 (
list
(ofint
)) – The lines id you want to remove from contingency list (will remove multiple “n-1” single contingency)- Returns:
success – Whether or not the contingency has been properly removed
- Return type:
bool
- remove_n1(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: int) bool
Remove a single “n-1” contingency from the contingency list to simulate.
- Parameters:
line_id (
int
) – The line id you would like to remove from contingency list (will remove a single “n-k” contingencies)- Returns:
success – Whether or not the contingency has been properly removed
- Return type:
bool
- remove_nk(self: lightsim2grid_cpp.SecurityAnalysisCPP, arg0: List[int]) bool
Remove a single “n-k” contingency from the contingency list to simulate. This removes at much one single contingency
- Parameters:
vect_nk (
list
(ofint
)) – The lines id you want to remove from contingency list.- Returns:
nb_removed – The total number of contingencies removed from the contingency list
- Return type:
int
- reset(self: lightsim2grid_cpp.SecurityAnalysisCPP) None
Clear the list of all contingencies. After a call to this method, you will need to re add some contingencies with
- solver_time(self: lightsim2grid_cpp.SecurityAnalysisCPP) float
Total time spent only in solving the powerflows (excluding pre processing the data, post processing them, initializing everything etc.)
It is given in seconds (
float
).
- total_time(self: lightsim2grid_cpp.SecurityAnalysisCPP) float
Total time spent in solving the powerflows, pre processing the data, post processing them, initializing everything etc.
It is given in seconds (
float
).