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.
Files changed (31) hide show
  1. {stepss-3.81/stepss.egg-info → stepss-3.81.2}/PKG-INFO +1 -1
  2. {stepss-3.81 → stepss-3.81.2}/stepss/__init__.py +4 -2
  3. {stepss-3.81 → stepss-3.81.2}/stepss/simulator.py +145 -0
  4. stepss-3.81.2/stepss/ssa.py +1709 -0
  5. {stepss-3.81 → stepss-3.81.2/stepss.egg-info}/PKG-INFO +1 -1
  6. {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/SOURCES.txt +1 -0
  7. {stepss-3.81 → stepss-3.81.2}/LICENSE.rst +0 -0
  8. {stepss-3.81 → stepss-3.81.2}/MANIFEST.in +0 -0
  9. {stepss-3.81 → stepss-3.81.2}/README.rst +0 -0
  10. {stepss-3.81 → stepss-3.81.2}/setup.cfg +0 -0
  11. {stepss-3.81 → stepss-3.81.2}/setup.py +0 -0
  12. {stepss-3.81 → stepss-3.81.2}/stepss/_bundled.py +0 -0
  13. {stepss-3.81 → stepss-3.81.2}/stepss/cases.py +0 -0
  14. {stepss-3.81 → stepss-3.81.2}/stepss/extractor.py +0 -0
  15. {stepss-3.81 → stepss-3.81.2}/stepss/globals.py +0 -0
  16. {stepss-3.81 → stepss-3.81.2}/stepss/helios.py +0 -0
  17. {stepss-3.81 → stepss-3.81.2}/stepss/libs/helios_api.h +0 -0
  18. {stepss-3.81 → stepss-3.81.2}/stepss/libs/lin/libhelios_api.so +0 -0
  19. {stepss-3.81 → stepss-3.81.2}/stepss/libs/lin/ramses.so +0 -0
  20. {stepss-3.81 → stepss-3.81.2}/stepss/libs/mac/libhelios_api.dylib +0 -0
  21. {stepss-3.81 → stepss-3.81.2}/stepss/libs/mac/ramses.so +0 -0
  22. {stepss-3.81 → stepss-3.81.2}/stepss/libs/ramses.h +0 -0
  23. {stepss-3.81 → stepss-3.81.2}/stepss/libs/win/helios_api.dll +0 -0
  24. {stepss-3.81 → stepss-3.81.2}/stepss/libs/win/ramses.dll +0 -0
  25. {stepss-3.81 → stepss-3.81.2}/stepss/live.py +0 -0
  26. {stepss-3.81 → stepss-3.81.2}/stepss/scripts/__init__.py +0 -0
  27. {stepss-3.81 → stepss-3.81.2}/stepss/scripts/exec.py +0 -0
  28. {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/dependency_links.txt +0 -0
  29. {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/entry_points.txt +0 -0
  30. {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/requires.txt +0 -0
  31. {stepss-3.81 → stepss-3.81.2}/stepss.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stepss
3
- Version: 3.81
3
+ Version: 3.81.2
4
4
  Summary: Python interface to the STEPSS power-system simulation platform.
5
5
  Home-page: https://stepss.sps-lab.org
6
6
  Author: Petros Aristidou
@@ -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