ltbams 1.0.6__py3-none-any.whl → 1.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. ams/_version.py +3 -3
  2. ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  3. ams/cases/5bus/pjm5bus_ev.xlsx +0 -0
  4. ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
  5. ams/cases/hawaii40/Hawaii40.m +375 -0
  6. ams/cases/ieee14/ieee14_uced.xlsx +0 -0
  7. ams/cases/ieee39/ieee39_uced.xlsx +0 -0
  8. ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
  9. ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
  10. ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
  11. ams/cases/matpower/case5.m +25 -0
  12. ams/core/matprocessor.py +7 -16
  13. ams/core/service.py +4 -3
  14. ams/interface.py +17 -4
  15. ams/io/__init__.py +1 -0
  16. ams/io/matpower.py +165 -7
  17. ams/io/psse.py +1 -1
  18. ams/models/__init__.py +1 -1
  19. ams/models/group.py +77 -6
  20. ams/models/line.py +3 -164
  21. ams/models/renewable/regc.py +1 -5
  22. ams/models/reserve.py +2 -2
  23. ams/models/static/gen.py +11 -3
  24. ams/models/static/pq.py +2 -2
  25. ams/models/timeslot.py +1 -1
  26. ams/routines/dcopf.py +1 -1
  27. ams/routines/dcpf0.py +1 -1
  28. ams/routines/dopf.py +10 -10
  29. ams/routines/ed.py +3 -4
  30. ams/routines/pflow.py +9 -10
  31. ams/routines/rted.py +20 -29
  32. ams/routines/uc.py +1 -1
  33. ams/system.py +11 -18
  34. docs/source/api.rst +2 -0
  35. docs/source/getting_started/copyright.rst +1 -1
  36. docs/source/release-notes.rst +24 -0
  37. {ltbams-1.0.6.dist-info → ltbams-1.0.8.dist-info}/METADATA +1 -1
  38. {ltbams-1.0.6.dist-info → ltbams-1.0.8.dist-info}/RECORD +48 -51
  39. {ltbams-1.0.6.dist-info → ltbams-1.0.8.dist-info}/WHEEL +1 -1
  40. tests/test_1st_system.py +31 -0
  41. tests/test_case.py +27 -19
  42. tests/test_io.py +15 -0
  43. tests/test_rtn_ed.py +4 -4
  44. tests/test_rtn_rted.py +4 -4
  45. tests/test_rtn_uc.py +4 -4
  46. tests/test_service.py +2 -2
  47. ams/cases/5bus/pjm5bus_uced.json +0 -1062
  48. ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
  49. ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
  50. ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
  51. tests/test_andes_mats.py +0 -61
  52. {ltbams-1.0.6.dist-info → ltbams-1.0.8.dist-info}/entry_points.txt +0 -0
  53. {ltbams-1.0.6.dist-info → ltbams-1.0.8.dist-info}/top_level.txt +0 -0
ams/io/matpower.py CHANGED
@@ -2,9 +2,10 @@
2
2
  MATPOWER parser.
