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.0746 seconds.
Zero Line parameters detected, adjusted to default values: rate_b, rate_c.
All bus type are PQ, adjusted given load and generator connection status.
System set up in 0.0020 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 0x30e664620),
('Bus', Bus (5 devices) at 0x30e6876b0),
('PQ', PQ (3 devices) at 0x30e684f50),
('Slack', Slack (1 device) at 0x30e6acc50),
('PV', PV (4 devices) at 0x30e6ad5b0),
('Shunt', Shunt (0 devices) at 0x30e6ade80),
('Line', Line (7 devices) at 0x30e687dd0),
('Jumper', Jumper (0 devices) at 0x30e6ae8d0),
('PVD1', PVD1 (1 device) at 0x30e2a7d10),
('ESD1', ESD1 (1 device) at 0x11086fb30),
('EV1', EV1 (0 devices) at 0x30e6afbc0),
('EV2', EV2 (0 devices) at 0x30e6afec0),
('REGCA1', REGCA1 (0 devices) at 0x30e6e0230),
('REGCV1', REGCV1 (4 devices) at 0x30e6e08f0),
('REGCV2', REGCV2 (0 devices) at 0x30e6e0c80),
('Area', Area (3 devices) at 0x30e6e1040),
('Zone', Zone (5 devices) at 0x30e6e13d0),
('SFR', SFR (3 devices) at 0x30e6e1730),
('SR', SR (3 devices) at 0x30e6e1d30),
('NSR', NSR (3 devices) at 0x30e6e1fa0),
('VSGR', VSGR (3 devices) at 0x30e6e2210),
('GCost', GCost (5 devices) at 0x30e4b81a0),
('SFRCost', SFRCost (5 devices) at 0x30e6e2e40),
('SRCost', SRCost (5 devices) at 0x30e6e30e0),
('NSRCost', NSRCost (5 devices) at 0x30e6e32f0),
('VSGCost', VSGCost (4 devices) at 0x30e6e3590),
('DCost', DCost (3 devices) at 0x30e6e3860),
('EDTSlot', EDTSlot (24 devices) at 0x30e6e3a70),
('UCTSlot', UCTSlot (24 devices) at 0x30e6aec90)])
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 0x30e687d10),
('PFlow', PFlow at 0x176a0bbf0),
('ACOPF', ACOPF at 0x176a2c740),
('DCOPF', DCOPF at 0x176a2e120),
('DCOPF2', DCOPF2 at 0x176a2f680),
('RTED', RTED at 0x176a48bf0),
('RTEDDG', RTEDDG at 0x176a4a690),
('RTEDESP', RTEDESP at 0x176ad4680),
('RTEDES', RTEDES at 0x176ad68d0),
('RTEDVIS', RTEDVIS at 0x176af0e60),
('RTED2', RTED2 at 0x176af2a50),
('RTED2DG', RTED2DG at 0x176af4560),
('RTED2ES', RTED2ES at 0x176af5ee0),
('ED', ED at 0x176b18680),
('EDDG', EDDG at 0x30e33eab0),
('EDES', EDES at 0x176b1ca40),
('ED2', ED2 at 0x176b1f710),
('ED2DG', ED2DG at 0x176b25a00),
('ED2ES', ED2ES at 0x176ad8440),
('UC', UC at 0x3121b5880),
('UCDG', UCDG at 0x3118a5220),
('UCES', UCES at 0x3127435f0),
('UC2', UC2 at 0x311aeec30),
('UC2DG', UC2DG at 0x3127a48c0),
('UC2ES', UC2ES at 0x3127a6b10),
('DOPF', DOPF at 0x311d29a00),
('DOPFVIS', DOPFVIS at 0x3127e3620),
('DCPF1', DCPF1 at 0x31281c560),
('PFlow1', PFlow1 at 0x31281d250),
('DCOPF1', DCOPF1 at 0x31281d880),
('ACOPF1', ACOPF1 at 0x176d71f70),
('OPF', OPF at 0x311aeee40)])
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.0085 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', 'GUROBI', 'MOSEK', 'OSQP', 'PIQP', 'SCIP', 'SCIPY', 'SCS']
[9]:
sp.DCOPF.run(solver='CLARABEL')
<DCOPF> solved as optimal in 0.0185 seconds, converged in 9 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]:
np.float64(9.535953244734864)
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])