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.
|
Base class for variables used in a routine. |
|
Base class for constraints. |
|
Base class for objective functions. |
|
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
Systemclass methodto_andes(). Using the file conversionto_andes()will automatically link the AMS system instance to the converted ANDES system instance in the AMS system attributedyn.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
addfileargument.- 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
Power flow models in the addfile will be skipped and only dynamic models will be used.
The addfile format is guessed based on the file extension. Currently only
xlsxis supported.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
Using the file conversion
to_andes()will automatically link the AMS system to the converted ANDES system in the attributedyn.
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,
recentwill 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,
recentwill 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.