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).