Skip to content

Commit

Permalink
Initial Oddie integration, including updates to tests
Browse files Browse the repository at this point in the history
  • Loading branch information
PMeira committed Jun 26, 2024
1 parent e4c8dfb commit 85783cf
Show file tree
Hide file tree
Showing 13 changed files with 514 additions and 118 deletions.
7 changes: 7 additions & 0 deletions dss/IBus.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,13 @@ def __call__(self, index: Union[int, str]) -> IBus:
return self.__getitem__(index)

def __iter__(self) -> Iterator[IBus]:
if self._api_util._is_odd:
for i in range(self._lib.Circuit_Get_NumBuses()):
self._check_for_error(self._lib.Circuit_SetActiveBusi(i))
yield self

return

n = self._check_for_error(self._lib.Circuit_SetActiveBusi(0))
while n == 0:
yield self
Expand Down
14 changes: 7 additions & 7 deletions dss/ICircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,14 @@ def __init__(self, api_util):
self.PVSystems = IPVSystems(api_util)
self.Vsources = IVsources(api_util)
self.LineCodes = ILineCodes(api_util)
self.LineGeometries = ILineGeometries(api_util)
self.LineSpacings = ILineSpacings(api_util)
self.WireData = IWireData(api_util)
self.CNData = ICNData(api_util)
self.TSData = ITSData(api_util)
self.Reactors = IReactors(api_util)
self.LineGeometries = ILineGeometries(api_util) if not api_util._is_odd else None
self.LineSpacings = ILineSpacings(api_util) if not api_util._is_odd else None
self.WireData = IWireData(api_util) if not api_util._is_odd else None
self.CNData = ICNData(api_util) if not api_util._is_odd else None
self.TSData = ITSData(api_util) if not api_util._is_odd else None
self.Reactors = IReactors(api_util) if not api_util._is_odd else None
self.ReduceCkt = IReduceCkt(api_util) #: Circuit Reduction Interface
self.Storages = IStorages(api_util)
self.Storages = IStorages(api_util) if not api_util._is_odd else None
self.GICSources = IGICSources(api_util)

if hasattr(api_util.lib, 'Parallel_CreateActor'):
Expand Down
10 changes: 7 additions & 3 deletions dss/IDSS.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ def __init__(self, api_util):
self.Executive = IDSS_Executive(api_util)

#: Kept for compatibility.
self.Events = IDSSEvents(api_util)
self.Events = IDSSEvents(api_util) if not api_util._is_odd else None

#: Kept for compatibility.
self.Parser = IParser(api_util)

#: Kept for compatibility. Apparently was used for DSSim-PC (now OpenDSS-G), a
#: closed-source software developed by EPRI using LabView.
self.DSSim_Coms = IDSSimComs(api_util)
self.DSSim_Coms = IDSSimComs(api_util) if not api_util._is_odd else None

#: The YMatrix interface provides advanced access to the internals of
#: the DSS engine. The sparse admittance matrix of the system is also
Expand All @@ -157,7 +157,7 @@ def __init__(self, api_util):
#: and run scripts inside the ZIP, without creating extra files on disk.
#:
#: **(API Extension)**
self.ZIP = IZIP(api_util)
self.ZIP = IZIP(api_util) if not api_util._is_odd else None

Base.__init__(self, api_util)