3
3
  """
4
4
  import logging
5
+ import re
5
6
  import numpy as np
6
7
 
7
- from andes.io.matpower import m2mpc
8
+ from andes.io import read_file_like
8
9
  from andes.shared import deg2rad, rad2deg
9
10
 
10
11
  logger = logging.getLogger(__name__)
@@ -29,6 +30,151 @@ def read(system, file):
29
30
  return mpc2system(mpc, system)
30
31
 
31
32
 
33
+ def m2mpc(infile: str) -> dict:
34
+ """
35
+ Parse a MATPOWER file and return a dictionary containing the parsed data.
36
+
37
+ This function processes MATPOWER case files and extracts relevant fields
38
+ into a structured dictionary. It is revised from ``andes.io.matpower.m2mpc``.
39
+
40
+ Supported fields include:
41
+ - `baseMVA`: The system base power in MVA.
42
+ - `bus`: Bus data, including voltage, load, and generation information.
43
+ - `bus_name`: Names of the buses (if available).
44
+ - `gen`: Generator data, including power limits and voltage setpoints.
45
+ - `branch`: Branch data, including line impedances and ratings.
46
+ - `gencost`: Generator cost data (parsed but not used in this implementation).
47
+ - `areas`: Area data (parsed but not used in this implementation).
48
+ - `gentype`: Generator type information (if available).
49
+ - `genfuel`: Generator fuel type information (if available).
50
+
51
+ Parameters
52
+ ----------
53
+ infile : str
54
+ Path to the MATPOWER file to be parsed.
55
+
56
+ Returns
57
+ -------
58
+ dict
59
+ A dictionary containing the parsed MATPOWER data, where keys correspond
60
+ to MATPOWER struct names and values are numpy arrays or lists.
61
+ """
62
+
63
+ func = re.compile(r'function\s')
64
+ mva = re.compile(r'\s*mpc.baseMVA\s*=\s*')
65
+ bus = re.compile(r'\s*mpc.bus\s*=\s*\[?')
66
+ gen = re.compile(r'\s*mpc.gen\s*=\s*\[')
67
+ branch = re.compile(r'\s*mpc.branch\s*=\s*\[')
68
+ area = re.compile(r'\s*mpc.areas\s*=\s*\[')
69
+ gencost = re.compile(r'\s*mpc.gencost\s*=\s*\[')
70
+ bus_name = re.compile(r'\s*mpc.bus_name\s*=\s*{')
71
+ gentype = re.compile(r'\s*mpc.gentype\s*=\s*{')
72
+ genfuel = re.compile(r'\s*mpc.genfuel\s*=\s*{')
73
+ end = re.compile(r'\s*[\];}]')
74
+ has_digit = re.compile(r'.*\d+\s*]?;?')
75
+
76
+ field = None
77
+ info = True
78
+
79
+ mpc = {
80
+ 'version': 2, # not in use
81
+ 'baseMVA': 100,
82
+ 'bus': [],
83
+ 'gen': [],
84
+ 'branch': [],
85
+ 'area': [],
86
+ 'gencost': [],
87
+ 'bus_name': [],
88
+ 'gentype': [],
89
+ 'genfuel': [],
90
+ }
91
+
92
+ input_list = read_file_like(infile)
93
+
94
+ for line in input_list:
95
+ line = line.strip().rstrip(';')
96
+ if not line:
97
+ continue
98
+ elif func.search(line): # skip function declaration
99
+ continue
100
+ elif len(line.split('%')[0]) == 0:
101
+ if info is True:
102
+ logger.info(line[1:])
103
+ info = False
104
+ else:
105
+ continue
106
+ elif mva.search(line):
107
+ mpc["baseMVA"] = float(line.split('=')[1])
108
+
109
+ if not field:
110
+ if bus.search(line):
111
+ field = 'bus'
112
+ elif gen.search(line):
113
+ field = 'gen'
114
+ elif branch.search(line):
115
+ field = 'branch'
116
+ elif area.search(line):
117
+ field = 'area'
118
+ elif gencost.search(line):
119
+ field = 'gencost'
120
+ elif bus_name.search(line):
121
+ field = 'bus_name'
122
+ elif gentype.search(line):
123
+ field = 'gentype'
124
+ elif genfuel.search(line):
125
+ field = 'genfuel'
126
+ else:
127
+ continue
128
+ elif end.search(line):
129
+ field = None
130
+ continue
131
+
132
+ # parse mpc sections
133
+ if field:
134
+ if line.find('=') >= 0:
135
+ line = line.split('=')[1]
136
+ if line.find('[') >= 0:
137
+ line = re.sub(r'\[', '', line)
138
+ elif line.find('{') >= 0:
139
+ line = re.sub(r'{', '', line)
140
+
141
+ if field in ['bus_name', 'gentype', 'genfuel']:
142
+ # Handle string-based fields
143
+ line = line.split(';')
144
+ data = [i.strip('\'').strip() for i in line if i.strip()]
145
+ mpc[field].extend(data)
146
+ else:
147
+ if not has_digit.search(line):
148
+ continue
149
+ line = line.split('%')[0].strip()
150
+ line = line.split(';')
151
+ for item in line:
152
+ if not has_digit.search(item):
153
+ continue
154
+ try:
155
+ data = np.array([float(val) for val in item.split()])
156
+ except Exception as e:
157
+ logger.error('Error parsing "%s"', infile)
158
+ raise e
159
+ mpc[field].append(data)
160
+
161
+ # convert mpc to np array
162
+ mpc_array = dict()
163
+ for key, val in mpc.items():
164
+ if isinstance(val, (float, int)):
165
+ mpc_array[key] = val
166
+ elif isinstance(val, list):
167
+ if len(val) == 0:
168
+ continue
169
+ if key in ['bus_name', 'gentype', 'genfuel']:
170
+ mpc_array[key] = np.array(val, dtype=object)
171
+ else:
172
+ mpc_array[key] = np.array(val)
173
+ else:
174
+ raise NotImplementedError("Unknown type for mpc, ", type(val))
175
+ return mpc_array
176
+
177
+
32
178
  def mpc2system(mpc: dict, system) -> bool:
33
179
  """
