stepss 3.81__tar.gz → 3.81.2__tar.gz
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-3.81/stepss.egg-info → stepss-3.81.2}/PKG-INFO +1 -1
- {stepss-3.81 → stepss-3.81.2}/stepss/__init__.py +4 -2
- {stepss-3.81 → stepss-3.81.2}/stepss/simulator.py +145 -0
- stepss-3.81.2/stepss/ssa.py +1709 -0
- {stepss-3.81 → stepss-3.81.2/stepss.egg-info}/PKG-INFO +1 -1
- {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/SOURCES.txt +1 -0
- {stepss-3.81 → stepss-3.81.2}/LICENSE.rst +0 -0
- {stepss-3.81 → stepss-3.81.2}/MANIFEST.in +0 -0
- {stepss-3.81 → stepss-3.81.2}/README.rst +0 -0
- {stepss-3.81 → stepss-3.81.2}/setup.cfg +0 -0
- {stepss-3.81 → stepss-3.81.2}/setup.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/_bundled.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/cases.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/extractor.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/globals.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/helios.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/helios_api.h +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/lin/libhelios_api.so +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/lin/ramses.so +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/mac/libhelios_api.dylib +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/mac/ramses.so +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/ramses.h +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/win/helios_api.dll +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/libs/win/ramses.dll +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/live.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/scripts/__init__.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss/scripts/exec.py +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/dependency_links.txt +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/entry_points.txt +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/requires.txt +0 -0
- {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ Public API exported by this package:
|
|
|
13
13
|
- :class:`~stepss.extractor.cur` - lightweight NamedTuple holding a (time, value, msg) timeseries.
|
|
14
14
|
- :func:`~stepss.extractor.curplot` - plot one or more :class:`cur` objects on a single axes.
|
|
15
15
|
- :class:`~stepss.live.monitor` - plot chosen quantities while a simulation runs.
|
|
16
|
+
- :mod:`stepss.ssa` - run and read a small-signal stability analysis.
|
|
16
17
|
- :class:`~stepss.helios.HeliosSession` - run AC power flows with the Helios engine.
|
|
17
18
|
- :class:`~stepss.globals.HeliosError` - exception raised by Helios calls.
|
|
18
19
|
|
|
@@ -24,7 +25,7 @@ Module-level flags set at import time:
|
|
|
24
25
|
"""
|
|
25
26
|
|
|
26
27
|
__package_name__ = "stepss"
|
|
27
|
-
__version__ = '3.81'
|
|
28
|
+
__version__ = '3.81.2'
|
|
28
29
|
__author__ = "Petros Aristidou"
|
|
29
30
|
__copyright__ = "Petros Aristidou"
|
|
30
31
|
__license__ = "Apache-2.0"
|
|
@@ -40,12 +41,13 @@ from .simulator import sim
|
|
|
40
41
|
from .extractor import extractor, curplot, cur
|
|
41
42
|
from .live import monitor
|
|
42
43
|
from . import helios
|
|
44
|
+
from . import ssa
|
|
43
45
|
from .helios import HeliosSession
|
|
44
46
|
from ._bundled import RAMSES_VERSION as __ramses_version__
|
|
45
47
|
from ._bundled import HELIOS_VERSION as __helios_version__
|
|
46
48
|
|
|
47
49
|
__all__ = ["cfg", "sim", "extractor", "cur", "curplot", "monitor", "helios",
|
|
48
|
-
"HeliosSession", "HeliosError"]
|
|
50
|
+
"ssa", "HeliosSession", "HeliosError"]
|
|
49
51
|
|
|
50
52
|
# No gnuplot probe. This package has always plotted with matplotlib, through
|
|
51
53
|
# curplot; the probe existed because RAMSES used to draw run-time observables
|
|
@@ -92,6 +92,13 @@ class sim(object):
|
|
|
92
92
|
|
|
93
93
|
sim.ramsesCount += 1
|
|
94
94
|
self._ramsesNum = sim.ramsesCount # This is the current instance of Ramses
|
|
95
|
+
# How many small-signal analyses this instance has completed. The
|
|
96
|
+
# engine retains one state matrix at a time and get_state_matrix
|
|
97
|
+
# refuses only an order mismatch, so two runs agreeing on nx, which is
|
|
98
|
+
# what re-running one case with changed data produces, would let a
|
|
99
|
+
# stale result hand back the newer matrix. A stepss.ssa.Results
|
|
100
|
+
# compares against this to refuse that instead.
|
|
101
|
+
self._ssaGeneration = 0
|
|
95
102
|
self._setcalls()
|
|
96
103
|
|
|
97
104
|
def __del__(self):
|
|
@@ -285,6 +292,144 @@ class sim(object):
|
|
|
285
292
|
|
|
286
293
|
return A, E
|
|
287
294
|
|
|
295
|
+
def _noteSsaAnalysis(self):
|
|
296
|
+
"""Record that a small-signal analysis has completed on this instance.
|
|
297
|
+
|
|
298
|
+
Called by :meth:`runSsa` and by :func:`stepss.ssa.run`'s disturbance
|
|
299
|
+
route, which are the two ways an analysis can be triggered. Both
|
|
300
|
+
replace the state matrix the engine retains.
|
|
301
|
+
"""
|
|
302
|
+
self._ssaGeneration += 1
|
|
303
|
+
|
|
304
|
+
def runSsa(self, basename, t=None):
|
|
305
|
+
"""Run the small-signal analysis and write its three results files.
|
|
306
|
+
|
|
307
|
+
The engine linearises about the operating point the run is currently
|
|
308
|
+
paused at, reduces the Jacobian to the state matrix, solves the dense
|
|
309
|
+
eigenproblem, and writes ``<basename>_modes.dat``, ``_pf.dat`` and
|
|
310
|
+
``_ms.dat`` in the working directory.
|
|
311
|
+
|
|
312
|
+
The C entry takes a basename and nothing else, so *t* is honoured here:
|
|
313
|
+
when it is later than the simulated time already reached, the run is
|
|
314
|
+
advanced to it first, with whatever disturbances the case carries. A
|
|
315
|
+
*t* that has already passed is refused rather than silently answered
|
|
316
|
+
with the current operating point, because the difference is invisible
|
|
317
|
+
in the results.
|
|
318
|
+
|
|
319
|
+
The analysis needs ``$SCHEME DE`` and ``$OMEGA_REF SYN``. Under
|
|
320
|
+
``$SCHEME IN`` the global Jacobian is algebraized and carries the step
|
|
321
|
+
size; under the centre-of-inertia reference frame the COI equations are
|
|
322
|
+
computed by finite differences at export time and never enter the
|
|
323
|
+
assembled Jacobian. The engine refuses both rather than producing a
|
|
324
|
+
plausible, wrong spectrum. :func:`stepss.ssa.run` supplies both itself.
|
|
325
|
+
|
|
326
|
+
:param str basename: names the three results files and is written into
|
|
327
|
+
the engine's own record, so it may contain only
|
|
328
|
+
letters, digits, dot, underscore and hyphen.
|
|
329
|
+
:param t: when to linearise, in seconds; defaults to
|
|
330
|
+
:data:`stepss.ssa.MIN_TIME`.
|
|
331
|
+
:type t: float or None
|
|
332
|
+
:returns: *basename*, so the call can be chained.
|
|
333
|
+
:rtype: str
|
|
334
|
+
:raises RAMSESError: if the basename or the time is rejected, or the
|
|
335
|
+
engine refuses the analysis. The engine's own
|
|
336
|
+
reason is included, from :meth:`getLastErr`.
|
|
337
|
+
|
|
338
|
+
:Example:
|
|
339
|
+
|
|
340
|
+
>>> import stepss
|
|
341
|
+
>>> ram = stepss.sim()
|
|
342
|
+
>>> case = stepss.cfg("cmd.txt")
|
|
343
|
+
>>> ram.execSim(case, 0.0) # initialise and pause at t = 0
|
|
344
|
+
>>> ram.runSsa("ssa")
|
|
345
|
+
'ssa'
|
|
346
|
+
"""
|
|
347
|
+
from . import ssa as _ssa
|
|
348
|
+
|
|
349
|
+
if t is None:
|
|
350
|
+
t = _ssa.MIN_TIME
|
|
351
|
+
if not _ssa.valid_basename(basename):
|
|
352
|
+
raise RAMSESError(
|
|
353
|
+
'RAMSES: the results basename %r cannot be used. It names the '
|
|
354
|
+
'three results files and is written into the analysis record, '
|
|
355
|
+
'so it may contain only letters, digits, dot, underscore and '
|
|
356
|
+
'hyphen.' % (basename,))
|
|
357
|
+
when = _ssa.check_time(t)
|
|
358
|
+
now = self.getSimTime()
|
|
359
|
+
if when < now - 1e-12:
|
|
360
|
+
raise RAMSESError(
|
|
361
|
+
'RAMSES: the analysis time %g s has already passed; the '
|
|
362
|
+
'simulation is at %g s. The engine linearises about wherever '
|
|
363
|
+
'the run currently sits, so this would answer a different '
|
|
364
|
+
'question silently.' % (when, now))
|
|
365
|
+
if when > now:
|
|
366
|
+
self.contSim(when)
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
retval = self._ramseslib.run_ssa(basename.encode('utf-8'))
|
|
370
|
+
except (AttributeError, KeyError):
|
|
371
|
+
raise RAMSESError('RAMSES: the bundled library exports no run_ssa; '
|
|
372
|
+
'small-signal analysis needs RAMSES 3.79 or newer.')
|
|
373
|
+
# 112 is ramses() reporting that it paused again after completing the
|
|
374
|
+
# request, exactly as get_Jac's return can also be 112.
|
|
375
|
+
if (retval != 0) and (retval != 112):
|
|
376
|
+
raise RAMSESError('RAMSES: Function runSsa() failed with the flag %i. '
|
|
377
|
+
'Last message was: %s' % (retval, self.getLastErr()))
|
|
378
|
+
self._noteSsaAnalysis()
|
|
379
|
+
return basename
|
|
380
|
+
|
|
381
|
+
def getStateMatrix(self):
|
|
382
|
+
"""Return the state matrix of the last small-signal analysis.
|
|
383
|
+
|
|
384
|
+
This is the Schur complement the engine formed before solving the
|
|
385
|
+
eigenproblem, retained by the library rather than written to any file.
|
|
386
|
+
|
|
387
|
+
The engine keeps one at a time, so read this before starting the next
|
|
388
|
+
analysis.
|
|
389
|
+
|
|
390
|
+
:returns: the matrix, of order ``nstates``
|
|
391
|
+
:rtype: numpy.ndarray
|
|
392
|
+
:raises RAMSESError: if no analysis has been run in this process, or
|
|
393
|
+
the last one refused.
|
|
394
|
+
|
|
395
|
+
:Example:
|
|
396
|
+
|
|
397
|
+
>>> import stepss
|
|
398
|
+
>>> ram = stepss.sim()
|
|
399
|
+
>>> case = stepss.cfg("cmd.txt")
|
|
400
|
+
>>> ram.execSim(case, 0.0)
|
|
401
|
+
>>> ram.runSsa("ssa")
|
|
402
|
+
'ssa'
|
|
403
|
+
>>> A = ram.getStateMatrix()
|
|
404
|
+
>>> A.shape[0] == A.shape[1]
|
|
405
|
+
True
|
|
406
|
+
"""
|
|
407
|
+
nx = ctypes.c_int(0)
|
|
408
|
+
try:
|
|
409
|
+
self._ramseslib.get_state_matrix_size(ctypes.byref(nx))
|
|
410
|
+
except (AttributeError, KeyError):
|
|
411
|
+
raise RAMSESError('RAMSES: the bundled library exports no '
|
|
412
|
+
'get_state_matrix_size; small-signal analysis '
|
|
413
|
+
'needs RAMSES 3.79 or newer.')
|
|
414
|
+
order = nx.value
|
|
415
|
+
if order <= 0:
|
|
416
|
+
raise RAMSESError('RAMSES: no small-signal analysis is retained. Run '
|
|
417
|
+
'one with runSsa() first; a refused analysis '
|
|
418
|
+
'retains nothing.')
|
|
419
|
+
buffer = (ctypes.c_double * (order * order))()
|
|
420
|
+
retval = self._ramseslib.get_state_matrix(order, buffer)
|
|
421
|
+
if retval != 0:
|
|
422
|
+
raise RAMSESError('RAMSES: Function getStateMatrix() failed with the '
|
|
423
|
+
'flag %i. Last message was: %s'
|
|
424
|
+
% (retval, self.getLastErr()))
|
|
425
|
+
# Column-major: entry (i, j) of the nx by nx matrix sits at
|
|
426
|
+
# a_sys[(j-1)*nx + (i-1)], which is what order='F' reconstructs. The
|
|
427
|
+
# copy detaches the array from the ctypes buffer, which is freed with
|
|
428
|
+
# this call's frame.
|
|
429
|
+
return np.reshape(np.frombuffer(buffer, dtype=np.float64,
|
|
430
|
+
count=order * order),
|
|
431
|
+
(order, order), order='F').copy()
|
|
432
|
+
|
|
288
433
|
def getCompName(self, comp_type, num):
|
|
289
434
|
"""Return the name of the *num*-th component of the given type.
|
|
290
435
|
|