Expand Down Expand Up @@ -444,6 +444,10 @@ def NewContext(self) -> IDSS:
**(API Extension)**
'''

if self._api_util._is_odd:
raise NotImplementedError("NewContext is not supported for the official OpenDSS engine.")

ffi = self._api_util.ffi
lib = self._api_util.lib_unpatched
new_ctx = ffi.gc(lib.ctx_New(), lib.ctx_Dispose)
Expand Down
84 changes: 84 additions & 0 deletions dss/Oddie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import annotations
from typing import Optional
from ._cffi_api_util import CffiApiUtil
from .IDSS import IDSS
from enum import Flag

class OddieOptions(Flag):
MapErrors = 0x01


class IOddieDSS(IDSS):
r'''
The OddieDSS class exposes the official OpenDSSDirect.DLL binary,
as distributed by EPRI, with the same API as the DSS-Python and
the official COM interface object on Windows. It uses AltDSS Oddie
to achieve this.
**Note:** This class requires the backend for Oddie to be included in
the `dss_python_backend` package. If it is not available, an import
error should occur when trying to use this.
AltDSS Oddie wraps OpenDSSDirect.DLL, providing a minimal compatiliby layer
to expose it with the same API as AltDSS/DSS C-API. With it, we can
just reuse most of the tools from the other projects on DSS-Extensions
without too much extra work.
Note that many functions from DSS-Extensions will not be available and/or
will return errors, and this is expected. There are some issues and/or
limitations from OpenDSSDirect.DLL that may or may not affect specific
use cases; check the documentation on https://dss-extensions.org for
more information.
:param library_path: The name or full path of the target dynamic library to
load. Defaults to trying to load "OpenDSSDirect" from `c:\Program Files\OpenDSS\x64`,
followed by trying to load it from the current path.
:param load_flags: Optional, flags to feed the [`LoadLibrary`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa)
(on Windows) or `dlopen` (on Linux and macOS, whenever EPRI supports it). If not provided, a default set will be used.
:param oddie_options: Optional, Oddie configuration flags. If none passed,
the default settings (recommended) will be used. For advanced users.
'''

def __init__(self, library_path: str = '', load_flags: Optional[int] = None, oddie_options: Optional[OddieOptions] = None):
from dss_python_backend import _altdss_oddie_capi
lib = _altdss_oddie_capi.lib
ffi = _altdss_oddie_capi.ffi
NULL = ffi.NULL

c_load_flags = NULL
if load_flags is not None:
c_load_flags = ffi.new('uint32_t*', load_flags)

if library_path:
library_path = library_path.encode()
lib.Oddie_SetLibOptions(library_path, c_load_flags)
ctx = lib.ctx_New()
else:
# Try the default install folder
library_path = rb'C:\Program Files\OpenDSS\x64\OpenDSSDirect.dll'
lib.Oddie_SetLibOptions(library_path, c_load_flags)
ctx = lib.ctx_New()
if ctx == NULL:
# Try from the general path, let the system resolve it
library_path = rb'OpenDSSDirect.dll'
lib.Oddie_SetLibOptions(library_path, c_load_flags)
ctx = lib.ctx_New()

if ctx == NULL:
raise RuntimeError("Could not load the target library.")

if lib.ctx_DSS_Start(ctx, 0) != 1:
raise RuntimeError("DSS_Start call was not successful.")

if oddie_options is not None:
lib.Oddie_SetOptions(oddie_options)

ctx = ffi.gc(ctx, lib.ctx_Dispose)
api_util = CffiApiUtil(ffi, lib, ctx, is_odd=True)
api_util._library_path = library_path
IDSS.__init__(self, api_util)


__all__ = ['IOddieDSS', 'OddieOptions']
4 changes: 2 additions & 2 deletions dss/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
lib.DSS_SetPropertiesMO(_properties_mo.encode())

from ._cffi_api_util import CffiApiUtil, DSSException, set_case_insensitive_attributes
# from .altdss import Edit
from .IDSS import IDSS
from .Oddie import IOddieDSS, OddieOptions
from .enums import *

DssException = DSSException
Expand All @@ -44,4 +44,4 @@
except:
__version__ = '0.0dev'

__all__ = ['dss', 'DSS', 'DSS_GR', 'prime_api_util', 'api_util', 'DSSException', 'patch_dss_com', 'set_case_insensitive_attributes', 'enums', 'Edit']
__all__ = ['dss', 'DSS', 'DSS_GR', 'prime_api_util', 'api_util', 'DSSException', 'patch_dss_com', 'set_case_insensitive_attributes', 'enums', 'IOddieDSS', 'OddieOptions']
11 changes: 10 additions & 1 deletion dss/_cffi_api_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ class CffiApiUtil(object):
'''
_ctx_to_util = WeakKeyDictionary()

def __init__(self, ffi, lib, ctx=None):
def __init__(self, ffi, lib, ctx=None, is_odd=False):
self._is_odd = is_odd
self.owns_ctx = True
self.codec = codec
self.ctx = ctx
Expand Down Expand Up @@ -466,18 +467,26 @@ def clear_callback(self, step: int):


def register_callbacks(self):
if self._is_odd:
return

mgr = get_manager_for_ctx(self.ctx)
# if multiple calls, the extras are ignored
mgr.register_func(AltDSSEvent.Clear, altdss_python_util_callback)
mgr.register_func(AltDSSEvent.ReprocessBuses, altdss_python_util_callback)

def unregister_callbacks(self):
if self._is_odd:
return
mgr = get_manager_for_ctx(self.ctx)
mgr.unregister_func(AltDSSEvent.Clear, altdss_python_util_callback)
mgr.unregister_func(AltDSSEvent.ReprocessBuses, altdss_python_util_callback)

# The context will die, no need to do anything else currently.
def __del__(self):
if self._is_odd:
return

