Routine#

Routine refers to scheduling-level model, and it includes two sectinos, namely, Data Section and Model Section.

Data Section#

A simplified code snippet for RTED is shown below as an example.

class RTED:

    def __init__(self):
        ... ...
        self.R10 = RParam(info='10-min ramp rate',
                          name='R10', tex_name=r'R_{10}',
                          model='StaticGen', src='R10',
                          unit='p.u./h',)
        self.gs = ZonalSum(u=self.zg, zone='Zone',
                           name='gs', tex_name=r'S_{g}',
                           info='Sum Gen vars vector in shape of zone',
                           no_parse=True, sparse=True)
        ... ...
        self.rbu = Constraint(name='rbu', is_eq=True,
                              info='RegUp reserve balance',
                              e_str = 'gs @ mul(ug, pru) - dud')
        ... ...

Routine Parameter#

As discussed in previous section, actual data parameters are stored in the device-level models. Thus, in routines, parameters are retrieved from target devices given the device name and the parameter name. In the example above, R10 is a 10-min ramp rate parameter for the static generator. The parameter is retrieved from the devices StaticGen with the parameter name R10.

Service#

Services are developed to assit the formulations. In the example above, ZonalSum is a service to sum the generator variables in a zone. Later, in the constraint, gs is multiplied to the reserve variable pru.

Model Section#

Descriptive Formulation#

Scheduling routine is the descriptive model of the optimization problem.

Further, to facilitate the routine definition, AMS developed a class ams.core.param.RParam to pass the model data to multiple routine modeling.

class ams.core.param.RParam(name: str | None = None, tex_name: str | None = None, info: str | None = None, src: str | None = None, unit: str | None = None, model: str | None = None, v: ndarray | None = None, indexer: str | None = None, imodel: str | None = None, expand_dims: int | None = None, no_parse: bool | None = False, nonneg: bool | None = False, nonpos: bool | None = False, cplx: bool | None = False, imag: bool | None = False, symmetric: bool | None = False, diag: bool | None = False, hermitian: bool | None = False, boolean: bool | None = False, integer: bool | None = False, pos: bool | None = False, neg: bool | None = False, sparse: list | None = None)[source]

Class for parameters used in a routine. This class is developed to simplify the routine definition.

RParm is further used to define Parameter in the optimization model.

no_parse is used to skip parsing the RParam in optimization model. It means that the RParam will not be added to the optimization model. This is useful when the RParam contains non-numeric values, or it is not necessary to be added to the optimization model.

Parameters:
namestr, optional

Name of this parameter. If not provided, name will be set to the attribute name.

tex_namestr, optional

LaTeX-formatted parameter name. If not provided, tex_name will be assigned the same as name.

infostr, optional

A description of this parameter

srcstr, optional

Source name of the parameter.

unitstr, optional

Unit of the parameter.

modelstr, optional

Name of the owner model or group.

vnp.ndarray, optional

External value of the parameter.

indexerstr, optional

Indexer of the parameter.

imodelstr, optional

Name of the owner model or group of the indexer.

no_parse: bool, optional

True to skip parsing the parameter.

nonneg: bool, optional

True to set the parameter as non-negative.

nonpos: bool, optional

True to set the parameter as non-positive.

cplx: bool, optional

True to set the parameter as complex.

imag: bool, optional

True to set the parameter as imaginary.

symmetric: bool, optional

True to set the parameter as symmetric.

diag: bool, optional

True to set the parameter as diagonal.

hermitian: bool, optional

True to set the parameter as hermitian.

boolean: bool, optional

True to set the parameter as boolean.

integer: bool, optional

True to set the parameter as integer.

pos: bool, optional

True to set the parameter as positive.

neg: bool, optional

True to set the parameter as negative.

sparse: bool, optional

True to set the parameter as sparse.

Examples

Example 1: Define a routine parameter from a source model or group.

In this example, we define the parameter cru from the source model SFRCost with the parameter cru.

>>> self.cru = RParam(info='RegUp reserve coefficient',
>>>                   tex_name=r'c_{r,u}',
>>>                   unit=r'$/(p.u.)',
>>>                   name='cru',
>>>                   src='cru',
>>>                   model='SFRCost'
>>>                   )

Example 2: Define a routine parameter with a user-defined value.

