Interoperation with ANDES#
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 scheduling-dynamics co-simulation using AMS and ANDES.
[1]:
import numpy as np
import andes
import ams
[2]:
ams.config_logger(stream_level=20)
Scheduling#
[3]:
sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
setup=True,
no_output=True,)
Parsing input file "/Users/jinningwang/work/ams/ams/cases/ieee14/ieee14_uced.xlsx"...
SFR: unused data {'zone': 'ZONE1'}
SFR: unused data {'zone': 'ZONE2'}
SR: unused data {'zone': 'ZONE1'}
SR: unused data {'zone': 'ZONE2'}
NSR: unused data {'zone': 'ZONE1'}
NSR: unused data {'zone': 'ZONE2'}
Input file parsed in 0.2065 seconds.
System set up in 0.0030 seconds.
[4]:
sp.RTED.init()
Building system matrices
Parsing OModel for <RTED>
Evaluating OModel for <RTED>
Finalizing OModel for <RTED>
<RTED> initialized in 0.0232 seconds.
[4]:
True
[5]:
sp.RTED.run(solver='CLARABEL')
<RTED> solved as optimal in 0.0205 seconds, converged in 10 iterations with CLARABEL.
[5]:
True
Convert to ANDES#
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.
[6]:
sa = sp.to_andes(setup=True,
addfile=andes.get_case('ieee14/ieee14_full.xlsx'))
Parsing additional file "/Users/jinningwang/work/andes/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.0781 seconds.
System converted to ANDES in 0.0892 seconds.
AMS system 0x105929720 is linked to the ANDES system 0x12f29ed40.
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.
[7]:
help(sp.to_andes)
Help on method to_andes in module ams.system:
to_andes(addfile=None, setup=False, no_output=False, default_config=True, verify=False, tol=0.001, **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.
This function is wrapped as the ``System`` class method ``to_andes()``.
Using the file conversion ``to_andes()`` will automatically
link the AMS system instance to the converted ANDES system instance
in the AMS system attribute ``dyn``.
It should be noted that detailed dynamic simualtion requires extra
dynamic models to be added to the ANDES system, which can be passed
through the ``addfile`` argument.
Parameters
----------
system : System
The AMS system to be converted to ANDES format.
addfile : str, optional
The additional file to be converted to ANDES dynamic mdoels.
setup : bool, optional
Whether to call `setup()` after the conversion. Default is True.
no_output : bool, optional
To ANDES system.
default_config : bool, optional
To ANDES system.
verify : bool
If True, the converted ANDES system will be verified with the source
AMS system using AC power flow.
tol : float
The tolerance of error.
Returns
-------
adsys : andes.system.System
The converted ANDES system.
Examples
--------
>>> import ams
>>> import andes
>>> sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'), setup=True)
>>> sa = sp.to_andes(addfile=andes.get_case('ieee14/ieee14_full.xlsx'),
... setup=False, overwrite=True, no_output=True)
Notes
-----
1. Power flow models in the addfile will be skipped and only dynamic models will be used.
2. The addfile format is guessed based on the file extension. Currently only ``xlsx`` is supported.
3. Index in the addfile is automatically adjusted when necessary.
Interoperation with ANDES#
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.
[8]:
sp.dyn.link
[8]:
| 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 |
Send#
As there is a gap between DC-based dispatch and AC-based TDS, a conversion is required to ensure the TDS initialization.
[9]:
sp.RTED.dc2ac()
Parsing OModel for <ACOPF>
Evaluating OModel for <ACOPF>
Finalizing OModel for <ACOPF>
<ACOPF> initialized in 0.0070 seconds.
<ACOPF> solved in 0.2544 seconds, converged in 12 iterations with PYPOWER-PIPS.
<RTED> converted to AC.
[9]:
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.
[10]:
sp.RTED.map2
[10]:
OrderedDict([('vBus', ('Bus', 'v0')),
('ug', ('StaticGen', 'u')),
('pg', ('StaticGen', 'p0'))])
[11]:
sp.dyn.send(adsys=sa, routine='RTED')
Send <RTED> results to ANDES <0x12f29ed40>...
*Send <vBus> to StaticGen.v0
Send <vBus> to Bus.v0
Send <ug> to StaticGen.u
Send <pg> to StaticGen.p0
[11]:
True
Run ANDES#
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.
[12]:
sa.TGOV1.alter(src='VMAX', idx=sa.TGOV1.idx.v, value=100*np.ones(sa.TGOV1.n))
sa.TGOV1.alter(src='VMIN', idx=sa.TGOV1.idx.v, value=np.zeros(sa.TGOV1.n))
Run power flow.
[13]:
sa.PFlow.run()
[13]:
True
Try to init TDS.
[14]:
_ = sa.TDS.init()
Run TDS.
[15]:
sa.TDS.config.no_tqdm = True # disable progress bar
sa.TDS.run()
[15]:
True
Receive#
[16]:
sp.RTED.map1
[16]:
OrderedDict([('ug', ('StaticGen', 'u')), ('pg0', ('StaticGen', 'p'))])
[17]:
sp.dyn.receive(adsys=sa, routine='RTED')
Receive <ug> from SynGen.u
Receive <pg0> from SynGen.Pe
[17]:
True
The RTED parameter pg0, is retrieved from ANDES as the corresponding generator output power.
[18]:
sp.RTED.pg0.v
[18]:
array([0.48417982, 0.01000094, 0.02000094, 0.01000095, 1.79503641])