Usage#
OpenDSSDirect.py is a package in the DSS-Extensions project. As such, it doesn’t require EPRI’s OpenDSS to be installed. OpenDSSDirect.py provides it’s own customized engine through DSS-Python, which in turn enables us to run the DSS engine on Windows, Linux and macOS (including newer Apple ARM processors).
After the installation, you can just import the module and start using it without further steps:
from opendssdirect import dss
print('OpenDSSDirect.py and engine versions:', dss.Version())
OpenDSSDirect.py and engine versions: DSS C-API Library version 0.14.1 revision 8b3da5b576f4dedd16d2bfb88bfb911c0aca3783 based on OpenDSS SVN 3723 [FPC 3.2.2] (64-bit build) MVMULT INCREMENTAL_Y CONTEXT_API PM 20240217013457; License Status: Open
DSS-Python version: 0.15.2
OpenDSSDirect.py version: 0.9.1
Import a file#
Use the high level interface for automatic error handling, mapping errors to Python exceptions.
This first example should give an error:
dss.Command('Redirect this_file_does_not_exist.dss')
---------------------------------------------------------------------------
DSSException Traceback (most recent call last)
Cell In[3], line 1
----> 1 dss.Command('Redirect this_file_does_not_exist.dss')
File /opt/python/lib/python3.12/site-packages/opendssdirect/Text.py:26, in IText.Command(self, *args)
24 if not isinstance(Value, bytes):
25 Value = Value.encode(self._api_util.codec)
---> 26 self._check_for_error(self._lib.Text_Set_Command(Value))
File /opt/python/lib/python3.12/site-packages/dss/_cffi_api_util.py:267, in Base._check_for_error(self, result)
265 error_num = self._errorPtr[0]
266 self._errorPtr[0] = 0
--> 267 raise DSSException(error_num, self._get_string(self._lib.Error_Get_Description()))
269 return result
DSSException: (#243) Redirect file not found: "this_file_does_not_exist.dss"
This second example uses a correct file path:
dss.Command('Redirect "../../tests/data/13Bus/IEEE13Nodeckt.dss"')
👉 In previous versions of this document, we used to recommend dss.run_command()
. Unfortunately the error-checking from run_command
can be confusing and we cannot change it, for historical and backwards-compatibility reasons.
dss.Command
itself is a shortcut to dss.Text.Command
, the interface function for dispatching single DSS commands. Since v0.9, when importing the dss
instance (from opendssdirect import dss
), one can also use just dss('Redirect "../../tests/data/13Bus/IEEE13Nodeckt.dss"')
instead of dss.Command('Redirect "../../tests/data/13Bus/IEEE13Nodeckt.dss"')
for an even shorter version. As an added feature, calling dss(script_string)
also allows passing multiple commands through multi-line strings.
If you need text output from the Text interface, you can use dss.Text.Result()
. In general, if there is a dedicated API for a certain class of component, prefere to use that for performance and safety reasons. Otherwise, for example, you can query the DSS engine:
dss('? Load.634a.kW')
dss.Text.Result()
'160'
Module Loads
#
The dss
object has many attributes, many of them representing specific DSS interfaces. Check the the API reference for details.
import types
import inspect
for name, obj in inspect.getmembers(dss):
if not isinstance(obj, (types.MethodType, types.FunctionType)) and not name.startswith('_'):
print(f'dss.{name}')
dss.ActiveClass
dss.Basic
dss.Bus
dss.CNData
dss.CapControls
dss.Capacitors
dss.Circuit
dss.CktElement
dss.CtrlQueue
dss.DSSCore
dss.DSSException
dss.Element
dss.Error
dss.Executive
dss.Fuses
dss.GICSources
dss.Generators
dss.Isource
dss.LineCodes
dss.LineGeometries
dss.LineSpacings
dss.Lines
dss.LoadShape
dss.Loads
dss.Meters
dss.Monitors
dss.PDElements
dss.PVsystems
dss.Parallel
dss.Parser
dss.Progress
dss.Properties
dss.Reactors
dss.Reclosers
dss.ReduceCkt
dss.RegControls
dss.Relays
dss.Sensors
dss.Settings
dss.Solution
dss.Storages
dss.SwtControls
dss.TSData
dss.Text
dss.Topology
dss.Transformers
dss.Vsources
dss.WireData
dss.XYCurves
dss.YMatrix
dss.ZIP
dss.dss
dss.dss_ffi
dss.dss_lib
dss.utils
Each class or submodule has functions that can be called. Many of them mirror the properties exposed in the official COM interface. For those, the documentation tries to provide links to the official OpenDSS site. Many other functions are exclusive to DSS-Extensions and are marked as “API Extension” in the docs.
for name, function in inspect.getmembers(dss.Loads):
if callable(function) and not name.startswith('_'):
print(f'dss.Loads.{name}')
dss.Loads.AllNames
dss.Loads.AllocationFactor
dss.Loads.CFactor
dss.Loads.CVRCurve
dss.Loads.CVRvars
dss.Loads.CVRwatts
dss.Loads.Class
dss.Loads.Count
dss.Loads.Daily
dss.Loads.Duty
dss.Loads.First
dss.Loads.Growth
dss.Loads.Idx
dss.Loads.IsDelta
dss.Loads.Model
dss.Loads.Name
dss.Loads.Next
dss.Loads.NumCust
dss.Loads.PF
dss.Loads.PctMean
dss.Loads.PctStdDev
dss.Loads.Phases
dss.Loads.RelWeighting
dss.Loads.Rneut
dss.Loads.Sensor
dss.Loads.Spectrum
dss.Loads.Status
dss.Loads.Vmaxpu
dss.Loads.VminEmerg
dss.Loads.VminNorm
dss.Loads.Vminpu
dss.Loads.XfkVA
dss.Loads.Xneut
dss.Loads.Yearly
dss.Loads.ZipV
dss.Loads.kV
dss.Loads.kVABase
dss.Loads.kW
dss.Loads.kWh
dss.Loads.kWhDays
dss.Loads.kvar
dss.Loads.puSeriesRL
For example, Loads.kW
is available in the official OpenDSS COM API…
? dss.Loads.kW
…but Loads.Phases
is not:
? dss.Loads.Phases
It is important to understand that OpenDSSDirect.py uses function calls instead of Python properties to expose the API. So, to get the value of a property, just call the function without arguments:
dss.Loads.AllNames()
['671',
'634a',
'634b',
'634c',
'645',
'646',
'692',
'675a',
'675b',
'675c',
'611',
'652',
'670a',
'670b',
'670c']
dss.Loads.Name()
'670c'
dss.Loads.kW()
117.0
Now, if you want to set the value of a property, pass the value as argument:
dss.Loads.kW(125)
dss.Loads.kW()
125.0
To get all kW
, one must iterate over all the loads. v0.9+ support Python iterators, so a simple for
will do. Remember that OpenDSS API restrictions still apply – a simple object of this type must be active. Check AltDSS-Python for an alternative.
Iterators and a few other features are only available when using classes instead of submodules.
for load in dss.Loads:
print(
'Name={name} \t kW={kW}'.format(
name=load.Name(),
kW=load.kW()
)
)
Name=671 kW=1155.0
Name=634a kW=160.0
Name=634b kW=120.0
Name=634c kW=120.0
Name=645 kW=170.0
Name=646 kW=230.0
Name=692 kW=170.0
Name=675a kW=485.0
Name=675b kW=68.0
Name=675c kW=290.0
Name=611 kW=170.0
Name=652 kW=128.0
Name=670a kW=17.0
Name=670b kW=66.0
Name=670c kW=125.0
List comprehensions also work great:
[l.kW() for l in dss.Loads]
[1155.0,
160.0,
120.0,
120.0,
170.0,
230.0,
170.0,
485.0,
68.0,
290.0,
170.0,
128.0,
17.0,
66.0,
125.0]
The (now deprecated) Iterator
class from the utils
module provides a similar option in pre-v0.9 versions. As seen below, it has greater overhead, so prefer the usual Python iteration for new code.
from opendssdirect.utils import Iterator
%timeit load_kW = [i() for i in Iterator(dss.Loads, 'kW')]
<magic-timeit>:1: DeprecationWarning: OpenDSSDirect.py's Iterator is deprecated; you can use native Python iterators directly now.
5.44 µs ± 64.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
%timeit load_kW = [l.kW() for l in dss.Loads]
3.56 µs ± 8.4 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
Low-level API (advanced topic)#
If you are a new OpenDSS user, you are not expected to use the low-level interface, i.e. the dss_lib
and dss_ffi
objects are for advanced users only, and may change without warning.
The low-level interface exposes DSS C-API as wrapped in DSS-Python. It’s useful to prototype low-level operations in Python before porting to C, which is not something most of the OpenDSS users will ever do.
Still, in some rare occasions, it might be useful to use the low-level interface. Beware that error checking needs to done by the user in this case. The reference for the low-level API is the source code of the DSS engine itself and the C header, e.g.
Advanced knowledge of DSS C-API, pointers, and the CFFI module are required.
dss.dss_lib.Text_Set_Command('Redirect ./../../tests/data/13Bus/IEEE13Nodeckt.dss'.encode())
Each of the properties from the official COM implementation of OpenDSS is implemented as a pair of functions. While in OpenDSSDirect.py one can use, e.g., dss.Loads.kW()
to read the active load element kW
and dss.Loads.kW(some_value)
to set the kW
value, the low-level interface exposes dss.dss_lib.Loads_Get_kW()
and dss.dss_lib.Loads_Set_kW()
.
dss.Loads.Name()
'670c'
dss.dss_lib.Loads_Get_kW()
117.0
dss.dss_lib.Loads_Set_kW(120)
dss.Loads.kW()
120.0