In this example, we define the parameter with a user-defined value. TODO: Add example

Numerical Optimization#

Optimization model is the optimization problem. Var, Constraint, and Objective are the basic building blocks of the optimization model. OModel is the container of the optimization model. A summary table is shown below.

Var([name, tex_name, info, src, unit, ...])

Base class for variables used in a routine.

Constraint([name, e_str, info, is_eq])

Base class for constraints.

Objective([name, e_str, info, unit, sense])

Base class for objective functions.

OModel(routine)

Base class for optimization models.

Interoperation with ANDES#

The interoperation with dynamic simulator invovles both file conversion and data exchange. In AMS, the built-in interface with ANDES is implemented in ams.interface.

File Format Converter#

Power flow data is the bridge between scheduling study and dynamics study, where it defines grid topology and power flow. An AMS case can be converted to an ANDES case, with the option to supply additional dynamic data.

ams.interface.to_andes(system, addfile=None, setup=False, no_output=False, default_config=True, verify=False, tol=0.001, **kwargs)[source]

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:
systemSystem

The AMS system to be converted to ANDES format.

addfilestr, optional

The additional file to be converted to ANDES dynamic mdoels.

setupbool, optional

Whether to call setup() after the conversion. Default is True.

no_outputbool, optional

To ANDES system.

default_configbool, optional

To ANDES system.

verifybool

If True, the converted ANDES system will be verified with the source AMS system using AC power flow.

tolfloat

The tolerance of error.

Returns:
adsysandes.system.System

The converted ANDES system.

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.

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)

Data Exchange in Simulation#

To achieve scheduling-dynamics cosimulation, it requires bi-directional data exchange between scheduling and dynamics study. From the perspective of AMS, two functions, send and receive, are developed. The maping relationship for a specific routine is defined in the routine class as map1 and map2. Additionally, a link table for the ANDES case is used for the controller connections.

Module ams.interface.Dynamic, contains the necessary functions and classes for file conversion and data exchange.

class ams.interface.Dynamic(amsys=None, adsys=None)[source]

ANDES interface class.

Parameters:
amsysAMS.system.System

The AMS system.

adsysANDES.system.System

The ANDES system.

Attributes:
linkpandas.DataFrame

The ANDES system link table.

Notes

  1. Using the file conversion to_andes() will automatically link the AMS system to the converted ANDES system in the attribute dyn.

Examples

>>> import ams
>>> import andes
>>> sp = ams.load(ams.get_case('ieee14/ieee14_rted.xlsx'), setup=True)
>>> sa = sp.to_andes(setup=True,
...                  addfile=andes.get_case('ieee14/ieee14_wt3.xlsx'),
...                  overwrite=True, keep=False, no_output=True)
>>> sp.RTED.run()
>>> sp.RTED.dc2ac()
>>> sp.dyn.send()  # send RTED results to ANDES system
>>> sa.PFlow.run()
>>> sp.TDS.run()
>>> sp.dyn.receive()  # receive TDS results from ANDES system
receive(adsys=None, routine=None, no_update=False)[source]

Receive ANDES system results to AMS devices.

Parameters:
adsysadsys.System.system, optional

The target ANDES dynamic system instance. If not provided, use the linked ANDES system isntance (sp.dyn.adsys).

routinestr, optional

The routine to be received from ANDES. If None, recent will be used.

no_updatebool, optional

True to skip update the AMS routine parameters after sync. Default is False.

send(adsys=None, routine=None)[source]

Send results of the recent sovled AMS routine (sp.recent) to the target ANDES system.

Note that converged AC conversion DOES NOT guarantee successful dynamic initialization TDS.init(). Failed initialization is usually caused by limiter violation.

Parameters:
adsysadsys.System.system, optional

The target ANDES dynamic system instance. If not provided, use the linked ANDES system isntance (sp.dyn.adsys).

routinestr, optional

The routine to be sent to ANDES. If None, recent will be used.

When you use this interface, it automatically picks either the dynamic or static model based on the TDS initialization status. If the TDS is running, it selects the dynamic model; otherwise, it goes for the static model. For more details, check out the full API reference or take a look at the source code.

Note

Check ANDES documentation StaticGen for more details about substituting static generators with dynamic generators.