Manipulate the Simulation#
This example shows how to play with the simulation, such as contingency analysis and manipulate the constraints.
import ams
import datetime
print("Last run time:", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print(f'ams:{ams.__version__}')
Last run time: 2024-01-29 20:44:33
ams:0.8.1.post37.dev0+gf76a132
ams.config_logger(stream_level=20)
Manipulate the Simulation#
Load Case#
sp = ams.load(ams.get_case('5bus/pjm5bus_uced.xlsx'),
setup=True,
no_output=True,)
Parsing input file "/Users/jinningwang/Documents/work/ams/ams/cases/5bus/pjm5bus_uced.xlsx"...
Input file parsed in 0.1167 seconds.
Zero line rates detacted in rate_a, rate_b, rate_c, adjusted to 999.
If expect a line outage, please set 'u' to 0.
System set up in 0.0026 seconds.
The system load are defined in model PQ.
sp.PQ.as_df()
| idx | u | name | bus | Vn | p0 | q0 | vmax | vmin | owner | ctrl | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| uid | |||||||||||
| 0 | PQ_1 | 1.0 | PQ 1 | Bus_2 | 230.0 | 3.0 | 0.9861 | 1.1 | 0.9 | None | 1.0 |
| 1 | PQ_2 | 1.0 | PQ 2 | Bus_3 | 230.0 | 3.0 | 0.9861 | 1.1 | 0.9 | None | 1.0 |
| 2 | PQ_3 | 1.0 | PQ 3 | Bus_4 | 230.0 | 4.0 | 1.3147 | 1.1 | 0.9 | None | 1.0 |
In RTED, system load is referred as pd.
sp.RTED.pd.v
array([3., 3., 4.])
Run Simulation#
RTED can be solved and one can inspect the results as discussed in previous example.
sp.RTED.run(solver='ECOS')
Routine <RTED> initialized in 0.0141 seconds.
RTED solved as optimal in 0.0166 seconds, converged after 9 iterations using solver ECOS.
True
Power generation pg and line flow plf can be accessed as follows.
sp.RTED.pg.v
array([2.1, 5.2, 0.7, 2. ])
sp.RTED.plf.v
array([ 0.70595331, 0.68616798, 0.00192539, -1.58809337, 0.61190663,
-0.70192539, 0.70595331])
Change Load#
The load values can be manipulated in the model PQ.
sp.PQ.set(src='p0', attr='v', idx=['PQ_1', 'PQ_2'], value=[3.2, 3.2])
True
According parameters need to be updated to make the changes effective in the optimization model.
If not sure which parameters need to be updated, one can use
update() to update all parameters.
sp.RTED.update('pd')
True
After manipulation, the routined can be solved again.
sp.RTED.run(solver='ECOS')
RTED solved as optimal in 0.0025 seconds, converged after 9 iterations using solver ECOS.
True
sp.RTED.pg.v
array([2.1, 5.2, 1.1, 2. ])
An alternative way is to alter the load through RTED.
As pd has owner StaticLoad and soruce p0, the parameter update through RTED actually happens to StaticLoad.p0.
sp.RTED.pd.owner
StaticLoad (3 devices) at 0x1379130d0
sp.RTED.pd.src
'p0'
Similarly, the load can be changed using set method.
sp.RTED.set(src='pd', attr='v', idx=['PQ_1', 'PQ_2'], value=[3.8, 3.8])
True
Remember to update the optimization parameters after the change.
sp.RTED.update('pd')
True
We can see that the original load is also updated.
sp.PQ.as_df()
| idx | u | name | bus | Vn | p0 | q0 | vmax | vmin | owner | ctrl | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| uid | |||||||||||
| 0 | PQ_1 | 1.0 | PQ 1 | Bus_2 | 230.0 | 3.8 | 0.9861 | 1.1 | 0.9 | None | 1.0 |
| 1 | PQ_2 | 1.0 | PQ 2 | Bus_3 | 230.0 | 3.8 | 0.9861 | 1.1 | 0.9 | None | 1.0 |
| 2 | PQ_3 | 1.0 | PQ 3 | Bus_4 | 230.0 | 4.0 | 1.3147 | 1.1 | 0.9 | None | 1.0 |
sp.RTED.run(solver='ECOS')
RTED solved as optimal in 0.0020 seconds, converged after 9 iterations using solver ECOS.
True
As expected, the power generation also changed.
sp.RTED.pg.v
array([2.1, 5.2, 2.3, 2. ])
Trip a Generator#
We can see that there are three PV generators in the system.
sp.PV.as_df()
| idx | u | name | Sn | Vn | bus | busr | p0 | q0 | pmax | ... | Qc2min | Qc2max | Ragc | R10 | R30 | Rq | apf | pg0 | td1 | td2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| uid | |||||||||||||||||||||
| 0 | PV_1 | 1.0 | Alta | 100.0 | 230.0 | Bus_1 | None | 1.0000 | 0.0 | 2.1 | ... | 0.0 | 0.0 | 999.0 | 999.0 | 999.0 | 999.0 | 0.0 | 0.0 | 0.5 | 0.0 |
| 1 | PV_3 | 1.0 | Solitude | 100.0 | 230.0 | Bus_3 | None | 3.2349 | 0.0 | 5.2 | ... | 0.0 | 0.0 | 999.0 | 999.0 | 999.0 | 999.0 | 0.0 | 0.0 | 0.5 | 0.0 |
| 2 | PV_5 | 1.0 | Brighton | 100.0 | 230.0 | Bus_5 | None | 4.6651 | 0.0 | 6.0 | ... | 0.0 | 0.0 | 999.0 | 999.0 | 999.0 | 999.0 | 0.0 | 0.0 | 0.5 | 0.0 |
3 rows × 33 columns
PV_1 is tripped by setting its connection status u to 0.
sp.StaticGen.set(src='u', attr='v', idx='PV_1', value=0)
True
In AMS, some parameters are defiend as constants in the numerical optimization model to follow the CVXPY DCP and DPP rules. Once non-parametric parameters are changed, the optimization model will be re-initialized to make the changes effective.
More details can be found at CVXPY - Disciplined Convex Programming.
sp.RTED.update()
Re-init RTED OModel due to non-parametric change.
True
Then we can re-solve the model.
sp.RTED.run(solver='ECOS')
RTED solved as optimal in 0.0164 seconds, converged after 8 iterations using solver ECOS.
True
We can see that the tripped generator has no power generation.
sp.RTED.pg.v.round(2)
array([-0. , 5.2, 4.4, 2. ])
Trip a Line#
We can inspect the Line model to check the system topology.
sp.Line.as_df()
| idx | u | name | bus1 | bus2 | Sn | fn | Vn1 | Vn2 | r | ... | tap | phi | rate_a | rate_b | rate_c | owner | xcoord | ycoord | amin | amax | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| uid | |||||||||||||||||||||
| 0 | Line_0 | 1.0 | Line AB | Bus_1 | Bus_2 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00281 | ... | 1.0 | 0.0 | 4.0 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
| 1 | Line_1 | 1.0 | Line AD | Bus_1 | Bus_4 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00304 | ... | 1.0 | 0.0 | 999.0 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
| 2 | Line_2 | 1.0 | Line AE | Bus_1 | Bus_5 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00064 | ... | 1.0 | 0.0 | 999.0 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
| 3 | Line_3 | 1.0 | Line BC | Bus_2 | Bus_3 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00108 | ... | 1.0 | 0.0 | 999.0 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
| 4 | Line_4 | 1.0 | Line CD | Bus_3 | Bus_4 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00297 | ... | 1.0 | 0.0 | 999.0 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
| 5 | Line_5 | 1.0 | Line DE | Bus_4 | Bus_5 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00297 | ... | 1.0 | 0.0 | 2.4 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
| 6 | Line_6 | 1.0 | Line AB2 | Bus_1 | Bus_2 | 100.0 | 60.0 | 230.0 | 230.0 | 0.00281 | ... | 1.0 | 0.0 | 4.0 | 999.0 | 999.0 | None | None | None | -6.283185 | 6.283185 |
7 rows × 28 columns
Here line 2 is tripped by setting its connection status u to 0.
Note that in ANDES, dynamic simulation of line tripping should use model Toggle.
sp.Line.set(src='u', attr='v', idx='Line_1', value=0)
True
sp.RTED.update()
Re-init RTED OModel due to non-parametric change.
True
sp.RTED.run(solver='ECOS')
RTED solved as optimal in 0.0159 seconds, converged after 8 iterations using solver ECOS.
True
Here we can see the tripped line has no flow.
sp.RTED.plf.v.round(2)
array([ 1.34, 0. , -2.68, -1.12, 0.28, -1.72, 1.34])
Disable the Constraints#
In addition to the system parameters, the constraints can also be manipulated.
Here, we load the case to a new system.
spc = ams.load(ams.get_case('5bus/pjm5bus_uced.xlsx'),
setup=True,
no_output=True,)
Parsing input file "/Users/jinningwang/Documents/work/ams/ams/cases/5bus/pjm5bus_uced.xlsx"...
Input file parsed in 0.0386 seconds.
Zero line rates detacted in rate_a, rate_b, rate_c, adjusted to 999.
If expect a line outage, please set 'u' to 0.
System set up in 0.0025 seconds.
spc.RTED.init()
Routine <RTED> initialized in 0.0101 seconds.
True
spc.RTED.set(src='rate_a', attr='v', idx=['Line_2'], value=1.4)
True
spc.RTED.update('rate_a')
True
We can inspect the constraints status as follows. All constraints are turned on by default.
spc.RTED.constrs
OrderedDict([('pglb', Constraint: pglb [ON]),
('pgub', Constraint: pgub [ON]),
('pb', Constraint: pb [ON]),
('plflb', Constraint: plflb [ON]),
('plfub', Constraint: plfub [ON]),
('alflb', Constraint: alflb [ON]),
('alfub', Constraint: alfub [ON]),
('rbu', Constraint: rbu [ON]),
('rbd', Constraint: rbd [ON]),
('rru', Constraint: rru [ON]),
('rrd', Constraint: rrd [ON]),
('rgu', Constraint: rgu [ON]),
('rgd', Constraint: rgd [ON])])
Then, solve the dispatch and inspect the line flow.
spc.RTED.run(solver='ECOS')
RTED solved as optimal in 0.0158 seconds, converged after 10 iterations using solver ECOS.
True
spc.RTED.plf.v.round(2)
array([ 0.71, 0.69, 0. , -1.59, 0.61, -0.7 , 0.71])
In the next, we can disable specific constraints, and the parameter name takes both single constraint name or a list of constraint names.
spc.RTED.disable(['plflb', 'plfub'])
Turn off constraints: plflb, plfub
True
Now, it can be seen that the two constraints are disabled.
spc.RTED.constrs
OrderedDict([('pglb', Constraint: pglb [ON]),
('pgub', Constraint: pgub [ON]),
('pb', Constraint: pb [ON]),
('plflb', Constraint: plflb [OFF]),
('plfub', Constraint: plfub [OFF]),
('alflb', Constraint: alflb [ON]),
('alfub', Constraint: alfub [ON]),
('rbu', Constraint: rbu [ON]),
('rbd', Constraint: rbd [ON]),
('rru', Constraint: rru [ON]),
('rrd', Constraint: rrd [ON]),
('rgu', Constraint: rgu [ON]),
('rgd', Constraint: rgd [ON])])
spc.RTED.run(solver='ECOS')
Disabled constraints: plflb, plfub
Routine <RTED> initialized in 0.0086 seconds.
RTED solved as optimal in 0.0141 seconds, converged after 9 iterations using solver ECOS.
True
We can see that the line flow limits are not in effect.
spc.RTED.plf.v.round(2)
array([ 0.71, 0.69, 0. , -1.59, 0.61, -0.7 , 0.71])
Similarly, you can also enable the constraints again.
spc.RTED.enable(['plflb', 'plfub'])
Turn on constraints: plflb, plfub
True
spc.RTED.constrs
OrderedDict([('pglb', Constraint: pglb [ON]),
('pgub', Constraint: pgub [ON]),
('pb', Constraint: pb [ON]),
('plflb', Constraint: plflb [ON]),
('plfub', Constraint: plfub [ON]),
('alflb', Constraint: alflb [ON]),
('alfub', Constraint: alfub [ON]),
('rbu', Constraint: rbu [ON]),
('rbd', Constraint: rbd [ON]),
('rru', Constraint: rru [ON]),
('rrd', Constraint: rrd [ON]),
('rgu', Constraint: rgu [ON]),
('rgd', Constraint: rgd [ON])])
spc.RTED.run(solver='ECOS')
Routine <RTED> initialized in 0.0101 seconds.
RTED solved as optimal in 0.0149 seconds, converged after 10 iterations using solver ECOS.
True
spc.RTED.plf.v.round(2)
array([ 0.71, 0.69, 0. , -1.59, 0.61, -0.7 , 0.71])
Alternatively, you can also force init the dispatch to rebuild the system matrices, enable all constraints, and re-init the optimization models.
spc.RTED.disable(['plflb', 'plfub', 'rgu', 'rgd'])
Turn off constraints: plflb, plfub, rgu, rgd
True
spc.RTED.init(force=True)
Routine <RTED> initialized in 0.0103 seconds.
True
spc.RTED.constrs
OrderedDict([('pglb', Constraint: pglb [ON]),
('pgub', Constraint: pgub [ON]),
('pb', Constraint: pb [ON]),
('plflb', Constraint: plflb [ON]),
('plfub', Constraint: plfub [ON]),
('alflb', Constraint: alflb [ON]),
('alfub', Constraint: alfub [ON]),
('rbu', Constraint: rbu [ON]),
('rbd', Constraint: rbd [ON]),
('rru', Constraint: rru [ON]),
('rrd', Constraint: rrd [ON]),
('rgu', Constraint: rgu [ON]),
('rgd', Constraint: rgd [ON])])
Alter the Config#
In AMS, routines have an config object as configuration settings.
spf = ams.load(ams.get_case('5bus/pjm5bus_uced.xlsx'),
setup=True,
no_output=True,)
Parsing input file "/Users/jinningwang/Documents/work/ams/ams/cases/5bus/pjm5bus_uced.xlsx"...
Input file parsed in 0.0946 seconds.
Zero line rates detacted in rate_a, rate_b, rate_c, adjusted to 999.
If expect a line outage, please set 'u' to 0.
System set up in 0.0027 seconds.
In RTED, the default interval is 5/60 [hour], and the formulations has been adjusted to fit the interval.
spf.RTED.config
OrderedDict([('t', 0.08333333333333333)])
spf.RTED.run(solver='ECOS')
Routine <RTED> initialized in 0.0108 seconds.
RTED solved as optimal in 0.0155 seconds, converged after 9 iterations using solver ECOS.
True
spf.RTED.obj.v
0.19537500005072062
We can update the interval to 1 [hour] and re-solve the dispatch.
Note that in this senario, compared to DCOPF, RTED has extra costs for pru and prd.
spf.RTED.config.t = 60/60
Remember to update the parameters after the change.
spf.RTED.update()
Re-init RTED OModel due to non-parametric change.
True
spf.RTED.run(solver='SCS')
RTED solved as optimal in 0.0200 seconds, converged after 325 iterations using solver SCS.
True
We can then get the objective value.
spf.RTED.obj.v
2.3444999986498134
Note that in this build-in case, the cru and crd are defined as zero.
spf.RTED.cru.v
array([0., 0., 0., 0.])
spf.RTED.crd.v
array([0., 0., 0., 0.])
As benchmark, we can solve the DCOPF.
spf.DCOPF.run(solver='SCS')
Routine <DCOPF> initialized in 0.0057 seconds.
DCOPF solved as optimal in 0.0092 seconds, converged after 225 iterations using solver SCS.
True
As expected, the DCOPF has a similar objective value.
spf.DCOPF.obj.v
2.3445094955490013