stepss 3.59__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.
- stepss/__init__.py +66 -0
- stepss/_bundled.py +13 -0
- stepss/cases.py +555 -0
- stepss/extractor.py +750 -0
- stepss/globals.py +154 -0
- stepss/helios.py +836 -0
- stepss/libs/helios_api.h +305 -0
- stepss/libs/lin/libhelios_api.so +0 -0
- stepss/libs/lin/ramses.so +0 -0
- stepss/libs/mac/libhelios_api.dylib +0 -0
- stepss/libs/mac/ramses.so +0 -0
- stepss/libs/ramses.h +41 -0
- stepss/libs/win/helios_api.dll +0 -0
- stepss/libs/win/ramses.dll +0 -0
- stepss/scripts/__init__.py +2 -0
- stepss/scripts/exec.py +38 -0
- stepss/simulator.py +1072 -0
- stepss-3.59.dist-info/METADATA +296 -0
- stepss-3.59.dist-info/RECORD +23 -0
- stepss-3.59.dist-info/WHEEL +5 -0
- stepss-3.59.dist-info/entry_points.txt +2 -0
- stepss-3.59.dist-info/licenses/LICENSE.rst +190 -0
- stepss-3.59.dist-info/top_level.txt +1 -0
stepss/__init__.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""stepss - Python interface to the STEPSS power-system simulation platform.
|
|
4
|
+
|
|
5
|
+
Drives RAMSES, the time-domain dynamic simulator, and Helios, the AC
|
|
6
|
+
power-flow engine, both of which ship as bundled shared libraries.
|
|
7
|
+
|
|
8
|
+
Public API exported by this package:
|
|
9
|
+
|
|
10
|
+
- :class:`~stepss.cases.cfg` - build and manage a simulation case (input/output files).
|
|
11
|
+
- :class:`~stepss.simulator.sim` - load the RAMSES shared library and run simulations.
|
|
12
|
+
- :class:`~stepss.extractor.extractor` - parse Fortran binary trajectory files post-simulation.
|
|
13
|
+
- :class:`~stepss.extractor.cur` - lightweight NamedTuple holding a (time, value, msg) timeseries.
|
|
14
|
+
- :func:`~stepss.extractor.curplot` - plot one or more :class:`cur` objects on a single axes.
|
|
15
|
+
- :class:`~stepss.helios.HeliosSession` - run AC power flows with the Helios engine.
|
|
16
|
+
- :class:`~stepss.globals.HeliosError` - exception raised by Helios calls.
|
|
17
|
+
|
|
18
|
+
Module-level flags set at import time:
|
|
19
|
+
|
|
20
|
+
- ``__runTimeObs__`` - ``True`` when gnuplot is available on the system PATH and runtime
|
|
21
|
+
observable plots are therefore enabled; ``False`` otherwise.
|
|
22
|
+
|
|
23
|
+
This package was previously distributed as ``pyramses``. The ``pyramses``
|
|
24
|
+
distribution on PyPI is now a forwarding shim onto this one.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__package_name__ = "stepss"
|
|
28
|
+
__version__ = '3.59'
|
|
29
|
+
__author__ = "Petros Aristidou"
|
|
30
|
+
__copyright__ = "Petros Aristidou"
|
|
31
|
+
__license__ = "Apache-2.0"
|
|
32
|
+
__maintainer__ = "Petros Aristidou"
|
|
33
|
+
__email__ = "apetros@pm.me"
|
|
34
|
+
__url__ = "https://stepss.sps-lab.org"
|
|
35
|
+
__status__ = "5 - Production/Stable"
|
|
36
|
+
|
|
37
|
+
import sys
|
|
38
|
+
from warnings import warn
|
|
39
|
+
|
|
40
|
+
from . import globals as _globals
|
|
41
|
+
from .cases import cfg
|
|
42
|
+
from .globals import __which, HeliosError
|
|
43
|
+
from .simulator import sim
|
|
44
|
+
from .extractor import extractor, curplot, cur
|
|
45
|
+
from . import helios
|
|
46
|
+
from .helios import HeliosSession
|
|
47
|
+
from ._bundled import RAMSES_VERSION as __ramses_version__
|
|
48
|
+
from ._bundled import HELIOS_VERSION as __helios_version__
|
|
49
|
+
|
|
50
|
+
__all__ = ["cfg", "sim", "extractor", "cur", "curplot", "helios",
|
|
51
|
+
"HeliosSession", "HeliosError"]
|
|
52
|
+
|
|
53
|
+
# Detect gnuplot at import time; propagate result to globals so that cases.py
|
|
54
|
+
# (which reads __runTimeObs__ from globals at import time) also gets the correct value.
|
|
55
|
+
if sys.platform in ('win32', 'cygwin'):
|
|
56
|
+
checkGnuplot = __which('gnuplot.exe')
|
|
57
|
+
else:
|
|
58
|
+
checkGnuplot = __which('gnuplot')
|
|
59
|
+
if checkGnuplot is None:
|
|
60
|
+
warn("RAMSES: Gnuplot executable could not be found in the system path, so the runtime observables are disabled.")
|
|
61
|
+
_globals.__runTimeObs__ = False
|
|
62
|
+
else:
|
|
63
|
+
_globals.__runTimeObs__ = True
|
|
64
|
+
|
|
65
|
+
# Re-export the (now-updated) flag under the expected public name.
|
|
66
|
+
__runTimeObs__ = _globals.__runTimeObs__
|
stepss/_bundled.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Upstream component versions bundled in this stepss release.
|
|
2
|
+
|
|
3
|
+
Written by tools/bump_version.sh, which the release automation invokes when
|
|
4
|
+
stepss-ramses or stepss-helios publishes, and on a manual python-only release.
|
|
5
|
+
Do not edit by hand: a manual edit is silently overwritten by the next sync.
|
|
6
|
+
|
|
7
|
+
Only the upstream that triggered a sync changes; the other is carried forward.
|
|
8
|
+
RAMSES_VERSION is also what the stepss version number is built from, so the
|
|
9
|
+
leading components of __version__ always name the library bundled here.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
RAMSES_VERSION = "v3.59"
|
|
13
|
+
HELIOS_VERSION = "v1.4.1"
|
stepss/cases.py
ADDED
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Simulation case configuration for stepss.
|
|
4
|
+
|
|
5
|
+
Defines :class:`cfg`, which collects all input and output file paths required
|
|
6
|
+
by the RAMSES solver and serialises them into the RAMSES command-file format.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import warnings
|
|
11
|
+
|
|
12
|
+
from . import globals as _globals
|
|
13
|
+
from .globals import RAMSESError, CustomWarning
|
|
14
|
+
|
|
15
|
+
warnings.showwarning = CustomWarning
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class cfg(object):
|
|
19
|
+
"""Simulation case configuration.
|
|
20
|
+
|
|
21
|
+
Collects and validates all file paths needed to run a RAMSES simulation:
|
|
22
|
+
data files, disturbance file, trajectory file, observables, trace outputs,
|
|
23
|
+
and optional runtime observable definitions. The assembled configuration
|
|
24
|
+
can be serialised to a RAMSES command file via :meth:`writeCmdFile`, which
|
|
25
|
+
is called automatically by :meth:`~stepss.simulator.sim.execSim`.
|
|
26
|
+
|
|
27
|
+
A case can be built programmatically (by calling the ``add*`` methods) or
|
|
28
|
+
loaded from an existing command file by passing its path to *cmd*.
|
|
29
|
+
|
|
30
|
+
**Instance attributes (all private):**
|
|
31
|
+
|
|
32
|
+
- ``_dataset`` (*list of str*) — data files (.dat) defining the network model and solver settings.
|
|
33
|
+
- ``_dstset`` (*list of str*) — the single active disturbance file (.dst).
|
|
34
|
+
- ``_init`` (*list of str*) — path for the initialisation trace output.
|
|
35
|
+
- ``_cont`` (*list of str*) — path for the continuous convergence trace output.
|
|
36
|
+
- ``_disc`` (*list of str*) — path for the discrete events trace output.
|
|
37
|
+
- ``_obs`` (*list of str*) — observables definition file (.dat or .obs).
|
|
38
|
+
- ``_trj`` (*list of str*) — Fortran binary trajectory output file (.trj).
|
|
39
|
+
- ``_runobs`` (*list of str*) — runtime observable descriptors for gnuplot.
|
|
40
|
+
- ``_out`` (*list of str*) — execution log / output trace file.
|
|
41
|
+
|
|
42
|
+
:Example:
|
|
43
|
+
|
|
44
|
+
>>> import stepss
|
|
45
|
+
>>> case = stepss.cfg()
|
|
46
|
+
>>> case.addData('dyn.dat')
|
|
47
|
+
>>> case.addData('volt_rat.dat')
|
|
48
|
+
>>> case.addData('settings.dat')
|
|
49
|
+
>>> case.addInit('init.trace')
|
|
50
|
+
>>> case.addDst('disturbance.dst')
|
|
51
|
+
>>> case.addTrj('output.trj')
|
|
52
|
+
>>> case.addObs('obs.dat')
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, cmd=None):
|
|
56
|
+
"""Initialise a case, optionally loading it from a command file.
|
|
57
|
+
|
|
58
|
+
When *cmd* is ``None`` (the default) all file lists are empty and must
|
|
59
|
+
be populated with the ``add*`` methods before the case can be used.
|
|
60
|
+
|
|
61
|
+
When *cmd* is provided the command file is parsed line-by-line using a
|
|
62
|
+
simple state machine (``typeread``) that moves through eight sections in
|
|
63
|
+
fixed order:
|
|
64
|
+
|
|
65
|
+
1. Data files — one per line; ends at the first blank line.
|
|
66
|
+
2. Init trace — single optional line.
|
|
67
|
+
3. Disturbance — single required line.
|
|
68
|
+
4. Trajectory — single optional line.
|
|
69
|
+
5. Observables — required when a trajectory file is present.
|
|
70
|
+
6. Continuous trace — single optional line.
|
|
71
|
+
7. Discrete trace — single optional line.
|
|
72
|
+
8. Runtime observables — zero or more lines.
|
|
73
|
+
|
|
74
|
+
:param cmd: path to an existing RAMSES command file to parse, or
|
|
75
|
+
``None`` to start with an empty case.
|
|
76
|
+
:type cmd: str or None
|
|
77
|
+
:raises IOError: if *cmd* is given but cannot be read.
|
|
78
|
+
:raises Exception: if the command file is missing required sections
|
|
79
|
+
(at least one data file, a disturbance file, or an
|
|
80
|
+
observables file when a trajectory is defined).
|
|
81
|
+
"""
|
|
82
|
+
self._out = [] # output file
|
|
83
|
+
self._dataset = [] # data files
|
|
84
|
+
self._dstset = [] # disturbance files
|
|
85
|
+
self._runobs = [] # runtime observables
|
|
86
|
+
self._init = [] # initialization file
|
|
87
|
+
self._disc = [] # discrete trace file
|
|
88
|
+
self._cont = [] # continuous trace file
|
|
89
|
+
self._obs = [] # observables file
|
|
90
|
+
self._trj = [] # trajectory file
|
|
91
|
+
if cmd:
|
|
92
|
+
try:
|
|
93
|
+
typeread = 1 # 1=data, 2=init, 3=dst, 4=trj, 5=obs,6=cont, 7=disc, 8=runtime obs
|
|
94
|
+
with open(cmd) as data:
|
|
95
|
+
content = data.read().splitlines()
|
|
96
|
+
for line in content:
|
|
97
|
+
token = line.split()
|
|
98
|
+
if typeread == 1:
|
|
99
|
+
if len(token) > 0:
|
|
100
|
+
self.addData(line)
|
|
101
|
+
else:
|
|
102
|
+
if not self._dataset:
|
|
103
|
+
raise Exception('RAMSES: At least one datafile is necessary in the command file.')
|
|
104
|
+
typeread += 1
|
|
105
|
+
elif typeread == 2:
|
|
106
|
+
if len(token) > 0:
|
|
107
|
+
self.addInit(line)
|
|
108
|
+
typeread += 1
|
|
109
|
+
else:
|
|
110
|
+
typeread += 1
|
|
111
|
+
elif typeread == 3:
|
|
112
|
+
if len(token) > 0:
|
|
113
|
+
self.addDst(line)
|
|
114
|
+
typeread += 1
|
|
115
|
+
else:
|
|
116
|
+
raise Exception('RAMSES: At least one dstfile is necessary in the command file.')
|
|
117
|
+
elif typeread == 4:
|
|
118
|
+
if len(token) > 0:
|
|
119
|
+
self.addTrj(line)
|
|
120
|
+
typeread += 1
|
|
121
|
+
else:
|
|
122
|
+
typeread += 2
|
|
123
|
+
elif typeread == 5:
|
|
124
|
+
if len(token) > 0:
|
|
125
|
+
self.addObs(line)
|
|
126
|
+
typeread += 1
|
|
127
|
+
else:
|
|
128
|
+
raise Exception(
|
|
129
|
+
'RAMSES: Since a trajectory file was defined, an observable file should be given.')
|
|
130
|
+
elif typeread == 6:
|
|
131
|
+
if len(token) > 0:
|
|
132
|
+
self.addCont(line)
|
|
133
|
+
typeread += 1
|
|
134
|
+
else:
|
|
135
|
+
typeread += 1
|
|
136
|
+
elif typeread == 7:
|
|
137
|
+
if len(token) > 0:
|
|
138
|
+
self.addDisc(line)
|
|
139
|
+
typeread += 1
|
|
140
|
+
else:
|
|
141
|
+
typeread += 1
|
|
142
|
+
elif typeread == 8:
|
|
143
|
+
if len(token) > 0:
|
|
144
|
+
self.addRunObs(line)
|
|
145
|
+
else:
|
|
146
|
+
typeread += 1
|
|
147
|
+
except IOError:
|
|
148
|
+
raise
|
|
149
|
+
|
|
150
|
+
def writeCmdFile(self, userFile=None):
|
|
151
|
+
"""Serialise the case to a RAMSES command file.
|
|
152
|
+
|
|
153
|
+
The command file format is a fixed-section plain-text file consumed
|
|
154
|
+
directly by the RAMSES solver. Sections are separated by blank lines
|
|
155
|
+
and appear in this order:
|
|
156
|
+
|
|
157
|
+
1. Data files (one per line)
|
|
158
|
+
2. Initialisation trace path (or blank)
|
|
159
|
+
3. Disturbance file path
|
|
160
|
+
4. Trajectory file path + observables file path (or blank)
|
|
161
|
+
5. Continuous trace path (or blank)
|
|
162
|
+
6. Discrete trace path (or blank)
|
|
163
|
+
7. Runtime observable descriptors (zero or more lines)
|
|
164
|
+
|
|
165
|
+
:param userFile: path where the command file should be written. If
|
|
166
|
+
``None`` the serialised text is returned as a string
|
|
167
|
+
without writing to disk.
|
|
168
|
+
:type userFile: str or None
|
|
169
|
+
:returns: ``0`` after writing to *userFile*, or the command-file text
|
|
170
|
+
as a string when *userFile* is ``None``.
|
|
171
|
+
:rtype: int or str
|
|
172
|
+
:raises RAMSESError: if ``_dataset`` or ``_dstset`` is empty.
|
|
173
|
+
:raises IOError: if the file cannot be written.
|
|
174
|
+
|
|
175
|
+
.. warning:: If *userFile* already exists it will be overwritten.
|
|
176
|
+
"""
|
|
177
|
+
if not self._dataset or not self._dstset:
|
|
178
|
+
raise RAMSESError('Dataset and disturbance set cannot be empty.')
|
|
179
|
+
|
|
180
|
+
cmdFile = ""
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
for i in self._dataset:
|
|
184
|
+
cmdFile = cmdFile + i + '\n'
|
|
185
|
+
cmdFile = cmdFile + '\n'
|
|
186
|
+
if self._init:
|
|
187
|
+
cmdFile = cmdFile + self._init[0] + '\n'
|
|
188
|
+
else:
|
|
189
|
+
cmdFile = cmdFile + '\n'
|
|
190
|
+
if self._dstset:
|
|
191
|
+
cmdFile = cmdFile + self._dstset[0] + '\n'
|
|
192
|
+
if self._trj:
|
|
193
|
+
cmdFile = cmdFile + self._trj[0] + '\n'
|
|
194
|
+
cmdFile = cmdFile + self._obs[0] + '\n'
|
|
195
|
+
else:
|
|
196
|
+
cmdFile = cmdFile + '\n'
|
|
197
|
+
if self._cont:
|
|
198
|
+
cmdFile = cmdFile + self._cont[0] + '\n'
|
|
199
|
+
else:
|
|
200
|
+
cmdFile = cmdFile + '\n'
|
|
201
|
+
if self._disc:
|
|
202
|
+
cmdFile = cmdFile + self._disc[0] + '\n'
|
|
203
|
+
else:
|
|
204
|
+
cmdFile = cmdFile + '\n'
|
|
205
|
+
for i in self._runobs:
|
|
206
|
+
cmdFile = cmdFile + i + '\n'
|
|
207
|
+
cmdFile = cmdFile + '\n'
|
|
208
|
+
cmdFile = cmdFile + '\n'
|
|
209
|
+
except IOError as e:
|
|
210
|
+
raise IOError("RAMSES: I/O error({0}): {1}".format(e.errno, e.strerror))
|
|
211
|
+
|
|
212
|
+
if userFile is None:
|
|
213
|
+
return cmdFile
|
|
214
|
+
else:
|
|
215
|
+
if os.path.isfile(userFile):
|
|
216
|
+
warnings.warn('The file %s already exists. It will be overwritten!' % (userFile))
|
|
217
|
+
with open(userFile, "w") as text_file:
|
|
218
|
+
text_file.write(cmdFile)
|
|
219
|
+
return 0
|
|
220
|
+
|
|
221
|
+
# def __del__(self):
|
|
222
|
+
# silentremove(self._cmdfile.name)
|
|
223
|
+
|
|
224
|
+
def addInit(self, afile):
|
|
225
|
+
"""Set the file where the simulation initialisation trace will be saved.
|
|
226
|
+
|
|
227
|
+
The initialisation trace records the output of RAMSES's power-flow
|
|
228
|
+
initialisation step. Only one initialisation file can be active at a
|
|
229
|
+
time; calling this method again replaces any previously set path.
|
|
230
|
+
|
|
231
|
+
:param str afile: the filename. The complete path can be given or a path
|
|
232
|
+
relative to the working directory (``os.getcwd()``).
|
|
233
|
+
|
|
234
|
+
:Example:
|
|
235
|
+
|
|
236
|
+
>>> import stepss
|
|
237
|
+
>>> case1 = stepss.cfg()
|
|
238
|
+
>>> case1.addInit("init.trace")
|
|
239
|
+
|
|
240
|
+
.. warning:: If the file already exists, it will be overwritten without warning!
|
|
241
|
+
"""
|
|
242
|
+
del self._init[:]
|
|
243
|
+
if os.path.isfile(afile):
|
|
244
|
+
warnings.warn('The file %s already exists. It will be overwritten!' % (afile))
|
|
245
|
+
self._init.append(afile)
|
|
246
|
+
|
|
247
|
+
def getInit(self):
|
|
248
|
+
"""Return the initialisation trace file path.
|
|
249
|
+
|
|
250
|
+
:returns: path of the initialisation trace file, or an empty string if none is set.
|
|
251
|
+
:rtype: str
|
|
252
|
+
"""
|
|
253
|
+
if self._init:
|
|
254
|
+
return self._init[0]
|
|
255
|
+
else:
|
|
256
|
+
return ''
|
|
257
|
+
|
|
258
|
+
def addOut(self, afile):
|
|
259
|
+
"""Define the file where the simulation output will be saved.
|
|
260
|
+
|
|
261
|
+
:param str afile: the filename. The complete path must be given.
|
|
262
|
+
|
|
263
|
+
:Example:
|
|
264
|
+
|
|
265
|
+
>>> import stepss
|
|
266
|
+
>>> import os
|
|
267
|
+
>>> case1 = stepss.cfg()
|
|
268
|
+
>>> case1.addOut(os.path.join(os.getcwd(),'output.trace'))
|
|
269
|
+
|
|
270
|
+
.. note:: If the file already exists, the output will be appended to that file.
|
|
271
|
+
"""
|
|
272
|
+
del self._out[:]
|
|
273
|
+
self._out.append(afile)
|
|
274
|
+
|
|
275
|
+
def getOut(self):
|
|
276
|
+
"""Return the output trace file path.
|
|
277
|
+
|
|
278
|
+
:returns: path of the output trace file, or an empty string if none is set.
|
|
279
|
+
:rtype: str
|
|
280
|
+
"""
|
|
281
|
+
if self._out:
|
|
282
|
+
return self._out[0]
|
|
283
|
+
else:
|
|
284
|
+
return ''
|
|
285
|
+
|
|
286
|
+
def addCont(self, afile):
|
|
287
|
+
"""Set the continuous trace output file.
|
|
288
|
+
|
|
289
|
+
The continuous trace records convergence information from the numerical
|
|
290
|
+
integration algorithm inside RAMSES. It is mainly useful for debugging
|
|
291
|
+
solver issues but can slow down the simulation significantly.
|
|
292
|
+
|
|
293
|
+
:param str afile: the filename. The complete path can be given or a path
|
|
294
|
+
relative to the working directory (``os.getcwd()``).
|
|
295
|
+
|
|
296
|
+
:Example:
|
|
297
|
+
|
|
298
|
+
>>> import stepss
|
|
299
|
+
>>> case1 = stepss.cfg()
|
|
300
|
+
>>> case1.addCont("cont.trace")
|
|
301
|
+
|
|
302
|
+
.. warning:: If the file already exists, it will be overwritten without warning!
|
|
303
|
+
"""
|
|
304
|
+
del self._cont[:]
|
|
305
|
+
if os.path.isfile(afile):
|
|
306
|
+
warnings.warn('The file %s already exists. It will be overwritten!' % (afile))
|
|
307
|
+
self._cont.append(afile)
|
|
308
|
+
|
|
309
|
+
def getCont(self):
|
|
310
|
+
"""Return the continuous trace file path.
|
|
311
|
+
|
|
312
|
+
:returns: path of the continuous trace file, or an empty string if none is set.
|
|
313
|
+
:rtype: str
|
|
314
|
+
|
|
315
|
+
"""
|
|
316
|
+
if self._cont:
|
|
317
|
+
return self._cont[0]
|
|
318
|
+
else:
|
|
319
|
+
return ''
|
|
320
|
+
|
|
321
|
+
def addTrj(self, afile):
|
|
322
|
+
"""Set the Fortran binary trajectory output file.
|
|
323
|
+
|
|
324
|
+
The trajectory file is written by RAMSES during the simulation and later
|
|
325
|
+
parsed by :class:`~stepss.extractor.extractor` to extract timeseries
|
|
326
|
+
for individual components.
|
|
327
|
+
|
|
328
|
+
:param str afile: the filename. The complete path can be given or a path
|
|
329
|
+
relative to the working directory (``os.getcwd()``).
|
|
330
|
+
|
|
331
|
+
:Example:
|
|
332
|
+
|
|
333
|
+
>>> import stepss
|
|
334
|
+
>>> case1 = stepss.cfg()
|
|
335
|
+
>>> case1.addTrj("output.trj")
|
|
336
|
+
|
|
337
|
+
.. warning:: If the file already exists, it will be overwritten without warning!
|
|
338
|
+
"""
|
|
339
|
+
del self._trj[:]
|
|
340
|
+
if os.path.isfile(afile):
|
|
341
|
+
warnings.warn('The file %s already exists. It will be overwritten!' % (afile))
|
|
342
|
+
self._trj.append(afile)
|
|
343
|
+
|
|
344
|
+
def getTrj(self):
|
|
345
|
+
"""Return the trajectory output file path.
|
|
346
|
+
|
|
347
|
+
:returns: path of the trajectory file, or an empty string if none is set.
|
|
348
|
+
:rtype: str
|
|
349
|
+
"""
|
|
350
|
+
if self._trj:
|
|
351
|
+
return self._trj[0]
|
|
352
|
+
else:
|
|
353
|
+
return ''
|
|
354
|
+
|
|
355
|
+
def addDisc(self, afile):
|
|
356
|
+
"""Set the discrete events trace output file.
|
|
357
|
+
|
|
358
|
+
The discrete trace records events that occur during the simulation, such
|
|
359
|
+
as discrete controller transitions, disturbance-file events, and state
|
|
360
|
+
changes inside injector, two-port, torque, or exciter models.
|
|
361
|
+
|
|
362
|
+
:param str afile: the filename. The complete path can be given or a path
|
|
363
|
+
relative to the working directory (``os.getcwd()``).
|
|
364
|
+
|
|
365
|
+
:Example:
|
|
366
|
+
|
|
367
|
+
>>> import stepss
|
|
368
|
+
>>> case1 = stepss.cfg()
|
|
369
|
+
>>> case1.addDisc("disc.trace")
|
|
370
|
+
|
|
371
|
+
.. warning:: If the file already exists, it will be overwritten without warning!
|
|
372
|
+
"""
|
|
373
|
+
del self._disc[:]
|
|
374
|
+
if os.path.isfile(afile):
|
|
375
|
+
warnings.warn('The file %s already exists. It will be overwritten!' % (afile))
|
|
376
|
+
self._disc.append(afile)
|
|
377
|
+
|
|
378
|
+
def clearDisc(self):
|
|
379
|
+
"""Remove the discrete trace file path from the case."""
|
|
380
|
+
del self._disc[:]
|
|
381
|
+
|
|
382
|
+
def getDisc(self):
|
|
383
|
+
"""Return the discrete trace file path.
|
|
384
|
+
|
|
385
|
+
:returns: path of the discrete trace file, or an empty string if none is set.
|
|
386
|
+
:rtype: str
|
|
387
|
+
"""
|
|
388
|
+
if self._disc:
|
|
389
|
+
return self._disc[0]
|
|
390
|
+
else:
|
|
391
|
+
return ''
|
|
392
|
+
|
|
393
|
+
def addRunObs(self, runobs):
|
|
394
|
+
"""Add runtime observable.
|
|
395
|
+
|
|
396
|
+
This defines some states that will be displayed during the simulation using gnuplot.
|
|
397
|
+
|
|
398
|
+
:param str runobs: Description of runtime observable as defined in the RAMSES userguide
|
|
399
|
+
|
|
400
|
+
:Example:
|
|
401
|
+
|
|
402
|
+
- BV BUSNAME: Voltage magnitude of bus::
|
|
403
|
+
|
|
404
|
+
>>> case.addRunObs('BV 1041') # observe the bus voltage of bus 1041
|
|
405
|
+
|
|
406
|
+
- MS SYNHRONOUS_MACHINE: Synchronous speed of machine::
|
|
407
|
+
|
|
408
|
+
>>> case.addRunObs('MS g1')
|
|
409
|
+
|
|
410
|
+
- RT RT: Real-time vs simulated time plot::
|
|
411
|
+
|
|
412
|
+
>>> case.addRunObs('RT RT')
|
|
413
|
+
|
|
414
|
+
- BPE/BQE/BPO/BQO BRANCH_NAME: Branch active (P), reactive (Q) power at the origin (O) or extremity (E) of a branch::
|
|
415
|
+
|
|
416
|
+
>>> case.addRunObs('BPO 1041-01') # active power at the origin of branch 1041-01
|
|
417
|
+
>>> case.addRunObs('BPE 1041-01') # active power at the extremity of branch 1041-01
|
|
418
|
+
|
|
419
|
+
- ON INJECTOR_NAME OBSERVABLE_NAME: Monitor a named observable from an injector ::
|
|
420
|
+
|
|
421
|
+
>>> case.addRunObs('ON WT1a Pw') # observable Pw from injector WT1a
|
|
422
|
+
|
|
423
|
+
- TO TWOP_NAME OBSERVABLE_NAME: Monitor a named observable from a two-port ::
|
|
424
|
+
|
|
425
|
+
>>> case.addRunObs('TO hvdc1 P1') # observable P1 from two-port hvdc1
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
"""
|
|
429
|
+
if _globals.__runTimeObs__:
|
|
430
|
+
if runobs not in self._runobs:
|
|
431
|
+
self._runobs.append(runobs)
|
|
432
|
+
else:
|
|
433
|
+
warnings.warn('Gnuplot is not the path, so runtime observables are disabled.')
|
|
434
|
+
|
|
435
|
+
def clearRunObs(self):
|
|
436
|
+
"""Remove all runtime observable descriptors from the case."""
|
|
437
|
+
del self._runobs[:]
|
|
438
|
+
|
|
439
|
+
def addObs(self, ofile):
|
|
440
|
+
"""Add observables file
|
|
441
|
+
|
|
442
|
+
:param str ofile: the filename. The complete path can be given or a the path relative to the working directory (os.getcwd())
|
|
443
|
+
|
|
444
|
+
:Example:
|
|
445
|
+
|
|
446
|
+
>>> import stepss
|
|
447
|
+
>>> case1 = stepss.cfg()
|
|
448
|
+
>>> case1.addObs("obs.dat")
|
|
449
|
+
|
|
450
|
+
"""
|
|
451
|
+
if os.path.isfile(ofile):
|
|
452
|
+
del self._obs[:]
|
|
453
|
+
self._obs.append(ofile)
|
|
454
|
+
else:
|
|
455
|
+
raise IOError('RAMSES: The observables file %s does not exist or is not valid.' % (ofile))
|
|
456
|
+
|
|
457
|
+
def getObs(self):
|
|
458
|
+
"""Return the observables file path.
|
|
459
|
+
|
|
460
|
+
:returns: path of the observables file, or an empty string if none is set.
|
|
461
|
+
:rtype: str
|
|
462
|
+
"""
|
|
463
|
+
if self._obs:
|
|
464
|
+
return self._obs[0]
|
|
465
|
+
else:
|
|
466
|
+
return ''
|
|
467
|
+
|
|
468
|
+
def addData(self, datafile):
|
|
469
|
+
"""Add a data file to the dataset.
|
|
470
|
+
|
|
471
|
+
Data files provide the network model (dynamic models, power-flow solution)
|
|
472
|
+
and solver parameters consumed by RAMSES. Multiple data files can be
|
|
473
|
+
added; they are passed to the solver in the order they were added.
|
|
474
|
+
Duplicate paths are silently ignored.
|
|
475
|
+
|
|
476
|
+
:param str datafile: path to an existing data file. The complete path
|
|
477
|
+
can be given or a path relative to the working
|
|
478
|
+
directory (``os.getcwd()``).
|
|
479
|
+
|
|
480
|
+
:Example:
|
|
481
|
+
|
|
482
|
+
>>> import stepss
|
|
483
|
+
>>> case1 = stepss.cfg()
|
|
484
|
+
>>> case1.addData("dyn_A.dat")
|
|
485
|
+
|
|
486
|
+
.. warning:: At least one data file must be added before running a simulation.
|
|
487
|
+
"""
|
|
488
|
+
if os.path.isfile(datafile):
|
|
489
|
+
if datafile not in self._dataset:
|
|
490
|
+
self._dataset.append(datafile)
|
|
491
|
+
else:
|
|
492
|
+
raise IOError('RAMSES: The datafile %s does not exist or is not valid.' % (datafile))
|
|
493
|
+
|
|
494
|
+
def getData(self):
|
|
495
|
+
"""Get datafile list
|
|
496
|
+
|
|
497
|
+
:returns: list of data file names
|
|
498
|
+
:rtype: list
|
|
499
|
+
|
|
500
|
+
"""
|
|
501
|
+
return self._dataset
|
|
502
|
+
|
|
503
|
+
def delData(self, datafile):
|
|
504
|
+
"""Remove a specific data file from the dataset.
|
|
505
|
+
|
|
506
|
+
:param str datafile: path of the data file to remove. No error is
|
|
507
|
+
raised if the file is not currently in the dataset.
|
|
508
|
+
"""
|
|
509
|
+
if datafile in self._dataset:
|
|
510
|
+
self._dataset.remove(datafile)
|
|
511
|
+
|
|
512
|
+
def clearData(self):
|
|
513
|
+
"""Remove all data files from the dataset."""
|
|
514
|
+
del self._dataset[:]
|
|
515
|
+
|
|
516
|
+
def addDst(self, dstfile):
|
|
517
|
+
"""Set the disturbance file for the simulation.
|
|
518
|
+
|
|
519
|
+
The disturbance file defines the fault or event sequence to be applied
|
|
520
|
+
during the simulation. Only one disturbance file is active at a time;
|
|
521
|
+
calling this method again replaces the previous one.
|
|
522
|
+
|
|
523
|
+
:param str dstfile: path to an existing ``.dst`` file. The complete
|
|
524
|
+
path can be given or a path relative to the working
|
|
525
|
+
directory (``os.getcwd()``).
|
|
526
|
+
|
|
527
|
+
:Example:
|
|
528
|
+
|
|
529
|
+
>>> import stepss
|
|
530
|
+
>>> case1 = stepss.cfg()
|
|
531
|
+
>>> case1.addDst("short.dst")
|
|
532
|
+
|
|
533
|
+
.. warning:: A disturbance file must be provided before the simulation can start.
|
|
534
|
+
"""
|
|
535
|
+
if os.path.isfile(dstfile):
|
|
536
|
+
del self._dstset[:]
|
|
537
|
+
self._dstset.append(dstfile)
|
|
538
|
+
else:
|
|
539
|
+
raise IOError('RAMSES: The dstfile %s does not exist or is not valid.' % (dstfile))
|
|
540
|
+
|
|
541
|
+
def getDst(self):
|
|
542
|
+
"""Get disturbance
|
|
543
|
+
|
|
544
|
+
:returns: name of disturbance file
|
|
545
|
+
:rtype: str
|
|
546
|
+
|
|
547
|
+
"""
|
|
548
|
+
if self._dstset:
|
|
549
|
+
return self._dstset[0]
|
|
550
|
+
else:
|
|
551
|
+
return ''
|
|
552
|
+
|
|
553
|
+
def clearDst(self):
|
|
554
|
+
"""Remove the disturbance file from the case."""
|
|
555
|
+
del self._dstset[:]
|