Simulate#
This example gives a “hello world” example to use AMS.
Import and Setting the Verbosity Level#
We first import the ams library.
[1]:
import ams
We can configure the verbosity level for logging (output messages) by passing a verbosity level (10-DEBUG, 20-INFO, 30-WARNING, 40-ERROR, 50-CRITICAL) to the stream_level argument of ams.main.config_logger(). Verbose level 10 is useful for getting debug output.
The logging level can be altered by calling config_logger again with new stream_level and file_level.
[2]:
ams.config_logger(stream_level=20)
Note that the above ams.config_logger() is a shorthand to ams.main.config_logger().
If this step is omitted, the default INFO level (stream_level=20) will be used.
Run Simulations#
Load Case#
AMS supports multiple input file formats, including AMS .xlsx file, MATPOWER .m file, PYPOWER .py file, and PSS/E .raw file.
Here we use the AMS .xlsx file as an example. The source file locates at $HOME/ams/ams/cases/ieee39/ieee39_uced.xlsx.
[3]:
sp = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),
setup=True,
no_output=True,)
Parsing input file "/Users/jinningwang/work/ams/ams/cases/5bus/pjm5bus_demo.xlsx"...
Input file parsed in 0.1986 seconds.
Zero line rates detacted in rate_b, rate_c, adjusted to 999.
System set up in 0.0033 seconds.
Inspect Models and Routines#
In AMS, model refers to the device-level models, and they are registered to an OrderedDict models.
[4]:
sp.models
[4]:
OrderedDict([('Summary', Summary (3 devices) at 0x11f65aa10),
('Bus', Bus (5 devices) at 0x11f6fece0),
('PQ', PQ (3 devices) at 0x11f744070),
('Slack', Slack (1 device) at 0x11f744730),
('PV', PV (4 devices) at 0x11f7451b0),
('Shunt', Shunt (0 devices) at 0x11f745c60),
('Line', Line (7 devices) at 0x11f7460b0),
('Jumper', Jumper (0 devices) at 0x11f746b60),
('PVD1', PVD1 (0 devices) at 0x11f746e30),
('ESD1', ESD1 (1 device) at 0x11f747760),
('EV1', EV1 (0 devices) at 0x11f747d00),
('EV2', EV2 (0 devices) at 0x11f778370),
('REGCA1', REGCA1 (0 devices) at 0x11f778850),
('REGCV1', REGCV1 (4 devices) at 0x11f778e50),
('REGCV2', REGCV2 (0 devices) at 0x11f779510),
('Area', Area (3 devices) at 0x11f779a80),
('Zone', Zone (5 devices) at 0x11f779ff0),
('SFR', SFR (3 devices) at 0x11f77a560),
('SR', SR (3 devices) at 0x11f77ab90),
('NSR', NSR (3 devices) at 0x11f77afb0),
('VSGR', VSGR (3 devices) at 0x11f77b3d0),
('GCost', GCost (5 devices) at 0x11f77b820),
('SFRCost', SFRCost (5 devices) at 0x11f77be80),
('SRCost', SRCost (5 devices) at 0x11f7a0460),
('NSRCost', NSRCost (5 devices) at 0x11f7a0880),
('VSGCost', VSGCost (4 devices) at 0x11f7a0ca0),
('DCost', DCost (3 devices) at 0x11f7a0fa0),
('TimeSlot', TimeSlot (0 devices) at 0x11f7a1510),
('EDTSlot', EDTSlot (24 devices) at 0x11f7a1b70),
('UCTSlot', UCTSlot (24 devices) at 0x11f7a1f90)])
We can inspect the detailed model data in the form of DataFrame.
[5]:
sp.PQ.as_df()
[5]:
| idx | u | name | bus | Vn | p0 | q0 | vmax | vmin | owner | ctrl | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| uid | |||||||||||
| 0 | PQ_1 | 1.0 | PQ 1 | 1 | 230.0 | 3.0 | 0.9861 | 1.1 | 0.9 | None | 1.0 |
| 1 | PQ_2 | 1.0 | PQ 2 | 2 | 230.0 | 3.0 | 0.9861 | 1.1 | 0.9 | None | 1.0 |
| 2 | PQ_3 | 1.0 | PQ 3 | 3 | 230.0 | 4.0 | 1.3147 | 1.1 | 0.9 | None | 1.0 |
In AMS, all supported routines are registered to an OrderedDict routines.
[6]:
sp.routines
[6]:
OrderedDict([('DCPF', DCPF at 0x11f6ff940),
('PFlow', PFlow at 0x11f7d0bb0),
('CPF', CPF at 0x11f7d11b0),
('ACOPF', ACOPF at 0x11fc2c070),
('DCOPF', DCOPF at 0x11fc2cc10),
('DCOPF2', DCOPF2 at 0x11fc2dc00),
('ED', ED at 0x11fc2ee00),
('EDDG', EDDG at 0x11fc7cf10),
('EDES', EDES at 0x11fc7eb60),
('RTED', RTED at 0x11fcb12a0),
('RTEDDG', RTEDDG at 0x11fcb1330),
('RTEDES', RTEDES at 0x11fcb3e50),
('RTEDVIS', RTEDVIS at 0x11fce1de0),
('UC', UC at 0x11fce3790),
('UCDG', UCDG at 0x15355d000),
('UCES', UCES at 0x15355f130),
('DOPF', DOPF at 0x153589d20),
('DOPFVIS', DOPFVIS at 0x15358b220),
('PFlow0', PFlow0 at 0x1535c4820),
('DCPF0', DCPF0 at 0x1535c4e20)])
Solve a Routine#
Before solving a routine, it must first be initialized. In this example, we use DCOPF.
In AMS, different routines require different input data. For instance, RTED necessitates regulating reserve-related data (SFR, SFRCost) for initialization.
If you only have base power flow data or DCOPF data, other scheduling routines may not be able to init.
You can use pjm5bus_demo.xlsx as an all-inclusive example to complete necessary input, as it contains all the necessary data for all routines.
[7]:
sp.DCOPF.init()
Building system matrices
Parsing OModel for <DCOPF>
Evaluating OModel for <DCOPF>
Finalizing OModel for <DCOPF>
<DCOPF> initialized in 0.0119 seconds.
[7]:
True
Then, one can solve it by calling run(). Here, argument solver can be passed to specify the solver to use, such as solver='ECOS'.
Installed solvers can be listed by ams.shared.installed_solvers, and more detailes of solver can be found at CVXPY-Choosing a solver.
[8]:
ams.shared.installed_solvers
[8]:
['CLARABEL',
'ECOS',
'ECOS_BB',
'GUROBI',
'MOSEK',
'OSQP',
'PIQP',
'SCIP',
'SCIPY',
'SCS']
[9]:
sp.DCOPF.run(solver='CLARABEL')
<DCOPF> solved as optimal in 0.0110 seconds, converged in 8 iterations with CLARABEL.
[9]:
True
The solved results are stored in each variable itself. For example, the solved power generation of ten generators are stored in pg.v.
[10]:
sp.DCOPF.pg.v
[10]:
array([0.2 , 1.43998388, 0.6 , 5.76001612, 2. ])
Here, get_all_idxes() can be used to get the index of a variable.
[11]:
sp.DCOPF.pg.get_all_idxes()
[11]:
['PV_1', 'PV_3', 'PV_5', 'PV_2', 'Slack_4']
Part of the solved results can be accessed with given indices.
[12]:
sp.DCOPF.get(src='pg', attr='v', idx=['PV_1', 'PV_3'])
[12]:
array([0.2 , 1.43998388])
All Vars are listed in an OrderedDict vars.
[13]:
sp.DCOPF.vars
[13]:
OrderedDict([('pg', Var: StaticGen.pg),
('vBus', Var: Bus.vBus),
('aBus', Var: Bus.aBus)])
The Objective value can be accessed with obj.v.
[14]:
sp.DCOPF.obj.v
[14]:
0.9585953247653323
Similarly, all Constrs are listed in an OrderedDict constrs, and the expression values can also be accessed.
[15]:
sp.DCOPF.constrs
[15]:
OrderedDict([('pb', Constraint: pb [ON]),
('sba', Constraint: sbus [ON]),
('pglb', Constraint: pglb [ON]),
('pgub', Constraint: pgub [ON]),
('plflb', Constraint: plflb [ON]),
('plfub', Constraint: plfub [ON]),
('alflb', Constraint: alflb [ON]),
('alfub', Constraint: alfub [ON])])
We can also inspect the Constraint values.
[16]:
sp.DCOPF.plflb.v
[16]:
array([-3.61999194, -2.78912153, -2.17089459, -4. , -2.43998388,
-1.62910541, -3.61999194])