34
180
  Load an mpc dict into an empty AMS system.
@@ -97,7 +243,20 @@ def mpc2system(mpc: dict, system) -> bool:
97
243
  mpc_gen[:, 19] = system.PV.Rq.default * base_mva / 60
98
244
  else:
99
245
  mpc_gen = mpc['gen']
100
- for data in mpc_gen:
246
+
247
+ # Ensure 'gentype' and 'genfuel' keys exist in mpc, with default values if missing
248
+ gentype = mpc.get('gentype', [''] * mpc_gen.shape[0])
249
+ genfuel = mpc.get('genfuel', [''] * mpc_gen.shape[0])
250
+
251
+ # Validate lengths of 'gentype' and 'genfuel' against the number of generators
252
+ if len(gentype) != mpc_gen.shape[0]:
253
+ raise ValueError(
254
+ f"'gentype' length ({len(gentype)}) does not match the number of generators ({mpc_gen.shape[0]})")
255
+ if len(genfuel) != mpc_gen.shape[0]:
256
+ raise ValueError(
257
+ f"'genfuel' length ({len(genfuel)}) does not match the number of generators ({mpc_gen.shape[0]})")
258
+
259
+ for data, gt, gf in zip(mpc_gen, gentype, genfuel):
101
260
  # bus pg qg qmax qmin vg mbase status pmax pmin
102
261
  # 0 1 2 3 4 5 6 7 8 9
103
262
  # pc1 pc2 qc1min qc1max qc2min qc2max ramp_agc ramp_10
@@ -142,7 +301,7 @@ def mpc2system(mpc: dict, system) -> bool:
142
301
  Qc2min=qc2min, Qc2max=qc2max,
143
302
  Ragc=ramp_agc, R10=ramp_10,
144
303
  R30=ramp_30, Rq=ramp_q,
145
- apf=apf)
304
+ apf=apf, gentype=gt, genfuel=gf)
146
305
  else:
147
306
  system.add('PV', idx=gen_idx, bus=bus_idx, busr=bus_idx,
148
307
  name=None,
@@ -155,7 +314,7 @@ def mpc2system(mpc: dict, system) -> bool:
155
314
  Qc2min=qc2min, Qc2max=qc2max,
156
315
  Ragc=ramp_agc, R10=ramp_10,
157
316
  R30=ramp_30, Rq=ramp_q,
158
- apf=apf)
317
+ apf=apf, gentype=gt, genfuel=gf)
159
318
 
160
319
  for data in mpc['branch']:
161
320
  # fbus tbus r x b rateA rateB rateC ratio angle
@@ -204,9 +363,8 @@ def mpc2system(mpc: dict, system) -> bool:
204
363
  gen_idx = np.arange(mpc['gen'].shape[0]) + 1
205
364
  mpc_cost = mpc['gencost']
206
365
  if mpc_cost[0, 0] == 1:
