One of the most interesting feature of AMS is its interoperation with dynamic simulator ANDES.
Interoperation includes compatible case conversion and data exchange, thus it facilitates dispatch-dynamic co-simulation using AMS and ANDES.
import numpy as np
import andes
import ams
import datetime
print("Last run time:", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print(f'andes:{andes.__version__}')
print(f'ams:{ams.__version__}')
Last run time: 2024-06-18 20:17:19 andes:1.9.2 ams:0.9.8
ams.config_logger(stream_level=20)
sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
setup=True,
no_output=True,)
Parsing input file "/Users/jinningwang/Documents/work/mambaforge/envs/amsre/lib/python3.9/site-packages/ams/cases/ieee14/ieee14_uced.xlsx"... Input file parsed in 0.1177 seconds. System set up in 0.0017 seconds.
sp.RTED.init()
<RTED> initialized in 0.0572 seconds.
True
sp.RTED.run(solver='CLARABEL')
<RTED> solved as optimal in 0.0157 seconds, converged in 10 iterations with CLARABEL.
True
The built-in ANDES interface can convert an AMS case to ANDES case in memory.
The bridge between AMS and converted ANDES is the shared power flow devices, Bus, PQ, PV, Slack, Line, and Shunt.
sa = sp.to_andes(setup=True,
addfile=andes.get_case('ieee14/ieee14_full.xlsx'))
Parsing additional file "/Users/jinningwang/Documents/work/mambaforge/envs/amsre/lib/python3.9/site-packages/andes/cases/ieee14/ieee14_full.xlsx"... Following PFlow models in addfile will be overwritten: <Bus>, <PQ>, <PV>, <Slack>, <Shunt>, <Line>, <Area> Addfile parsed in 0.0474 seconds. System converted to ANDES in 0.1402 seconds. /Users/jinningwang/Documents/work/mambaforge/envs/amsre/lib/python3.9/site-packages/ams/interop/andes.py:933: FutureWarning: Downcasting object dtype arrays on .fillna, .ffill, .bfill is deprecated and will change in a future version. Call result.infer_objects(copy=False) instead. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)` ssa_key0 = ssa_key0.fillna(value=False) AMS system 0x107bf5640 is linked to the ANDES system 0x1591bc310. <PFlow> initialized in 0.0014 seconds. 0: |F(x)| = 0.4665790376 1: |F(x)| = 0.01697226536 2: |F(x)| = 3.214367637e-05 3: |F(x)| = 1.533567526e-10 <PFlow> solved in 0.0102 seconds, converged in 3 iterations with PYPOWER-Newton. Power flow results are consistent.
If you wish to add devices to the converted ANDES system, set setup=False
to skip the ANDES setup process.
As indicated by the output information, in the conversion process, ANDES power flow devices will be overwritten by AMS ones, if exists.
Upon a successful conversion, you are ready to enjoy full capability of ANDES.
help
command can give a quick reference.
help(sp.to_andes)
Help on method to_andes in module ams.system: to_andes(setup=True, addfile=None, **kwargs) method of ams.system.System instance Convert the AMS system to an ANDES system. A preferred dynamic system file to be added has following features: 1. The file contains both power flow and dynamic models. 2. The file can run in ANDES natively. 3. Power flow models are in the same shape as the AMS system. 4. Dynamic models, if any, are in the same shape as the AMS system. Parameters ---------- setup : bool, optional Whether to call `setup()` after the conversion. Default is True. addfile : str, optional The additional file to be converted to ANDES dynamic mdoels. **kwargs : dict Keyword arguments to be passed to `andes.system.System`. Returns ------- andes : andes.system.System The converted ANDES system. Examples -------- >>> import ams >>> import andes >>> sp = ams.load(ams.get_case('ieee14/ieee14_rted.xlsx'), setup=True) >>> sa = sp.to_andes(setup=False, ... addfile=andes.get_case('ieee14/ieee14_wt3.xlsx'), ... overwrite=True, no_keep=True, no_output=True)
In the interface class dyn
, the link table is stored in dyn.link
.
It describes the mapping relationships between power flow devices and dynamic devices.
sp.dyn.link
stg_idx | bus_idx | syg_idx | gov_idx | dg_idx | rg_idx | gammap | gammaq | |
---|---|---|---|---|---|---|---|---|
0 | Slack_1 | 1 | GENROU_1 | TGOV1_1 | NaN | NaN | 1.0 | 1.0 |
1 | PV_5 | 8 | GENROU_5 | TGOV1_5 | NaN | NaN | 1.0 | 1.0 |
2 | PV_4 | 6 | GENROU_4 | TGOV1_4 | NaN | NaN | 1.0 | 1.0 |
3 | PV_3 | 3 | GENROU_3 | TGOV1_3 | NaN | NaN | 1.0 | 1.0 |
4 | PV_2 | 2 | GENROU_2 | TGOV1_2 | NaN | NaN | 1.0 | 1.0 |
As there is a gap between DC-based dispatch and AC-based TDS, a conversion is required to ensure the TDS initialization.
sp.RTED.dc2ac()
<ACOPF> initialized in 0.0037 seconds. <ACOPF> solved in 0.1872 seconds, converged in 12 iterations with PYPOWER-PIPS. <RTED> converted to AC.
True
In the RTED routine, there are two mapping dictionaries to define the data exchange, namely, map1
for receiving data from ANDES and map2
for sending data to ANDES.
sp.RTED.map2
OrderedDict([('vBus', ('Bus', 'v0')), ('ug', ('StaticGen', 'u')), ('pg', ('StaticGen', 'p0'))])
sp.dyn.send(adsys=sa, routine='RTED')
Send <RTED> results to ANDES <0x1591bc310>... *Send <vBus> to StaticGen.v0 Send <vBus> to Bus.v0 Send <ug> to StaticGen.u Send <pg> to StaticGen.p0
True
Sometimes, the ANDES TDS initialization may fail due to inapproriate limits.
Here, we alleviate the TGOV1
limit issue by enlarging the Pmax
and Pmin
to the same value.
sa.TGOV1.set(src='VMAX', attr='v', idx=sa.TGOV1.idx.v, value=100*np.ones(sa.TGOV1.n))
sa.TGOV1.set(src='VMIN', attr='v', idx=sa.TGOV1.idx.v, value=np.zeros(sa.TGOV1.n))
True
Run power flow.
sa.PFlow.run()
True
Try to init TDS.
_ = sa.TDS.init()
Run TDS.
sa.TDS.config.no_tqdm = True # disable progress bar
sa.TDS.run()
True
sp.RTED.map1
OrderedDict([('ug', ('StaticGen', 'u')), ('pg0', ('StaticGen', 'p'))])
sp.dyn.receive(adsys=sa, routine='RTED')
Receive <ug> from SynGen.u Receive <pg0> from SynGen.Pe
True
The RTED parameter pg0
, is retrieved from ANDES as the corresponding generator output power.
sp.RTED.pg0.v
array([1.79492221, 0.48428986, 0.01000094, 0.02000094, 0.01000095])