self.clear_callback(0)
self.clear_callback(1)
self.unregister_callbacks()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ packages = ["dss"]
name = "dss-python"
dynamic = ["version"]
dependencies = [
"dss_python_backend==0.14.5",
"dss_python_backend==0.14.6a1",
"numpy>=1.21.0",
"typing_extensions>=4.5,<5",
]
Expand Down
45 changes: 37 additions & 8 deletions tests/_settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@

import sys, os

import faulthandler
faulthandler.disable()
from dss import DSS, IOddieDSS
faulthandler.enable()

org_dir = os.getcwd()

USE_ODDIE = os.getenv('DSS_PYTHON_ODDIE', None)
if USE_ODDIE:
# print("Using Oddie:", USE_ODDIE)
if USE_ODDIE != '1':
DSS = IOddieDSS(USE_ODDIE)
else:
DSS = IOddieDSS()

os.chdir(org_dir)


WIN32 = (sys.platform == 'win32')
if os.path.exists('../../electricdss-tst/'):
BASE_DIR = os.path.abspath('../../electricdss-tst/')
Expand All @@ -25,6 +44,21 @@
#"L!Distrib/IEEETestCases/4wire-Delta/Kersting4wireIndMotor.dss",

test_filenames = '''
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-mm-csv-p.dss
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-mm-csv-pq.dss
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-mm-dbl-p.dss
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-mm-sng-p.dss
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-mm-txt-p.dss
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-mm-txt-pq.dss
Version8/Distrib/Examples/MemoryMappingLoadShapes/ckt24/master_ckt24-nomm.dss
Version8/Distrib/IEEETestCases/4Bus-DY-Bal/4Bus-DY-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-GrdYD-Bal/4Bus-GrdYD-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-OYOD-Bal/4Bus-OYOD-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-OYOD-UnBal/4bus-OYOD-UnBal.dss
Version8/Distrib/IEEETestCases/4Bus-YD-Bal/4Bus-YD-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-YY-Bal/4Bus-YY-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-YYD/YYD-Master.DSS
Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss
Test/CapControlFollow.dss
Test/CapacitorConfigs.dss
Test/ReactorConfigs.dss
Expand All @@ -47,14 +81,6 @@
L!Version8/Distrib/IEEETestCases/123Bus/IEEE123Master.dss
Version8/Distrib/IEEETestCases/37Bus/ieee37.dss
Version8/Distrib/IEEETestCases/IEEE 30 Bus/Master.dss
Version8/Distrib/IEEETestCases/4Bus-DY-Bal/4Bus-DY-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-GrdYD-Bal/4Bus-GrdYD-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-OYOD-Bal/4Bus-OYOD-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-OYOD-UnBal/4bus-OYOD-UnBal.dss
Version8/Distrib/IEEETestCases/4Bus-YD-Bal/4Bus-YD-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-YY-Bal/4Bus-YY-Bal.DSS
Version8/Distrib/IEEETestCases/4Bus-YYD/YYD-Master.DSS
Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss
Test/IEEE13_LineSpacing.dss
Test/IEEE13_LineGeometry.dss
Test/IEEE13_Assets.dss
Expand Down Expand Up @@ -105,6 +131,9 @@
L!Version8/Distrib/Examples/Microgrid/GridFormingInverter/GFM_IEEE8500/Run_8500Node_GFMDailySmallerPV.dss
L!Version8/Distrib/Examples/Microgrid/GridFormingInverter/GFM_IEEE8500/Run_8500Node_GFMSnap.dss
L!Version8/Distrib/Examples/Microgrid/GridFormingInverter/GFM_IEEE8500/Run_8500Node_Unbal.dss
L!Version8/Distrib/Examples/Microgrid/GridFormingInverter/GFM_AmpsLimit_123/Run_IEEE123Bus_GFMDaily.DSS
L!Version8/Distrib/Examples/Microgrid/GridFormingInverter/GFM_AmpsLimit_123/Run_IEEE123Bus_GFMDailySwapRef.DSS
L!Version8/Distrib/Examples/Microgrid/GridFormingInverter/GFM_AmpsLimit_123/Run_IEEE123Bus_GFMSnap.DSS
L!Version8/Distrib/IEEETestCases/123Bus/RevRegTest.dss
L!Version8/Distrib/Examples/IBRDynamics_Cases/GFM_IEEE123/Run_IEEE123Bus_GFMDaily.DSS
L!Version8/Distrib/Examples/IBRDynamics_Cases/GFM_IEEE123/Run_IEEE123Bus_GFMDaily_CannotPickUpLoad.DSS
Expand Down
Loading

0 comments on commit 85783cf

Please sign in to comment.