207
- logger.warning("Type 1 gencost detected. "
208
- "This is not supported in AMS. "
209
- "Default type 2 cost parameters will be used as a fallback."
366
+ logger.warning("Type 1 gencost detected, which is not supported in AMS.\n"
367
+ "Default type 2 cost parameters will be used as a fallback.\n"
210
368
  "It is recommended to manually convert the gencost data to type 2.")
211
369
  mpc_cost = np.repeat(np.array([[2, 0, 0, 3, 0, 0, 0]]),
212
370
  mpc_cost.shape[0], axis=0)
ams/io/psse.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Excel reader and writer for AMS.
2
+ PSS/E .raw reader for AMS.
3
3
  This module is the existing module in ``andes.io.psse``.
4
4
  """
5
5
 
ams/models/__init__.py CHANGED
@@ -18,7 +18,7 @@ ams_file_classes = list([
18
18
  ('reserve', ['SFR', 'SR', 'NSR', 'VSGR']),
19
19
  ('cost', ['GCost', 'SFRCost', 'SRCost', 'NSRCost', 'VSGCost']),
20
20
  ('cost', ['DCost']),
21
- ('timeslot', ['TimeSlot', 'EDTSlot', 'UCTSlot']),
21
+ ('timeslot', ['EDTSlot', 'UCTSlot']),
22
22
  ])
23
23
 
24
24
  file_classes = ams_file_classes
ams/models/group.py CHANGED
@@ -175,20 +175,91 @@ class Reserve(GroupBase):
175
175
 
176
176
  class StaticGen(GroupBase):
177
177
  """
178
- Generator group.
178
+ Static Generator Group.
179
+
180
+ The generator types and fuel types are referenced from MATPOWER.
181
+
182
+ Generator Types
183
+ ---------------
184
+ The following codes represent the types of generators:
185
+ - BA : Energy Storage, Battery
186
+ - CE : Energy Storage, Compressed Air
187
+ - CP : Energy Storage, Concentrated Solar Power
188
+ - FW : Energy Storage, Flywheel
189
+ - PS : Hydraulic Turbine, Reversible (pumped storage)
190
+ - ES : Energy Storage, Other
191
+ - ST : Steam Turbine (includes nuclear, geothermal, and solar steam)
192
+ - GT : Combustion (Gas) Turbine
193
+ - IC : Internal Combustion Engine (diesel, piston, reciprocating)
194
+ - CA : Combined Cycle Steam Part
195
+ - CT : Combined Cycle Combustion Turbine Part
196
+ - CS : Combined Cycle Single Shaft
197
+ - CC : Combined Cycle Total Unit
198
+ - HA : Hydrokinetic, Axial Flow Turbine
199
+ - HB : Hydrokinetic, Wave Buoy
200
+ - HK : Hydrokinetic, Other
201
+ - HY : Hydroelectric Turbine
202
+ - BT : Turbines Used in a Binary Cycle
203
+ - PV : Photovoltaic
204
+ - WT : Wind Turbine, Onshore
205
+ - WS : Wind Turbine, Offshore
206
+ - FC : Fuel Cell
207
+ - OT : Other
208
+ - UN : Unknown
209
+ - JE : Jet Engine
210
+ - NB : ST - Boiling Water Nuclear Reactor
211
+ - NG : ST - Graphite Nuclear Reactor
212
+ - NH : ST - High Temperature Gas Nuclear Reactor
213
+ - NP : ST - Pressurized Water Nuclear Reactor
214
+ - IT : Internal Combustion Turbo Charged
215
+ - SC : Synchronous Condenser
216
+ - DC : DC ties
217
+ - MP : Motor/Pump
218
+ - W1 : Wind Turbine, Type 1
219
+ - W2 : Wind Turbine, Type 2
220
+ - W3 : Wind Turbine, Type 3
221
+ - W4 : Wind Turbine, Type 4
222
+ - SV : Static Var Compensator
223
+ - DL : Dispatchable Load
224
+
225
+ Fuel Types
226
+ ----------
227
+ The following codes represent the fuel types:
228
+ - biomass : Biomass
229
+ - coal : Coal
230
+ - dfo : Distillate Fuel Oil
231
+ - geothermal : Geothermal
232
+ - hydro : Hydro
233
+ - hydrops : Hydro Pumped Storage
234
+ - jetfuel : Jet Fuel
235
+ - lng : Liquefied Natural Gas
236
+ - ng : Natural Gas
237
+ - nuclear : Nuclear
238
+ - oil : Unspecified Oil
239
+ - refuse : Refuse, Municipal Solid Waste
240
+ - rfo : Residual Fuel Oil
241
+ - solar : Solar
242
+ - syncgen : Synchronous Condenser
243
+ - wasteheat : Waste Heat
244
+ - wind : Wind
245
+ - wood : Wood or Wood Waste
246
+ - other : Other
247
+ - unknown : Unknown
248
+ - dl : Dispatchable Load
249
+ - ess : Energy Storage System
179
250
 
180
251
  Notes
181
252
  -----
182
- For co-simulation with ANDES, check
183
- `ANDES StaticGen <https://docs.andes.app/en/latest/groupdoc/StaticGen.html#staticgen>`_
184
- for replacing static generators with dynamic generators.
253
+ For co-simulation with ANDES, refer to the `ANDES StaticGen Documentation
254
+ <https://docs.andes.app/en/latest/groupdoc/StaticGen.html#staticgen>`_ for
255
+ replacing static generators with dynamic generators.
185
256
  """
186
257
 
187
258
  def __init__(self):
188
259
  super().__init__()
189
260
  self.common_params.extend(('bus', 'Sn', 'Vn', 'p0', 'q0', 'ra', 'xs', 'subidx',
190
261
  'pmax', 'pmin', 'pg0', 'ctrl', 'R10', 'td1', 'td2',
191
- 'zone'))
262
+ 'area', 'zone', 'gentype', 'genfuel'))
192
263
  self.common_vars.extend(('p', 'q'))
193
264
 
194
265
 
@@ -211,7 +282,7 @@ class StaticLoad(GroupBase):
211
282
 
212
283
  def __init__(self):
213
284
  super().__init__()
214
- self.common_params.extend(('bus', 'p0', 'q0', 'ctrl', 'zone'))
285
+ self.common_params.extend(('bus', 'p0', 'q0', 'ctrl', 'area', 'zone'))
215
286
 
216
287
 
217
288
  class StaticShunt(GroupBase):
ams/models/line.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from andes.models.line.line import LineData
2
2
  from andes.models.line.jumper import JumperData
3
3
  from andes.core.param import NumParam
4
- from andes.shared import deg2rad, np, spmatrix
4
+ from andes.shared import deg2rad
5
5
 
6
6
  from ams.core.model import Model
7
7
 
@@ -18,8 +18,8 @@ class Line(LineData, Model):
18
18
 
19
19
  Notes
20
20
  -----
21
- There is a known issue that adding Algeb ``ud`` will cause Line.algebs run into
22
- AttributeError: 'NoneType' object has no attribute 'n'. Not figured out why yet.
21
+ 1. Adding Algeb ``ud`` causes Line.algebs to encounter an AttributeError: 'NoneType'
22
+ object has no attribute 'n'. The root cause is still under investigation.
23
23
  """
24
24
 
25
25
  def __init__(self, system=None, config=None) -> None:
@@ -49,167 +49,6 @@ class Line(LineData, Model):
49
49
  self.a1a = None
50
50
  self.a2a = None
51
51
 
52
- # NOTE: following code are minly copied from `andes.models.line.Line`
53
- # and they are not fully verified
54
- # potential issues:
55
- # `build_Bp` contains 'fdxb', which is not included in the input parameters,
56
- # and the results are the negative of `Bbus` from `makeBdc` in PYPOWER
57
- # `build_Bpp` ignores the line resistance for all three methods
58
- # `build_Bdc` results are the negative of `Bbus` from `makeBdc` in PYPOWER
59
- # `build_y` results have inignorable differences at diagonal elements with `makeYbus` in PYPOWER
60
-
61
- def build_y(self):
62
- """
63
- Build bus admittance matrix. Copied from ``andes.models.line.line.Line``.
64
-
65
- Returns
66
- -------
67
- Y : spmatrix
68
- Bus admittance matrix.
69
- """
70
-
71
- nb = self.system.Bus.n
72
-
73
- y1 = self.u.v * (self.g1.v + self.b1.v * 1j)
74
- y2 = self.u.v * (self.g2.v + self.b2.v * 1j)
75
- y12 = self.u.v / (self.r.v + self.x.v * 1j)
76
- m = self.tap.v * np.exp(1j * self.phi.v)
77
- m2 = self.tap.v**2
78
- mconj = np.conj(m)
79
-
80
- # build self and mutual admittances into Y
81
- Y = spmatrix((y12 + y1 / m2), self.a1a, self.a1a, (nb, nb), 'z')
82
- Y -= spmatrix(y12 / mconj, self.a1a, self.a2a, (nb, nb), 'z')
83
- Y -= spmatrix(y12 / m, self.a2a, self.a1a, (nb, nb), 'z')
84
- Y += spmatrix(y12 + y2, self.a2a, self.a2a, (nb, nb), 'z')
85
-
86
- return Y
87
-
88
- def build_Bp(self, method='fdpf'):
89
- """
90
- Function for building B' matrix.
91
-
92
- Parameters
93
- ----------
94
- method : str
95
- Method for building B' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'.
96
-
97
- Returns
98
- -------
99
- Bp : spmatrix
100
- B' matrix.
101
- """
102
- nb = self.system.Bus.n
103
-
104
- if method not in ("fdpf", "fdbx", "dcpf"):
105
- raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'")
106
-
107
- # Build B prime matrix -- FDPF
108
- # `y1`` neglects line charging shunt, and g1 is usually 0 in HV lines
109
- # `y2`` neglects line charging shunt, and g2 is usually 0 in HV lines
110
- y1 = self.u.v * self.g1.v
111
- y2 = self.u.v * self.g2.v
112
-
113
- # `m` neglected tap ratio
114
- m = np.exp(self.phi.v * 1j)
115
- mconj = np.conj(m)
116
- m2 = np.ones(self.n)
117
-
118
- if method in ('fdxb', 'dcpf'):
119
- # neglect line resistance in Bp in XB method
120
- y12 = self.u.v / (self.x.v * 1j)
121
- else:
122
- y12 = self.u.v / (self.r.v + self.x.v * 1j)
123
-
124
- Bdc = spmatrix((y12 + y1) / m2, self.a1a, self.a1a, (nb, nb), 'z')
125
- Bdc -= spmatrix(y12 / mconj, self.a1a, self.a2a, (nb, nb), 'z')
126
- Bdc -= spmatrix(y12 / m, self.a2a, self.a1a, (nb, nb), 'z')
127
- Bdc += spmatrix(y12 + y2, self.a2a, self.a2a, (nb, nb), 'z')
128
- Bdc = Bdc.imag()
129
-
130
- for item in range(nb):
131
- if abs(Bdc[item, item]) == 0:
132
- Bdc[item, item] = 1e-6 + 0j
133
-
134
- return Bdc
135
-
136
- def build_Bpp(self, method='fdpf'):
137
- """
138
- Function for building B'' matrix.
139
-
140
- Parameters
141
- ----------
142
- method : str
143
- Method for building B'' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'.
144
-
145
- Returns
146
- -------
147
- Bpp : spmatrix
148
- B'' matrix.
149
- """
150
-
151
- nb = self.system.Bus.n
152
-
153
- if method not in ("fdpf", "fdbx", "dcpf"):
154
- raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'")
155
-
156
- # Build B double prime matrix
157
- # y1 neglected line charging shunt, and g1 is usually 0 in HV lines
158
- # y2 neglected line charging shunt, and g2 is usually 0 in HV lines
159
- # m neglected phase shifter
160
- y1 = self.u.v * (self.g1.v + self.b1.v * 1j)
161
- y2 = self.u.v * (self.g2.v + self.b2.v * 1j)
162
-
163
- m = self.tap.v
164
- m2 = abs(m)**2
165
-
166
- if method in ('fdbx', 'fdpf', 'dcpf'):
167
- # neglect line resistance in Bpp in BX method
168
- y12 = self.u.v / (self.x.v * 1j)
169
- else:
170
- y12 = self.u.v / (self.r.v + self.x.v * 1j)
171
-
172
- Bpp = spmatrix((y12 + y1) / m2, self.a1a, self.a1a, (nb, nb), 'z')
173
- Bpp -= spmatrix(y12 / np.conj(m), self.a1a, self.a2a, (nb, nb), 'z')
174
- Bpp -= spmatrix(y12 / m, self.a2a, self.a1a, (nb, nb), 'z')
175
- Bpp += spmatrix(y12 + y2, self.a2a, self.a2a, (nb, nb), 'z')
176
- Bpp = Bpp.imag()
177
-
178
- for item in range(nb):
179
- if abs(Bpp[item, item]) == 0:
180
- Bpp[item, item] = 1e-6 + 0j
181
-
182
- return Bpp
183
-
184
- def build_Bdc(self):
185
- """
186
- The MATPOWER-flavor Bdc matrix for DC power flow.
187
-
188
- The method neglects line charging and line resistance. It retains tap ratio.
189
-
190
- Returns
191
- -------
192
- Bdc : spmatrix
193
- Bdc matrix.
194
- """
195
-
196
- nb = self.system.Bus.n
197
-
198
- y12 = self.u.v / (self.x.v * 1j)
199
- y12 = y12 / self.tap.v
200
-
201
- Bdc = spmatrix(y12, self.a1a, self.a1a, (nb, nb), 'z')
202
- Bdc -= spmatrix(y12, self.a1a, self.a2a, (nb, nb), 'z')
203
- Bdc -= spmatrix(y12, self.a2a, self.a1a, (nb, nb), 'z')
204
- Bdc += spmatrix(y12, self.a2a, self.a2a, (nb, nb), 'z')
205
- Bdc = Bdc.imag()
206
-
207
- for item in range(nb):
208
- if abs(Bdc[item, item]) == 0:
209
- Bdc[item, item] = 1e-6
210
-
211
- return Bdc
212
-
213
52
 
214
53
  class Jumper(JumperData, Model):
215
54
  """
@@ -2,7 +2,7 @@
2
2
  RenGen scheduling model.
3
3
  """
4
4
 
5
- from andes.core.param import NumParam, IdxParam, ExtParam
5
+ from andes.core.param import NumParam, IdxParam
6
6
  from andes.core.model import ModelData
7
7
  from ams.core.model import Model
8
8
 
@@ -80,10 +80,6 @@ class REGCV1(REGCData, Model):
80
80
  REGCData.__init__(self)
81
81
  Model.__init__(self, system, config)
82
82
  self.group = 'VSG'
83
- self.zone = ExtParam(model='Bus', src='zone',
84
- indexer=self.bus, export=False,
85
- info='Retrieved zone idx',
86
- vtype=str, default=None)
87
83
  self.M = NumParam(default=10, tex_name='M',
88
84
  info='Inertia emulation',
89
85
  unit='s',
ams/models/reserve.py CHANGED
@@ -10,8 +10,8 @@ from ams.core.model import Model
10
10
  class ReserveData(ModelData):
11
11
  def __init__(self):
12
12
  super().__init__()
13
- self.zone = IdxParam(model='Zone',
14
- default=None, info="Zone code",)
13
+ self.area = IdxParam(model='Area',
14
+ default=None, info="Area idx",)
15
15
 
16
16
 
17
17
  class SFR(ReserveData, Model):
ams/models/static/gen.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from collections import OrderedDict
2
2
 
3
- from andes.core.param import NumParam, ExtParam
3
+ from andes.core.param import NumParam, ExtParam, DataParam
4
4
  from andes.models.static.pv import PVData
5
5
  from andes.models.static.slack import SlackData
6
6
 
@@ -81,6 +81,14 @@ class GenParam:
81
81
  tex_name=r't_{d2}',
82
82
  unit='h',
83
83
  )
84
+ self.gentype = DataParam(default=None,
85
+ info='generator type',
86
+ tex_name=r'g_{type}',
87
+ )
88
+ self.genfuel = DataParam(default=None,
89
+ info='generator fuel type',
90
+ tex_name=r'g_{fuel}',
91
+ )
84
92
 
85
93
 
86
94
  class PVModel(Model):
@@ -120,8 +128,8 @@ class PVModel(Model):
120
128
  err_tol=r"\epsilon_{tol}"
121
129
  )
