Examples#

One example is provided to demonstrate descriptive scheduling modeling.

DCOPF#

DC optimal power flow (DCOPF) is a fundamental routine used in power system analysis. In this example, we demonstrate how to implement a DCOPF routine in a descriptive manner using AMS. Below is a simplified DCOPF code snippet. The full code can be found in ams.routines.dcopf.DCOPF.

Data Section#

 1   class DCOPF(RoutineBase):
 2
 3       def __init__(self, system, config):
 4           RoutineBase.__init__(self, system, config)
 5           self.info = 'DC Optimal Power Flow'
 6           self.type = 'DCED'
 7           # --- Data Section ---
 8           # --- generator cost ---
 9           self.c1 = RParam(info='Gen cost coefficient 1',
10                            name='c1', tex_name=r'c_{1}', unit=r'$/(p.u.)',
11                            model='GCost', src='c1',
12                            indexer='gen', imodel='StaticGen',)
13           # --- generator ---
14           self.ug = RParam(info='Gen connection status',
15                            name='ug', tex_name=r'u_{g}',
16                            model='StaticGen', src='u',
17                            no_parse=True)
18           self.ctrl = RParam(info='Gen controllability',
19                              name='ctrl', tex_name=r'c_{trl}',
20                              model='StaticGen', src='ctrl',
21                              no_parse=True)
22           self.ctrle = NumOpDual(info='Effective Gen controllability',
23                                  name='ctrle', tex_name=r'c_{trl, e}',
24                                  u=self.ctrl, u2=self.ug,
25                                  fun=np.multiply, no_parse=True)
26           # --- load ---
27           self.pd = RParam(info='active demand',
28                            name='pd', tex_name=r'p_{d}',
29                            model='StaticLoad', src='p0',
30                            unit='p.u.',)
31           # --- line ---
32           self.rate_a = RParam(info='long-term flow limit',
33                                name='rate_a', tex_name=r'R_{ATEA}',
34                                unit='MVA', model='Line',)
35           # --- line angle difference ---
36           self.amax = NumOp(u=self.rate_a, fun=np.ones_like,
37                             rfun=np.dot, rargs=dict(b=np.pi),
38                             name='amax', tex_name=r'\theta_{max}',
39                             info='max line angle difference',
40                             no_parse=True,)
41           # --- connection matrix ---
42           self.Cg = RParam(info='Gen connection matrix',
43                            name='Cg', tex_name=r'C_{g}',
44                            model='mats', src='Cg',
45                            no_parse=True, sparse=True,)
46           # --- system matrix ---
47           self.Bbus = RParam(info='Bus admittance matrix',
48                              name='Bbus', tex_name=r'B_{bus}',
49                              model='mats', src='Bbus',
50                              no_parse=True, sparse=True,)

Lines 1-4: Derive subclass DCOPF from RoutineBase.

Lines 5-6: Define routine information and type.

Lines 9-12: Define linear generator cost coefficients c1, where it sources from GCost.c1 and sorted by StaticGen.gen.

Lines 22-24: Define effective controllability as service ctrle, where it multiplies ctrl and ug.

Lines 42-45: Define generator connection matrix Cg, where it sources from matrix processor mats.Cg and is sparse.

Model Section#

51           # --- Model Section ---
52           # --- generation ---
53           self.pg = Var(info='Gen active power',
54                         unit='p.u.',
55                         name='pg', tex_name=r'p_g',
56                         model='StaticGen', src='p',
57                         v0=self.pg0)
58           # --- bus ---
59           self.aBus = Var(info='Bus voltage angle',
60                           unit='rad',
61                           name='aBus', tex_name=r'\theta_{bus}',
62                           model='Bus', src='a',)
63           # --- power balance ---
64           pb = 'Bbus@aBus + Pbusinj + Cl@pd + Csh@gsh - Cg@pg == 0'
65           self.pb = Constraint(name='pb', info='power balance',
66                                e_str=pb,)
67           # --- line flow ---
68           self.plf = Var(info='Line flow',
69                          unit='p.u.',
70                          name='plf', tex_name=r'p_{lf}',
71                          model='Line',)
72           self.plflb = Constraint(info='line flow lower bound',
73                                   name='plflb',
74                                   e_str='-Bf@aBus - Pfinj - rate_a <= 0',)
75           self.plfub = Constraint(info='line flow upper bound',
76                                   name='plfub',
77                                   e_str='Bf@aBus + Pfinj - rate_a <= 0',)
78           # --- objective ---
79           obj = 'cp.sum(cp.multiply(c2, pg**2))'
80           obj += '+ cp.sum(cp.multiply(c1, pg))'
81           obj += '+ cp.sum(cp.multiply(ug, c0))'
82           self.obj = Objective(name='obj',
83                                info='total cost', unit='$',
84                                sense='min', e_str=obj,)

Continued from the above code.

Lines 53-57: Define variable pg, where it links to StaticGen.p and initial value pg0.

Lines 68-71: Define variable plf, where it links to Line with no target source variable nor initial value.

Lines 72-77: Define inequality constraints plflb and plfub for line flow limits.

Lines 79-84: Define objective function obj for minimizing total cost.

Finalize#

Lastly, similar to finalize a device model, we need to finalize the routine by adding the RTED to the routine list in $HOME/ams/ams/routines/__init__.py, where 'rted' is the file name, and 'RTED' is the routine name.

all_routines = OrderedDict([
      ... ...
      ('dcopf', ['DCOPF']),
      ('ed', ['ED', 'EDDG', 'EDES']),
      ('rted', ['RTED', 'RTEDDG', 'RTEDES', 'RTEDVIS']),
      ... ...
])

Note

See examples/ex8.ipynb (rendered under Examples) for post-init customization patterns — sp.DCOPF.obj.e_str += '+ ...' and ams.routines.routine.RoutineBase.addConstrs() — that do not require modification of the source code. Customizations must use canonical CVXPY syntax; see Migration: canonical CVXPY in e_str (v1.2.3).