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 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-01-29 20:45:23
andes:1.8.10.post31+gfa2e2e5c
ams:0.8.1.post37.dev0+gf76a132
ams.config_logger(stream_level=20)

Dispatch#

sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
              setup=True,
              no_output=True,)
Parsing input file "/Users/jinningwang/Documents/work/ams/ams/cases/ieee14/ieee14_uced.xlsx"...
Input file parsed in 0.1181 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.0021 seconds.
sp.RTED.init()
Routine <RTED> initialized in 0.0136 seconds.
True
sp.RTED.run(solver='ECOS')
RTED solved as optimal in 0.0200 seconds, converged after 11 iterations using solver ECOS.
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.

sa = sp.to_andes(setup=True,
                 addfile=andes.get_case('ieee14/ieee14_full.xlsx'))
Parsing additional file "/Users/jinningwang/Documents/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.0847 seconds.
System converted to ANDES in 0.1945 seconds.
AMS system 0x109a2c2b0 is linked to the ANDES system 0x109a2be20.

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)

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.

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

Send#

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()
Routine <ACOPF> initialized in 0.0041 seconds.
ACOPF solved in 0.2367 seconds, converged after 12 iterations using solver PYPOWER-PIPS.
Attribute <aBus> already exists in <RTED>.
<RTED> is 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 <0x109a2be20>...
Send <vBus> to Bus.v0
Send <ug> to StaticGen.u
Send <pg> to StaticGen.p0
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.

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

Receive#

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([0.32260084, 0.01      , 0.02      , 0.01      , 1.97393997])