122
130
 
123
- self.zone = ExtParam(model='Bus', src='zone', indexer=self.bus, export=False,
124
- info='Retrieved zone idx', vtype=str, default=None,
131
+ self.area = ExtParam(model='Bus', src='area', indexer=self.bus, export=False,
132
+ info='Retrieved area idx', vtype=str, default=None,
125
133
  )
126
134
 
127
135
  self.ud = Algeb(info='commitment decision',
ams/models/static/pq.py CHANGED
@@ -53,8 +53,8 @@ class PQ(PQData, Model):
53
53
  q2z=r"\gamma_{q2z}",
54
54
  )
55
55
 
56
- self.zone = ExtParam(model='Bus', src='zone', indexer=self.bus, export=False,
57
- info='Retrieved zone idx', vtype=str, default=None,
56
+ self.area = ExtParam(model='Bus', src='area', indexer=self.bus, export=False,
57
+ info='Retrieved area idx', vtype=str, default=None,
58
58
  )
59
59
  self.ctrl = NumParam(default=1,
60
60
  info="load controllability",
ams/models/timeslot.py CHANGED
@@ -18,7 +18,7 @@ def str_list_oconv(x):
18
18
 
19
19
  class TimeSlot(ModelData, Model):
20
20
  """
21
- Time slot data for rolling horizon.
21
+ Base model for time slot data used in multi-interval scheduling.
22
22
  """
23
23
 
24
24
  def __init__(self, system=None, config=None):
ams/routines/dcopf.py CHANGED
@@ -152,7 +152,7 @@ class DCOPF(DCPFBase):
152
152
 
153
153
  def dc2ac(self, kloss=1.0, **kwargs):
154
154
  """
155
- Convert the RTED results with ACOPF.
155
+ Convert the results using ACOPF.
156
156
 
157
157
  Parameters
158
158
  ----------
ams/routines/dcpf0.py CHANGED
@@ -168,7 +168,7 @@ class DCPF0(RoutineBase):
168
168
  if self.exit_code == 0:
169
169
  msg = f"<{self.class_name}> solved in {s}, converged in "
170
170
  msg += n_iter_str + f"with {sstats['solver_name']}."
171
- logger.info(msg)
171
+ logger.warning(msg)
172
172
  try:
173
173
  self.unpack(res)
174
174
  except Exception as e:
ams/routines/dopf.py CHANGED
@@ -16,11 +16,11 @@ class DOPF(DCOPF):
16
16
 
17
17
  UNDER DEVELOPMENT!
18
18
 
19
- Reference:
20
-
21
- [1] L. Bai, J. Wang, C. Wang, C. Chen, and F. Li, “Distribution Locational Marginal Pricing (DLMP)
22
- for Congestion Management and Voltage Support,” IEEE Trans. Power Syst., vol. 33, no. 4,
23
- pp. 40614073, Jul. 2018, doi: 10.1109/TPWRS.2017.2767632.
19
+ References
20
+ -----------------
21
+ 1. L. Bai, J. Wang, C. Wang, C. Chen, and F. Li, “Distribution Locational Marginal Pricing (DLMP)
22
+ for Congestion Management and Voltage Support,” IEEE Trans. Power Syst., vol. 33, no. 4,
23
+ pp. 4061-4073, Jul. 2018, doi: 10.1109/TPWRS.2017.2767632.
24
24
  """
25
25
 
26
26
  def __init__(self, system, config):
@@ -115,11 +115,11 @@ class DOPFVIS(DOPF):
115
115
 
116
116
  UNDER DEVELOPMENT!
117
117
 
118
- Reference:
119
-
120
- [1] L. Bai, J. Wang, C. Wang, C. Chen, and F. Li, “Distribution Locational Marginal Pricing (DLMP)
121
- for Congestion Management and Voltage Support,” IEEE Trans. Power Syst., vol. 33, no. 4,
122
- pp. 40614073, Jul. 2018, doi: 10.1109/TPWRS.2017.2767632.
118
+ References
119
+ -----------------
120
+ 1. L. Bai, J. Wang, C. Wang, C. Chen, and F. Li, “Distribution Locational Marginal Pricing (DLMP)
121
+ for Congestion Management and Voltage Support,” IEEE Trans. Power Syst., vol. 33, no. 4,
122
+ pp. 4061-4073, Jul. 2018, doi: 10.1109/TPWRS.2017.2767632.
123
123
  """
124
124
 
125
125
  def __init__(self, system, config):