├── .github └── workflows │ └── builds.yml ├── .gitignore ├── LICENSE ├── OPENDSS_LICENSE ├── README.md ├── ci ├── build_conda.sh ├── build_linux.sh ├── build_wheel.sh └── test_wheel.sh ├── conda ├── README.md ├── bld.bat ├── build.sh ├── conda_build_config.yaml └── meta.yaml ├── docs ├── Makefile ├── README.md ├── _static │ ├── dss-python-dark.svg │ ├── dss-python.svg │ └── dssx.png ├── changelog.md ├── conf.py ├── enumerations.md ├── examples │ ├── GettingStarted.ipynb │ ├── JSON.ipynb │ ├── Multithreading.ipynb │ ├── Plotting.ipynb │ └── UserModels │ │ └── PyIndMach012 │ │ ├── PyIndMach012.py │ │ ├── README.ipynb │ │ └── master.dss ├── images │ └── repomap.svg ├── index.md └── make.bat ├── dss ├── IActiveClass.py ├── IBus.py ├── ICNData.py ├── ICapControls.py ├── ICapacitors.py ├── ICircuit.py ├── ICktElement.py ├── ICtrlQueue.py ├── IDSS.py ├── IDSSElement.py ├── IDSSEvents.py ├── IDSSProgress.py ├── IDSSProperty.py ├── IDSS_Executive.py ├── IDSSimComs.py ├── IError.py ├── IFuses.py ├── IGICSources.py ├── IGenerators.py ├── IISources.py ├── ILineCodes.py ├── ILineGeometries.py ├── ILineSpacings.py ├── ILines.py ├── ILoadShapes.py ├── ILoads.py ├── IMeters.py ├── IMonitors.py ├── IPDElements.py ├── IPVSystems.py ├── IParallel.py ├── IParser.py ├── IReactors.py ├── IReclosers.py ├── IReduceCkt.py ├── IRegControls.py ├── IRelays.py ├── ISensors.py ├── ISettings.py ├── ISolution.py ├── IStorages.py ├── ISwtControls.py ├── ITSData.py ├── IText.py ├── ITopology.py ├── ITransformers.py ├── IVsources.py ├── IWireData.py ├── IXYCurves.py ├── IYMatrix.py ├── IZIP.py ├── Oddie.py ├── UserModels │ ├── __init__.py │ ├── bases.py │ └── wrappers.py ├── __init__.py ├── _cffi_api_util.py ├── _types.py ├── enums.py ├── examples.py ├── patch_dss_com.py └── plot.py ├── pyproject.toml ├── ruff.toml └── tests ├── __init__.py ├── _settings.py ├── compare_outputs.py ├── data └── 13Bus.zip ├── save_outputs.py ├── test_alt.py ├── test_alt_em.py ├── test_batch.py ├── test_ctrlqueue.py ├── test_events.py ├── test_general.py ├── test_obj.py └── test_past_issues.py /.github/workflows/builds.yml: -------------------------------------------------------------------------------- 1 | name: Builds 2 | 3 | # TODO: generalize steps 4 | 5 | env: 6 | ARTIFACTS_FOLDER: '${{ github.workspace }}/artifacts' 7 | DSS_CAPI_TAG: '0.14.5' 8 | 9 | on: 10 | # release: 11 | # types: [created] 12 | push: 13 | 14 | jobs: 15 | build_linux_x64: 16 | name: 'Linux x64' 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | container-image: [ 21 | 'quay.io/pypa/manylinux_2_28_x86_64', 22 | 'quay.io/pypa/manylinux2014_x86_64' 23 | ] 24 | container: 25 | image: ${{ matrix.container-image }} 26 | env: 27 | CONDA_SUBDIR: 'linux-64' 28 | CONDA: "/opt/miniconda/" 29 | steps: 30 | - name: 'Checkout' 31 | run: | 32 | git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY dss_python 33 | cd dss_python 34 | git checkout $GITHUB_SHA 35 | - name: 'Download/extract message catalogs' 36 | run: | 37 | curl -s -L https://github.com/dss-extensions/dss_capi/releases/download/${DSS_CAPI_TAG}/messages.tar.gz -o messages.tar.gz 38 | cd dss_python/dss 39 | tar zxf ../../messages.tar.gz 40 | - name: Build wheel 41 | run: | 42 | mkdir -p artifacts 43 | mkdir -p artifacts_raw 44 | bash dss_python/ci/build_linux.sh x64 45 | # - name: Build conda packages 46 | # continue-on-error: true 47 | # run: | 48 | # bash dss_python/ci/build_conda.sh 49 | - name: Try installing the wheel 50 | run: bash dss_python/ci/test_wheel.sh 51 | - name: 'Upload artifacts' 52 | uses: "actions/upload-artifact@v3" 53 | with: 54 | name: 'packages' 55 | path: '${{ github.workspace }}/artifacts' 56 | 57 | # build_linux_x86: 58 | # name: 'Linux x86' 59 | # runs-on: ubuntu-latest 60 | # env: 61 | # CONDA_SUBDIR: 'linux-32' 62 | # DOCKER_IMAGE: 'pmeira/manylinux_wheel_fpc322_i686' 63 | # steps: 64 | # - name: 'Checkout' 65 | # run: | 66 | # git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY dss_python 67 | # cd dss_python 68 | # git checkout $GITHUB_SHA 69 | # - name: 'Setup Docker' 70 | # run: | 71 | # docker pull $DOCKER_IMAGE 72 | # - name: 'Download/extract message catalogs' 73 | # run: | 74 | # curl -s -L https://github.com/dss-extensions/dss_capi/releases/download/${DSS_CAPI_TAG}/messages.tar.gz -o messages.tar.gz 75 | # cd dss_python/dss 76 | # tar zxf ../../messages.tar.gz 77 | # - name: Build wheel 78 | # run: | 79 | # mkdir -p artifacts 80 | # mkdir -p artifacts_raw 81 | # docker run -e GITHUB_SHA -e GITHUB_REF -v "${PWD}:/build" -w /build $DOCKER_IMAGE bash /build/dss_python/ci/build_linux.sh x86 82 | # - name: 'Upload artifacts' 83 | # uses: "actions/upload-artifact@v3" 84 | # with: 85 | # name: 'packages' 86 | # path: '${{ github.workspace }}/artifacts' 87 | 88 | build_macos_x64: 89 | name: 'macOS x64' 90 | runs-on: 'macos-11' 91 | env: 92 | SDKROOT: '${{ github.workspace }}/MacOSX10.13.sdk' 93 | PYTHON: python3 94 | steps: 95 | - uses: actions/checkout@v3 96 | with: 97 | fetch-depth: 0 98 | path: 'dss_python' 99 | - name: 'Download/extract message catalogs' 100 | run: | 101 | curl -s -L https://github.com/dss-extensions/dss_capi/releases/download/${DSS_CAPI_TAG}/messages.tar.gz -o messages.tar.gz 102 | cd dss_python/dss 103 | tar zxf ../../messages.tar.gz 104 | - name: Build wheel 105 | run: | 106 | bash dss_python/ci/build_wheel.sh 107 | # - name: Build conda packages 108 | # continue-on-error: true 109 | # run: | 110 | # sudo chown -R $UID $CONDA 111 | # bash dss_python/ci/build_conda.sh 112 | - name: 'Upload artifacts' 113 | uses: "actions/upload-artifact@v3" 114 | with: 115 | name: 'packages' 116 | path: '${{ github.workspace }}/artifacts' 117 | 118 | build_macos_arm64: 119 | name: 'macOS ARM64' 120 | runs-on: 'macos-11' 121 | env: 122 | PYTHON: python3 123 | _PYTHON_HOST_PLATFORM: macosx-11.0-arm64 124 | ARCHFLAGS: '-arch arm64' 125 | 126 | steps: 127 | - uses: actions/checkout@v3 128 | with: 129 | fetch-depth: 0 130 | path: 'dss_python' 131 | - name: 'Download/extract message catalogs' 132 | run: | 133 | curl -s -L https://github.com/dss-extensions/dss_capi/releases/download/${DSS_CAPI_TAG}/messages.tar.gz -o messages.tar.gz 134 | cd dss_python/dss 135 | tar zxf ../../messages.tar.gz 136 | # - name: 'Download macOS SDK 10.13' 137 | # run: | 138 | # curl -s -L https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.13.sdk.tar.xz -o macOSsdk.tar.xz 139 | # tar xf macOSsdk.tar.xz 140 | - name: Build wheel 141 | run: | 142 | bash dss_python/ci/build_wheel.sh 143 | # - name: Build conda packages 144 | # continue-on-error: true 145 | # run: | 146 | # sudo chown -R $UID $CONDA 147 | # bash dss_python/ci/build_conda.sh 148 | - name: 'Upload artifacts' 149 | uses: "actions/upload-artifact@v3" 150 | with: 151 | name: 'packages' 152 | path: '${{ github.workspace }}/artifacts' 153 | 154 | build_win_x64: 155 | name: 'Windows x64' 156 | runs-on: windows-2019 157 | env: 158 | CONDA_SUBDIR: 'win-64' 159 | PYTHON: python 160 | steps: 161 | - uses: actions/checkout@v3 162 | with: 163 | fetch-depth: 0 164 | path: 'dss_python' 165 | - name: 'Download/extract message catalogs' 166 | shell: cmd 167 | run: | 168 | "c:\Program Files\Git\mingw64\bin\curl" -s -L https://github.com/dss-extensions/dss_capi/releases/download/%DSS_CAPI_TAG%/messages.zip -o messages.zip 169 | cd dss_python\dss 170 | tar zxf ..\..\messages.zip 171 | - name: Build wheel 172 | shell: bash 173 | run: | 174 | bash dss_python/ci/build_wheel.sh 175 | # - name: Build conda packages 176 | # continue-on-error: true 177 | # shell: bash 178 | # run: | 179 | # bash dss_python/ci/build_conda.sh 180 | - name: 'Upload artifacts' 181 | uses: "actions/upload-artifact@v3" 182 | with: 183 | name: 'packages' 184 | path: '${{ github.workspace }}/artifacts' 185 | 186 | build_win_x86: 187 | name: 'Windows x86' 188 | runs-on: windows-2019 189 | env: 190 | CONDA_SUBDIR: 'win-32' 191 | PYTHON: python 192 | steps: 193 | - uses: actions/checkout@v3 194 | with: 195 | fetch-depth: 0 196 | path: 'dss_python' 197 | - uses: actions/setup-python@v3 198 | with: 199 | python-version: '3.7' 200 | architecture: 'x86' 201 | - name: 'Download/extract message catalogs' 202 | shell: cmd 203 | run: | 204 | "c:\Program Files\Git\mingw64\bin\curl" -s -L https://github.com/dss-extensions/dss_capi/releases/download/%DSS_CAPI_TAG%/messages.zip -o messages.zip 205 | cd dss_python\dss 206 | tar zxf ..\..\messages.zip 207 | - name: Build wheel 208 | shell: bash 209 | run: | 210 | bash dss_python/ci/build_wheel.sh 211 | - name: 'Upload artifacts' 212 | uses: "actions/upload-artifact@v3" 213 | with: 214 | name: 'packages' 215 | path: '${{ github.workspace }}/artifacts' 216 | 217 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # Py.test 29 | .pytest_cache 30 | 31 | # Test reports 32 | tests/reports 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | .hypothesis/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # dotenv 89 | .env 90 | 91 | # virtualenv 92 | .venv 93 | venv/ 94 | ENV/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | .vscode 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | 111 | *.dll* 112 | 113 | README.rst 114 | 115 | conda_wheels/ 116 | dss/*.lib 117 | dss/**/*.h 118 | dss/**/*.hpp 119 | dss/messages/* 120 | 121 | *.CSV 122 | *.lib 123 | *.exp 124 | *.orig 125 | *.obj 126 | *.pickle 127 | *.dbl 128 | *.sng 129 | *.pdb 130 | 131 | docs/apidocs 132 | tests/result*.zip 133 | tmp/ 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017-2023, Paulo Meira 4 | Copyright (c) 2017-2023, DSS-Python contributors 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /OPENDSS_LICENSE: -------------------------------------------------------------------------------- 1 | * Copyright (c) 2008-2023, Electric Power Research Institute, Inc. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the Electric Power Research Institute, Inc., nor 12 | * the names of its contributors may be used to endorse or promote 13 | * products derived from this software without specific prior written 14 | * permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY Electric Power Research Institute, Inc., "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL Electric Power Research Institute, Inc., BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | 28 | -------------------------------------------------------------------------------- /ci/build_conda.sh: -------------------------------------------------------------------------------- 1 | cd dss_python 2 | 3 | if [ "$RUNNER_OS" = "Windows" ]; then 4 | CONDA=`cygpath "$CONDA"` 5 | export ARTIFACTS_FOLDER=`readlink -f ../artifacts` 6 | fi 7 | 8 | export PATH=$CONDA:$CONDA/bin:$CONDA/scripts:$PATH 9 | 10 | source $CONDA/etc/profile.d/conda.sh 11 | 12 | # Update the recipe 13 | #DSS_PYTHON_PREPARE_BOA=1 python setup.py 14 | 15 | conda config --set always_yes yes --set changeps1 no 16 | conda config --add channels conda-forge 17 | conda create -p ../boa boa anaconda-client 18 | conda activate boa 19 | 20 | PYTHON_VERSIONS="3.7 3.8 3.9 3.10 3.11" 21 | for pyversion in $PYTHON_VERSIONS 22 | do 23 | # Workaround to build when a single version fails 24 | echo "python:" > conda/version.yaml 25 | echo " - $pyversion" >> conda/version.yaml 26 | boa build --no-test conda -m conda/version.yaml 27 | done 28 | 29 | #boa build --no-test conda -m conda/conda_build_config.yaml 30 | 31 | mv $CONDA/conda-bld/*-64 $ARTIFACTS_FOLDER/ 32 | -------------------------------------------------------------------------------- /ci/build_linux.sh: -------------------------------------------------------------------------------- 1 | set -e -x 2 | export PATH=/opt/python/cp39-cp39/bin/:$PATH 3 | 4 | cd dss_python 5 | python3 -m pip install --upgrade pip wheel hatch 6 | python3 -m hatch build "../artifacts" 7 | cd .. 8 | -------------------------------------------------------------------------------- /ci/build_wheel.sh: -------------------------------------------------------------------------------- 1 | mkdir -p artifacts 2 | cd dss_python 3 | $PYTHON -m pip install --upgrade pip wheel hatch 4 | $PYTHON -m pip install cffi 5 | $PYTHON -m hatch build "$ARTIFACTS_FOLDER" 6 | -------------------------------------------------------------------------------- /ci/test_wheel.sh: -------------------------------------------------------------------------------- 1 | # Currently only for Linux 2 | 3 | set -e -x 4 | 5 | ORIGINAL_PATH=$PATH 6 | PYTHON_DIRS="cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311" 7 | 8 | for pydir in $PYTHON_DIRS 9 | do 10 | echo Installing for CPython $pydir 11 | export PATH=/opt/python/${pydir}/bin/:$ORIGINAL_PATH 12 | python -m pip install scipy matplotlib 13 | python -m pip install artifacts/dss_python-*.whl 14 | python -c 'from dss import DSS; DSS.Plotting.enable(); DSS("new circuit.test123")' 15 | done 16 | -------------------------------------------------------------------------------- /conda/README.md: -------------------------------------------------------------------------------- 1 | **NOTE: this hasn't been updated for a few years. Probably will not work as-is.** 2 | 3 | On Mac, still need to download and unpack: 4 | https://github.com/phracker/MacOSX-SDKs/releases/download/10.13/MacOSX10.9.sdk.tar.xz 5 | 6 | And add it to conda_build_config.yaml as in 7 | https://conda.io/docs/user-guide/tasks/build-packages/compiler-tools.html#macos-sdk 8 | 9 | e.g. 10 | 11 | CONDA_BUILD_SYSROOT: 12 | - /opt/MacOSX10.9.sdk # [osx] -------------------------------------------------------------------------------- /conda/bld.bat: -------------------------------------------------------------------------------- 1 | REM Build dss_python 2 | rd /s /q build 3 | rd /s /q dist 4 | rd /s /q .eggs 5 | "%PYTHON%" setup.py install --single-version-externally-managed --record=record.txt 6 | if errorlevel 1 exit 1 7 | -------------------------------------------------------------------------------- /conda/build.sh: -------------------------------------------------------------------------------- 1 | BLD_PREV_DIR=`pwd` 2 | echo "Building dss_python in $BLD_PREV_DIR" 3 | rm -rf build dist .eggs 4 | $PYTHON setup.py install --single-version-externally-managed --record=record.txt 5 | -------------------------------------------------------------------------------- /conda/conda_build_config.yaml: -------------------------------------------------------------------------------- 1 | python: 2 | - 3.7 3 | - 3.8 4 | - 3.9 5 | - 3.10 6 | - 3.11 7 | 8 | 9 | -------------------------------------------------------------------------------- /conda/meta.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: dss_python 3 | version: "{{ load_setup_py_data().version }}" 4 | 5 | source: 6 | #git_rev: "0.10.3" 7 | #git_url: https://github.com/dss-extensions/dss_python.git 8 | path: .. 9 | 10 | build: 11 | script_env: 12 | - DSS_CAPI_PATH 13 | - SDKROOT 14 | requirements: 15 | build: 16 | - "{{ compiler('c') }}" 17 | # - {{ compiler('cxx') }} 18 | - "python {{ python }}" 19 | - cffi 20 | - setuptools 21 | - wheel 22 | # - pip 23 | # - cmake [osx] 24 | # - git 25 | 26 | host: 27 | - python 28 | - setuptools 29 | - cffi 30 | 31 | run: 32 | - python 33 | - cffi 34 | - numpy 35 | 36 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = dss_python 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | View the last compiled version of the API docs at https://dss-extensions.org/dss_python/ 2 | 3 | More links at https://dss-extensions.org/ and other notes at [DSS-Extensions — OpenDSS: Overview of Python APIs](https://github.com/dss-extensions/dss-extensions/blob/main/python_apis.md). 4 | -------------------------------------------------------------------------------- /docs/_static/dssx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dss-extensions/DSS-Python/85783cf811568dae931d06d3f434a34424599817/docs/_static/dssx.png -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import os, sys, shutil 2 | 3 | if not os.path.exists('../../dss-extensions'): 4 | raise RuntimeError('dss-extensions common repo not found. Be sure to clone it side-by-side with OpenDSSDirect.py when building docs.') 5 | 6 | if not os.path.exists('../../dss_python_backend'): 7 | raise RuntimeError('dss_python_backend common repo not found. Be sure to clone it side-by-side with OpenDSSDirect.py when building docs.') 8 | 9 | sys.path.append('../../dss-extensions/docs') 10 | from common_conf import * 11 | import dss 12 | 13 | project = 'DSS-Python' 14 | copyright = '2018-2024 Paulo Meira, Dheepak Krishnamurthy, DSS-Extensions contributors' 15 | author = 'Paulo Meira, Dheepak Krishnamurthy, DSS-Extensions contributors' 16 | version = dss.__version__ 17 | release = dss.__version__ 18 | 19 | # extensions = [ 20 | # 'sphinx.ext.autodoc', 21 | # # 'sphinx.ext.viewcode', 22 | # 'sphinx.ext.githubpages', 23 | # 'sphinx.ext.autosummary', 24 | # 'sphinx.ext.autosectionlabel', 25 | # 'sphinx_autodoc_typehints', 26 | # 'guzzle_sphinx_theme', 27 | # 'nbsphinx', 28 | # 'myst_parser', 29 | # ] 30 | 31 | # If we ever need more extensions or change settings, we are free to change it here. e.g. 32 | extensions.append('autodoc2') 33 | 34 | autodoc2_packages = [ 35 | { 36 | "path": "../dss", 37 | "auto_mode": True, 38 | }, 39 | { 40 | "path": "../../dss_python_backend/dss_python_backend", 41 | "auto_mode": False, 42 | }, 43 | ] 44 | 45 | autodoc2_docstrings = 'all' 46 | autodoc2_sort_names = True 47 | autodoc2_class_docstring = 'both' 48 | autodoc2_hidden_regexes = [ 49 | r'.*\.__setattr__$', 50 | r'.*\.__slots__$', 51 | r'.*\.__all__$', 52 | r'dss\.UserModels', 53 | ] 54 | 55 | html_logo = '_static/dss-python.svg' 56 | html_theme_options["logo"] = { 57 | "image_dark": '_static/dss-python-dark.svg', 58 | } 59 | html_theme_options["show_toc_level"] = 1 60 | html_favicon = '_static/dssx.png' 61 | 62 | # autosummary_generate = True 63 | 64 | exclude_patterns.append('**electricdss-tst') 65 | 66 | source_suffix = ['.md', '.rst', ] 67 | 68 | # def try_cleaning(app, docname, source): 69 | # if os.path.exists('examples/electricdss-tst'): 70 | # shutil.rmtree('examples/electricdss-tst') 71 | 72 | # def setup(app): 73 | # app.connect('source-read', try_cleaning) 74 | 75 | 76 | # Ugly patches... 77 | 78 | # This one is to make it run at all 79 | patch_autodoc2() 80 | 81 | # # This one is related to node_ids being too long with duplicated data 82 | # # 83 | # import sphinx.domains.python 84 | # add_target_and_index_org = sphinx.domains.python.PyObject.add_target_and_index 85 | # class PatchPyObject: 86 | # def add_target_and_index(self, name_cls, sig, signode): 87 | # if name_cls[0].count('.') > 2: 88 | # mod_name = self.options.get('module', self.env.ref_context.get('py:module')) 89 | # assert mod_name == name_cls[0][:len(mod_name)] 90 | # name_cls = (name_cls[0][1 + len(mod_name):], name_cls[1]) 91 | # parts = name_cls[0].split('.') 92 | # if len(parts) > 1 and parts[0] == parts[1]: 93 | # name_cls = ('.'.join(parts[1:]), name_cls[1]) 94 | 95 | # return add_target_and_index_org(self, name_cls, sig, signode) 96 | 97 | # sphinx.domains.python.PyObject.add_target_and_index = PatchPyObject.add_target_and_index -------------------------------------------------------------------------------- /docs/enumerations.md: -------------------------------------------------------------------------------- 1 | # Enumerations 2 | 3 | The enumeration classes from DSS-Python-Backend are reexported as `dss.enums`, and the enums are also imported to the main module `dss` (so you can use `from dss import SolveModes`, for example). Many functions allow using these for better code quality, and some are required to correctly use the extended API. Note that AltDSS-Python includes a lot more enumerations derived from the internal DSS schema. 4 | 5 | ```{include} ../../dss-extensions/docs/python/enums.md 6 | ``` 7 | -------------------------------------------------------------------------------- /docs/examples/UserModels/PyIndMach012/master.dss: -------------------------------------------------------------------------------- 1 | // This is a slightly simplified version of the InductionMachine example from 2 | // the official OpenDSS distribution (found in Examples/InductionMachine) 3 | // to illustrate UserModels in DSS-Python. 4 | // Since it's a small system, all files were merged into this one. 5 | Clear 6 | 7 | New Circuit.IndMach012Test 8 | 9 | !Redirect Vsource.dss 10 | Edit "Vsource.source" bus1=Bx basekv=115 pu=1.0475 angle=0 frequency=60 phases=3 MVAsc3=20000 MVAsc1=21000 x1r1=4 x0r0=3 11 | 12 | !Redirect LineCode.dss 13 | New "LineCode.unbalanced 336 acsr" nphases=3 baseFreq=60 normamps=570.4 emergamps=713 rmatrix="0.0868455 | 0.0298305 0.0887966 | 0.0288883 0.0298305 0.0868455" xmatrix="0.2025449 | 0.084721 0.1961452 | 0.0719161 0.084721 0.2025449" cmatrix="2.74 | -0.7 2.96 | -0.34 -0.71 2.74" units=kft 14 | 15 | !Redirect LoadShape.dss 16 | New "LoadShape.default" interval=1.0 mult=(0.677, 0.6256, 0.6087, 0.5833, 0.58028, 0.6025, 0.657, 0.7477, 0.832, 0.88, 0.94, 0.989, 0.985, 0.98, 0.9898, 0.999, 1, 0.958, 0.936, 0.913, 0.876, 0.876, 0.828, 0.756, ) npts=24 mean=0.826 stddev=0.2 17 | !New "LoadShape.wind2400" npts=2501 interval=0.000277778 csvfile=WPWind2400.csv action=normalize mean=0.603 stddev=0.2 18 | 19 | !Redirect TCC_Curve.dss 20 | New "TCC_Curve.a" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(7.1769, 2.2079, 1.234, 0.534, 0.2215, 0.134, 0.0965, 0.0769, 0.0653, 0.0578, 0.0528, 0.0492, 0.0378, 0.0346, 0.0342, ) 21 | New "TCC_Curve.c" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(125.2574, 38.0345, 20.962, 8.7258, 3.3034, 1.802, 1.1647, 0.8336, 0.6394, 0.5156, 0.432, 0.3728, 0.1882, 0.1389, 0.1321, ) 22 | New "TCC_Curve.tlink" npts=7 C_array=(2, 2.1, 3, 4, 6, 22, 50, ) T_array=(300, 100, 10.1, 4, 1.4, 0.1, 0.02, ) 23 | New "TCC_Curve.klink" npts=6 C_array=(2, 2.2, 3, 4, 6, 30, ) T_array=(300, 20, 4, 1.3, 0.41, 0.02, ) 24 | New "TCC_Curve.ext_inv" npts=15 C_array=(1.1, 1.3, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 100, ) T_array=(134.4074, 40.9913, 22.6817, 9.5217, 3.6467, 2.0017, 1.2967, 0.9274, 0.7092, 0.5693, 0.4742, 0.4065, 0.1924, 0.133, 0.1245, ) 25 | 26 | !Redirect line.dss 27 | New "line.l1" bus1=B0 bus2=B1 linecode="Unbalanced 336 ACSR" length=10 units=kft 28 | New "line.l2" bus1=B1 bus2=B2 linecode="Unbalanced 336 ACSR" length=10 units=kft 29 | New "line.l3" bus1=Bm bus2=B3 linecode="Unbalanced 336 ACSR" length=10 units=kft 30 | New "line.l4" bus1=B3 bus2=B4 linecode="Unbalanced 336 ACSR" length=10 units=kft 31 | New "line.l5" bus1=B2 bus2=B5 linecode="Unbalanced 336 ACSR" length=10 units=kft 32 | New "line.l6" bus1=B3 bus2=B6 linecode="Unbalanced 336 ACSR" length=10 units=kft 33 | New "line.l7" bus1=B0 bus2=B7 linecode="Unbalanced 336 ACSR" length=10 units=kft 34 | 35 | !Redirect Load.dss 36 | New "Load.ldb0" bus1=B0 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 37 | New "Load.ldb1" bus1=B1 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 38 | New "Load.ldb2" bus1=B2 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 39 | New "Load.ldb3" bus1=B3 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 40 | New "Load.ldb4" bus1=B4 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 41 | New "Load.ldb5" bus1=B5 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 42 | New "Load.ldb6" bus1=B6 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 43 | New "Load.ldb7" bus1=B7 conn=wye phases=3 kW=0.1 pf=1 model=2 kV=12.47 44 | 45 | !Redirect Transformer.dss 46 | New "Transformer.sub" phases=3 windings=2 buses=(bx, b0, ) conns=(delta, wye, ) kVs=(115, 12.47, ) kVAs=(20000, 20000, ) taps=(1, 1, ) Xhl=7 47 | New "Transformer.tg" phases=3 windings=2 buses=(b4, bg, ) conns=(wye, wye, ) kVs=(12.47, 0.48, ) kVAs=(1500, 1500, ) taps=(1, 1, ) Xhl=5 48 | New "Transformer.reg1a" phases=1 windings=2 buses=(b2.1, bm.1, ) conns=(wye, wye, ) kVs=(12.47, 12.47, ) kVAs=(2000, 2000, ) Xhl=1 49 | New "Transformer.reg1b" phases=1 windings=2 buses=(b2.2, bm.2, ) conns=(wye, wye, ) kVs=(12.47, 12.47, ) kVAs=(2000, 2000, ) Xhl=1 50 | New "Transformer.reg1c" phases=1 windings=2 buses=(b2.3, bm.3, ) conns=(wye, wye, ) kVs=(12.47, 12.47, ) kVAs=(2000, 2000, ) Xhl=1 51 | 52 | !Redirect RegControl.dss 53 | New "RegControl.reg1a" transformer=Reg1a winding=2 vreg=119.9926 band=3 ptratio=60 delay=15 tapdelay=2 reversible=yes revvreg=119.9926 revband=3 CTprim=600 R=5 X=3 revR=5 revX=3 54 | New "RegControl.reg1b" transformer=Reg1b winding=2 vreg=119.9926 band=3 ptratio=60 delay=15 tapdelay=2 reversible=yes revvreg=119.9926 revband=3 CTprim=600 R=5 X=3 revR=5 revX=3 55 | New "RegControl.reg1c" transformer=Reg1c winding=2 vreg=119.9926 band=3 ptratio=60 delay=15 tapdelay=2 reversible=yes revvreg=119.9926 revband=3 CTprim=600 R=5 X=3 revR=5 revX=3 56 | New "RegControl.sub" transformer=sub winding=2 vreg=119.9926 band=3 ptratio=60 delay=10 tapdelay=2 57 | 58 | !Redirect Capacitor.dss 59 | New "Capacitor.c1" conn=wye bus1=B1 phases=3 kvar=( 600) kv=12.47 60 | New "Capacitor.c2" conn=wye bus1=B3 phases=3 kvar=( 600) kv=12.47 61 | New "Capacitor.cg" conn=wye bus1=Bg phases=3 kvar=( 600) kv=0.48 62 | 63 | !Redirect Fault.dss 64 | New "Fault.thefault" bus1=B3.1 bus2=B3.0 phases=1 r=0.0001 ONtime=.3 temporary=yes 65 | 66 | !Redirect IndMach012.dss 67 | // short line to Induction Motor 68 | New Line.MotorLeads Bus1=bg bus2=bg2 length=.010 69 | 70 | // We'll create the actual machine in Python later 71 | !New "IndMach012.Motor1" bus1=Bg2 kW=1200 conn=delta kVA=1500.000 H=6 duty=Wind2400 purs=0.048 puxs=0.075 purr=0.018 puxr=0.12 puxm=3.8 slip=0.02 SlipOption=variableslip kv=0.48 72 | 73 | !Redirect Relay.dss 74 | New "Relay.mfrov/uv" MonitoredObj=line.MotorLeads MonitoredTerm=1 type=voltage RecloseIntervals=(5.0) kvbase=0.48 Shots=1 Delay=0 75 | New "Relay.mfr46" MonitoredObj=line.MotorLeads MonitoredTerm=1 type=46 46BaseAmps=1800 46%Pickup=20 46isqt=1 Delay=0.1 76 | New "Relay.mfr47" MonitoredObj=line.MotorLeads MonitoredTerm=1 type=47 kvbase=0.48 47%Pickup=2 Delay=0.1 77 | 78 | !Redirect Recloser.dss 79 | New "Recloser.cb1" MonitoredObj=Line.L1 MonitoredTerm=1 NumFast=4 PhaseFast=Ext_Inv PhaseDelayed=Ext_Inv PhaseTrip=800 TDPhFast=1 TDPhDelayed=1 PhaseInst=2400 GroundFast=Ext_Inv GroundDelayed=Ext_Inv GroundTrip=800 TDGrFast=1 TDGrDelayed=1 GroundInst=1200 Shots=4 RecloseIntervals=(0.5, 2, 2, ) 80 | New "Recloser.cb2" MonitoredObj=Line.L7 MonitoredTerm=1 NumFast=4 PhaseFast=Ext_Inv PhaseDelayed=Ext_Inv PhaseTrip=800 TDPhFast=1 TDPhDelayed=1 PhaseInst=2400 GroundFast=Ext_Inv GroundDelayed=Ext_Inv GroundTrip=800 TDGrFast=1 TDGrDelayed=1 GroundInst=1200 Shots=4 RecloseIntervals=(0.5, 2, 2, ) 81 | New "Recloser.rec1" MonitoredObj=Line.L3 MonitoredTerm=1 NumFast=1 PhaseFast=A PhaseDelayed=C PhaseTrip=280 TDPhFast=1 TDPhDelayed=1 Shots=4 RecloseIntervals=(0.5, 2, 2, ) 82 | New "Recloser.rec2" MonitoredObj=Line.L5 MonitoredTerm=1 NumFast=1 PhaseFast=A PhaseDelayed=C PhaseTrip=400 TDPhFast=1 TDPhDelayed=1 Shots=4 RecloseIntervals=(0.5, 2, 2, ) 83 | 84 | !Redirect Fuse.dss 85 | New "Fuse.f1" MonitoredObj=Line.L6 MonitoredTerm=1 FuseCurve=Klink RatedCurrent=65 86 | New "Fuse.f2" MonitoredObj=Transformer.Tg MonitoredTerm=1 FuseCurve=Tlink RatedCurrent=65 87 | 88 | !Redirect Monitor.dss 89 | // We'll create a single monitor in Python 90 | 91 | Set VoltageBases=(115 12.47 .48) 92 | 93 | Set Maxcontroliter=20 94 | Set maxiterations=20 95 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=dss_python 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /dss/IActiveClass.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from __future__ import annotations 5 | from ._cffi_api_util import Base 6 | from .enums import DSSJSONFlags 7 | from typing import AnyStr, List, Iterator 8 | 9 | class IActiveClass(Base): 10 | __slots__ = [] 11 | 12 | _columns = [ 13 | 'ActiveClassName', 14 | 'ActiveClassParent', 15 | 'Name', 16 | 'NumElements', 17 | ] 18 | 19 | @property 20 | def ActiveClassName(self) -> str: 21 | ''' 22 | Returns name of active class. 23 | 24 | Original COM help: https://opendss.epri.com/ActiveClassName.html 25 | ''' 26 | return self._get_string(self._check_for_error(self._lib.ActiveClass_Get_ActiveClassName())) 27 | 28 | @property 29 | def AllNames(self) -> List[str]: 30 | ''' 31 | Array of strings consisting of all element names in the active class. 32 | 33 | Original COM help: https://opendss.epri.com/AllNames.html 34 | ''' 35 | return self._check_for_error(self._get_string_array(self._lib.ActiveClass_Get_AllNames)) 36 | 37 | @property 38 | def Count(self) -> int: 39 | ''' 40 | Number of elements in Active Class. Same as NumElements Property. 41 | 42 | Original COM help: https://opendss.epri.com/Count.html 43 | ''' 44 | return self._check_for_error(self._lib.ActiveClass_Get_Count()) 45 | 46 | def __len__(self) -> int: 47 | return self._check_for_error(self._lib.ActiveClass_Get_Count()) 48 | 49 | def __iter__(self) -> Iterator[IActiveClass]: 50 | n = self.First 51 | while n: 52 | yield self 53 | n = self.Next 54 | 55 | @property 56 | def First(self) -> int: 57 | ''' 58 | Sets first element in the active class to be the active DSS object. 59 | If the object is a CktElement, ActiveCktELement also points to this element. 60 | 61 | Returns 0 if none. 62 | 63 | Original COM help: https://opendss.epri.com/First.html 64 | ''' 65 | return self._check_for_error(self._lib.ActiveClass_Get_First()) 66 | 67 | @property 68 | def Name(self) -> str: 69 | ''' 70 | Name of the Active Element of the Active Class 71 | 72 | Original COM help: https://opendss.epri.com/Name.html 73 | ''' 74 | return self._get_string(self._check_for_error(self._lib.ActiveClass_Get_Name())) 75 | 76 | @Name.setter 77 | def Name(self, Value: AnyStr): 78 | if not isinstance(Value, bytes): 79 | Value = Value.encode(self._api_util.codec) 80 | 81 | self._check_for_error(self._lib.ActiveClass_Set_Name(Value)) 82 | 83 | @property 84 | def Next(self) -> int: 85 | ''' 86 | Sets next element in active class to be the active DSS object. 87 | If the object is a CktElement, ActiveCktElement also points to this element. 88 | 89 | Returns 0 if no more. 90 | 91 | Original COM help: https://opendss.epri.com/Next.html 92 | ''' 93 | return self._check_for_error(self._lib.ActiveClass_Get_Next()) 94 | 95 | @property 96 | def NumElements(self) -> int: 97 | ''' 98 | Number of elements in this class. Same as Count property. 99 | 100 | Original COM help: https://opendss.epri.com/NumElements.html 101 | ''' 102 | return self._check_for_error(self._lib.ActiveClass_Get_NumElements()) 103 | 104 | @property 105 | def ActiveClassParent(self) -> str: 106 | ''' 107 | Get the name of the parent class of the active class 108 | 109 | Original COM help: https://opendss.epri.com/ActiveClassParent.html 110 | ''' 111 | return self._get_string(self._check_for_error(self._lib.ActiveClass_Get_ActiveClassParent())) 112 | 113 | def ToJSON(self, options: DSSJSONFlags = 0) -> str: 114 | ''' 115 | Returns the data (as a list) of all elements from the active class as a JSON-encoded string. 116 | 117 | The `options` parameter contains bit-flags to toggle specific features. 118 | See `Obj_ToJSON` (C-API) for more, or `DSSObj.to_json` in Python. 119 | 120 | Additionally, the `ExcludeDisabled` flag can be used to excluded disabled elements from the output. 121 | 122 | **(API Extension)** 123 | ''' 124 | return self._get_string(self._check_for_error(self._lib.ActiveClass_ToJSON(options))) 125 | -------------------------------------------------------------------------------- /dss/ICNData.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import Union 6 | from .enums import LineUnits 7 | 8 | class ICNData(Iterable): 9 | ''' 10 | CNData objects 11 | 12 | (API Extension) 13 | ''' 14 | 15 | __slots__ = [] 16 | 17 | _columns = [ 18 | 'Name', 19 | 'idx', 20 | 'NormAmps', 21 | 'EmergAmps', 22 | 'Rdc', 23 | 'Rac', 24 | 'ResistanceUnits', 25 | 'GMRac', 26 | 'GMRUnits', 27 | 'Radius', 28 | 'Diameter', 29 | 'RadiusUnits', 30 | 'EpsR', 31 | 'InsLayer', 32 | 'DiaIns', 33 | 'DiaCable', 34 | 'DiaStrand', 35 | 'RStrand', 36 | 'k', 37 | ] 38 | 39 | @property 40 | def EmergAmps(self) -> float: 41 | '''Emergency ampere rating''' 42 | return self._check_for_error(self._lib.CNData_Get_EmergAmps()) 43 | 44 | @EmergAmps.setter 45 | def EmergAmps(self, Value: float): 46 | self._check_for_error(self._lib.CNData_Set_EmergAmps(Value)) 47 | 48 | @property 49 | def NormAmps(self) -> float: 50 | '''Normal Ampere rating''' 51 | return self._check_for_error(self._lib.CNData_Get_NormAmps()) 52 | 53 | @NormAmps.setter 54 | def NormAmps(self, Value: float): 55 | self._check_for_error(self._lib.CNData_Set_NormAmps(Value)) 56 | 57 | @property 58 | def Rdc(self) -> float: 59 | return self._check_for_error(self._lib.CNData_Get_Rdc()) 60 | 61 | @Rdc.setter 62 | def Rdc(self, Value: float): 63 | self._check_for_error(self._lib.CNData_Set_Rdc(Value)) 64 | 65 | @property 66 | def Rac(self) -> float: 67 | return self._check_for_error(self._lib.CNData_Get_Rac()) 68 | 69 | @Rac.setter 70 | def Rac(self, Value: float): 71 | self._check_for_error(self._lib.CNData_Set_Rac(Value)) 72 | 73 | @property 74 | def GMRac(self) -> float: 75 | return self._check_for_error(self._lib.CNData_Get_GMRac()) 76 | 77 | @GMRac.setter 78 | def GMRac(self, Value: float): 79 | self._check_for_error(self._lib.CNData_Set_GMRac(Value)) 80 | 81 | @property 82 | def GMRUnits(self) -> LineUnits: 83 | return LineUnits(self._check_for_error(self._lib.CNData_Get_GMRUnits())) 84 | 85 | @GMRUnits.setter 86 | def GMRUnits(self, Value: int): 87 | self._check_for_error(self._lib.CNData_Set_GMRUnits(Value)) 88 | 89 | @property 90 | def Radius(self) -> float: 91 | return self._check_for_error(self._lib.CNData_Get_Radius()) 92 | 93 | @Radius.setter 94 | def Radius(self, Value: float): 95 | self._check_for_error(self._lib.CNData_Set_Radius(Value)) 96 | 97 | @property 98 | def RadiusUnits(self) -> LineUnits: 99 | return LineUnits(self._check_for_error(self._lib.CNData_Get_RadiusUnits())) 100 | 101 | @RadiusUnits.setter 102 | def RadiusUnits(self, Value: Union[int, LineUnits]): 103 | self._check_for_error(self._lib.CNData_Set_RadiusUnits(Value)) 104 | 105 | @property 106 | def ResistanceUnits(self) -> LineUnits: 107 | return LineUnits(self._check_for_error(self._lib.CNData_Get_ResistanceUnits())) 108 | 109 | @ResistanceUnits.setter 110 | def ResistanceUnits(self, Value: Union[int, LineUnits]): 111 | self._check_for_error(self._lib.CNData_Set_ResistanceUnits(Value)) 112 | 113 | @property 114 | def Diameter(self) -> float: 115 | return self._check_for_error(self._lib.CNData_Get_Diameter()) 116 | 117 | @Diameter.setter 118 | def Diameter(self, Value: float): 119 | self._check_for_error(self._lib.CNData_Set_Diameter(Value)) 120 | 121 | @property 122 | def EpsR(self) -> float: 123 | return self._check_for_error(self._lib.CNData_Get_EpsR()) 124 | 125 | @EpsR.setter 126 | def EpsR(self, Value: float): 127 | self._check_for_error(self._lib.CNData_Set_EpsR(Value)) 128 | 129 | @property 130 | def InsLayer(self) -> float: 131 | return self._check_for_error(self._lib.CNData_Get_InsLayer()) 132 | 133 | @InsLayer.setter 134 | def InsLayer(self, Value: float): 135 | self._check_for_error(self._lib.CNData_Set_InsLayer(Value)) 136 | 137 | @property 138 | def DiaIns(self) -> float: 139 | return self._check_for_error(self._lib.CNData_Get_DiaIns()) 140 | 141 | @DiaIns.setter 142 | def DiaIns(self, Value: float): 143 | self._check_for_error(self._lib.CNData_Set_DiaIns(Value)) 144 | 145 | @property 146 | def DiaCable(self) -> float: 147 | return self._check_for_error(self._lib.CNData_Get_DiaCable()) 148 | 149 | @DiaCable.setter 150 | def DiaCable(self, Value: float): 151 | self._check_for_error(self._lib.CNData_Set_DiaCable(Value)) 152 | 153 | @property 154 | def k(self) -> int: 155 | return self._check_for_error(self._lib.CNData_Get_k()) 156 | 157 | @k.setter 158 | def k(self, Value: int): 159 | self._check_for_error(self._lib.CNData_Set_k(Value)) 160 | 161 | @property 162 | def DiaStrand(self) -> float: 163 | return self._check_for_error(self._lib.CNData_Get_DiaStrand()) 164 | 165 | @DiaStrand.setter 166 | def DiaStrand(self, Value: float): 167 | self._check_for_error(self._lib.CNData_Set_DiaStrand(Value)) 168 | 169 | @property 170 | def GmrStrand(self) -> float: 171 | return self._check_for_error(self._lib.CNData_Get_GmrStrand()) 172 | 173 | @GmrStrand.setter 174 | def GmrStrand(self, Value: float): 175 | self._check_for_error(self._lib.CNData_Set_GmrStrand(Value)) 176 | 177 | @property 178 | def RStrand(self) -> float: 179 | return self._check_for_error(self._lib.CNData_Get_RStrand()) 180 | 181 | @RStrand.setter 182 | def RStrand(self, Value: float): 183 | self._check_for_error(self._lib.CNData_Set_RStrand(Value)) 184 | 185 | 186 | -------------------------------------------------------------------------------- /dss/ICapControls.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import AnyStr, Union 6 | from .enums import CapControlModes 7 | 8 | class ICapControls(Iterable): 9 | __slots__ = [] 10 | 11 | _columns = [ 12 | 'Name', 13 | 'idx', 14 | 'Capacitor', 15 | 'CTratio', 16 | 'PTratio', 17 | 'DeadTime', 18 | 'Delay', 19 | 'DelayOff', 20 | 'Vmin', 21 | 'Vmax', 22 | 'UseVoltOverride', 23 | 'Mode', 24 | 'MonitoredObj', 25 | 'MonitoredTerm', 26 | 'OFFSetting', 27 | 'ONSetting', 28 | ] 29 | 30 | def Reset(self): 31 | ''' 32 | Force a reset of this CapControl. 33 | 34 | Original COM help: https://opendss.epri.com/Reset.html 35 | ''' 36 | self._check_for_error(self._lib.CapControls_Reset()) 37 | 38 | @property 39 | def CTratio(self) -> float: 40 | ''' 41 | Transducer ratio from primary current to control current. 42 | 43 | Original COM help: https://opendss.epri.com/CTratio.html 44 | ''' 45 | return self._check_for_error(self._lib.CapControls_Get_CTratio()) 46 | 47 | @CTratio.setter 48 | def CTratio(self, Value: float): 49 | self._check_for_error(self._lib.CapControls_Set_CTratio(Value)) 50 | 51 | @property 52 | def Capacitor(self) -> str: 53 | ''' 54 | Name of the Capacitor that is controlled. 55 | 56 | Original COM help: https://opendss.epri.com/Capacitor.html 57 | ''' 58 | return self._get_string(self._check_for_error(self._lib.CapControls_Get_Capacitor())) 59 | 60 | @Capacitor.setter 61 | def Capacitor(self, Value: AnyStr): 62 | if not isinstance(Value, bytes): 63 | Value = Value.encode(self._api_util.codec) 64 | 65 | self._check_for_error(self._lib.CapControls_Set_Capacitor(Value)) 66 | 67 | @property 68 | def DeadTime(self) -> float: 69 | ''' 70 | Dead time after capacitor is turned OFF before it can be turned back ON for the active CapControl. 71 | 72 | Default is 300 sec. 73 | 74 | Original COM help: https://opendss.epri.com/DeadTime.html 75 | ''' 76 | return self._check_for_error(self._lib.CapControls_Get_DeadTime()) 77 | 78 | @DeadTime.setter 79 | def DeadTime(self, Value: float): 80 | self._check_for_error(self._lib.CapControls_Set_DeadTime(Value)) 81 | 82 | @property 83 | def Delay(self) -> float: 84 | ''' 85 | Time delay [s] to switch on after arming. Control may reset before actually switching. 86 | 87 | Original COM help: https://opendss.epri.com/Delay.html 88 | ''' 89 | return self._check_for_error(self._lib.CapControls_Get_Delay()) 90 | 91 | @Delay.setter 92 | def Delay(self, Value: float): 93 | self._check_for_error(self._lib.CapControls_Set_Delay(Value)) 94 | 95 | @property 96 | def DelayOff(self) -> float: 97 | ''' 98 | Time delay [s] before switching off a step. Control may reset before actually switching. 99 | 100 | Original COM help: https://opendss.epri.com/DelayOff.html 101 | ''' 102 | return self._check_for_error(self._lib.CapControls_Get_DelayOff()) 103 | 104 | @DelayOff.setter 105 | def DelayOff(self, Value: float): 106 | self._check_for_error(self._lib.CapControls_Set_DelayOff(Value)) 107 | 108 | @property 109 | def Mode(self) -> CapControlModes: 110 | ''' 111 | Type of automatic controller. 112 | 113 | Original COM help: https://opendss.epri.com/Mode.html 114 | ''' 115 | return CapControlModes(self._check_for_error(self._lib.CapControls_Get_Mode())) 116 | 117 | @Mode.setter 118 | def Mode(self, Value: Union[CapControlModes, int]): 119 | self._check_for_error(self._lib.CapControls_Set_Mode(Value)) 120 | 121 | @property 122 | def MonitoredObj(self) -> int: 123 | ''' 124 | Full name of the element that PT and CT are connected to. 125 | 126 | Original COM help: https://opendss.epri.com/MonitoredObj.html 127 | ''' 128 | return self._get_string(self._check_for_error(self._lib.CapControls_Get_MonitoredObj())) 129 | 130 | @MonitoredObj.setter 131 | def MonitoredObj(self, Value: AnyStr): 132 | if not isinstance(Value, bytes): 133 | Value = Value.encode(self._api_util.codec) 134 | 135 | self._check_for_error(self._lib.CapControls_Set_MonitoredObj(Value)) 136 | 137 | @property 138 | def MonitoredTerm(self) -> int: 139 | ''' 140 | Terminal number on the element that PT and CT are connected to. 141 | 142 | Original COM help: https://opendss.epri.com/MonitoredTerm.html 143 | ''' 144 | return self._check_for_error(self._lib.CapControls_Get_MonitoredTerm()) 145 | 146 | @MonitoredTerm.setter 147 | def MonitoredTerm(self, Value: int): 148 | self._check_for_error(self._lib.CapControls_Set_MonitoredTerm(Value)) 149 | 150 | @property 151 | def OFFSetting(self) -> float: 152 | ''' 153 | Threshold to switch off a step. See Mode for units. 154 | 155 | Original COM help: https://opendss.epri.com/OFFSetting.html 156 | ''' 157 | return self._check_for_error(self._lib.CapControls_Get_OFFSetting()) 158 | 159 | @OFFSetting.setter 160 | def OFFSetting(self, Value: float): 161 | self._check_for_error(self._lib.CapControls_Set_OFFSetting(Value)) 162 | 163 | @property 164 | def ONSetting(self) -> float: 165 | ''' 166 | Threshold to arm or switch on a step. See Mode for units. 167 | 168 | Original COM help: https://opendss.epri.com/ONSetting.html 169 | ''' 170 | return self._check_for_error(self._lib.CapControls_Get_ONSetting()) 171 | 172 | @ONSetting.setter 173 | def ONSetting(self, Value: float): 174 | self._check_for_error(self._lib.CapControls_Set_ONSetting(Value)) 175 | 176 | @property 177 | def PTratio(self) -> float: 178 | ''' 179 | Transducer ratio from primary feeder to control voltage. 180 | 181 | Original COM help: https://opendss.epri.com/PTratio.html 182 | ''' 183 | return self._check_for_error(self._lib.CapControls_Get_PTratio()) 184 | 185 | @PTratio.setter 186 | def PTratio(self, Value: float): 187 | self._check_for_error(self._lib.CapControls_Set_PTratio(Value)) 188 | 189 | @property 190 | def UseVoltOverride(self) -> float: 191 | ''' 192 | Enables Vmin and Vmax to override the control Mode 193 | 194 | Original COM help: https://opendss.epri.com/UseVoltOverride.html 195 | ''' 196 | return self._check_for_error(self._lib.CapControls_Get_UseVoltOverride()) != 0 197 | 198 | @UseVoltOverride.setter 199 | def UseVoltOverride(self, Value: float): 200 | self._check_for_error(self._lib.CapControls_Set_UseVoltOverride(Value)) 201 | 202 | @property 203 | def Vmax(self) -> float: 204 | ''' 205 | With VoltOverride, swtich off whenever PT voltage exceeds this level. 206 | 207 | Original COM help: https://opendss.epri.com/Vmax.html 208 | ''' 209 | return self._check_for_error(self._lib.CapControls_Get_Vmax()) 210 | 211 | @Vmax.setter 212 | def Vmax(self, Value: float): 213 | self._check_for_error(self._lib.CapControls_Set_Vmax(Value)) 214 | 215 | @property 216 | def Vmin(self) -> float: 217 | ''' 218 | With VoltOverride, switch ON whenever PT voltage drops below this level. 219 | 220 | Original COM help: https://opendss.epri.com/Vmin.html 221 | ''' 222 | return self._check_for_error(self._lib.CapControls_Get_Vmin()) 223 | 224 | @Vmin.setter 225 | def Vmin(self, Value: float): 226 | self._check_for_error(self._lib.CapControls_Set_Vmin(Value)) 227 | -------------------------------------------------------------------------------- /dss/ICapacitors.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Int32Array 6 | 7 | class ICapacitors(Iterable): 8 | __slots__ = [] 9 | _is_circuit_element = True 10 | 11 | _columns = [ 12 | 'Name', 13 | 'idx', 14 | 'kV', 15 | 'NumSteps', 16 | 'AvailableSteps', 17 | 'IsDelta', 18 | 'States', 19 | 'kvar', 20 | ] 21 | 22 | def AddStep(self) -> bool: 23 | return self._check_for_error(self._lib.Capacitors_AddStep()) != 0 24 | 25 | def Close(self): 26 | self._check_for_error(self._lib.Capacitors_Close()) 27 | 28 | def Open(self): 29 | self._check_for_error(self._lib.Capacitors_Open()) 30 | 31 | def SubtractStep(self) -> bool: 32 | return self._check_for_error(self._lib.Capacitors_SubtractStep()) != 0 33 | 34 | @property 35 | def AvailableSteps(self) -> int: 36 | ''' 37 | Number of Steps available in cap bank to be switched ON. 38 | 39 | Original COM help: https://opendss.epri.com/AvailableSteps.html 40 | ''' 41 | return self._check_for_error(self._lib.Capacitors_Get_AvailableSteps()) 42 | 43 | @property 44 | def IsDelta(self) -> bool: 45 | ''' 46 | Delta connection or wye? 47 | 48 | Original COM help: https://opendss.epri.com/IsDelta.html 49 | ''' 50 | return self._check_for_error(self._lib.Capacitors_Get_IsDelta()) != 0 51 | 52 | @IsDelta.setter 53 | def IsDelta(self, Value: bool): 54 | self._check_for_error(self._lib.Capacitors_Set_IsDelta(Value)) 55 | 56 | @property 57 | def NumSteps(self) -> int: 58 | ''' 59 | Number of steps (default 1) for distributing and switching the total bank kVAR. 60 | 61 | Original COM help: https://opendss.epri.com/NumSteps.html 62 | ''' 63 | return self._check_for_error(self._lib.Capacitors_Get_NumSteps()) 64 | 65 | @NumSteps.setter 66 | def NumSteps(self, Value: int): 67 | self._check_for_error(self._lib.Capacitors_Set_NumSteps(Value)) 68 | 69 | @property 70 | def States(self) -> Int32Array: 71 | ''' 72 | An array of integers [0..NumSteps-1] indicating state of each step. If the read value is -1 an error has occurred. 73 | 74 | Original COM help: https://opendss.epri.com/States.html 75 | ''' 76 | self._check_for_error(self._lib.Capacitors_Get_States_GR()) 77 | return self._get_int32_gr_array() 78 | 79 | @States.setter 80 | def States(self, Value: Int32Array): 81 | Value, ValuePtr, ValueCount = self._prepare_int32_array(Value) 82 | self._check_for_error(self._lib.Capacitors_Set_States(ValuePtr, ValueCount)) 83 | 84 | @property 85 | def kV(self) -> float: 86 | ''' 87 | Bank kV rating. Use LL for 2 or 3 phases, or actual can rating for 1 phase. 88 | 89 | Original COM help: https://opendss.epri.com/kV.html 90 | ''' 91 | return self._check_for_error(self._lib.Capacitors_Get_kV()) 92 | 93 | @kV.setter 94 | def kV(self, Value): 95 | self._check_for_error(self._lib.Capacitors_Set_kV(Value)) 96 | 97 | @property 98 | def kvar(self) -> float: 99 | '''Total bank KVAR, distributed equally among phases and steps.''' 100 | return self._check_for_error(self._lib.Capacitors_Get_kvar()) 101 | 102 | @kvar.setter 103 | def kvar(self, Value: float): 104 | self._check_for_error(self._lib.Capacitors_Set_kvar(Value)) 105 | -------------------------------------------------------------------------------- /dss/ICtrlQueue.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from typing import List 6 | 7 | class ICtrlQueue(Base): 8 | __slots__ = [] 9 | 10 | _columns = [ 11 | 'Queue', 12 | 'DeviceHandle', 13 | 'QueueSize', 14 | 'ActionCode', 15 | 'NumActions', 16 | ] 17 | 18 | def ClearActions(self): 19 | ''' 20 | Clear all actions from the Control Proxy's Action List (they are popped off the list). 21 | 22 | Original COM help: https://opendss.epri.com/ClearActions.html 23 | ''' 24 | self._check_for_error(self._lib.CtrlQueue_ClearActions()) 25 | 26 | def ClearQueue(self): 27 | ''' 28 | Clear the control queue. 29 | 30 | Original COM help: https://opendss.epri.com/ClearQueue.html 31 | ''' 32 | self._check_for_error(self._lib.CtrlQueue_ClearQueue()) 33 | 34 | def Delete(self, ActionHandle): 35 | ''' 36 | Delete an Action from the DSS Control Queue by the handle that is returned when the action is added. 37 | 38 | (The Push function returns the handle.) 39 | 40 | Original COM help: https://opendss.epri.com/Delete.html 41 | ''' 42 | self._check_for_error(self._lib.CtrlQueue_Delete(ActionHandle)) 43 | 44 | def DoAllQueue(self): 45 | ''' 46 | Execute all actions currently on the Control Queue. 47 | 48 | Side effect: clears the queue. 49 | 50 | Original COM help: https://opendss.epri.com/DoAllQueue.html 51 | ''' 52 | self._check_for_error(self._lib.CtrlQueue_DoAllQueue()) 53 | 54 | def Show(self): 55 | ''' 56 | Export the queue to a CSV table and show it. 57 | 58 | Original COM help: https://opendss.epri.com/Show.html 59 | ''' 60 | self._check_for_error(self._lib.CtrlQueue_Show()) 61 | 62 | @property 63 | def ActionCode(self) -> int: 64 | ''' 65 | Code for the active action. Integer code to tell the control device what to do. 66 | 67 | Use this to determine what the user-defined controls are supposed to do. 68 | It can be any 32-bit integer of the user's choosing and is the same value that the control pushed onto the control queue earlier. 69 | 70 | Original COM help: https://opendss.epri.com/ActionCode.html 71 | ''' 72 | return self._check_for_error(self._lib.CtrlQueue_Get_ActionCode()) 73 | 74 | @property 75 | def DeviceHandle(self) -> int: 76 | ''' 77 | Handle (User defined) to device that must act on the pending action. 78 | 79 | The user-written code driving the interface may support more than one 80 | control element as necessary to perform the simulation. This handle is 81 | an index returned to the user program that lets the program know which 82 | control is to perform the active action. 83 | 84 | Original COM help: https://opendss.epri.com/DeviceHandle.html 85 | ''' 86 | return self._check_for_error(self._lib.CtrlQueue_Get_DeviceHandle()) 87 | 88 | @property 89 | def NumActions(self) -> int: 90 | ''' 91 | Number of Actions on the current action list (that have been popped off the control queue by CheckControlActions) 92 | 93 | Original COM help: https://opendss.epri.com/NumActions.html 94 | ''' 95 | return self._check_for_error(self._lib.CtrlQueue_Get_NumActions()) 96 | 97 | def Push(self, Hour: int, Seconds: float, ActionCode: int, DeviceHandle: int): 98 | ''' 99 | Push a control action onto the DSS control queue by time, action code, and device handle (user defined). Returns Control Queue handle. 100 | 101 | Original COM help: https://opendss.epri.com/Push.html 102 | ''' 103 | return self._check_for_error(self._lib.CtrlQueue_Push(Hour, Seconds, ActionCode, DeviceHandle)) 104 | 105 | @property 106 | def PopAction(self) -> int: 107 | ''' 108 | Pops next action off the action list and makes it the active action. Returns zero if none. 109 | 110 | Original COM help: https://opendss.epri.com/PopAction.html 111 | ''' 112 | return self._check_for_error(self._lib.CtrlQueue_Get_PopAction()) 113 | 114 | @property 115 | def Queue(self) -> List[str]: 116 | ''' 117 | Array of strings containing the entire queue in CSV format 118 | 119 | Original COM help: https://opendss.epri.com/Queue.html 120 | ''' 121 | return self._check_for_error(self._get_string_array(self._lib.CtrlQueue_Get_Queue)) 122 | 123 | @property 124 | def QueueSize(self) -> int: 125 | ''' 126 | Number of items on the OpenDSS control Queue 127 | 128 | Original COM help: https://opendss.epri.com/QueueSize.html 129 | ''' 130 | return self._check_for_error(self._lib.CtrlQueue_Get_QueueSize()) 131 | 132 | @property 133 | def Action(self) -> int: 134 | ''' 135 | (write-only) Set the active action by index 136 | 137 | Original COM help: https://opendss.epri.com/Action.html 138 | ''' 139 | raise AttributeError("This property is write-only!") 140 | 141 | @Action.setter 142 | def Action(self, Param1: int): 143 | self._check_for_error(self._lib.CtrlQueue_Set_Action(Param1)) 144 | 145 | -------------------------------------------------------------------------------- /dss/IDSSElement.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from .IDSSProperty import IDSSProperty 6 | from typing import List 7 | from .enums import DSSJSONFlags 8 | 9 | class IDSSElement(Base): 10 | __slots__ = [ 11 | 'Properties' 12 | ] 13 | 14 | _columns = [ 15 | 'AllPropertyNames', 16 | 'Name', 17 | 'NumProperties', 18 | ] 19 | 20 | def __init__(self, api_util): 21 | self.Properties = IDSSProperty(api_util) 22 | Base.__init__(self, api_util) 23 | 24 | @property 25 | def AllPropertyNames(self) -> List[str]: 26 | ''' 27 | Array of strings containing the names of all properties for the active DSS object. 28 | 29 | Original COM help: https://opendss.epri.com/AllPropertyNames1.html 30 | ''' 31 | return self._check_for_error(self._get_string_array(self._lib.DSSElement_Get_AllPropertyNames)) 32 | 33 | @property 34 | def Name(self) -> str: 35 | ''' 36 | Full Name of Active DSS Object (general element or circuit element). 37 | 38 | Original COM help: https://opendss.epri.com/Name5.html 39 | ''' 40 | return self._get_string(self._check_for_error(self._lib.DSSElement_Get_Name())) 41 | 42 | @property 43 | def NumProperties(self) -> int: 44 | ''' 45 | Number of Properties for the active DSS object. 46 | 47 | Original COM help: https://opendss.epri.com/NumProperties1.html 48 | ''' 49 | return self._check_for_error(self._lib.DSSElement_Get_NumProperties()) 50 | 51 | def ToJSON(self, options: DSSJSONFlags = 0) -> str: 52 | ''' 53 | Returns the properties of the active DSS object as a JSON-encoded string. 54 | 55 | The `options` parameter contains bit-flags to toggle specific features. 56 | See `Obj_ToJSON` (C-API) for more, or `DSSObj.to_json` in Python. 57 | 58 | **(API Extension)** 59 | ''' 60 | return self._get_string(self._check_for_error(self._lib.DSSElement_ToJSON(options))) 61 | -------------------------------------------------------------------------------- /dss/IDSSProgress.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from typing import AnyStr 6 | 7 | class IDSSProgress(Base): 8 | __slots__ = [] 9 | 10 | def Close(self): 11 | self._check_for_error(self._lib.DSSProgress_Close()) 12 | 13 | def Show(self): 14 | self._check_for_error(self._lib.DSSProgress_Show()) 15 | 16 | @property 17 | def Caption(self) -> str: 18 | ''' 19 | (write-only) Caption to appear on the bottom of the DSS Progress form. 20 | 21 | Original COM help: https://opendss.epri.com/Caption.html 22 | ''' 23 | raise AttributeError("This property is write-only!") 24 | 25 | @Caption.setter 26 | def Caption(self, Value: AnyStr): 27 | if not isinstance(Value, bytes): 28 | Value = Value.encode(self._api_util.codec) 29 | 30 | self._check_for_error(self._lib.DSSProgress_Set_Caption(Value)) 31 | 32 | @property 33 | def PctProgress(self) -> int: 34 | ''' 35 | (write-only) Percent progress to indicate [0..100] 36 | 37 | Original COM help: https://opendss.epri.com/PctProgress.html 38 | ''' 39 | raise AttributeError("This property is write-only!") 40 | 41 | @PctProgress.setter 42 | def PctProgress(self, Value: int): 43 | self._check_for_error(self._lib.DSSProgress_Set_PctProgress(Value)) 44 | 45 | 46 | -------------------------------------------------------------------------------- /dss/IDSSProperty.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from __future__ import annotations 5 | from ._cffi_api_util import Base 6 | from typing import AnyStr, Union 7 | 8 | class IDSSProperty(Base): 9 | __slots__ = [] 10 | 11 | _columns = [ 12 | 'Description', 13 | 'Name', 14 | 'Val', 15 | ] 16 | 17 | @property 18 | def Description(self) -> str: 19 | ''' 20 | Description of the property. 21 | 22 | Original COM help: https://opendss.epri.com/Description.html 23 | ''' 24 | return self._get_string(self._check_for_error(self._lib.DSSProperty_Get_Description())) 25 | 26 | @property 27 | def Name(self) -> str: 28 | ''' 29 | Name of Property 30 | 31 | Original COM help: https://opendss.epri.com/Name6.html 32 | ''' 33 | return self._get_string(self._check_for_error(self._lib.DSSProperty_Get_Name())) 34 | 35 | @property 36 | def Val(self) -> str: 37 | ''' 38 | Get/set the value of the active property. The value must be specified as a string. 39 | 40 | Original COM help: https://opendss.epri.com/Val.html 41 | ''' 42 | return self._get_string(self._check_for_error(self._lib.DSSProperty_Get_Val())) 43 | 44 | @Val.setter 45 | def Val(self, Value: AnyStr): 46 | if not isinstance(Value, bytes): 47 | Value = str(Value).encode(self._api_util.codec) 48 | 49 | self._check_for_error(self._lib.DSSProperty_Set_Val(Value)) 50 | 51 | def __getitem__(self, propname_index: Union[AnyStr, int]) -> IDSSProperty: 52 | if isinstance(propname_index, int): 53 | self._check_for_error(self._lib.DSSProperty_Set_Index(propname_index)) 54 | else: 55 | if not isinstance(propname_index, bytes): 56 | propname_index = propname_index.encode(self._api_util.codec) 57 | 58 | self._check_for_error(self._lib.DSSProperty_Set_Name(propname_index)) 59 | 60 | return self 61 | 62 | def __call__(self, propname_index) -> IDSSProperty: 63 | return self.__getitem__(propname_index) 64 | 65 | -------------------------------------------------------------------------------- /dss/IDSS_Executive.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | 6 | class IDSS_Executive(Base): 7 | __slots__ = [] 8 | 9 | _columns = [ 10 | 'NumCommands', 11 | 'NumOptions', 12 | ] 13 | 14 | def Command(self, i: int) -> str: 15 | ''' 16 | Get i-th command 17 | 18 | Original COM help: https://opendss.epri.com/Command.html 19 | ''' 20 | return self._get_string(self._check_for_error(self._lib.DSS_Executive_Get_Command(i))) 21 | 22 | def CommandHelp(self, i: int) -> str: 23 | ''' 24 | Get help string for i-th command 25 | 26 | Original COM help: https://opendss.epri.com/CommandHelp.html 27 | ''' 28 | return self._get_string(self._check_for_error(self._lib.DSS_Executive_Get_CommandHelp(i))) 29 | 30 | def Option(self, i: int) -> str: 31 | ''' 32 | Get i-th option 33 | 34 | Original COM help: https://opendss.epri.com/Option.html 35 | ''' 36 | return self._get_string(self._check_for_error(self._lib.DSS_Executive_Get_Option(i))) 37 | 38 | def OptionHelp(self, i: int) -> str: 39 | ''' 40 | Get help string for i-th option 41 | 42 | Original COM help: https://opendss.epri.com/OptionHelp.html 43 | ''' 44 | return self._get_string(self._check_for_error(self._lib.DSS_Executive_Get_OptionHelp(i))) 45 | 46 | def OptionValue(self, i: int) -> str: 47 | ''' 48 | Get present value of i-th option 49 | 50 | Original COM help: https://opendss.epri.com/OptionValue.html 51 | ''' 52 | return self._get_string(self._check_for_error(self._lib.DSS_Executive_Get_OptionValue(i))) 53 | 54 | @property 55 | def NumCommands(self) -> int: 56 | ''' 57 | Number of DSS Executive Commands 58 | 59 | Original COM help: https://opendss.epri.com/NumCommands.html 60 | ''' 61 | return self._check_for_error(self._lib.DSS_Executive_Get_NumCommands()) 62 | 63 | @property 64 | def NumOptions(self) -> int: 65 | ''' 66 | Number of DSS Executive Options 67 | 68 | Original COM help: https://opendss.epri.com/NumOptions.html 69 | ''' 70 | return self._check_for_error(self._lib.DSS_Executive_Get_NumOptions()) 71 | 72 | -------------------------------------------------------------------------------- /dss/IDSSimComs.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from ._types import Float64Array 6 | 7 | class IDSSimComs(Base): 8 | __slots__ = [] 9 | 10 | def BusVoltage(self, Index: int) -> Float64Array: 11 | self._check_for_error(self._lib.DSSimComs_BusVoltage_GR(Index)) 12 | return self._get_float64_gr_array() 13 | 14 | def BusVoltagepu(self, Index: int) -> Float64Array: 15 | self._check_for_error(self._lib.DSSimComs_BusVoltagepu_GR(Index)) 16 | return self._get_float64_gr_array() 17 | 18 | 19 | -------------------------------------------------------------------------------- /dss/IError.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | 6 | class IError(Base): 7 | __slots__ = [] 8 | 9 | _columns = [ 10 | 'Description', 11 | 'Number', 12 | 'EarlyAbort', 13 | ] 14 | 15 | @property 16 | def Description(self) -> str: 17 | ''' 18 | Description of error for last operation 19 | 20 | Original COM help: https://opendss.epri.com/Description1.html 21 | ''' 22 | return self._get_string(self._lib.Error_Get_Description()) 23 | 24 | @property 25 | def Number(self) -> int: 26 | ''' 27 | Error Number (returns current value and then resets to zero) 28 | 29 | Original COM help: https://opendss.epri.com/Number.html 30 | ''' 31 | return self._lib.Error_Get_Number() 32 | 33 | @property 34 | def EarlyAbort(self) -> bool: 35 | ''' 36 | EarlyAbort controls whether all errors halts the DSS script processing (Compile/Redirect), defaults to True. 37 | 38 | **(API Extension)** 39 | ''' 40 | return self._lib.Error_Get_EarlyAbort() != 0 41 | 42 | @EarlyAbort.setter 43 | def EarlyAbort(self, Value: bool): 44 | self._lib.Error_Set_EarlyAbort(Value) 45 | 46 | @property 47 | def ExtendedErrors(self) -> bool: 48 | ''' 49 | Controls whether the extended error mechanism is used. Defaults to True. 50 | 51 | Extended errors are errors derived from checks across the API to ensure 52 | a valid state. Although many of these checks are already present in the 53 | original/official COM interface, the checks do not produce any error 54 | message. An error value can be returned by a function but this value 55 | can, for many of the functions, be a valid value. As such, the user 56 | has no means to detect an invalid API call. 57 | 58 | Extended errors use the Error interface to provide a more clear message 59 | and should help users, especially new users, to find usage issues earlier. 60 | 61 | At Python level, an exception is raised when an error is detected through 62 | the Error interface. 63 | 64 | The current default state is ON. For compatibility, the user can turn it 65 | off to restore the previous behavior. 66 | 67 | **(API Extension)** 68 | ''' 69 | return self._lib.Error_Get_ExtendedErrors() != 0 70 | 71 | @ExtendedErrors.setter 72 | def ExtendedErrors(self, Value: bool): 73 | self._lib.Error_Set_ExtendedErrors(Value) 74 | 75 | @property 76 | def UseExceptions(self) -> bool: 77 | """ 78 | Controls whether the automatic error checking mechanism is enable, i.e., if 79 | the DSS engine errors (from the `Error` interface) are mapped exception when 80 | detected. 81 | 82 | **When disabled, the user takes responsibility for checking for errors.** 83 | This can be done through the `Error` interface. When `Error.Number` is not 84 | zero, there should be an error message in `Error.Description`. This is compatible 85 | with the behavior on the official OpenDSS (Windows-only COM implementation) when 86 | `AllowForms` is disabled. 87 | 88 | Users can also use the DSS command `Export ErrorLog` to inspect for errors. 89 | 90 | **WARNING:** This is a global setting, affects all DSS instances from DSS-Python, 91 | OpenDSSDirect.py and AltDSS. 92 | 93 | **(API Extension)** 94 | """ 95 | return Base._use_exceptions 96 | 97 | @UseExceptions.setter 98 | def UseExceptions(self, value: bool): 99 | Base._enable_exceptions(value) 100 | -------------------------------------------------------------------------------- /dss/IFuses.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import List, AnyStr 6 | 7 | class IFuses(Iterable): 8 | __slots__ = [] 9 | _is_circuit_element = True 10 | 11 | _columns = [ 12 | 'Name', 13 | 'idx', 14 | 'NumPhases', 15 | 'MonitoredObj', 16 | 'MonitoredTerm', 17 | 'Delay', 18 | 'IsBlown', 19 | 'TCCcurve', 20 | 'RatedCurrent', 21 | 'SwitchedObj', 22 | 'SwitchedTerm', 23 | 'State', 24 | 'NormalState', 25 | ] 26 | 27 | 28 | def Close(self): 29 | ''' 30 | Close all phases of the fuse. 31 | 32 | Original COM help: https://opendss.epri.com/Close3.html 33 | ''' 34 | self._check_for_error(self._lib.Fuses_Close()) 35 | 36 | def IsBlown(self) -> bool: 37 | ''' 38 | Current state of the fuses. TRUE if any fuse on any phase is blown. Else FALSE. 39 | 40 | Original COM help: https://opendss.epri.com/IsBlown.html 41 | ''' 42 | return self._check_for_error(self._lib.Fuses_IsBlown()) != 0 43 | 44 | def Open(self): 45 | ''' 46 | Manual opening of all phases of the fuse. 47 | 48 | Original COM help: https://opendss.epri.com/Open2.html 49 | ''' 50 | self._check_for_error(self._lib.Fuses_Open()) 51 | 52 | def Reset(self): 53 | ''' 54 | Reset fuse to normal state. 55 | 56 | Original COM help: https://opendss.epri.com/Reset7.html 57 | ''' 58 | self._check_for_error(self._lib.Fuses_Reset()) 59 | 60 | @property 61 | def Delay(self) -> float: 62 | ''' 63 | A fixed delay time in seconds added to the fuse blowing time determined by the TCC curve. Default is 0. 64 | This represents a fuse clear or other delay. 65 | 66 | Original COM help: https://opendss.epri.com/Delay1.html 67 | ''' 68 | return self._check_for_error(self._lib.Fuses_Get_Delay()) 69 | 70 | @Delay.setter 71 | def Delay(self, Value: float): 72 | self._check_for_error(self._lib.Fuses_Set_Delay(Value)) 73 | 74 | @property 75 | def MonitoredObj(self) -> str: 76 | ''' 77 | Full name of the circuit element to which the fuse is connected. 78 | 79 | Original COM help: https://opendss.epri.com/MonitoredObj1.html 80 | ''' 81 | return self._get_string(self._check_for_error(self._lib.Fuses_Get_MonitoredObj())) 82 | 83 | @MonitoredObj.setter 84 | def MonitoredObj(self, Value: AnyStr): 85 | if not isinstance(Value, bytes): 86 | Value = Value.encode(self._api_util.codec) 87 | 88 | self._check_for_error(self._lib.Fuses_Set_MonitoredObj(Value)) 89 | 90 | @property 91 | def MonitoredTerm(self) -> int: 92 | ''' 93 | Terminal number to which the fuse is connected. 94 | 95 | Original COM help: https://opendss.epri.com/MonitoredTerm1.html 96 | ''' 97 | return self._check_for_error(self._lib.Fuses_Get_MonitoredTerm()) 98 | 99 | @MonitoredTerm.setter 100 | def MonitoredTerm(self, Value: int): 101 | self._check_for_error(self._lib.Fuses_Set_MonitoredTerm(Value)) 102 | 103 | @property 104 | def NumPhases(self) -> int: 105 | ''' 106 | Number of phases, this fuse. 107 | 108 | Original COM help: https://opendss.epri.com/NumPhases1.html 109 | ''' 110 | return self._check_for_error(self._lib.Fuses_Get_NumPhases()) 111 | 112 | @property 113 | def RatedCurrent(self) -> float: 114 | ''' 115 | Multiplier or actual amps for the TCCcurve object. Defaults to 1.0. 116 | 117 | Multiply current values of TCC curve by this to get actual amps. 118 | 119 | Original COM help: https://opendss.epri.com/RatedCurrent.html 120 | ''' 121 | return self._check_for_error(self._lib.Fuses_Get_RatedCurrent()) 122 | 123 | @RatedCurrent.setter 124 | def RatedCurrent(self, Value: float): 125 | self._check_for_error(self._lib.Fuses_Set_RatedCurrent(Value)) 126 | 127 | @property 128 | def SwitchedObj(self) -> str: 129 | ''' 130 | Full name of the circuit element switch that the fuse controls. 131 | Defaults to the MonitoredObj. 132 | 133 | Original COM help: https://opendss.epri.com/SwitchedObj.html 134 | ''' 135 | return self._get_string(self._check_for_error(self._lib.Fuses_Get_SwitchedObj())) 136 | 137 | @SwitchedObj.setter 138 | def SwitchedObj(self, Value: AnyStr): 139 | if not isinstance(Value, bytes): 140 | Value = Value.encode(self._api_util.codec) 141 | 142 | self._check_for_error(self._lib.Fuses_Set_SwitchedObj(Value)) 143 | 144 | @property 145 | def SwitchedTerm(self) -> int: 146 | ''' 147 | Number of the terminal of the controlled element containing the switch controlled by the fuse. 148 | 149 | Original COM help: https://opendss.epri.com/SwitchedTerm.html 150 | ''' 151 | return self._check_for_error(self._lib.Fuses_Get_SwitchedTerm()) 152 | 153 | @SwitchedTerm.setter 154 | def SwitchedTerm(self, Value: int): 155 | self._check_for_error(self._lib.Fuses_Set_SwitchedTerm(Value)) 156 | 157 | @property 158 | def TCCcurve(self) -> str: 159 | ''' 160 | Name of the TCCcurve object that determines fuse blowing. 161 | 162 | Original COM help: https://opendss.epri.com/TCCcurve.html 163 | ''' 164 | return self._get_string(self._check_for_error(self._lib.Fuses_Get_TCCcurve())) 165 | 166 | @TCCcurve.setter 167 | def TCCcurve(self, Value: AnyStr): 168 | if not isinstance(Value, bytes): 169 | Value = Value.encode(self._api_util.codec) 170 | 171 | self._check_for_error(self._lib.Fuses_Set_TCCcurve(Value)) 172 | 173 | @property 174 | def State(self) -> List[str]: 175 | ''' 176 | Array of strings indicating the state of each phase of the fuse. 177 | 178 | Original COM help: https://opendss.epri.com/State2.html 179 | ''' 180 | return self._check_for_error(self._get_string_array(self._lib.Fuses_Get_State)) 181 | 182 | @State.setter 183 | def State(self, Value: List[AnyStr]): 184 | self._check_for_error(self._set_string_array(self._lib.Fuses_Set_State, Value)) 185 | 186 | @property 187 | def NormalState(self) -> List[str]: 188 | ''' 189 | Array of strings indicating the normal state of each phase of the fuse. 190 | 191 | Original COM help: https://opendss.epri.com/NormalState2.html 192 | ''' 193 | return self._check_for_error(self._get_string_array(self._lib.Fuses_Get_NormalState)) 194 | 195 | @NormalState.setter 196 | def NormalState(self, Value: List[AnyStr]): 197 | self._check_for_error(self._set_string_array(self._lib.Fuses_Set_NormalState, Value)) 198 | -------------------------------------------------------------------------------- /dss/IGICSources.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2023-2024 Paulo Meira 3 | # Copyright (c) 2023-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | 6 | class IGICSources(Iterable): 7 | __slots__ = [] 8 | _is_circuit_element = True 9 | 10 | _columns = [ 11 | 'Name', 12 | 'idx', 13 | 'Phases', 14 | 'Bus1', 15 | 'Bus2', 16 | 'EN', 17 | 'EE', 18 | 'Lat1', 19 | 'Lat2', 20 | 'Lon1', 21 | 'Lon2', 22 | 'Volts', 23 | ] 24 | 25 | @property 26 | def Bus1(self) -> str: 27 | '''First bus name of GICSource (Created name)''' 28 | return self._get_string(self._check_for_error(self._lib.GICSources_Get_Bus1())) 29 | 30 | @property 31 | def Bus2(self) -> str: 32 | '''Second bus name''' 33 | return self._get_string(self._check_for_error(self._lib.GICSources_Get_Bus2())) 34 | 35 | @property 36 | def Phases(self) -> int: 37 | '''Number of Phases, this GICSource element.''' 38 | return self._check_for_error(self._lib.GICSources_Get_Phases()) 39 | 40 | @Phases.setter 41 | def Phases(self, Value: int): 42 | self._check_for_error(self._lib.GICSources_Set_Phases(Value)) 43 | 44 | @property 45 | def EN(self) -> float: 46 | '''Northward E Field V/km''' 47 | return self._check_for_error(self._lib.GICSources_Get_EN()) 48 | 49 | @EN.setter 50 | def EN(self, Value: float): 51 | self._check_for_error(self._lib.GICSources_Set_EN(Value)) 52 | 53 | @property 54 | def EE(self) -> float: 55 | '''Eastward E Field, V/km''' 56 | return self._check_for_error(self._lib.GICSources_Get_EE()) 57 | 58 | @EE.setter 59 | def EE(self, Value: float): 60 | self._check_for_error(self._lib.GICSources_Set_EE(Value)) 61 | 62 | @property 63 | def Lat1(self) -> float: 64 | '''Latitude of Bus1 (degrees)''' 65 | return self._check_for_error(self._lib.GICSources_Get_Lat1()) 66 | 67 | @Lat1.setter 68 | def Lat1(self, Value: float): 69 | self._check_for_error(self._lib.GICSources_Set_Lat1(Value)) 70 | 71 | @property 72 | def Lat2(self) -> float: 73 | '''Latitude of Bus2 (degrees)''' 74 | return self._check_for_error(self._lib.GICSources_Get_Lat2()) 75 | 76 | @Lat2.setter 77 | def Lat2(self, Value: float): 78 | self._check_for_error(self._lib.GICSources_Set_Lat2(Value)) 79 | 80 | @property 81 | def Lon1(self) -> float: 82 | '''Longitude of Bus1 (Degrees)''' 83 | return self._check_for_error(self._lib.GICSources_Get_Lon1()) 84 | 85 | @Lon1.setter 86 | def Lon1(self, Value: float): 87 | self._check_for_error(self._lib.GICSources_Set_Lon1(Value)) 88 | 89 | @property 90 | def Lon2(self) -> float: 91 | '''Longitude of Bus2 (Degrees)''' 92 | return self._check_for_error(self._lib.GICSources_Get_Lon2()) 93 | 94 | @Lon2.setter 95 | def Lon2(self, Value: float): 96 | self._check_for_error(self._lib.GICSources_Set_Lon2(Value)) 97 | 98 | @property 99 | def Volts(self) -> float: 100 | '''Specify dc voltage directly''' 101 | return self._check_for_error(self._lib.GICSources_Get_Volts()) 102 | 103 | @Volts.setter 104 | def Volts(self, Value: float): 105 | self._check_for_error(self._lib.GICSources_Set_Volts(Value)) 106 | -------------------------------------------------------------------------------- /dss/IISources.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | 6 | class IISources(Iterable): 7 | __slots__ = [] 8 | _is_circuit_element = True 9 | 10 | _columns = [ 11 | 'Name', 12 | 'idx', 13 | 'Amps', 14 | 'AngleDeg', 15 | 'Frequency', 16 | ] 17 | 18 | @property 19 | def Amps(self) -> float: 20 | ''' 21 | Magnitude of the ISource in amps 22 | 23 | Original COM help: https://opendss.epri.com/Amps.html 24 | ''' 25 | return self._check_for_error(self._lib.ISources_Get_Amps()) 26 | 27 | @Amps.setter 28 | def Amps(self, Value: float): 29 | self._check_for_error(self._lib.ISources_Set_Amps(Value)) 30 | 31 | @property 32 | def AngleDeg(self) -> float: 33 | ''' 34 | Phase angle for ISource, degrees 35 | 36 | Original COM help: https://opendss.epri.com/AngleDeg.html 37 | ''' 38 | return self._check_for_error(self._lib.ISources_Get_AngleDeg()) 39 | 40 | @AngleDeg.setter 41 | def AngleDeg(self, Value: float): 42 | self._check_for_error(self._lib.ISources_Set_AngleDeg(Value)) 43 | 44 | @property 45 | def Frequency(self) -> float: 46 | ''' 47 | The present frequency of the ISource, Hz 48 | 49 | Original COM help: https://opendss.epri.com/Frequency.html 50 | ''' 51 | return self._check_for_error(self._lib.ISources_Get_Frequency()) 52 | 53 | @Frequency.setter 54 | def Frequency(self, Value: float): 55 | self._check_for_error(self._lib.ISources_Set_Frequency(Value)) 56 | -------------------------------------------------------------------------------- /dss/ILineCodes.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | from typing import Union 7 | from .enums import LineUnits 8 | 9 | class ILineCodes(Iterable): 10 | __slots__ = [] 11 | 12 | _columns = [ 13 | 'Name', 14 | 'idx', 15 | 'Phases', 16 | 'Units', 17 | 'IsZ1Z0', 18 | 'R0', 19 | 'R1', 20 | 'X0', 21 | 'X1', 22 | 'C0', 23 | 'C1', 24 | 'Rmatrix', 25 | 'Xmatrix', 26 | 'Cmatrix', 27 | 'EmergAmps', 28 | 'NormAmps', 29 | ] 30 | 31 | @property 32 | def C0(self): 33 | ''' 34 | Zero-sequence capacitance, nF per unit length 35 | 36 | Original COM help: https://opendss.epri.com/C2.html 37 | ''' 38 | return self._check_for_error(self._lib.LineCodes_Get_C0()) 39 | 40 | @C0.setter 41 | def C0(self, Value): 42 | self._check_for_error(self._lib.LineCodes_Set_C0(Value)) 43 | 44 | @property 45 | def C1(self): 46 | ''' 47 | Positive-sequence capacitance, nF per unit length 48 | 49 | Original COM help: https://opendss.epri.com/C3.html 50 | ''' 51 | return self._check_for_error(self._lib.LineCodes_Get_C1()) 52 | 53 | @C1.setter 54 | def C1(self, Value): 55 | self._check_for_error(self._lib.LineCodes_Set_C1(Value)) 56 | 57 | @property 58 | def Cmatrix(self) -> Float64Array: 59 | ''' 60 | Capacitance matrix, nF per unit length 61 | 62 | Original COM help: https://opendss.epri.com/Cmatrix1.html 63 | ''' 64 | self._check_for_error(self._lib.LineCodes_Get_Cmatrix_GR()) 65 | return self._get_float64_gr_array() 66 | 67 | @Cmatrix.setter 68 | def Cmatrix(self, Value: Float64Array): 69 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 70 | self._check_for_error(self._lib.LineCodes_Set_Cmatrix(ValuePtr, ValueCount)) 71 | 72 | @property 73 | def EmergAmps(self) -> float: 74 | ''' 75 | Emergency ampere rating 76 | 77 | Original COM help: https://opendss.epri.com/EmergAmps2.html 78 | ''' 79 | return self._check_for_error(self._lib.LineCodes_Get_EmergAmps()) 80 | 81 | @EmergAmps.setter 82 | def EmergAmps(self, Value: float): 83 | self._check_for_error(self._lib.LineCodes_Set_EmergAmps(Value)) 84 | 85 | @property 86 | def IsZ1Z0(self) -> bool: 87 | ''' 88 | Flag denoting whether impedance data were entered in symmetrical components 89 | 90 | Original COM help: https://opendss.epri.com/IsZ1Z0.html 91 | ''' 92 | return self._check_for_error(self._lib.LineCodes_Get_IsZ1Z0()) != 0 93 | 94 | @property 95 | def NormAmps(self) -> float: 96 | ''' 97 | Normal Ampere rating 98 | 99 | Original COM help: https://opendss.epri.com/NormAmps1.html 100 | ''' 101 | return self._check_for_error(self._lib.LineCodes_Get_NormAmps()) 102 | 103 | @NormAmps.setter 104 | def NormAmps(self, Value: float): 105 | self._check_for_error(self._lib.LineCodes_Set_NormAmps(Value)) 106 | 107 | @property 108 | def Phases(self) -> int: 109 | ''' 110 | Number of Phases 111 | 112 | Original COM help: https://opendss.epri.com/Phases2.html 113 | ''' 114 | return self._check_for_error(self._lib.LineCodes_Get_Phases()) 115 | 116 | @Phases.setter 117 | def Phases(self, Value: int): 118 | self._check_for_error(self._lib.LineCodes_Set_Phases(Value)) 119 | 120 | @property 121 | def R0(self) -> float: 122 | ''' 123 | Zero-Sequence Resistance, ohms per unit length 124 | 125 | Original COM help: https://opendss.epri.com/R2.html 126 | ''' 127 | return self._check_for_error(self._lib.LineCodes_Get_R0()) 128 | 129 | @R0.setter 130 | def R0(self, Value: float): 131 | self._check_for_error(self._lib.LineCodes_Set_R0(Value)) 132 | 133 | @property 134 | def R1(self) -> float: 135 | ''' 136 | Positive-sequence resistance ohms per unit length 137 | 138 | Original COM help: https://opendss.epri.com/R3.html 139 | ''' 140 | return self._check_for_error(self._lib.LineCodes_Get_R1()) 141 | 142 | @R1.setter 143 | def R1(self, Value: float): 144 | self._check_for_error(self._lib.LineCodes_Set_R1(Value)) 145 | 146 | @property 147 | def Rmatrix(self) -> Float64Array: 148 | ''' 149 | Resistance matrix, ohms per unit length 150 | 151 | Original COM help: https://opendss.epri.com/Rmatrix1.html 152 | ''' 153 | self._check_for_error(self._lib.LineCodes_Get_Rmatrix_GR()) 154 | return self._get_float64_gr_array() 155 | 156 | @Rmatrix.setter 157 | def Rmatrix(self, Value: Float64Array): 158 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 159 | self._check_for_error(self._lib.LineCodes_Set_Rmatrix(ValuePtr, ValueCount)) 160 | 161 | @property 162 | def Units(self) -> LineUnits: 163 | return LineUnits(self._check_for_error(self._lib.LineCodes_Get_Units())) 164 | 165 | @Units.setter 166 | def Units(self, Value: Union[int, LineUnits]): 167 | self._check_for_error(self._lib.LineCodes_Set_Units(Value)) 168 | 169 | @property 170 | def X0(self) -> float: 171 | ''' 172 | Zero Sequence Reactance, Ohms per unit length 173 | 174 | Original COM help: https://opendss.epri.com/X2.html 175 | ''' 176 | return self._check_for_error(self._lib.LineCodes_Get_X0()) 177 | 178 | @X0.setter 179 | def X0(self, Value: float): 180 | self._check_for_error(self._lib.LineCodes_Set_X0(Value)) 181 | 182 | @property 183 | def X1(self) -> float: 184 | ''' 185 | Positive-sequence reactance, ohms per unit length 186 | 187 | Original COM help: https://opendss.epri.com/X3.html 188 | ''' 189 | return self._check_for_error(self._lib.LineCodes_Get_X1()) 190 | 191 | @X1.setter 192 | def X1(self, Value: float): 193 | self._check_for_error(self._lib.LineCodes_Set_X1(Value)) 194 | 195 | @property 196 | def Xmatrix(self) -> Float64Array: 197 | ''' 198 | Reactance matrix, ohms per unit length 199 | 200 | Original COM help: https://opendss.epri.com/Xmatrix1.html 201 | ''' 202 | self._check_for_error(self._lib.LineCodes_Get_Xmatrix_GR()) 203 | return self._get_float64_gr_array() 204 | 205 | @Xmatrix.setter 206 | def Xmatrix(self, Value: Float64Array): 207 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 208 | self._check_for_error(self._lib.LineCodes_Set_Xmatrix(ValuePtr, ValueCount)) 209 | -------------------------------------------------------------------------------- /dss/ILineGeometries.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import List, Union 6 | from ._types import Float64Array, Int32Array, Float64ArrayOrComplexArray 7 | from .enums import LineUnits 8 | 9 | class ILineGeometries(Iterable): 10 | ''' 11 | LineGeometry objects 12 | 13 | (API Extension) 14 | ''' 15 | 16 | __slots__ = [] 17 | 18 | _columns = [ 19 | 'Name', 20 | 'idx', 21 | 'Nconds', 22 | 'Phases', 23 | 'RhoEarth', 24 | 'Reduce', 25 | 'Units', 26 | 'Conductors', 27 | 'Xcoords', 28 | 'Ycoords', 29 | 'Rmatrix', 30 | 'Xmatrix', 31 | 'Zmatrix', 32 | 'NormAmps', 33 | 'EmergAmps', 34 | ] 35 | 36 | @property 37 | def Conductors(self) -> List[str]: 38 | '''Array of strings with names of all conductors in the active LineGeometry object''' 39 | return self._check_for_error(self._get_string_array(self._lib.LineGeometries_Get_Conductors)) 40 | 41 | @property 42 | def EmergAmps(self) -> float: 43 | '''Emergency ampere rating''' 44 | return self._check_for_error(self._lib.LineGeometries_Get_EmergAmps()) 45 | 46 | @EmergAmps.setter 47 | def EmergAmps(self, Value: float): 48 | self._check_for_error(self._lib.LineGeometries_Set_EmergAmps(Value)) 49 | 50 | @property 51 | def NormAmps(self) -> float: 52 | '''Normal ampere rating''' 53 | return self._check_for_error(self._lib.LineGeometries_Get_NormAmps()) 54 | 55 | @NormAmps.setter 56 | def NormAmps(self, Value: float): 57 | self._check_for_error(self._lib.LineGeometries_Set_NormAmps(Value)) 58 | 59 | @property 60 | def RhoEarth(self) -> float: 61 | return self._check_for_error(self._lib.LineGeometries_Get_RhoEarth()) 62 | 63 | @RhoEarth.setter 64 | def RhoEarth(self, Value: float): 65 | self._check_for_error(self._lib.LineGeometries_Set_RhoEarth(Value)) 66 | 67 | @property 68 | def Reduce(self) -> bool: 69 | return self._check_for_error(self._lib.LineGeometries_Get_Reduce()) != 0 70 | 71 | @Reduce.setter 72 | def Reduce(self, Value: bool): 73 | self._check_for_error(self._lib.LineGeometries_Set_Reduce(Value)) 74 | 75 | @property 76 | def Phases(self) -> int: 77 | '''Number of Phases''' 78 | return self._check_for_error(self._lib.LineGeometries_Get_Phases()) 79 | 80 | @Phases.setter 81 | def Phases(self, Value: int): 82 | self._check_for_error(self._lib.LineGeometries_Set_Phases(Value)) 83 | 84 | def Rmatrix(self, Frequency: float, Length: float, Units: int) -> Float64Array: 85 | '''Resistance matrix, ohms''' 86 | self._check_for_error(self._lib.LineGeometries_Get_Rmatrix_GR(Frequency, Length, Units)) 87 | return self._get_float64_gr_array() 88 | 89 | def Xmatrix(self, Frequency: float, Length: float, Units: int) -> Float64Array: 90 | '''Reactance matrix, ohms''' 91 | self._check_for_error(self._lib.LineGeometries_Get_Xmatrix_GR(Frequency, Length, Units)) 92 | return self._get_float64_gr_array() 93 | 94 | def Zmatrix(self, Frequency: float, Length: float, Units: int) -> Float64ArrayOrComplexArray: 95 | '''Complex impedance matrix, ohms''' 96 | self._check_for_error(self._lib.LineGeometries_Get_Zmatrix_GR(Frequency, Length, Units)) 97 | return self._get_complex128_gr_array() 98 | 99 | def Cmatrix(self, Frequency: float, Length: float, Units: int) -> Float64Array: 100 | '''Capacitance matrix, nF''' 101 | self._check_for_error(self._lib.LineGeometries_Get_Cmatrix_GR(Frequency, Length, Units)) 102 | return self._get_float64_gr_array() 103 | 104 | @property 105 | def Units(self) -> List[LineUnits]: 106 | self._check_for_error(self._lib.LineGeometries_Get_Units_GR()) 107 | return [LineUnits(unit) for unit in self._get_int32_gr_array()] 108 | 109 | @Units.setter 110 | def Units(self, Value: Union[Int32Array, List[LineUnits]]): 111 | Value, ValuePtr, ValueCount = self._prepare_int32_array(Value) 112 | self._check_for_error(self._lib.LineGeometries_Set_Units(ValuePtr, ValueCount)) 113 | 114 | @property 115 | def Xcoords(self) -> Float64Array: 116 | '''Get/Set the X (horizontal) coordinates of the conductors''' 117 | self._check_for_error(self._lib.LineGeometries_Get_Xcoords_GR()) 118 | return self._get_float64_gr_array() 119 | 120 | @Xcoords.setter 121 | def Xcoords(self, Value: Float64Array): 122 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 123 | self._check_for_error(self._lib.LineGeometries_Set_Xcoords(ValuePtr, ValueCount)) 124 | 125 | @property 126 | def Ycoords(self) -> Float64Array: 127 | '''Get/Set the Y (vertical/height) coordinates of the conductors''' 128 | self._check_for_error(self._lib.LineGeometries_Get_Ycoords_GR()) 129 | return self._get_float64_gr_array() 130 | 131 | @Ycoords.setter 132 | def Ycoords(self, Value: Float64Array): 133 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 134 | self._check_for_error(self._lib.LineGeometries_Set_Ycoords(ValuePtr, ValueCount)) 135 | 136 | @property 137 | def Nconds(self) -> int: 138 | '''Number of conductors in this geometry. Default is 3. Triggers memory allocations. Define first!''' 139 | return self._check_for_error(self._lib.LineGeometries_Get_Nconds()) 140 | 141 | @Nconds.setter 142 | def Nconds(self, Value: int): 143 | self._check_for_error(self._lib.LineGeometries_Set_Nconds(Value)) 144 | -------------------------------------------------------------------------------- /dss/ILineSpacings.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | from typing import Union 7 | from .enums import LineUnits 8 | 9 | class ILineSpacings(Iterable): 10 | ''' 11 | LineSpacing objects 12 | 13 | (API Extension) 14 | ''' 15 | 16 | __slots__ = [] 17 | 18 | _columns = [ 19 | 'Name', 20 | 'idx', 21 | 'Nconds', 22 | 'Phases', 23 | 'Units', 24 | 'Xcoords', 25 | 'Ycoords', 26 | ] 27 | 28 | @property 29 | def Phases(self) -> int: 30 | '''Number of Phases''' 31 | return self._check_for_error(self._lib.LineSpacings_Get_Phases()) 32 | 33 | @Phases.setter 34 | def Phases(self, Value: int): 35 | self._check_for_error(self._lib.LineSpacings_Set_Phases(Value)) 36 | 37 | @property 38 | def Nconds(self) -> int: 39 | return self._check_for_error(self._lib.LineSpacings_Get_Nconds()) 40 | 41 | @Nconds.setter 42 | def Nconds(self, Value: int): 43 | self._check_for_error(self._lib.LineSpacings_Set_Nconds(Value)) 44 | 45 | @property 46 | def Units(self) -> LineUnits: 47 | return LineUnits(self._check_for_error(self._lib.LineSpacings_Get_Units())) 48 | 49 | @Units.setter 50 | def Units(self, Value: Union[int, LineUnits]): 51 | self._check_for_error(self._lib.LineSpacings_Set_Units(Value)) 52 | 53 | @property 54 | def Xcoords(self) -> Float64Array: 55 | '''Get/Set the X (horizontal) coordinates of the conductors''' 56 | self._check_for_error(self._lib.LineSpacings_Get_Xcoords_GR()) 57 | return self._get_float64_gr_array() 58 | 59 | @Xcoords.setter 60 | def Xcoords(self, Value: Float64Array): 61 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 62 | self._check_for_error(self._lib.LineSpacings_Set_Xcoords(ValuePtr, ValueCount)) 63 | 64 | @property 65 | def Ycoords(self) -> Float64Array: 66 | '''Get/Set the Y (vertical/height) coordinates of the conductors''' 67 | self._check_for_error(self._lib.LineSpacings_Get_Ycoords_GR()) 68 | return self._get_float64_gr_array() 69 | 70 | @Ycoords.setter 71 | def Ycoords(self, Value: Float64Array): 72 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 73 | self._check_for_error(self._lib.LineSpacings_Set_Ycoords(ValuePtr, ValueCount)) 74 | -------------------------------------------------------------------------------- /dss/ILoadShapes.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | from typing import AnyStr 7 | 8 | class ILoadShapes(Iterable): 9 | __slots__ = [] 10 | 11 | _columns = [ 12 | 'Name', 13 | 'idx', 14 | 'UseActual', 15 | 'Npts', 16 | 'HrInterval', 17 | 'MinInterval', 18 | 'sInterval', 19 | 'PBase', 20 | 'Pmult', 21 | 'QBase', 22 | 'Qmult', 23 | 'TimeArray', 24 | ] 25 | 26 | def New(self, Name: AnyStr): 27 | '''Create a new LoadShape, with default parameters''' 28 | if not isinstance(Name, bytes): 29 | Name = Name.encode(self._api_util.codec) 30 | 31 | return self._check_for_error(self._lib.LoadShapes_New(Name)) 32 | 33 | def Normalize(self): 34 | '''Normalize the LoadShape data inplace''' 35 | self._check_for_error(self._lib.LoadShapes_Normalize()) 36 | 37 | @property 38 | def HrInterval(self) -> float: 39 | ''' 40 | Fixed interval time value, in hours. 41 | 42 | Original COM help: https://opendss.epri.com/HrInterval.html 43 | ''' 44 | return self._check_for_error(self._lib.LoadShapes_Get_HrInterval()) 45 | 46 | @HrInterval.setter 47 | def HrInterval(self, Value: float): 48 | self._check_for_error(self._lib.LoadShapes_Set_HrInterval(Value)) 49 | 50 | @property 51 | def MinInterval(self) -> float: 52 | ''' 53 | Fixed Interval time value, in minutes 54 | 55 | Original COM help: https://opendss.epri.com/MinInterval.html 56 | ''' 57 | return self._check_for_error(self._lib.LoadShapes_Get_MinInterval()) 58 | 59 | @MinInterval.setter 60 | def MinInterval(self, Value: float): 61 | self._check_for_error(self._lib.LoadShapes_Set_MinInterval(Value)) 62 | 63 | @property 64 | def Npts(self) -> int: 65 | ''' 66 | Get/set Number of points in active Loadshape. 67 | 68 | Original COM help: https://opendss.epri.com/Npts.html 69 | ''' 70 | return self._check_for_error(self._lib.LoadShapes_Get_Npts()) 71 | 72 | @Npts.setter 73 | def Npts(self, Value: int): 74 | self._check_for_error(self._lib.LoadShapes_Set_Npts(Value)) 75 | 76 | @property 77 | def PBase(self) -> float: 78 | ''' 79 | Base P value for normalization. Default is zero, meaning the peak will be used. 80 | 81 | Original COM help: https://opendss.epri.com/Pbase.html 82 | ''' 83 | return self._check_for_error(self._lib.LoadShapes_Get_PBase()) 84 | 85 | @PBase.setter 86 | def PBase(self, Value: float): 87 | self._check_for_error(self._lib.LoadShapes_Set_PBase(Value)) 88 | 89 | Pbase = PBase 90 | 91 | @property 92 | def Pmult(self) -> Float64Array: 93 | ''' 94 | Array of doubles for the P multiplier in the Loadshape. 95 | 96 | Original COM help: https://opendss.epri.com/Pmult.html 97 | ''' 98 | self._check_for_error(self._lib.LoadShapes_Get_Pmult_GR()) 99 | return self._get_float64_gr_array() 100 | 101 | @Pmult.setter 102 | def Pmult(self, Value: Float64Array): 103 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 104 | self._check_for_error(self._lib.LoadShapes_Set_Pmult(ValuePtr, ValueCount)) 105 | 106 | @property 107 | def QBase(self) -> float: 108 | ''' 109 | Base for normalizing Q curve. If left at zero, the peak value is used. 110 | 111 | Original COM help: https://opendss.epri.com/Qbase.html 112 | ''' 113 | return self._check_for_error(self._lib.LoadShapes_Get_Qbase()) 114 | 115 | @QBase.setter 116 | def QBase(self, Value: float): 117 | self._check_for_error(self._lib.LoadShapes_Set_Qbase(Value)) 118 | 119 | Qbase = QBase 120 | 121 | @property 122 | def Qmult(self) -> Float64Array: 123 | ''' 124 | Array of doubles containing the Q multipliers. 125 | 126 | Original COM help: https://opendss.epri.com/Qmult.html 127 | ''' 128 | self._check_for_error(self._lib.LoadShapes_Get_Qmult_GR()) 129 | return self._get_float64_gr_array() 130 | 131 | @Qmult.setter 132 | def Qmult(self, Value: Float64Array): 133 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 134 | self._check_for_error(self._lib.LoadShapes_Set_Qmult(ValuePtr, ValueCount)) 135 | 136 | @property 137 | def TimeArray(self) -> Float64Array: 138 | ''' 139 | Time array in hours corresponding to P and Q multipliers when the Interval=0. 140 | 141 | Original COM help: https://opendss.epri.com/TimeArray.html 142 | ''' 143 | self._check_for_error(self._lib.LoadShapes_Get_TimeArray_GR()) 144 | return self._get_float64_gr_array() 145 | 146 | @TimeArray.setter 147 | def TimeArray(self, Value: Float64Array): 148 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 149 | self._check_for_error(self._lib.LoadShapes_Set_TimeArray(ValuePtr, ValueCount)) 150 | 151 | @property 152 | def UseActual(self) -> bool: 153 | ''' 154 | Boolean flag to let Loads know to use the actual value in the curve rather than use the value as a multiplier. 155 | 156 | Original COM help: https://opendss.epri.com/UseActual.html 157 | ''' 158 | return self._check_for_error(self._lib.LoadShapes_Get_UseActual()) != 0 159 | 160 | @UseActual.setter 161 | def UseActual(self, Value: bool): 162 | self._check_for_error(self._lib.LoadShapes_Set_UseActual(Value)) 163 | 164 | @property 165 | def sInterval(self) -> float: 166 | ''' 167 | Fixed interval time value, in seconds. 168 | 169 | Original COM help: https://opendss.epri.com/Sinterval.html 170 | ''' 171 | return self._check_for_error(self._lib.LoadShapes_Get_SInterval()) 172 | 173 | @sInterval.setter 174 | def sInterval(self, Value: float): 175 | self._check_for_error(self._lib.LoadShapes_Set_SInterval(Value)) 176 | 177 | Sinterval = sInterval 178 | SInterval = sInterval 179 | 180 | def UseFloat32(self): 181 | ''' 182 | Converts the current LoadShape data to float32/single precision. 183 | If there is no data or the data is already represented using float32, nothing is done. 184 | 185 | **(API Extension)** 186 | ''' 187 | self._check_for_error(self._lib.LoadShapes_UseFloat32()) 188 | 189 | def UseFloat64(self): 190 | ''' 191 | Converts the current LoadShape data to float64/double precision. 192 | If there is no data or the data is already represented using float64, nothing is done. 193 | 194 | **(API Extension)** 195 | ''' 196 | self._check_for_error(self._lib.LoadShapes_UseFloat64()) 197 | -------------------------------------------------------------------------------- /dss/IParallel.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from ._types import Int32Array 6 | 7 | class IParallel(Base): 8 | ''' 9 | Parallel machine interface 10 | 11 | On DSS-Extensions, prefer using DSSContexts and native threading capabilities of your programming 12 | language, if available. 13 | ''' 14 | 15 | __slots__ = [] 16 | 17 | def CreateActor(self): 18 | ''' 19 | Create a new actor, if there are still cores available. 20 | ''' 21 | self._check_for_error(self._lib.Parallel_CreateActor()) 22 | 23 | def Wait(self): 24 | ''' 25 | Suspends the host's thread until all the OpenDSS running jobs finish. 26 | 27 | Original COM help: https://opendss.epri.com/Wait.html 28 | ''' 29 | self._check_for_error(self._lib.Parallel_Wait()) 30 | 31 | @property 32 | def ActiveActor(self) -> int: 33 | ''' 34 | Gets/sets the ID of the Active Actor 35 | 36 | Original COM help: https://opendss.epri.com/ActiveActor.html 37 | ''' 38 | return self._check_for_error(self._lib.Parallel_Get_ActiveActor()) 39 | 40 | @ActiveActor.setter 41 | def ActiveActor(self, Value: int): 42 | self._check_for_error(self._lib.Parallel_Set_ActiveActor(Value)) 43 | 44 | @property 45 | def ActiveParallel(self) -> int: 46 | ''' 47 | (read) Sets ON/OFF (1/0) Parallel features of the Engine 48 | (write) Delivers if the Parallel features of the Engine are Active 49 | 50 | Original COM help: https://opendss.epri.com/ActiveParallel.html 51 | ''' 52 | return self._check_for_error(self._lib.Parallel_Get_ActiveParallel()) #TODO: use boolean for consistency 53 | 54 | @ActiveParallel.setter 55 | def ActiveParallel(self, Value: int): 56 | self._check_for_error(self._lib.Parallel_Set_ActiveParallel(Value)) 57 | 58 | @property 59 | def ActorCPU(self) -> int: 60 | ''' 61 | Gets/sets the CPU of the Active Actor 62 | 63 | Original COM help: https://opendss.epri.com/ActorCPU.html 64 | ''' 65 | return self._check_for_error(self._lib.Parallel_Get_ActorCPU()) 66 | 67 | @ActorCPU.setter 68 | def ActorCPU(self, Value: int): 69 | self._check_for_error(self._lib.Parallel_Set_ActorCPU(Value)) 70 | 71 | @property 72 | def ActorProgress(self) -> Int32Array: 73 | ''' 74 | Gets the progress of all existing actors in pct 75 | 76 | Original COM help: https://opendss.epri.com/ActorProgress.html 77 | ''' 78 | self._check_for_error(self._lib.Parallel_Get_ActorProgress_GR()) 79 | return self._get_int32_gr_array() 80 | 81 | @property 82 | def ActorStatus(self) -> Int32Array: 83 | ''' 84 | Gets the status of each actor 85 | 86 | Original COM help: https://opendss.epri.com/ActorStatus.html 87 | ''' 88 | self._check_for_error(self._lib.Parallel_Get_ActorStatus_GR()) 89 | return self._get_int32_gr_array() 90 | 91 | @property 92 | def ConcatenateReports(self) -> int: 93 | ''' 94 | (read) Reads the values of the ConcatenateReports option (1=enabled, 0=disabled) 95 | (write) Enable/Disable (1/0) the ConcatenateReports option for extracting monitors data 96 | 97 | Original COM help: https://opendss.epri.com/ConcatenateReports.html 98 | ''' 99 | return self._check_for_error(self._lib.Parallel_Get_ConcatenateReports()) #TODO: use boolean for consistency 100 | 101 | @ConcatenateReports.setter 102 | def ConcatenateReports(self, Value: int): 103 | self._check_for_error(self._lib.Parallel_Set_ConcatenateReports(Value)) 104 | 105 | @property 106 | def NumCPUs(self) -> int: 107 | ''' 108 | Delivers the number of CPUs on the current PC 109 | 110 | Original COM help: https://opendss.epri.com/NumCPUs.html 111 | ''' 112 | return self._check_for_error(self._lib.Parallel_Get_NumCPUs()) 113 | 114 | @property 115 | def NumCores(self) -> int: 116 | ''' 117 | Delivers the number of Cores of the local PC 118 | 119 | Original COM help: https://opendss.epri.com/NumCores.html 120 | ''' 121 | return self._check_for_error(self._lib.Parallel_Get_NumCores()) 122 | 123 | @property 124 | def NumOfActors(self) -> int: 125 | ''' 126 | Gets the number of Actors created 127 | 128 | Original COM help: https://opendss.epri.com/NumOfActors.html 129 | ''' 130 | return self._check_for_error(self._lib.Parallel_Get_NumOfActors()) 131 | 132 | 133 | -------------------------------------------------------------------------------- /dss/IParser.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from ._types import Float64Array 6 | from typing import AnyStr 7 | 8 | class IParser(Base): 9 | __slots__ = [] 10 | 11 | _columns = [ 12 | 'Delimiters', 'EndQuote', 'CmdString', 'BeginQuote', 'WhiteSpace', 'AutoIncrement', 13 | ] 14 | 15 | def Matrix(self, ExpectedOrder: int) -> Float64Array: 16 | '''Use this property to parse a Matrix token in OpenDSS format. Returns square matrix of order specified. Order same as default Fortran order: column by column.''' 17 | self._check_for_error(self._lib.Parser_Get_Matrix_GR(ExpectedOrder)) 18 | return self._get_float64_gr_array() 19 | 20 | def SymMatrix(self, ExpectedOrder: int) -> Float64Array: 21 | '''Use this property to parse a matrix token specified in lower triangle form. Symmetry is forced.''' 22 | self._check_for_error(self._lib.Parser_Get_SymMatrix_GR(ExpectedOrder)) 23 | return self._get_float64_gr_array() 24 | 25 | def Vector(self, ExpectedSize: int) -> Float64Array: 26 | '''Returns token as array of doubles. For parsing quoted array syntax.''' 27 | self._check_for_error(self._lib.Parser_Get_Vector_GR(ExpectedSize)) 28 | return self._get_float64_gr_array() 29 | 30 | def ResetDelimiters(self): 31 | ''' 32 | Reset the delimiters to their default values. 33 | 34 | Original COM help: https://opendss.epri.com/ResetDelimiters.html 35 | ''' 36 | self._check_for_error(self._lib.Parser_ResetDelimiters()) 37 | 38 | @property 39 | def AutoIncrement(self) -> bool: 40 | ''' 41 | Default is FALSE. If TRUE, the parser automatically advances to next token after DblValue, IntValue, or StrValue. Simpler when you don't need to check for parameter names. 42 | 43 | Original COM help: https://opendss.epri.com/AutoIncrement.html 44 | ''' 45 | return self._check_for_error(self._lib.Parser_Get_AutoIncrement()) != 0 46 | 47 | @AutoIncrement.setter 48 | def AutoIncrement(self, Value: bool): 49 | self._check_for_error(self._lib.Parser_Set_AutoIncrement(Value)) 50 | 51 | @property 52 | def BeginQuote(self) -> str: 53 | ''' 54 | Get/Set String containing the the characters for Quoting in OpenDSS scripts. Matching pairs defined in EndQuote. Default is "'([{. 55 | 56 | Original COM help: https://opendss.epri.com/BeginQuote.html 57 | ''' 58 | return self._get_string(self._check_for_error(self._lib.Parser_Get_BeginQuote())) 59 | 60 | @BeginQuote.setter 61 | def BeginQuote(self, Value: AnyStr): 62 | if not isinstance(Value, bytes): 63 | Value = Value.encode(self._api_util.codec) 64 | 65 | self._check_for_error(self._lib.Parser_Set_BeginQuote(Value)) 66 | 67 | @property 68 | def CmdString(self) -> str: 69 | ''' 70 | String to be parsed. Loading this string resets the Parser to the beginning of the line. Then parse off the tokens in sequence. 71 | 72 | Original COM help: https://opendss.epri.com/CmdString.html 73 | ''' 74 | return self._get_string(self._check_for_error(self._lib.Parser_Get_CmdString())) 75 | 76 | @CmdString.setter 77 | def CmdString(self, Value: AnyStr): 78 | if not isinstance(Value, bytes): 79 | Value = Value.encode(self._api_util.codec) 80 | 81 | self._check_for_error(self._lib.Parser_Set_CmdString(Value)) 82 | 83 | @property 84 | def DblValue(self) -> float: 85 | ''' 86 | Return next parameter as a double. 87 | 88 | Original COM help: https://opendss.epri.com/DblValue.html 89 | ''' 90 | return self._check_for_error(self._lib.Parser_Get_DblValue()) 91 | 92 | @property 93 | def Delimiters(self) -> str: 94 | ''' 95 | String defining hard delimiters used to separate token on the command string. Default is , and =. The = separates token name from token value. These override whitespace to separate tokens. 96 | 97 | Original COM help: https://opendss.epri.com/Delimiters.html 98 | ''' 99 | return self._get_string(self._check_for_error(self._lib.Parser_Get_Delimiters())) 100 | 101 | @Delimiters.setter 102 | def Delimiters(self, Value: AnyStr): 103 | if not isinstance(Value, bytes): 104 | Value = Value.encode(self._api_util.codec) 105 | 106 | self._check_for_error(self._lib.Parser_Set_Delimiters(Value)) 107 | 108 | @property 109 | def EndQuote(self) -> str: 110 | ''' 111 | String containing characters, in order, that match the beginning quote characters in BeginQuote. Default is "')]} 112 | 113 | Original COM help: https://opendss.epri.com/EndQuote.html 114 | ''' 115 | return self._get_string(self._check_for_error(self._lib.Parser_Get_EndQuote())) 116 | 117 | @EndQuote.setter 118 | def EndQuote(self, Value: AnyStr): 119 | if not isinstance(Value, bytes): 120 | Value = Value.encode(self._api_util.codec) 121 | 122 | self._check_for_error(self._lib.Parser_Set_EndQuote(Value)) 123 | 124 | @property 125 | def IntValue(self) -> int: 126 | ''' 127 | Return next parameter as a long integer. 128 | 129 | Original COM help: https://opendss.epri.com/IntValue.html 130 | ''' 131 | return self._check_for_error(self._lib.Parser_Get_IntValue()) 132 | 133 | @property 134 | def NextParam(self) -> str: 135 | ''' 136 | Get next token and return tag name (before = sign) if any. See AutoIncrement. 137 | 138 | Original COM help: https://opendss.epri.com/NextParam.html 139 | ''' 140 | return self._get_string(self._check_for_error(self._lib.Parser_Get_NextParam())) 141 | 142 | @property 143 | def StrValue(self) -> str: 144 | ''' 145 | Return next parameter as a string 146 | 147 | Original COM help: https://opendss.epri.com/StrValue.html 148 | ''' 149 | return self._get_string(self._check_for_error(self._lib.Parser_Get_StrValue())) 150 | 151 | @property 152 | def WhiteSpace(self) -> str: 153 | ''' 154 | Get/set the characters used for White space in the command string. Default is blank and Tab. 155 | 156 | Original COM help: https://opendss.epri.com/WhiteSpace.html 157 | ''' 158 | return self._get_string(self._check_for_error(self._lib.Parser_Get_WhiteSpace())) 159 | 160 | @WhiteSpace.setter 161 | def WhiteSpace(self, Value: AnyStr): 162 | if not isinstance(Value, bytes): 163 | Value = Value.encode(self._api_util.codec) 164 | 165 | self._check_for_error(self._lib.Parser_Set_WhiteSpace(Value)) 166 | 167 | -------------------------------------------------------------------------------- /dss/IReclosers.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | from typing import AnyStr 7 | 8 | class IReclosers(Iterable): 9 | __slots__ = [] 10 | _is_circuit_element = True 11 | 12 | _columns = [ 13 | 'Name', 14 | 'idx', 15 | 'GroundInst', 16 | 'GroundTrip', 17 | 'MonitoredObj', 18 | 'MonitoredTerm', 19 | 'SwitchedObj', 20 | 'SwitchedTerm', 21 | 'NumFast', 22 | 'PhaseInst', 23 | 'PhaseTrip', 24 | 'RecloseIntervals', 25 | 'Shots', 26 | 'State', 27 | 'NormalState', 28 | ] 29 | 30 | def Close(self): 31 | self._check_for_error(self._lib.Reclosers_Close()) 32 | 33 | def Open(self): 34 | self._check_for_error(self._lib.Reclosers_Open()) 35 | 36 | @property 37 | def GroundInst(self) -> float: 38 | ''' 39 | Ground (3I0) instantaneous trip setting - curve multiplier or actual amps. 40 | 41 | Original COM help: https://opendss.epri.com/GroundInst.html 42 | ''' 43 | return self._check_for_error(self._lib.Reclosers_Get_GroundInst()) 44 | 45 | @GroundInst.setter 46 | def GroundInst(self, Value: float): 47 | self._check_for_error(self._lib.Reclosers_Set_GroundInst(Value)) 48 | 49 | @property 50 | def GroundTrip(self) -> float: 51 | ''' 52 | Ground (3I0) trip multiplier or actual amps 53 | 54 | Original COM help: https://opendss.epri.com/GroundTrip.html 55 | ''' 56 | return self._check_for_error(self._lib.Reclosers_Get_GroundTrip()) 57 | 58 | @GroundTrip.setter 59 | def GroundTrip(self, Value: float): 60 | self._check_for_error(self._lib.Reclosers_Set_GroundTrip(Value)) 61 | 62 | @property 63 | def MonitoredObj(self) -> str: 64 | ''' 65 | Full name of object this Recloser to be monitored. 66 | 67 | Original COM help: https://opendss.epri.com/MonitoredObj2.html 68 | ''' 69 | return self._get_string(self._check_for_error(self._lib.Reclosers_Get_MonitoredObj())) 70 | 71 | @MonitoredObj.setter 72 | def MonitoredObj(self, Value: AnyStr): 73 | if not isinstance(Value, bytes): 74 | Value = Value.encode(self._api_util.codec) 75 | 76 | self._check_for_error(self._lib.Reclosers_Set_MonitoredObj(Value)) 77 | 78 | @property 79 | def MonitoredTerm(self) -> int: 80 | ''' 81 | Terminal number of Monitored object for the Recloser 82 | 83 | Original COM help: https://opendss.epri.com/MonitoredTerm2.html 84 | ''' 85 | return self._check_for_error(self._lib.Reclosers_Get_MonitoredTerm()) 86 | 87 | @MonitoredTerm.setter 88 | def MonitoredTerm(self, Value: int): 89 | self._check_for_error(self._lib.Reclosers_Set_MonitoredTerm(Value)) 90 | 91 | @property 92 | def NumFast(self) -> int: 93 | ''' 94 | Number of fast shots 95 | 96 | Original COM help: https://opendss.epri.com/NumFast.html 97 | ''' 98 | return self._check_for_error(self._lib.Reclosers_Get_NumFast()) 99 | 100 | @NumFast.setter 101 | def NumFast(self, Value: int): 102 | self._check_for_error(self._lib.Reclosers_Set_NumFast(Value)) 103 | 104 | @property 105 | def PhaseInst(self) -> float: 106 | ''' 107 | Phase instantaneous curve multiplier or actual amps 108 | 109 | Original COM help: https://opendss.epri.com/PhaseInst.html 110 | ''' 111 | return self._check_for_error(self._lib.Reclosers_Get_PhaseInst()) 112 | 113 | @PhaseInst.setter 114 | def PhaseInst(self, Value: float): 115 | self._check_for_error(self._lib.Reclosers_Set_PhaseInst(Value)) 116 | 117 | @property 118 | def PhaseTrip(self) -> float: 119 | ''' 120 | Phase trip curve multiplier or actual amps 121 | 122 | Original COM help: https://opendss.epri.com/PhaseTrip.html 123 | ''' 124 | return self._check_for_error(self._lib.Reclosers_Get_PhaseTrip()) 125 | 126 | @PhaseTrip.setter 127 | def PhaseTrip(self, Value: float): 128 | self._check_for_error(self._lib.Reclosers_Set_PhaseTrip(Value)) 129 | 130 | @property 131 | def RecloseIntervals(self) -> Float64Array: 132 | ''' 133 | Array of Doubles: reclose intervals, s, between shots. 134 | 135 | Original COM help: https://opendss.epri.com/RecloseIntervals.html 136 | ''' 137 | self._check_for_error(self._lib.Reclosers_Get_RecloseIntervals_GR()) 138 | return self._get_float64_gr_array() 139 | 140 | @property 141 | def Shots(self) -> int: 142 | ''' 143 | Number of shots to lockout (fast + delayed) 144 | 145 | Original COM help: https://opendss.epri.com/Shots.html 146 | ''' 147 | return self._check_for_error(self._lib.Reclosers_Get_Shots()) 148 | 149 | @Shots.setter 150 | def Shots(self, Value: int): 151 | self._check_for_error(self._lib.Reclosers_Set_Shots(Value)) 152 | 153 | @property 154 | def SwitchedObj(self) -> str: 155 | ''' 156 | Full name of the circuit element that is being switched by the Recloser. 157 | 158 | Original COM help: https://opendss.epri.com/SwitchedObj1.html 159 | ''' 160 | return self._get_string(self._check_for_error(self._lib.Reclosers_Get_SwitchedObj())) 161 | 162 | @SwitchedObj.setter 163 | def SwitchedObj(self, Value: AnyStr): 164 | if not isinstance(Value, bytes): 165 | Value = Value.encode(self._api_util.codec) 166 | 167 | self._check_for_error(self._lib.Reclosers_Set_SwitchedObj(Value)) 168 | 169 | @property 170 | def SwitchedTerm(self) -> int: 171 | ''' 172 | Terminal number of the controlled device being switched by the Recloser 173 | 174 | Original COM help: https://opendss.epri.com/SwitchedTerm1.html 175 | ''' 176 | return self._check_for_error(self._lib.Reclosers_Get_SwitchedTerm()) 177 | 178 | @SwitchedTerm.setter 179 | def SwitchedTerm(self, Value: int): 180 | self._check_for_error(self._lib.Reclosers_Set_SwitchedTerm(Value)) 181 | 182 | 183 | def Reset(self): 184 | ''' 185 | Reset recloser to normal state. 186 | If open, lock out the recloser. 187 | If closed, resets recloser to first operation. 188 | ''' 189 | self._check_for_error(self._lib.Reclosers_Reset()) 190 | 191 | @property 192 | def State(self) -> int: 193 | ''' 194 | Get/Set present state of recloser. 195 | If set to open (ActionCodes.Open=1), open recloser's controlled element and lock out the recloser. 196 | If set to close (ActionCodes.Close=2), close recloser's controlled element and resets recloser to first operation. 197 | ''' 198 | return self._check_for_error(self._lib.Reclosers_Get_State()) 199 | 200 | @State.setter 201 | def State(self, Value: int): 202 | self._check_for_error(self._lib.Reclosers_Set_State(Value)) 203 | 204 | @property 205 | def NormalState(self) -> int: 206 | ''' 207 | Get/set normal state (ActionCodes.Open=1, ActionCodes.Close=2) of the recloser. 208 | 209 | Original COM help: https://opendss.epri.com/NormalState1.html 210 | ''' 211 | return self._check_for_error(self._lib.Reclosers_Get_NormalState()) 212 | 213 | @NormalState.setter 214 | def NormalState(self, Value: int): 215 | self._check_for_error(self._lib.Reclosers_Set_NormalState(Value)) 216 | -------------------------------------------------------------------------------- /dss/IReduceCkt.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2019-2024 Paulo Meira 3 | # Copyright (c) 2019-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from typing import AnyStr 6 | 7 | class IReduceCkt(Base): 8 | '''Circuit Reduction interface''' 9 | 10 | __slots__ = [] 11 | 12 | @property 13 | def Zmag(self) -> float: 14 | ''' 15 | Zmag (ohms) for Reduce Option for Z of short lines 16 | 17 | Original COM help: https://opendss.epri.com/Zmag.html 18 | ''' 19 | return self._check_for_error(self._lib.ReduceCkt_Get_Zmag()) 20 | 21 | @Zmag.setter 22 | def Zmag(self, Value: float): 23 | self._check_for_error(self._lib.ReduceCkt_Set_Zmag(Value)) 24 | 25 | @property 26 | def KeepLoad(self) -> bool: 27 | ''' 28 | Keep load flag for Reduction options that remove branches 29 | 30 | Original COM help: https://opendss.epri.com/KeepLoad.html 31 | ''' 32 | return self._check_for_error(self._lib.ReduceCkt_Get_KeepLoad()) != 0 33 | 34 | @KeepLoad.setter 35 | def KeepLoad(self, Value: bool): 36 | self._check_for_error(self._lib.ReduceCkt_Set_KeepLoad(bool(Value))) 37 | 38 | @property 39 | def EditString(self) -> str: 40 | ''' 41 | Edit String for RemoveBranches functions 42 | 43 | Original COM help: https://opendss.epri.com/EditString.html 44 | ''' 45 | return self._get_string(self._check_for_error(self._lib.ReduceCkt_Get_EditString())) 46 | 47 | @EditString.setter 48 | def EditString(self, Value: AnyStr): 49 | if not isinstance(Value, bytes): 50 | Value = Value.encode(self._api_util.codec) 51 | 52 | self._check_for_error(self._lib.ReduceCkt_Set_EditString(Value)) 53 | 54 | @property 55 | def StartPDElement(self) -> str: 56 | ''' 57 | Start element for Remove Branch function 58 | 59 | Original COM help: https://opendss.epri.com/StartPDElement.html 60 | ''' 61 | return self._get_string(self._check_for_error(self._lib.ReduceCkt_Get_StartPDElement())) 62 | 63 | @StartPDElement.setter 64 | def StartPDElement(self, Value: AnyStr): 65 | if not isinstance(Value, bytes): 66 | Value = Value.encode(self._api_util.codec) 67 | 68 | self._check_for_error(self._lib.ReduceCkt_Set_StartPDElement(Value)) 69 | 70 | @property 71 | def EnergyMeter(self) -> str: 72 | ''' 73 | Name of EnergyMeter to use for reduction 74 | 75 | Original COM help: https://opendss.epri.com/EnergyMeter1.html 76 | ''' 77 | return self._get_string(self._check_for_error(self._lib.ReduceCkt_Get_EnergyMeter())) 78 | 79 | @EnergyMeter.setter 80 | def EnergyMeter(self, Value: AnyStr): 81 | if not isinstance(Value, bytes): 82 | Value = Value.encode(self._api_util.codec) 83 | 84 | self._check_for_error(self._lib.ReduceCkt_Set_EnergyMeter(Value)) 85 | 86 | def SaveCircuit(self, CktName: AnyStr): 87 | ''' 88 | Save present (reduced) circuit 89 | Filename is listed in the Text Result interface 90 | ''' 91 | if not isinstance(CktName, bytes): 92 | CktName = CktName.encode(self._api_util.codec) 93 | 94 | self._check_for_error(self._lib.ReduceCkt_SaveCircuit(CktName)) 95 | 96 | def DoDefault(self): 97 | ''' 98 | Do Default Reduction algorithm 99 | 100 | Original COM help: https://opendss.epri.com/DoDefault.html 101 | ''' 102 | self._check_for_error(self._lib.ReduceCkt_DoDefault()) 103 | 104 | def DoShortLines(self): 105 | ''' 106 | Do ShortLines algorithm: Set Zmag first if you don't want the default 107 | 108 | Original COM help: https://opendss.epri.com/DoShortLines.html 109 | ''' 110 | self._check_for_error(self._lib.ReduceCkt_DoShortLines()) 111 | 112 | def DoDangling(self): 113 | ''' 114 | Reduce Dangling Algorithm; branches with nothing connected 115 | 116 | Original COM help: https://opendss.epri.com/DoDangling.html 117 | ''' 118 | self._check_for_error(self._lib.ReduceCkt_DoDangling()) 119 | 120 | def DoLoopBreak(self): 121 | ''' 122 | Break (disable) all the loops found in the active circuit. 123 | 124 | Disables one of the Line objects at the head of a loop to force the circuit to be radial. 125 | ''' 126 | self._check_for_error(self._lib.ReduceCkt_DoLoopBreak()) 127 | 128 | def DoParallelLines(self): 129 | ''' 130 | Merge all parallel lines found in the circuit to facilitate its reduction. 131 | ''' 132 | self._check_for_error(self._lib.ReduceCkt_DoParallelLines()) 133 | 134 | def DoSwitches(self): 135 | ''' 136 | Merge Line objects in which the IsSwitch property is true with the down-line Line object. 137 | ''' 138 | self._check_for_error(self._lib.ReduceCkt_DoSwitches()) 139 | 140 | def Do1phLaterals(self): 141 | ''' 142 | Remove all 1-phase laterals in the active EnergyMeter's zone. 143 | 144 | Loads and other shunt elements are moved to the parent 3-phase bus. 145 | ''' 146 | self._check_for_error(self._lib.ReduceCkt_Do1phLaterals()) 147 | 148 | def DoBranchRemove(self): 149 | ''' 150 | Remove (disable) all branches down-line from the active PDElement. 151 | 152 | Circuit must have an EnergyMeter on this branch. 153 | If KeepLoad=Y (default), a new Load element is defined and kW, kvar are set to present power flow solution for the first element eliminated. 154 | The EditString is applied to each new Load element defined. 155 | ''' 156 | self._check_for_error(self._lib.ReduceCkt_DoBranchRemove()) 157 | -------------------------------------------------------------------------------- /dss/IRelays.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import AnyStr 6 | 7 | class IRelays(Iterable): 8 | __slots__ = [] 9 | _is_circuit_element = True 10 | 11 | _columns = [ 12 | 'Name', 13 | 'idx', 14 | 'MonitoredObj', 15 | 'MonitoredTerm', 16 | 'SwitchedObj', 17 | 'SwitchedTerm', 18 | 'State', 19 | 'NormalState' 20 | ] 21 | 22 | @property 23 | def MonitoredObj(self) -> str: 24 | ''' 25 | Full name of object this Relay is monitoring. 26 | 27 | Original COM help: https://opendss.epri.com/MonitoredObj3.html 28 | ''' 29 | return self._get_string(self._check_for_error(self._lib.Relays_Get_MonitoredObj())) 30 | 31 | @MonitoredObj.setter 32 | def MonitoredObj(self, Value: AnyStr): 33 | if not isinstance(Value, bytes): 34 | Value = Value.encode(self._api_util.codec) 35 | 36 | self._check_for_error(self._lib.Relays_Set_MonitoredObj(Value)) 37 | 38 | @property 39 | def MonitoredTerm(self) -> int: 40 | ''' 41 | Number of terminal of monitored element that this Relay is monitoring. 42 | 43 | Original COM help: https://opendss.epri.com/MonitoredTerm3.html 44 | ''' 45 | return self._check_for_error(self._lib.Relays_Get_MonitoredTerm()) 46 | 47 | @MonitoredTerm.setter 48 | def MonitoredTerm(self, Value: int): 49 | self._check_for_error(self._lib.Relays_Set_MonitoredTerm(Value)) 50 | 51 | @property 52 | def SwitchedObj(self) -> str: 53 | ''' 54 | Full name of element that will be switched when relay trips. 55 | 56 | Original COM help: https://opendss.epri.com/SwitchedObj2.html 57 | ''' 58 | return self._get_string(self._check_for_error(self._lib.Relays_Get_SwitchedObj())) 59 | 60 | @SwitchedObj.setter 61 | def SwitchedObj(self, Value: AnyStr): 62 | if not isinstance(Value, bytes): 63 | Value = Value.encode(self._api_util.codec) 64 | 65 | self._check_for_error(self._lib.Relays_Set_SwitchedObj(Value)) 66 | 67 | @property 68 | def SwitchedTerm(self) -> int: 69 | ''' 70 | Terminal number of the switched object that will be opened when the relay trips. 71 | 72 | Original COM help: https://opendss.epri.com/SwitchedTerm2.html 73 | ''' 74 | return self._check_for_error(self._lib.Relays_Get_SwitchedTerm()) 75 | 76 | @SwitchedTerm.setter 77 | def SwitchedTerm(self, Value: int): 78 | self._check_for_error(self._lib.Relays_Set_SwitchedTerm(Value)) 79 | 80 | def Open(self): 81 | ''' 82 | Open relay's controlled element and lock out the relay. 83 | 84 | Original COM help: https://opendss.epri.com/Open4.html 85 | ''' 86 | self._check_for_error(self._lib.Relays_Open()) 87 | 88 | def Close(self): 89 | ''' 90 | Close the switched object controlled by the relay. Resets relay to first operation. 91 | 92 | Original COM help: https://opendss.epri.com/Close5.html 93 | ''' 94 | self._check_for_error(self._lib.Relays_Close()) 95 | 96 | def Reset(self): 97 | ''' 98 | Reset relay to normal state. 99 | If open, lock out the relay. 100 | If closed, resets relay to first operation. 101 | ''' 102 | self._check_for_error(self._lib.Relays_Reset()) 103 | 104 | @property 105 | def State(self) -> int: 106 | ''' 107 | Get/Set present state of relay. 108 | If set to open, open relay's controlled element and lock out the relay. 109 | If set to close, close relay's controlled element and resets relay to first operation. 110 | ''' 111 | return self._check_for_error(self._lib.Relays_Get_State()) 112 | 113 | @State.setter 114 | def State(self, Value: int): 115 | self._check_for_error(self._lib.Relays_Set_State(Value)) 116 | 117 | @property 118 | def NormalState(self) -> int: 119 | ''' 120 | Normal state of relay. 121 | 122 | Original COM help: https://opendss.epri.com/NormalState3.html 123 | ''' 124 | return self._check_for_error(self._lib.Relays_Get_NormalState()) 125 | 126 | @NormalState.setter 127 | def NormalState(self, Value: int): 128 | self._check_for_error(self._lib.Relays_Set_NormalState(Value)) 129 | -------------------------------------------------------------------------------- /dss/ISensors.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | from typing import AnyStr 7 | 8 | class ISensors(Iterable): 9 | __slots__ = [] 10 | _is_circuit_element = True 11 | 12 | _columns = [ 13 | 'Name', 14 | 'idx', 15 | 'MeteredElement', 16 | 'MeteredTerminal', 17 | 'IsDelta', 18 | 'ReverseDelta', 19 | 'Currents', 20 | 'PctError', 21 | 'Weight', 22 | 'kVbase', 23 | 'kVARS', 24 | 'kVS', 25 | 'kWS', 26 | 'AllocationFactor', 27 | ] 28 | 29 | def Reset(self): 30 | self._check_for_error(self._lib.Sensors_Reset()) 31 | 32 | def ResetAll(self): 33 | self._check_for_error(self._lib.Sensors_ResetAll()) 34 | 35 | @property 36 | def Currents(self) -> Float64Array: 37 | ''' 38 | Array of doubles for the line current measurements; don't use with kWS and kVARS. 39 | 40 | Original COM help: https://opendss.epri.com/Currents2.html 41 | ''' 42 | self._check_for_error(self._lib.Sensors_Get_Currents_GR()) 43 | return self._get_float64_gr_array() 44 | 45 | @Currents.setter 46 | def Currents(self, Value: Float64Array): 47 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 48 | self._check_for_error(self._lib.Sensors_Set_Currents(ValuePtr, ValueCount)) 49 | 50 | @property 51 | def IsDelta(self) -> bool: 52 | ''' 53 | True if measured voltages are line-line. Currents are always line currents. 54 | 55 | Original COM help: https://opendss.epri.com/IsDelta2.html 56 | ''' 57 | return self._check_for_error(self._lib.Sensors_Get_IsDelta()) != 0 58 | 59 | @IsDelta.setter 60 | def IsDelta(self, Value: bool): 61 | self._check_for_error(self._lib.Sensors_Set_IsDelta(Value)) 62 | 63 | @property 64 | def MeteredElement(self) -> str: 65 | ''' 66 | Full Name of the measured element 67 | 68 | Original COM help: https://opendss.epri.com/MeteredElement1.html 69 | ''' 70 | return self._get_string(self._check_for_error(self._lib.Sensors_Get_MeteredElement())) 71 | 72 | @MeteredElement.setter 73 | def MeteredElement(self, Value: AnyStr): 74 | if not isinstance(Value, bytes): 75 | Value = Value.encode(self._api_util.codec) 76 | 77 | self._check_for_error(self._lib.Sensors_Set_MeteredElement(Value)) 78 | 79 | @property 80 | def MeteredTerminal(self) -> int: 81 | ''' 82 | Number of the measured terminal in the measured element. 83 | 84 | Original COM help: https://opendss.epri.com/MeteredTerminal1.html 85 | ''' 86 | return self._check_for_error(self._lib.Sensors_Get_MeteredTerminal()) 87 | 88 | @MeteredTerminal.setter 89 | def MeteredTerminal(self, Value: int): 90 | self._check_for_error(self._lib.Sensors_Set_MeteredTerminal(Value)) 91 | 92 | @property 93 | def PctError(self) -> float: 94 | ''' 95 | Assumed percent error in the Sensor measurement. Default is 1. 96 | 97 | Original COM help: https://opendss.epri.com/PctError.html 98 | ''' 99 | return self._check_for_error(self._lib.Sensors_Get_PctError()) 100 | 101 | @PctError.setter 102 | def PctError(self, Value: float): 103 | self._check_for_error(self._lib.Sensors_Set_PctError(Value)) 104 | 105 | @property 106 | def ReverseDelta(self) -> bool: 107 | ''' 108 | True if voltage measurements are 1-3, 3-2, 2-1. 109 | 110 | Original COM help: https://opendss.epri.com/ReverseDelta.html 111 | ''' 112 | return self._check_for_error(self._lib.Sensors_Get_ReverseDelta()) != 0 113 | 114 | @ReverseDelta.setter 115 | def ReverseDelta(self, Value: bool): 116 | self._check_for_error(self._lib.Sensors_Set_ReverseDelta(Value)) 117 | 118 | @property 119 | def Weight(self) -> float: 120 | ''' 121 | Weighting factor for this Sensor measurement with respect to other Sensors. Default is 1. 122 | 123 | Original COM help: https://opendss.epri.com/Weight.html 124 | ''' 125 | return self._check_for_error(self._lib.Sensors_Get_Weight()) 126 | 127 | @Weight.setter 128 | def Weight(self, Value: float): 129 | self._check_for_error(self._lib.Sensors_Set_Weight(Value)) 130 | 131 | @property 132 | def kVARS(self) -> Float64Array: 133 | ''' 134 | Array of doubles for Q measurements. Overwrites Currents with a new estimate using kWS. 135 | 136 | Original COM help: https://opendss.epri.com/kVARS.html 137 | ''' 138 | self._check_for_error(self._lib.Sensors_Get_kVARS_GR()) 139 | return self._get_float64_gr_array() 140 | 141 | @kVARS.setter 142 | def kVARS(self, Value): 143 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 144 | self._check_for_error(self._lib.Sensors_Set_kVARS(ValuePtr, ValueCount)) 145 | 146 | @property 147 | def kVS(self) -> Float64Array: 148 | ''' 149 | Array of doubles for the LL or LN (depending on Delta connection) voltage measurements. 150 | 151 | Original COM help: https://opendss.epri.com/kVS.html 152 | ''' 153 | self._check_for_error(self._lib.Sensors_Get_kVS_GR()) 154 | return self._get_float64_gr_array() 155 | 156 | @kVS.setter 157 | def kVS(self, Value: Float64Array): 158 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 159 | self._check_for_error(self._lib.Sensors_Set_kVS(ValuePtr, ValueCount)) 160 | 161 | @property 162 | def kVbase(self) -> float: 163 | ''' 164 | Voltage base for the sensor measurements. LL for 2 and 3-phase sensors, LN for 1-phase sensors. 165 | 166 | Original COM help: https://opendss.epri.com/kVBase1.html 167 | ''' 168 | return self._check_for_error(self._lib.Sensors_Get_kVbase()) 169 | 170 | @kVbase.setter 171 | def kVbase(self, Value: float): 172 | self._check_for_error(self._lib.Sensors_Set_kVbase(Value)) 173 | 174 | @property 175 | def kWS(self) -> Float64Array: 176 | ''' 177 | Array of doubles for P measurements. Overwrites Currents with a new estimate using kVARS. 178 | 179 | Original COM help: https://opendss.epri.com/kWS.html 180 | ''' 181 | self._check_for_error(self._lib.Sensors_Get_kWS_GR()) 182 | return self._get_float64_gr_array() 183 | 184 | @kWS.setter 185 | def kWS(self, Value: Float64Array): 186 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 187 | self._check_for_error(self._lib.Sensors_Set_kWS(ValuePtr, ValueCount)) 188 | 189 | @property 190 | def AllocationFactor(self): 191 | ''' 192 | Array of doubles for the allocation factors for each phase. 193 | 194 | Original COM help: https://opendss.epri.com/AllocationFactor1.html 195 | ''' 196 | self._check_for_error(self._lib.Sensors_Get_AllocationFactor_GR()) 197 | return self._get_float64_gr_array() 198 | -------------------------------------------------------------------------------- /dss/IStorages.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2023-2024 Paulo Meira 3 | # Copyright (c) 2023-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | from typing import List, Union 7 | from .enums import StorageStates 8 | 9 | class IStorages(Iterable): 10 | '''Storage objects''' 11 | 12 | __slots__ = [] 13 | _is_circuit_element = True 14 | 15 | _columns = [ 16 | 'Name', 17 | 'idx', 18 | 'RegisterNames', 19 | 'RegisterValues', 20 | 'puSOC', 21 | 'State', 22 | ] 23 | 24 | 25 | @property 26 | def puSOC(self) -> float: 27 | '''Per unit state of charge''' 28 | return self._check_for_error(self._lib.Storages_Get_puSOC()) 29 | 30 | @puSOC.setter 31 | def puSOC(self, Value: float): 32 | self._check_for_error(self._lib.Storages_Set_puSOC(Value)) 33 | 34 | @property 35 | def State(self) -> StorageStates: 36 | ''' 37 | Get/set state: 0=Idling; 1=Discharging; -1=Charging; 38 | ''' 39 | return StorageStates(self._check_for_error(self._lib.Storages_Get_State())) 40 | 41 | @State.setter 42 | def State(self, Value: Union[int, StorageStates]): 43 | self._check_for_error(self._lib.Storages_Set_State(Value)) 44 | 45 | @property 46 | def RegisterNames(self) -> List[str]: 47 | ''' 48 | Array of Storage energy meter register names 49 | 50 | See also the enum `GeneratorRegisters`. 51 | ''' 52 | return self._check_for_error(self._get_string_array(self._lib.Storages_Get_RegisterNames)) 53 | 54 | @property 55 | def RegisterValues(self) -> Float64Array: 56 | '''Array of values in Storage registers.''' 57 | self._check_for_error(self._lib.Storages_Get_RegisterValues_GR()) 58 | return self._get_float64_gr_array() 59 | 60 | -------------------------------------------------------------------------------- /dss/ISwtControls.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import AnyStr, Union 6 | from .enums import ActionCodes 7 | 8 | class ISwtControls(Iterable): 9 | __slots__ = [] 10 | _is_circuit_element = True 11 | 12 | _columns = [ 13 | 'Name', 14 | 'idx', 15 | 'Action', 16 | 'Delay', 17 | 'IsLocked', 18 | 'NormalState', 19 | 'State', 20 | 'SwitchedObj', 21 | 'SwitchedTerm', 22 | ] 23 | 24 | def Reset(self): 25 | self._check_for_error(self._lib.SwtControls_Reset()) 26 | 27 | @property 28 | def Action(self) -> int: 29 | ''' 30 | Open or Close the switch. No effect if switch is locked. However, Reset removes any lock and then closes the switch (shelf state). 31 | 32 | Original COM help: https://opendss.epri.com/Action1.html 33 | ''' 34 | return self._check_for_error(self._lib.SwtControls_Get_Action()) 35 | 36 | @Action.setter 37 | def Action(self, Value: int): 38 | self._check_for_error(self._lib.SwtControls_Set_Action(Value)) 39 | 40 | @property 41 | def Delay(self) -> float: 42 | ''' 43 | Time delay [s] between arming and opening or closing the switch. Control may reset before actually operating the switch. 44 | 45 | Original COM help: https://opendss.epri.com/Delay3.html 46 | ''' 47 | return self._check_for_error(self._lib.SwtControls_Get_Delay()) 48 | 49 | @Delay.setter 50 | def Delay(self, Value: float): 51 | self._check_for_error(self._lib.SwtControls_Set_Delay(Value)) 52 | 53 | @property 54 | def IsLocked(self) -> bool: 55 | ''' 56 | The lock prevents both manual and automatic switch operation. 57 | 58 | Original COM help: https://opendss.epri.com/IsLocked.html 59 | ''' 60 | return self._check_for_error(self._lib.SwtControls_Get_IsLocked()) != 0 61 | 62 | @IsLocked.setter 63 | def IsLocked(self, Value: bool): 64 | self._check_for_error(self._lib.SwtControls_Set_IsLocked(Value)) 65 | 66 | @property 67 | def NormalState(self) -> ActionCodes: 68 | ''' 69 | Get/set Normal state of switch (see ActionCodes) dssActionOpen or dssActionClose 70 | ''' 71 | return ActionCodes(self._check_for_error(self._lib.SwtControls_Get_NormalState())) 72 | 73 | @NormalState.setter 74 | def NormalState(self, Value: Union[int, ActionCodes]): 75 | self._check_for_error(self._lib.SwtControls_Set_NormalState(Value)) 76 | 77 | @property 78 | def State(self) -> int: 79 | ''' 80 | Set it to force the switch to a specified state, otherwise read its present state. 81 | 82 | Original COM help: https://opendss.epri.com/State.html 83 | ''' 84 | return self._check_for_error(self._lib.SwtControls_Get_State()) 85 | 86 | @State.setter 87 | def State(self, Value: int): 88 | self._check_for_error(self._lib.SwtControls_Set_State(Value)) 89 | 90 | @property 91 | def SwitchedObj(self) -> str: 92 | ''' 93 | Full name of the switched element. 94 | 95 | Original COM help: https://opendss.epri.com/SwitchedObj3.html 96 | ''' 97 | return self._get_string(self._check_for_error(self._lib.SwtControls_Get_SwitchedObj())) 98 | 99 | @SwitchedObj.setter 100 | def SwitchedObj(self, Value: AnyStr): 101 | if not isinstance(Value, bytes): 102 | Value = Value.encode(self._api_util.codec) 103 | 104 | self._check_for_error(self._lib.SwtControls_Set_SwitchedObj(Value)) 105 | 106 | @property 107 | def SwitchedTerm(self) -> int: 108 | ''' 109 | Terminal number where the switch is located on the SwitchedObj 110 | 111 | Original COM help: https://opendss.epri.com/SwitchedTerm3.html 112 | ''' 113 | return self._check_for_error(self._lib.SwtControls_Get_SwitchedTerm()) 114 | 115 | @SwitchedTerm.setter 116 | def SwitchedTerm(self, Value: int): 117 | self._check_for_error(self._lib.SwtControls_Set_SwitchedTerm(Value)) 118 | 119 | -------------------------------------------------------------------------------- /dss/ITSData.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | 6 | class ITSData(Iterable): 7 | ''' 8 | TSData objects 9 | 10 | (API Extension) 11 | ''' 12 | 13 | __slots__ = [] 14 | 15 | _columns = [ 16 | 'Name', 17 | 'idx', 18 | 'NormAmps', 19 | 'EmergAmps', 20 | 'Rdc', 21 | 'Rac', 22 | 'GMRac', 23 | 'GMRUnits', 24 | 'Radius', 25 | 'RadiusUnits', 26 | 'ResistanceUnits', 27 | 'Diameter', 28 | 'TapeLayer', 29 | 'TapeLap', 30 | 'DiaShield', 31 | 'DiaCable', 32 | 'DiaIns', 33 | 'InsLayer', 34 | 'EpsR', 35 | ] 36 | 37 | @property 38 | def EmergAmps(self) -> float: 39 | '''Emergency ampere rating''' 40 | return self._check_for_error(self._lib.TSData_Get_EmergAmps()) 41 | 42 | @EmergAmps.setter 43 | def EmergAmps(self, Value: float): 44 | self._check_for_error(self._lib.TSData_Set_EmergAmps(Value)) 45 | 46 | @property 47 | def NormAmps(self) -> float: 48 | '''Normal Ampere rating''' 49 | return self._check_for_error(self._lib.TSData_Get_NormAmps()) 50 | 51 | @NormAmps.setter 52 | def NormAmps(self, Value: float): 53 | self._check_for_error(self._lib.TSData_Set_NormAmps(Value)) 54 | 55 | @property 56 | def Rdc(self) -> float: 57 | return self._check_for_error(self._lib.TSData_Get_Rdc()) 58 | 59 | @Rdc.setter 60 | def Rdc(self, Value: float): 61 | self._check_for_error(self._lib.TSData_Set_Rdc(Value)) 62 | 63 | @property 64 | def Rac(self) -> float: 65 | return self._check_for_error(self._lib.TSData_Get_Rac()) 66 | 67 | @Rac.setter 68 | def Rac(self, Value: float): 69 | self._check_for_error(self._lib.TSData_Set_Rac(Value)) 70 | 71 | @property 72 | def GMRac(self) -> float: 73 | return self._check_for_error(self._lib.TSData_Get_GMRac()) 74 | 75 | @GMRac.setter 76 | def GMRac(self, Value: float): 77 | self._check_for_error(self._lib.TSData_Set_GMRac(Value)) 78 | 79 | @property 80 | def GMRUnits(self) -> int: 81 | return self._check_for_error(self._lib.TSData_Get_GMRUnits()) 82 | 83 | @GMRUnits.setter 84 | def GMRUnits(self, Value: int): 85 | self._check_for_error(self._lib.TSData_Set_GMRUnits(Value)) 86 | 87 | @property 88 | def Radius(self) -> float: 89 | return self._check_for_error(self._lib.TSData_Get_Radius()) 90 | 91 | @Radius.setter 92 | def Radius(self, Value: float): 93 | self._check_for_error(self._lib.TSData_Set_Radius(Value)) 94 | 95 | @property 96 | def RadiusUnits(self) -> int: 97 | return self._check_for_error(self._lib.TSData_Get_RadiusUnits()) 98 | 99 | @RadiusUnits.setter 100 | def RadiusUnits(self, Value: int): 101 | self._check_for_error(self._lib.TSData_Set_RadiusUnits(Value)) 102 | 103 | @property 104 | def ResistanceUnits(self) -> int: 105 | return self._check_for_error(self._lib.TSData_Get_ResistanceUnits()) 106 | 107 | @ResistanceUnits.setter 108 | def ResistanceUnits(self, Value: int): 109 | self._check_for_error(self._lib.TSData_Set_ResistanceUnits(Value)) 110 | 111 | @property 112 | def Diameter(self) -> float: 113 | return self._check_for_error(self._lib.TSData_Get_Diameter()) 114 | 115 | @Diameter.setter 116 | def Diameter(self, Value: float): 117 | self._check_for_error(self._lib.TSData_Set_Diameter(Value)) 118 | 119 | @property 120 | def EpsR(self) -> float: 121 | return self._check_for_error(self._lib.TSData_Get_EpsR()) 122 | 123 | @EpsR.setter 124 | def EpsR(self, Value: float): 125 | self._check_for_error(self._lib.TSData_Set_EpsR(Value)) 126 | 127 | @property 128 | def InsLayer(self) -> float: 129 | return self._check_for_error(self._lib.TSData_Get_InsLayer()) 130 | 131 | @InsLayer.setter 132 | def InsLayer(self, Value: float): 133 | self._check_for_error(self._lib.TSData_Set_InsLayer(Value)) 134 | 135 | @property 136 | def DiaIns(self) -> float: 137 | return self._check_for_error(self._lib.TSData_Get_DiaIns()) 138 | 139 | @DiaIns.setter 140 | def DiaIns(self, Value: float): 141 | self._check_for_error(self._lib.TSData_Set_DiaIns(Value)) 142 | 143 | @property 144 | def DiaCable(self) -> float: 145 | return self._check_for_error(self._lib.TSData_Get_DiaCable()) 146 | 147 | @DiaCable.setter 148 | def DiaCable(self, Value: float): 149 | self._check_for_error(self._lib.TSData_Set_DiaCable(Value)) 150 | 151 | @property 152 | def DiaShield(self) -> float: 153 | return self._check_for_error(self._lib.TSData_Get_DiaShield()) 154 | 155 | @DiaShield.setter 156 | def DiaShield(self, Value: float): 157 | self._check_for_error(self._lib.TSData_Set_DiaShield(Value)) 158 | 159 | @property 160 | def TapeLayer(self) -> float: 161 | return self._check_for_error(self._lib.TSData_Get_TapeLayer()) 162 | 163 | @TapeLayer.setter 164 | def TapeLayer(self, Value: float): 165 | self._check_for_error(self._lib.TSData_Set_TapeLayer(Value)) 166 | 167 | @property 168 | def TapeLap(self) -> float: 169 | return self._check_for_error(self._lib.TSData_Get_TapeLap()) 170 | 171 | @TapeLap.setter 172 | def TapeLap(self, Value: float): 173 | self._check_for_error(self._lib.TSData_Set_TapeLap(Value)) 174 | -------------------------------------------------------------------------------- /dss/IText.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from typing import AnyStr, List, Union 6 | 7 | class IText(Base): 8 | __slots__ = [] 9 | 10 | @property 11 | def Command(self) -> str: 12 | ''' 13 | Input command string for the DSS. 14 | 15 | Original COM help: https://opendss.epri.com/Command1.html 16 | ''' 17 | return self._get_string(self._check_for_error(self._lib.Text_Get_Command())) 18 | 19 | @Command.setter 20 | def Command(self, Value: str): 21 | if not isinstance(Value, bytes): 22 | Value = Value.encode(self._api_util.codec) 23 | 24 | self._check_for_error(self._lib.Text_Set_Command(Value)) 25 | 26 | @property 27 | def Result(self) -> str: 28 | ''' 29 | Result string for the last command. 30 | 31 | Original COM help: https://opendss.epri.com/Result.html 32 | ''' 33 | return self._get_string(self._check_for_error(self._lib.Text_Get_Result())) 34 | 35 | def Commands(self, Value: Union[AnyStr, List[AnyStr]]): 36 | ''' 37 | Runs a list of strings or a large string as commands directly in the DSS engine. 38 | Intermediate results (from Text.Result) are ignored. 39 | 40 | Value can be a list of strings, or a single large string (usually faster). 41 | 42 | **(API Extension)** 43 | ''' 44 | if isinstance(Value, str) or isinstance(Value, bytes): 45 | if not isinstance(Value, bytes): 46 | Value = Value.encode(self._api_util.codec) 47 | 48 | self._check_for_error(self._lib.Text_CommandBlock(Value)) 49 | else: 50 | self._check_for_error(self._set_string_array(self._lib.Text_CommandArray, Value)) 51 | 52 | -------------------------------------------------------------------------------- /dss/ITopology.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | from typing import List, AnyStr 6 | 7 | class ITopology(Base): 8 | __slots__ = [] 9 | 10 | _columns = [ 11 | 'NumIsolatedLoads', 12 | 'AllIsolatedBranches', 13 | 'NumIsolatedBranches', 14 | 'AllIsolatedLoads', 15 | 'ActiveLevel', 16 | 'BranchName', 17 | 'AllLoopedPairs', 18 | 'NumLoops', 19 | 'ActiveBranch' 20 | ] 21 | 22 | @property 23 | def ActiveBranch(self) -> int: 24 | ''' 25 | Returns index of the active branch 26 | 27 | Original COM help: https://opendss.epri.com/ActiveBranch.html 28 | ''' 29 | return self._check_for_error(self._lib.Topology_Get_ActiveBranch()) 30 | 31 | @property 32 | def ActiveLevel(self) -> int: 33 | ''' 34 | Topological depth of the active branch 35 | 36 | Original COM help: https://opendss.epri.com/ActiveLevel.html 37 | ''' 38 | return self._check_for_error(self._lib.Topology_Get_ActiveLevel()) 39 | 40 | @property 41 | def AllIsolatedBranches(self) -> List[str]: 42 | ''' 43 | Array of all isolated branch names. 44 | 45 | Original COM help: https://opendss.epri.com/AllIsolatedBranches.html 46 | ''' 47 | return self._check_for_error(self._get_string_array(self._lib.Topology_Get_AllIsolatedBranches)) 48 | 49 | @property 50 | def AllIsolatedLoads(self) -> List[str]: 51 | ''' 52 | Array of all isolated load names. 53 | 54 | Original COM help: https://opendss.epri.com/AllIsolatedLoads.html 55 | ''' 56 | return self._check_for_error(self._get_string_array(self._lib.Topology_Get_AllIsolatedLoads)) 57 | 58 | @property 59 | def AllLoopedPairs(self) -> List[str]: 60 | ''' 61 | Array of all looped element names, by pairs. 62 | 63 | Original COM help: https://opendss.epri.com/AllLoopedPairs.html 64 | ''' 65 | return self._check_for_error(self._get_string_array(self._lib.Topology_Get_AllLoopedPairs)) 66 | 67 | @property 68 | def BackwardBranch(self) -> int: 69 | ''' 70 | Move back toward the source, return index of new active branch, or 0 if no more. 71 | 72 | Original COM help: https://opendss.epri.com/BackwardBranch.html 73 | ''' 74 | return self._check_for_error(self._lib.Topology_Get_BackwardBranch()) 75 | 76 | @property 77 | def BranchName(self) -> str: 78 | ''' 79 | Name of the active branch. 80 | 81 | Original COM help: https://opendss.epri.com/BranchName.html 82 | ''' 83 | return self._get_string(self._check_for_error(self._lib.Topology_Get_BranchName())) 84 | 85 | @BranchName.setter 86 | def BranchName(self, Value: AnyStr): 87 | if not isinstance(Value, bytes): 88 | Value = Value.encode(self._api_util.codec) 89 | 90 | self._check_for_error(self._lib.Topology_Set_BranchName(Value)) 91 | 92 | @property 93 | def BusName(self) -> str: 94 | ''' 95 | Set the active branch to one containing this bus, return index or 0 if not found 96 | 97 | Original COM help: https://opendss.epri.com/BusName.html 98 | ''' 99 | return self._get_string(self._check_for_error(self._lib.Topology_Get_BusName())) 100 | 101 | @BusName.setter 102 | def BusName(self, Value: AnyStr): 103 | if not isinstance(Value, bytes): 104 | Value = Value.encode(self._api_util.codec) 105 | 106 | self._check_for_error(self._lib.Topology_Set_BusName(Value)) 107 | 108 | @property 109 | def First(self) -> int: 110 | ''' 111 | Sets the first branch active, returns 0 if none. 112 | 113 | Original COM help: https://opendss.epri.com/First19.html 114 | ''' 115 | return self._check_for_error(self._lib.Topology_Get_First()) 116 | 117 | @property 118 | def FirstLoad(self) -> int: 119 | ''' 120 | First load at the active branch, return index or 0 if none. 121 | 122 | Original COM help: https://opendss.epri.com/FirstLoad.html 123 | ''' 124 | return self._check_for_error(self._lib.Topology_Get_FirstLoad()) 125 | 126 | @property 127 | def ForwardBranch(self) -> int: 128 | ''' 129 | Move forward in the tree, return index of new active branch or 0 if no more 130 | 131 | Original COM help: https://opendss.epri.com/ForwardBranch.html 132 | ''' 133 | return self._check_for_error(self._lib.Topology_Get_ForwardBranch()) 134 | 135 | @property 136 | def LoopedBranch(self) -> int: 137 | ''' 138 | Move to looped branch, return index or 0 if none. 139 | 140 | Original COM help: https://opendss.epri.com/LoopedBranch.html 141 | ''' 142 | return self._check_for_error(self._lib.Topology_Get_LoopedBranch()) 143 | 144 | @property 145 | def Next(self) -> int: 146 | ''' 147 | Sets the next branch active, returns 0 if no more. 148 | 149 | Original COM help: https://opendss.epri.com/Next18.html 150 | ''' 151 | return self._check_for_error(self._lib.Topology_Get_Next()) 152 | 153 | @property 154 | def NextLoad(self) -> int: 155 | ''' 156 | Next load at the active branch, return index or 0 if no more. 157 | 158 | Original COM help: https://opendss.epri.com/NextLoad.html 159 | ''' 160 | return self._check_for_error(self._lib.Topology_Get_NextLoad()) 161 | 162 | @property 163 | def NumIsolatedBranches(self) -> int: 164 | ''' 165 | Number of isolated branches (PD elements and capacitors). 166 | 167 | Original COM help: https://opendss.epri.com/NumIsolatedBranches.html 168 | ''' 169 | return self._check_for_error(self._lib.Topology_Get_NumIsolatedBranches()) 170 | 171 | @property 172 | def NumIsolatedLoads(self) -> int: 173 | ''' 174 | Number of isolated loads 175 | 176 | Original COM help: https://opendss.epri.com/NumIsolatedLoads.html 177 | ''' 178 | return self._check_for_error(self._lib.Topology_Get_NumIsolatedLoads()) 179 | 180 | @property 181 | def NumLoops(self) -> int: 182 | ''' 183 | Number of loops 184 | 185 | Original COM help: https://opendss.epri.com/NumLoops.html 186 | ''' 187 | return self._check_for_error(self._lib.Topology_Get_NumLoops()) 188 | 189 | @property 190 | def ParallelBranch(self) -> int: 191 | ''' 192 | Move to directly parallel branch, return index or 0 if none. 193 | 194 | Original COM help: https://opendss.epri.com/ParallelBranch.html 195 | ''' 196 | return self._check_for_error(self._lib.Topology_Get_ParallelBranch()) 197 | 198 | -------------------------------------------------------------------------------- /dss/IVsources.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | 6 | class IVsources(Iterable): 7 | __slots__ = [] 8 | _is_circuit_element = True 9 | 10 | _columns = [ 11 | 'Name', 12 | 'idx', 13 | 'Phases', 14 | 'BasekV', 15 | 'AngleDeg', 16 | 'Frequency', 17 | 'pu', 18 | ] 19 | 20 | @property 21 | def AngleDeg(self) -> float: 22 | ''' 23 | Phase angle of first phase in degrees 24 | 25 | Original COM help: https://opendss.epri.com/AngleDeg1.html 26 | ''' 27 | return self._check_for_error(self._lib.Vsources_Get_AngleDeg()) 28 | 29 | @AngleDeg.setter 30 | def AngleDeg(self, Value: float): 31 | self._check_for_error(self._lib.Vsources_Set_AngleDeg(Value)) 32 | 33 | @property 34 | def BasekV(self) -> float: 35 | ''' 36 | Source voltage in kV 37 | 38 | Original COM help: https://opendss.epri.com/BasekV.html 39 | ''' 40 | return self._check_for_error(self._lib.Vsources_Get_BasekV()) 41 | 42 | @BasekV.setter 43 | def BasekV(self, Value: float): 44 | self._check_for_error(self._lib.Vsources_Set_BasekV(Value)) 45 | 46 | @property 47 | def Frequency(self) -> float: 48 | ''' 49 | Source frequency in Hz 50 | 51 | Original COM help: https://opendss.epri.com/Frequency2.html 52 | ''' 53 | return self._check_for_error(self._lib.Vsources_Get_Frequency()) 54 | 55 | @Frequency.setter 56 | def Frequency(self, Value: float): 57 | self._check_for_error(self._lib.Vsources_Set_Frequency(Value)) 58 | 59 | @property 60 | def Phases(self) -> int: 61 | ''' 62 | Number of phases 63 | 64 | Original COM help: https://opendss.epri.com/Phases3.html 65 | ''' 66 | return self._check_for_error(self._lib.Vsources_Get_Phases()) 67 | 68 | @Phases.setter 69 | def Phases(self, Value: int): 70 | self._check_for_error(self._lib.Vsources_Set_Phases(Value)) 71 | 72 | @property 73 | def pu(self) -> float: 74 | ''' 75 | Per-unit value of source voltage 76 | 77 | Original COM help: https://opendss.epri.com/pu.html 78 | ''' 79 | return self._check_for_error(self._lib.Vsources_Get_pu()) 80 | 81 | @pu.setter 82 | def pu(self, Value: float): 83 | self._check_for_error(self._lib.Vsources_Set_pu(Value)) 84 | -------------------------------------------------------------------------------- /dss/IWireData.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from typing import Union 6 | from .enums import LineUnits 7 | 8 | class IWireData(Iterable): 9 | ''' 10 | WireData objects 11 | 12 | (API Extension) 13 | ''' 14 | 15 | __slots__ = [] 16 | 17 | _columns = [ 18 | 'Name', 19 | 'idx', 20 | 'NormAmps', 21 | 'EmergAmps', 22 | 'Rdc', 23 | 'Rac', 24 | 'ResistanceUnits', 25 | 'GMRac', 26 | 'GMRUnits', 27 | 'Radius', 28 | 'Diameter', 29 | 'RadiusUnits', 30 | 'CapRadius', 31 | ] 32 | 33 | @property 34 | def EmergAmps(self) -> float: 35 | '''Emergency ampere rating''' 36 | return self._check_for_error(self._lib.WireData_Get_EmergAmps()) 37 | 38 | @EmergAmps.setter 39 | def EmergAmps(self, Value: float): 40 | self._check_for_error(self._lib.WireData_Set_EmergAmps(Value)) 41 | 42 | @property 43 | def NormAmps(self) -> float: 44 | '''Normal Ampere rating''' 45 | return self._check_for_error(self._lib.WireData_Get_NormAmps()) 46 | 47 | @NormAmps.setter 48 | def NormAmps(self, Value: float): 49 | self._check_for_error(self._lib.WireData_Set_NormAmps(Value)) 50 | 51 | @property 52 | def Rdc(self) -> float: 53 | return self._check_for_error(self._lib.WireData_Get_Rdc()) 54 | 55 | @Rdc.setter 56 | def Rdc(self, Value: float): 57 | self._check_for_error(self._lib.WireData_Set_Rdc(Value)) 58 | 59 | @property 60 | def Rac(self) -> float: 61 | return self._check_for_error(self._lib.WireData_Get_Rac()) 62 | 63 | @Rac.setter 64 | def Rac(self, Value: float): 65 | self._check_for_error(self._lib.WireData_Set_Rac(Value)) 66 | 67 | @property 68 | def GMRac(self) -> float: 69 | return self._check_for_error(self._lib.WireData_Get_GMRac()) 70 | 71 | @GMRac.setter 72 | def GMRac(self, Value: float): 73 | self._check_for_error(self._lib.WireData_Set_GMRac(Value)) 74 | 75 | @property 76 | def GMRUnits(self) -> LineUnits: 77 | return LineUnits(self._check_for_error(self._lib.WireData_Get_GMRUnits())) 78 | 79 | @GMRUnits.setter 80 | def GMRUnits(self, Value: Union[int, LineUnits]): 81 | self._check_for_error(self._lib.WireData_Set_GMRUnits(Value)) 82 | 83 | @property 84 | def Radius(self) -> float: 85 | return self._check_for_error(self._lib.WireData_Get_Radius()) 86 | 87 | @Radius.setter 88 | def Radius(self, Value: float): 89 | self._check_for_error(self._lib.WireData_Set_Radius(Value)) 90 | 91 | @property 92 | def RadiusUnits(self) -> int: 93 | return self._check_for_error(self._lib.WireData_Get_RadiusUnits()) 94 | 95 | @RadiusUnits.setter 96 | def RadiusUnits(self, Value: int): 97 | self._check_for_error(self._lib.WireData_Set_RadiusUnits(Value)) 98 | 99 | @property 100 | def ResistanceUnits(self) -> LineUnits: 101 | return LineUnits(self._check_for_error(self._lib.WireData_Get_ResistanceUnits())) 102 | 103 | @ResistanceUnits.setter 104 | def ResistanceUnits(self, Value: Union[int, LineUnits]): 105 | self._check_for_error(self._lib.WireData_Set_ResistanceUnits(Value)) 106 | 107 | @property 108 | def Diameter(self) -> float: 109 | return self._check_for_error(self._lib.WireData_Get_Diameter()) 110 | 111 | @Diameter.setter 112 | def Diameter(self, Value: float): 113 | self._check_for_error(self._lib.WireData_Set_Diameter(Value)) 114 | 115 | @property 116 | def CapRadius(self) -> float: 117 | '''Equivalent conductor radius for capacitance calcs. Specify this for bundled conductors. Defaults to same value as radius.''' 118 | return self._check_for_error(self._lib.WireData_Get_CapRadius()) 119 | 120 | @CapRadius.setter 121 | def CapRadius(self, Value: float): 122 | self._check_for_error(self._lib.WireData_Set_CapRadius(Value)) 123 | -------------------------------------------------------------------------------- /dss/IXYCurves.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Iterable 5 | from ._types import Float64Array 6 | 7 | class IXYCurves(Iterable): 8 | __slots__ = [] 9 | 10 | _columns = [ 11 | 'Name', 12 | 'idx', 13 | 'Npts', 14 | 'Xarray', 15 | 'Xscale', 16 | 'Xshift', 17 | 'Yarray', 18 | 'Yscale', 19 | 'Yshift', 20 | 'x', 21 | 'y', 22 | ] 23 | 24 | @property 25 | def Npts(self) -> int: 26 | ''' 27 | Get/Set Number of points in X-Y curve 28 | 29 | Original COM help: https://opendss.epri.com/Npts1.html 30 | ''' 31 | return self._check_for_error(self._lib.XYCurves_Get_Npts()) 32 | 33 | @Npts.setter 34 | def Npts(self, Value: int): 35 | self._check_for_error(self._lib.XYCurves_Set_Npts(Value)) 36 | 37 | @property 38 | def Xarray(self) -> Float64Array: 39 | ''' 40 | Get/set X values as a Array of doubles. Set Npts to max number expected if setting 41 | 42 | Original COM help: https://opendss.epri.com/Xarray.html 43 | ''' 44 | self._check_for_error(self._lib.XYCurves_Get_Xarray_GR()) 45 | return self._get_float64_gr_array() 46 | 47 | @Xarray.setter 48 | def Xarray(self, Value: Float64Array): 49 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 50 | self._check_for_error(self._lib.XYCurves_Set_Xarray(ValuePtr, ValueCount)) 51 | 52 | @property 53 | def Xscale(self) -> float: 54 | ''' 55 | Factor to scale X values from original curve 56 | 57 | Original COM help: https://opendss.epri.com/Xscale.html 58 | ''' 59 | return self._check_for_error(self._lib.XYCurves_Get_Xscale()) 60 | 61 | @Xscale.setter 62 | def Xscale(self, Value: float): 63 | self._check_for_error(self._lib.XYCurves_Set_Xscale(Value)) 64 | 65 | @property 66 | def Xshift(self) -> float: 67 | ''' 68 | Amount to shift X value from original curve 69 | 70 | Original COM help: https://opendss.epri.com/Xshift.html 71 | ''' 72 | return self._check_for_error(self._lib.XYCurves_Get_Xshift()) 73 | 74 | @Xshift.setter 75 | def Xshift(self, Value: float): 76 | self._check_for_error(self._lib.XYCurves_Set_Xshift(Value)) 77 | 78 | @property 79 | def Yarray(self) -> Float64Array: 80 | ''' 81 | Get/Set Y values in curve; Set Npts to max number expected if setting 82 | 83 | Original COM help: https://opendss.epri.com/Yarray.html 84 | ''' 85 | self._check_for_error(self._lib.XYCurves_Get_Yarray_GR()) 86 | return self._get_float64_gr_array() 87 | 88 | @Yarray.setter 89 | def Yarray(self, Value: Float64Array): 90 | Value, ValuePtr, ValueCount = self._prepare_float64_array(Value) 91 | self._check_for_error(self._lib.XYCurves_Set_Yarray(ValuePtr, ValueCount)) 92 | 93 | @property 94 | def Yscale(self) -> float: 95 | ''' 96 | Factor to scale Y values from original curve 97 | 98 | Original COM help: https://opendss.epri.com/Yscale.html 99 | ''' 100 | return self._check_for_error(self._lib.XYCurves_Get_Yscale()) 101 | 102 | @Yscale.setter 103 | def Yscale(self, Value: float): 104 | self._check_for_error(self._lib.XYCurves_Set_Yscale(Value)) 105 | 106 | @property 107 | def Yshift(self) -> float: 108 | ''' 109 | Amount to shift Y value from original curve 110 | 111 | Original COM help: https://opendss.epri.com/Yshift.html 112 | ''' 113 | return self._check_for_error(self._lib.XYCurves_Get_Yshift()) 114 | 115 | @Yshift.setter 116 | def Yshift(self, Value: float): 117 | self._check_for_error(self._lib.XYCurves_Set_Yshift(Value)) 118 | 119 | @property 120 | def x(self) -> float: 121 | ''' 122 | Set X value or get interpolated value after setting Y 123 | 124 | Original COM help: https://opendss.epri.com/x4.html 125 | ''' 126 | return self._check_for_error(self._lib.XYCurves_Get_x()) 127 | 128 | @x.setter 129 | def x(self, Value: float): 130 | self._check_for_error(self._lib.XYCurves_Set_x(Value)) 131 | 132 | @property 133 | def y(self) -> float: 134 | ''' 135 | Set Y value or get interpolated Y value after setting X 136 | 137 | Original COM help: https://opendss.epri.com/y1.html 138 | ''' 139 | return self._check_for_error(self._lib.XYCurves_Get_y()) 140 | 141 | @y.setter 142 | def y(self, Value: float): 143 | self._check_for_error(self._lib.XYCurves_Set_y(Value)) 144 | -------------------------------------------------------------------------------- /dss/IYMatrix.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2016-2024 Paulo Meira 3 | # Copyright (c) 2018-2024 DSS-Extensions contributors 4 | from ._cffi_api_util import Base 5 | import numpy as np 6 | from ._types import Int32Array, ComplexArray 7 | from typing import Tuple, List 8 | 9 | class IYMatrix(Base): 10 | ''' 11 | YMatrix provides access to some lower-level solution aspects. 12 | 13 | Part of this class is ported from the original OpenDSSDirect.DLL back in 2017, but 14 | part is new. Since this is not exposed in the official COM API, it is marked as an extension. 15 | 16 | (**API Extension**) 17 | ''' 18 | 19 | __slots__ = [] 20 | 21 | def GetCompressedYMatrix(self, factor: bool = True) -> Tuple[ComplexArray, Int32Array, Int32Array]: 22 | '''Return as (data, indices, indptr) that can fed into `scipy.sparse.csc_matrix`''' 23 | 24 | ffi = self._api_util.ffi 25 | 26 | nBus = ffi.new('uint32_t*') 27 | nBus[0] = 0 28 | nNz = ffi.new('uint32_t*') 29 | nNz[0] = 0 30 | 31 | ColPtr = ffi.new('int32_t**') 32 | RowIdxPtr = ffi.new('int32_t**') 33 | cValsPtr = ffi.new('double**') 34 | 35 | self._lib.YMatrix_GetCompressedYMatrix(factor, nBus, nNz, ColPtr, RowIdxPtr, cValsPtr) 36 | 37 | if not nBus[0] or not nNz[0]: 38 | res = None 39 | else: 40 | # return as (data, indices, indptr) that can fed into scipy.sparse.csc_matrix 41 | res = ( 42 | np.frombuffer(ffi.buffer(cValsPtr[0], nNz[0] * 16), dtype=complex).copy(), 43 | np.frombuffer(ffi.buffer(RowIdxPtr[0], nNz[0] * 4), dtype=np.int32).copy(), 44 | np.frombuffer(ffi.buffer(ColPtr[0], (nBus[0] + 1) * 4), dtype=np.int32).copy() 45 | ) 46 | 47 | self._lib.DSS_Dispose_PInteger(ColPtr) 48 | self._lib.DSS_Dispose_PInteger(RowIdxPtr) 49 | self._lib.DSS_Dispose_PDouble(cValsPtr) 50 | 51 | self._check_for_error() 52 | 53 | return res 54 | 55 | def ZeroInjCurr(self): 56 | self._check_for_error(self._lib.YMatrix_ZeroInjCurr()) 57 | 58 | def GetSourceInjCurrents(self): 59 | self._check_for_error(self._lib.YMatrix_GetSourceInjCurrents()) 60 | 61 | def GetPCInjCurr(self): 62 | self._check_for_error(self._lib.YMatrix_GetPCInjCurr()) 63 | 64 | def BuildYMatrixD(self, BuildOps: int, AllocateVI: bool): 65 | self._check_for_error(self._lib.YMatrix_BuildYMatrixD(BuildOps, AllocateVI)) 66 | 67 | def AddInAuxCurrents(self, SType): 68 | self._check_for_error(self._lib.YMatrix_AddInAuxCurrents(SType)) 69 | 70 | def GetIPointer(self): 71 | '''Get access to the internal Current pointer''' 72 | IvectorPtr = self._api_util.ffi.new('double**') 73 | self._check_for_error(self._lib.YMatrix_getIpointer(IvectorPtr)) 74 | return IvectorPtr[0] 75 | 76 | def GetVPointer(self): 77 | '''Get access to the internal Voltage pointer''' 78 | VvectorPtr = self._api_util.ffi.new('double**') 79 | self._check_for_error(self._lib.YMatrix_getVpointer(VvectorPtr)) 80 | return VvectorPtr[0] 81 | 82 | def SolveSystem(self, NodeV=None) -> int: 83 | if NodeV is not None and type(NodeV) is not np.ndarray: 84 | NodeV = np.array(NodeV) 85 | 86 | if NodeV is None: 87 | NodeVPtr = self._api_util.ffi.NULL 88 | else: 89 | NodeVPtr = self._api_util.ffi.cast("double *", NodeV.ctypes.data) 90 | 91 | result = self._check_for_error(self._lib.YMatrix_SolveSystem(NodeVPtr)) 92 | return result 93 | 94 | @property 95 | def SystemYChanged(self) -> bool: 96 | return self._check_for_error(self._lib.YMatrix_Get_SystemYChanged() != 0) 97 | 98 | @SystemYChanged.setter 99 | def SystemYChanged(self, value: bool): 100 | self._check_for_error(self._lib.YMatrix_Set_SystemYChanged(value)) 101 | 102 | @property 103 | def UseAuxCurrents(self) -> bool: 104 | return self._check_for_error(self._lib.YMatrix_Get_UseAuxCurrents() != 0) 105 | 106 | @UseAuxCurrents.setter 107 | def UseAuxCurrents(self, value: bool): 108 | self._check_for_error(self._lib.YMatrix_Set_UseAuxCurrents(value)) 109 | 110 | # for better compatibility with OpenDSSDirect.py 111 | getYSparse = GetCompressedYMatrix 112 | 113 | @property 114 | def SolverOptions(self) -> int: 115 | '''Sparse solver options. See the enumeration SparseSolverOptions''' 116 | return self._lib.YMatrix_Get_SolverOptions() 117 | 118 | @SolverOptions.setter 119 | def SolverOptions(self, Value: int): 120 | self._lib.YMatrix_Set_SolverOptions(Value) 121 | 122 | def getI(self) -> List[float]: 123 | '''Get the data from the internal Current pointer''' 124 | IvectorPtr = self.GetIPointer() 125 | return self._api_util.ffi.unpack(IvectorPtr, 2 * (self._check_for_error(self._lib.Circuit_Get_NumNodes() + 1))) 126 | 127 | def getV(self) -> List[float]: 128 | '''Get the data from the internal Voltage pointer''' 129 | VvectorPtr = self.GetVPointer() 130 | return self._api_util.ffi.unpack(VvectorPtr, 2 * (self._check_for_error(self._lib.Circuit_Get_NumNodes() + 1))) 131 | 132 | def CheckConvergence(self) -> bool: 133 | return self._check_for_error(self._lib.YMatrix_CheckConvergence() != 0) 134 | 135 | def SetGeneratordQdV(self): 136 | self._check_for_error(self._lib.YMatrix_SetGeneratordQdV()) 137 | 138 | @property 139 | def LoadsNeedUpdating(self) -> bool: 140 | return self._check_for_error(self._lib.YMatrix_Get_LoadsNeedUpdating() != 0) 141 | 142 | @LoadsNeedUpdating.setter 143 | def LoadsNeedUpdating(self, value: bool): 144 | self._check_for_error(self._lib.YMatrix_Set_LoadsNeedUpdating(value)) 145 | 146 | @property 147 | def SolutionInitialized(self) -> bool: 148 | return self._check_for_error(self._lib.YMatrix_Get_SolutionInitialized() != 0) 149 | 150 | @SolutionInitialized.setter 151 | def SolutionInitialized(self, value: bool): 152 | self._check_for_error(self._lib.YMatrix_Set_SolutionInitialized(value)) 153 | 154 | @property 155 | def Iteration(self) -> int: 156 | return self._check_for_error(self._lib.YMatrix_Get_Iteration()) 157 | 158 | @Iteration.setter 159 | def Iteration(self, value: int): 160 | self._check_for_error(self._lib.YMatrix_Set_Iteration(value)) 161 | -------------------------------------------------------------------------------- /dss/IZIP.py: -------------------------------------------------------------------------------- 1 | # A compatibility layer for DSS C-API that mimics the official OpenDSS COM interface. 2 | # Copyright (c) 2021-2024 Paulo Meira 3 | from ._cffi_api_util import Base 4 | from typing import AnyStr, Optional, List 5 | 6 | class IZIP(Base): 7 | ''' 8 | ZIP allows controlling the ZIP-compressed file functions from AltDSS/DSS C-API. 9 | 10 | It allows opening a ZIP file, and loading circuits directly from it, without requiring extracting the contents to files before reading. 11 | 12 | The implementation provides a specialization which allows more efficient access if the ZIP file is open and reused for many circuits. 13 | Doing so reduces the overhead of the initial opening and indexing of the file contents. 14 | 15 | (**API Extension**) 16 | ''' 17 | 18 | __slots__ = [] 19 | 20 | _columns = [] 21 | 22 | def Open(self, FileName: AnyStr): 23 | ''' 24 | Opens and prepares a ZIP file to be used by the DSS text parser. 25 | Currently, the ZIP format support is limited by what is provided in the Free Pascal distribution. 26 | Besides that, the full filenames inside the ZIP must be shorter than 256 characters. 27 | The limitations should be removed in a future revision. 28 | 29 | **(API Extension)** 30 | ''' 31 | if not isinstance(FileName, bytes): 32 | FileName = FileName.encode(self._api_util.codec) 33 | 34 | self._check_for_error(self._lib.ZIP_Open(FileName)) 35 | 36 | def Close(self): 37 | ''' 38 | Closes the current open ZIP file 39 | 40 | **(API Extension)** 41 | ''' 42 | self._check_for_error(self._lib.ZIP_Close()) 43 | 44 | def Redirect(self, FileInZip: AnyStr): 45 | ''' 46 | Runs a "Redirect" command inside the current (open) ZIP file. 47 | In the current implementation, all files required by the script must 48 | be present inside the ZIP, using relative paths. The only exceptions are 49 | memory-mapped files. 50 | 51 | **(API Extension)** 52 | ''' 53 | if not isinstance(FileInZip, bytes): 54 | FileInZip = FileInZip.encode(self._api_util.codec) 55 | 56 | self._check_for_error(self._lib.ZIP_Redirect(FileInZip)) 57 | 58 | def Extract(self, FileName: AnyStr) -> bytes: 59 | ''' 60 | Extracts the contents of the file "FileName" from the current (open) ZIP file. 61 | Returns a byte-string. 62 | 63 | **(API Extension)** 64 | ''' 65 | api_util = self._api_util 66 | if not isinstance(FileName, bytes): 67 | FileName = FileName.encode(api_util.codec) 68 | 69 | self._check_for_error(self._lib.ZIP_Extract_GR(FileName)) 70 | ptr, cnt = api_util.gr_int8_pointers 71 | return bytes(api_util.ffi.buffer(ptr[0], cnt[0])) 72 | 73 | def List(self, regexp: Optional[AnyStr]=None) -> List[str]: 74 | ''' 75 | List of strings consisting of all names match the regular expression provided in regexp. 76 | If no expression is provided, all names in the current open ZIP are returned. 77 | 78 | See https://regex.sorokin.engineer/en/latest/regular_expressions.html for information on 79 | the expression syntax and options. 80 | 81 | **(API Extension)** 82 | ''' 83 | if regexp is None or not regexp: 84 | regexp = self._api_util.ffi.NULL 85 | else: 86 | if not isinstance(regexp, bytes): 87 | regexp = regexp.encode(self._api_util.codec) 88 | 89 | return self._check_for_error(self._get_string_array(self._lib.ZIP_List, regexp)) 90 | 91 | def Contains(self, Name: AnyStr) -> bool: 92 | ''' 93 | Check if the given path name is present in the current ZIP file. 94 | 95 | **(API Extension)** 96 | ''' 97 | if not isinstance(Name, bytes): 98 | Name = Name.encode(self._api_util.codec) 99 | 100 | return self._check_for_error(self._lib.ZIP_Contains(Name)) != 0 101 | 102 | def __getitem__(self, FileName) -> bytes: 103 | return self.Extract(FileName) 104 | 105 | def __contains__(self, Name) -> bool: 106 | return self.Contains(Name) 107 | 108 | -------------------------------------------------------------------------------- /dss/Oddie.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from typing import Optional 3 | from ._cffi_api_util import CffiApiUtil 4 | from .IDSS import IDSS 5 | from enum import Flag 6 | 7 | class OddieOptions(Flag): 8 | MapErrors = 0x01 9 | 10 | 11 | class IOddieDSS(IDSS): 12 | r''' 13 | The OddieDSS class exposes the official OpenDSSDirect.DLL binary, 14 | as distributed by EPRI, with the same API as the DSS-Python and 15 | the official COM interface object on Windows. It uses AltDSS Oddie 16 | to achieve this. 17 | 18 | **Note:** This class requires the backend for Oddie to be included in 19 | the `dss_python_backend` package. If it is not available, an import 20 | error should occur when trying to use this. 21 | 22 | AltDSS Oddie wraps OpenDSSDirect.DLL, providing a minimal compatiliby layer 23 | to expose it with the same API as AltDSS/DSS C-API. With it, we can 24 | just reuse most of the tools from the other projects on DSS-Extensions 25 | without too much extra work. 26 | 27 | Note that many functions from DSS-Extensions will not be available and/or 28 | will return errors, and this is expected. There are some issues and/or 29 | limitations from OpenDSSDirect.DLL that may or may not affect specific 30 | use cases; check the documentation on https://dss-extensions.org for 31 | more information. 32 | 33 | :param library_path: The name or full path of the target dynamic library to 34 | load. Defaults to trying to load "OpenDSSDirect" from `c:\Program Files\OpenDSS\x64`, 35 | followed by trying to load it from the current path. 36 | 37 | :param load_flags: Optional, flags to feed the [`LoadLibrary`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa) 38 | (on Windows) or `dlopen` (on Linux and macOS, whenever EPRI supports it). If not provided, a default set will be used. 39 | 40 | :param oddie_options: Optional, Oddie configuration flags. If none passed, 41 | the default settings (recommended) will be used. For advanced users. 42 | ''' 43 | 44 | def __init__(self, library_path: str = '', load_flags: Optional[int] = None, oddie_options: Optional[OddieOptions] = None): 45 | from dss_python_backend import _altdss_oddie_capi 46 | lib = _altdss_oddie_capi.lib 47 | ffi = _altdss_oddie_capi.ffi 48 | NULL = ffi.NULL 49 | 50 | c_load_flags = NULL 51 | if load_flags is not None: 52 | c_load_flags = ffi.new('uint32_t*', load_flags) 53 | 54 | if library_path: 55 | library_path = library_path.encode() 56 | lib.Oddie_SetLibOptions(library_path, c_load_flags) 57 | ctx = lib.ctx_New() 58 | else: 59 | # Try the default install folder 60 | library_path = rb'C:\Program Files\OpenDSS\x64\OpenDSSDirect.dll' 61 | lib.Oddie_SetLibOptions(library_path, c_load_flags) 62 | ctx = lib.ctx_New() 63 | if ctx == NULL: 64 | # Try from the general path, let the system resolve it 65 | library_path = rb'OpenDSSDirect.dll' 66 | lib.Oddie_SetLibOptions(library_path, c_load_flags) 67 | ctx = lib.ctx_New() 68 | 69 | if ctx == NULL: 70 | raise RuntimeError("Could not load the target library.") 71 | 72 | if lib.ctx_DSS_Start(ctx, 0) != 1: 73 | raise RuntimeError("DSS_Start call was not successful.") 74 | 75 | if oddie_options is not None: 76 | lib.Oddie_SetOptions(oddie_options) 77 | 78 | ctx = ffi.gc(ctx, lib.ctx_Dispose) 79 | api_util = CffiApiUtil(ffi, lib, ctx, is_odd=True) 80 | api_util._library_path = library_path 81 | IDSS.__init__(self, api_util) 82 | 83 | 84 | __all__ = ['IOddieDSS', 'OddieOptions'] -------------------------------------------------------------------------------- /dss/UserModels/__init__.py: -------------------------------------------------------------------------------- 1 | from ..enums import SolveModes 2 | from .wrappers import ( 3 | # CapUserControl, 4 | GenUserModel, 5 | # PVSystemUserModel, 6 | # StoreDynaModel, 7 | # StoreUserModel 8 | ) 9 | 10 | __all__ = ['SolveModes', 'GenUserModel'] 11 | -------------------------------------------------------------------------------- /dss/__init__.py: -------------------------------------------------------------------------------- 1 | '''``dss`` is the main package for DSS-Python. DSS-Python is a compatibility layer for the DSS C-API library that mimics the official OpenDSS COM interface, with many extensions and a few limitations. 2 | 3 | This module used to provide instances for the OpenDSS Version 7 implementation. 4 | As of 2022, most of the parallel-machine functions of EPRI's OpenDSS have been reimplemented using a different approach. Therefore the PM functions are available in the instances of this module too. 5 | Besides the parallel-machine mechanisms, DSS-Python also exposes the DSSContext mechanism provided by DSS-Extensions. DSSContexts allow using multiple OpenDSS instances directly, including user-managed multi-threading, without using the internal OpenDSS actors. 6 | 7 | Starting on April 2023, our CFFI extension modules and DSS C-API binaries moved to `dss_python_backend` and are just consumed here. 8 | 9 | In January 2024, the code based on our new low-level API extensions, Obj and Alt, move to the new package [AltDSS-Python](https://dss-extensions.org/AltDSS-Python/) (`altdss` on PyPI and in Python). 10 | ''' 11 | 12 | import os 13 | from .patch_dss_com import patch_dss_com 14 | from dss_python_backend import ffi, lib 15 | _properties_mo = os.path.join(os.path.dirname(__file__), 'messages', 'properties-en-US.mo') 16 | if os.path.exists(_properties_mo): 17 | lib.DSS_SetPropertiesMO(_properties_mo.encode()) 18 | 19 | from ._cffi_api_util import CffiApiUtil, DSSException, set_case_insensitive_attributes 20 | from .IDSS import IDSS 21 | from .Oddie import IOddieDSS, OddieOptions 22 | from .enums import * 23 | 24 | DssException = DSSException 25 | 26 | if not hasattr(lib, 'ctx_New'): 27 | # Module was built without the context API 28 | api_util: CffiApiUtil = CffiApiUtil(ffi, lib) #: API utility functions and low-level access to the classic API 29 | prime_api_util = None 30 | DSS_GR: IDSS = IDSS(api_util) #: GR (Global Result) interface 31 | else: 32 | api_util = prime_api_util = CffiApiUtil(ffi, lib, lib.ctx_Get_Prime()) #: API utility functions and low-level access for DSSContext API 33 | DSS_GR: IDSS = IDSS(prime_api_util) #: GR (Global Result) interface using the new DSSContext API 34 | 35 | DSS_IR: IDSS = DSS_GR #: IR was removed in DSS-Python v0.13.x, we'll keep mapping it to DSS_GR for this version 36 | 37 | # Added "dss" for v0.12+ (feedback from some users) 38 | dss: IDSS 39 | DSS: IDSS 40 | dss = DSS = DSS_GR #: Same as DSS_GR 41 | 42 | try: 43 | from ._version import __version__ 44 | except: 45 | __version__ = '0.0dev' 46 | 47 | __all__ = ['dss', 'DSS', 'DSS_GR', 'prime_api_util', 'api_util', 'DSSException', 'patch_dss_com', 'set_case_insensitive_attributes', 'enums', 'IOddieDSS', 'OddieOptions'] -------------------------------------------------------------------------------- /dss/_types.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from typing import Union 3 | try: 4 | import numpy.typing as npt 5 | ComplexArray = npt.NDArray[np.complex128] 6 | Float64Array = npt.NDArray[np.float64] 7 | Float32Array = npt.NDArray[np.float32] 8 | Int32Array = npt.NDArray[np.int32] 9 | Int8Array = npt.NDArray[np.int8] 10 | BoolArray = npt.NDArray[np.bool_] 11 | except (ModuleNotFoundError, ImportError, AttributeError): 12 | from typing import List 13 | ComplexArray = List[complex] 14 | Float64Array = List[np.float64] 15 | Float32Array = List[np.float32] 16 | Int32Array = List[np.int32] 17 | Int8Array = List[np.int8] 18 | BoolArray = List[bool] 19 | 20 | Float64ArrayOrComplexArray = Union[Float64Array, ComplexArray] 21 | Float64ArrayOrSimpleComplex = Union[Float64Array, complex] 22 | -------------------------------------------------------------------------------- /dss/enums.py: -------------------------------------------------------------------------------- 1 | from dss_python_backend.enums import * 2 | -------------------------------------------------------------------------------- /dss/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | Help/utility functions for the documentation and examples 3 | """ 4 | 5 | import urllib.request 6 | from zipfile import ZipFile 7 | import pathlib 8 | from typing import Union 9 | import io 10 | 11 | def download_repo_snapshot(target_path: Union[pathlib.Path, str], error_if_exists=False, repo_name='dss_python', use_version=True) -> bool: 12 | """ 13 | This will try to download the snapshot of `repo_name` (from DSS-Extensions) as a 14 | zip file, decompressing it inside `target_path`, renaming the resulting folder 15 | to `repo_name`. 16 | 17 | The file will be decompressed inside `target_path` first to avoid potential 18 | issues with moving across different filesystems. 19 | """ 20 | if use_version: 21 | from . import __version__ as ver 22 | if 'dev' not in ver: 23 | # Download the tagged version 24 | ref = f'tags/{ver}.zip' 25 | tag = ver 26 | 27 | if not use_version or 'dev' in ver: 28 | # Download the master branch 29 | ref = 'heads/master.zip' 30 | tag = 'master' 31 | 32 | target = pathlib.Path(target_path).absolute() 33 | 34 | if not target.exists(): 35 | target.mkdir(parents=True) 36 | 37 | tmp_target = target / f'{repo_name}-{tag}' 38 | final_target = target / repo_name 39 | if final_target.exists(): 40 | if not error_if_exists: 41 | return final_target 42 | 43 | raise FileExistsError(f"Target path already exists: {final_target}") 44 | 45 | if tmp_target.exists(): 46 | if not error_if_exists: 47 | return None 48 | 49 | raise FileExistsError(f"Temporary target path already exists: {tmp_target}") 50 | 51 | url = f"https://github.com/dss-extensions/{repo_name}/archive/refs/{ref}" 52 | with urllib.request.urlopen(url) as f: 53 | zip_data = f.read() 54 | 55 | with io.BytesIO(zip_data) as f, ZipFile(f) as zf: 56 | zf.extractall(target_path) 57 | 58 | tmp_target.rename(final_target) 59 | return final_target 60 | 61 | 62 | def download_examples(target_path: Union[pathlib.Path, str], error_if_exists=False, repo_name='dss_python'): 63 | out_path = download_repo_snapshot(target_path=target_path, error_if_exists=error_if_exists, repo_name=repo_name) 64 | download_repo_snapshot(target_path=out_path / 'docs' / 'examples', error_if_exists=error_if_exists, repo_name='electricdss-tst') 65 | 66 | 67 | __all__ = [ 68 | 'download_repo_snapshot', 69 | ] 70 | -------------------------------------------------------------------------------- /dss/patch_dss_com.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import inspect 3 | from .IDSS import IDSS 4 | from .IBus import IBus 5 | from .ICircuit import ICircuit 6 | from .ICtrlQueue import ICtrlQueue 7 | from .ISettings import ISettings 8 | from .ISolution import ISolution 9 | from .ITopology import ITopology 10 | 11 | from .ICktElement import ICktElement 12 | from .ICapacitors import ICapacitors 13 | from .ICapControls import ICapControls 14 | from .IISources import IISources 15 | from .IGenerators import IGenerators 16 | from .IFuses import IFuses 17 | from .ILineCodes import ILineCodes 18 | from .IMeters import IMeters 19 | from .ILoadShapes import ILoadShapes 20 | from .ILines import ILines 21 | from .ILoads import ILoads 22 | from .IMonitors import IMonitors 23 | from .IPDElements import IPDElements 24 | from .IPVSystems import IPVSystems 25 | from .IRelays import IRelays 26 | from .IReclosers import IReclosers 27 | from .ISensors import ISensors 28 | from .IRegControls import IRegControls 29 | from .ISwtControls import ISwtControls 30 | from .IVsources import IVsources 31 | from .ITransformers import ITransformers 32 | from .IXYCurves import IXYCurves 33 | from .IGICSources import IGICSources 34 | # from .IStorages import IStorages 35 | 36 | 37 | def custom_iter(self): 38 | idx = self.First 39 | while idx != 0: 40 | yield self 41 | idx = self.Next 42 | 43 | def custom_len(self): 44 | return self.Count 45 | 46 | 47 | def Monitors_AsMatrix(self): 48 | '''Matrix of the active monitor, containing the hour vector, seconds vector, and all channels (index 2 = channel 1)''' 49 | 50 | buffer = np.array(self.ByteStream, dtype=np.int8) 51 | if len(buffer) <= 1: 52 | return None #np.zeros((0,), dtype=np.float32) 53 | 54 | record_size = buffer.view(dtype=np.int32)[2] + 2 55 | data = buffer[272:].view(dtype=np.float32) 56 | data = data.reshape((len(data) // record_size, record_size)).copy() 57 | return data 58 | 59 | 60 | def patch_dss_com(obj): 61 | if hasattr(type(obj.ActiveCircuit.Monitors), 'AsMatrix'): 62 | raise TypeError("Object already patched") 63 | 64 | def Load_Phases(self): 65 | current_load = self.Name 66 | obj.Text.Command = '? Load.{name}.Phases'.format(name=current_load) 67 | return int(obj.Text.Result) 68 | 69 | def Load_Set_Phases(self, value): 70 | # try: 71 | current_load = self.Name 72 | obj.Text.Command = 'Load.{name}.Phases={value}'.format(name=current_load, value=value) 73 | # except e: 74 | # print('Error') 75 | # print(e) 76 | 77 | def custom_bus_iter(self): 78 | for i in range(obj.ActiveCircuit.NumBuses): 79 | obj.ActiveCircuit.SetActiveBusi(i) 80 | yield self 81 | 82 | def custom_bus_len(self): 83 | return obj.ActiveCircuit.NumBuses 84 | 85 | 86 | def custom_dss_call(self, cmds): 87 | if '\n' in cmds: 88 | for cmd in cmds.split('\n'): 89 | self.Text.Command = cmd 90 | 91 | return 92 | 93 | self.Text.Command = cmds 94 | 95 | # Callable DSS 96 | type(obj).__call__ = custom_dss_call 97 | 98 | # Monitors AsMatrix 99 | type(obj.ActiveCircuit.Monitors).AsMatrix = Monitors_AsMatrix 100 | 101 | # Load Phases 102 | type(obj.ActiveCircuit.Loads).Phases = property(Load_Phases, Load_Set_Phases) 103 | 104 | # Bus iterator and len 105 | type(obj.ActiveCircuit.ActiveBus).__iter__ = custom_bus_iter 106 | type(obj.ActiveCircuit.ActiveBus).__len__ = custom_bus_len 107 | type(obj.ActiveCircuit.ActiveBus)._columns = IBus._columns 108 | type(obj.ActiveCircuit.Buses).__iter__ = custom_bus_iter 109 | type(obj.ActiveCircuit.Buses).__len__ = custom_bus_len 110 | type(obj.ActiveCircuit.Buses)._columns = IBus._columns 111 | 112 | def add_dunders(cls): 113 | cls.__iter__ = custom_iter 114 | cls.__len__ = custom_len 115 | 116 | # We keep an explicit map since we may change our names later 117 | com_classes_to_dsspy = { 118 | 'Capacitors': ICapacitors, 119 | 'CapControls': ICapControls, 120 | 'ISources': IISources, 121 | 'Generators': IGenerators, 122 | 'Fuses': IFuses, 123 | 'LineCodes': ILineCodes, 124 | 'Meters': IMeters, 125 | 'LoadShapes': ILoadShapes, 126 | 'Lines': ILines, 127 | 'Loads': ILoads, 128 | 'Monitors': IMonitors, 129 | 'PDElements': IPDElements, 130 | 'PVSystems': IPVSystems, 131 | 'Relays': IRelays, 132 | 'Reclosers': IReclosers, 133 | 'Sensors': ISensors, 134 | 'RegControls': IRegControls, 135 | 'SwtControls': ISwtControls, 136 | 'Vsources': IVsources, 137 | 'Transformers': ITransformers, 138 | 'XYCurves': IXYCurves, 139 | 'GICSources': IGICSources, 140 | # 'Storages': IStorages, 141 | } 142 | 143 | def filter_cols(py_cls): 144 | return [ 145 | c 146 | for c in py_cls._columns 147 | if not (inspect.getdoc(getattr(py_cls, c)) or '').rstrip().endswith('(API Extension)') 148 | ] 149 | 150 | # Add some more info to the classes 151 | type(obj.ActiveCircuit.ActiveCktElement)._py_cls = ICktElement 152 | type(obj.ActiveCircuit.ActiveCktElement)._columns = filter_cols(ICktElement) 153 | 154 | type(obj)._py_cls = IDSS 155 | type(obj)._columns = filter_cols(IDSS) 156 | 157 | type(obj.ActiveCircuit)._py_cls = ICircuit 158 | type(obj.ActiveCircuit)._columns = filter_cols(ICircuit) 159 | 160 | type(obj.ActiveCircuit.CtrlQueue)._py_cls = ICtrlQueue 161 | type(obj.ActiveCircuit.CtrlQueue)._columns = filter_cols(ICtrlQueue) 162 | 163 | type(obj.ActiveCircuit.Settings)._py_cls = ISettings 164 | type(obj.ActiveCircuit.Settings)._columns = filter_cols(ISettings) 165 | 166 | type(obj.ActiveCircuit.Solution)._py_cls = ISolution 167 | type(obj.ActiveCircuit.Solution)._columns = filter_cols(ISolution) 168 | 169 | type(obj.ActiveCircuit.Topology)._py_cls = ITopology 170 | type(obj.ActiveCircuit.Topology)._columns = filter_cols(ITopology) 171 | 172 | for name, py_cls in com_classes_to_dsspy.items(): 173 | cls = type(getattr(obj.ActiveCircuit, name)) 174 | add_dunders(cls) 175 | cls._py_cls = py_cls 176 | # Filter columns, removing 177 | cls._columns = filter_cols(py_cls) 178 | 179 | if getattr(py_cls, '_is_circuit_element', False): 180 | cls._is_circuit_element = True 181 | 182 | add_dunders(cls) 183 | 184 | return obj 185 | 186 | __all__ = ['patch_dss_com'] 187 | 188 | if __name__ == '__main__': 189 | # Simple test 190 | 191 | import comtypes.client 192 | DSS = comtypes.client.CreateObject("OpenDSSEngine.DSS") 193 | patch_dss_com(DSS) 194 | ckt = DSS.ActiveCircuit 195 | 196 | DSS.Text.Command = r'compile "..\electricdss-tst\Distrib\IEEETestCases\13Bus\IEEE13Nodeckt.dss"' 197 | 198 | for cls in [ckt.ActiveBus, ckt.Capacitors, ckt.CapControls, ckt.ISources, ckt.Generators, ckt.Fuses, ckt.LineCodes, ckt.Meters, ckt.LoadShapes, ckt.Lines, ckt.Loads, ckt.Monitors, ckt.PDElements, ckt.PVSystems, ckt.Relays, ckt.Reclosers, ckt.Sensors, ckt.RegControls, ckt.SwtControls, ckt.Vsources, ckt.Transformers, ckt.XYCurves]: 199 | cls_name = type(cls).__name__ 200 | for e in cls: 201 | print(cls_name, e.Name) 202 | 203 | print() 204 | 205 | for e in ckt.Loads: 206 | print('Load {} has {} phases'.format(e.Name, e.Phases)) 207 | e.Phases = int(e.Phases) 208 | 209 | for e in ckt.Monitors: 210 | print(e, e.name, e.AsMatrix()) -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling", 4 | "versioningit", 5 | ] 6 | build-backend = "hatchling.build" 7 | 8 | [tool.hatch.version] 9 | source = "versioningit" 10 | 11 | [tool.versioningit] 12 | 13 | [tool.versioningit.write] 14 | file = "dss/_version.py" 15 | 16 | [tool.hatch.build] 17 | artifacts = [ 18 | "dss/_version.py", 19 | "dss/messages/*.mo", 20 | ] 21 | packages = ["dss"] 22 | 23 | [project] 24 | name = "dss-python" 25 | dynamic = ["version"] 26 | dependencies = [ 27 | "dss_python_backend==0.14.6a1", 28 | "numpy>=1.21.0", 29 | "typing_extensions>=4.5,<5", 30 | ] 31 | requires-python = ">=3.7" 32 | authors = [ 33 | {name = "Paulo Meira", email = "pmeira@ieee.org"}, 34 | {name = "Dheepak Krishnamurthy", email = "me@kdheepak.com"}, 35 | ] 36 | maintainers = [ 37 | {name = "Paulo Meira", email = "pmeira@ieee.org"}, 38 | ] 39 | description = "Python interface (bindings and tools) for OpenDSS. Based on the AltDSS/DSS C-API project, the alternative OpenDSS implementation from DSS-Extensions.org. Multiplatform, API-compatible/drop-in replacement for the COM version of OpenDSS." 40 | readme = "README.md" 41 | license = {file = "LICENSE"} 42 | keywords = ["opendss", "altdss", "electric power systems", "opendssdirect", "powerflow", "short-circuit", ] 43 | classifiers = [ 44 | 'Intended Audience :: Science/Research', 45 | 'Intended Audience :: Education', 46 | 'Programming Language :: Python :: 3.7', 47 | 'Programming Language :: Python :: 3.8', 48 | 'Programming Language :: Python :: 3.9', 49 | 'Programming Language :: Python :: 3.10', 50 | 'Programming Language :: Python :: 3.11', 51 | 'Programming Language :: Python :: 3.12', 52 | 'Programming Language :: Python :: Implementation :: CPython', 53 | 'Programming Language :: Python :: Implementation :: PyPy', 54 | 'Development Status :: 5 - Production/Stable', 55 | 'Topic :: Scientific/Engineering', 56 | 'License :: OSI Approved :: BSD License' 57 | ] 58 | 59 | [project.urls] 60 | Homepage = "https://github.com/dss-extensions/DSS-Python" 61 | Documentation = "https://dss-extensions.org/DSS-Python" 62 | Repository = "https://github.com/dss-extensions/DSS-Python.git" 63 | "Bug Tracker" = "https://github.com/dss-extensions/DSS-Python/issues" 64 | Changelog = "https://github.com/dss-extensions/DSS-Python/blob/main/docs/changelog.md" 65 | 66 | [project.optional-dependencies] 67 | test = [ 68 | "ruff", 69 | "xmldiff", 70 | "pytest", 71 | "scipy", 72 | "pandas", 73 | ] 74 | plot = [ 75 | "matplotlib", 76 | "scipy", 77 | ] 78 | all = [ 79 | "matplotlib", 80 | "scipy", 81 | "opendssdirect-py[extras]", 82 | "altdss", 83 | "pandas", 84 | ] 85 | -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | line-length = 999 2 | ignore = ["E401", "E402", "E741", "E722"] 3 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dss-extensions/DSS-Python/85783cf811568dae931d06d3f434a34424599817/tests/__init__.py -------------------------------------------------------------------------------- /tests/data/13Bus.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dss-extensions/DSS-Python/85783cf811568dae931d06d3f434a34424599817/tests/data/13Bus.zip -------------------------------------------------------------------------------- /tests/test_alt.py: -------------------------------------------------------------------------------- 1 | # This moved to AltDSS-Python, initially at: 2 | # https://github.com/dss-extensions/altdss-python/blob/master/tests/test_alt.py 3 | -------------------------------------------------------------------------------- /tests/test_alt_em.py: -------------------------------------------------------------------------------- 1 | # This moved to AltDSS-Python, initially at: 2 | # https://github.com/dss-extensions/altdss-python/blob/master/tests/test_alt_em.py 3 | -------------------------------------------------------------------------------- /tests/test_batch.py: -------------------------------------------------------------------------------- 1 | # This moved to AltDSS-Python, initially at: 2 | # https://github.com/dss-extensions/altdss-python/blob/master/tests/test_batch.py 3 | -------------------------------------------------------------------------------- /tests/test_ctrlqueue.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 2019-02-28: Ported from CtrlQueueTest.bas by Paulo Meira (@pmeira) 3 | 2023-03-19: Modified to add to DSS-Python's tests 4 | ''' 5 | import sys 6 | import numpy as np 7 | 8 | try: 9 | from ._settings import BASE_DIR, WIN32, DSS as DSSRef 10 | except ImportError: 11 | from _settings import BASE_DIR, WIN32, DSS as DSSRef 12 | 13 | 14 | USE_COM = False # Change to True to test with the COM DLL 15 | 16 | def test_ctrlqueue(): 17 | '''Example of implementing a simple voltage control for Capacitors via the interface''' 18 | 19 | if not USE_COM: 20 | if WIN32: 21 | # When running pytest, the faulthandler seems too eager to grab FPC's exceptions, even when handled 22 | import faulthandler 23 | faulthandler.disable() 24 | DSSobj = DSSRef 25 | faulthandler.enable() 26 | else: 27 | from dss import DSS as DSSobj 28 | 29 | else: 30 | import os 31 | old_cd = os.getcwd() 32 | import win32com.client 33 | DSSobj = win32com.client.gencache.EnsureDispatch('OpenDSSengine.DSS') 34 | os.chdir(old_cd) 35 | 36 | DSSobj.AllowForms = False 37 | DSSText = DSSobj.Text 38 | DSSCircuit = DSSobj.ActiveCircuit 39 | DSSSolution = DSSCircuit.Solution 40 | DSSControlQueue = DSSCircuit.CtrlQueue 41 | DSSMeters = DSSCircuit.Meters 42 | DSSBus = DSSCircuit.ActiveBus 43 | DSSCapacitors = DSSCircuit.Capacitors 44 | # Run simple capacitor interface test and execute local cap control that emulates CapControl 45 | # with these settings: 46 | # PT=125.09 Type=voltage onsetting=118.8 offsetting=121.2 47 | 48 | # this test case has a four-step capacitor bank named "cap" and can be found in the Test Folder 49 | 50 | DSSText.Command = f'Compile "{BASE_DIR}/Test/Master_TestCapInterface.DSS"' 51 | 52 | # Set all capacitor steps open for first capacitor 53 | iCap = DSSCapacitors.First # should check iCap for >0 54 | iStates = [0] * DSSCapacitors.NumSteps 55 | 56 | for i in range(DSSCapacitors.NumSteps): 57 | iStates[i] = 0 58 | 59 | DSSCapacitors.States = iStates # push over the interface to OpenDSS 60 | 61 | # check to make sure it worked 62 | DSSText.Command = "? Capacitor.Cap.States" 63 | assert DSSText.Result == '[ 0 0 0 0]' 64 | 65 | # Base solution 66 | DSSSolution.Solve() 67 | 68 | # Each message we push onto the queue will get a 5 s delay 69 | hour = 0 70 | secDelay = 5 # delay 71 | 72 | PTratio = 125.09 # for 26 kV system 73 | ONsetting = 118.8 74 | OFFsetting = 121.2 75 | ActionCodeAdd = 201 # just an arbitrary action code 76 | ActionCodeSub = 202 # just another arbitrary action code 77 | DeviceHandle = 123 # arbitrary handle that signifies this control 78 | 79 | # now, we'll crank the load up in 10% steps, checking the voltage at each step 80 | # until all cap steps are on (no more available) 81 | 82 | i = 0 83 | v_step_up = [ 84 | 119.26520933058164, 85 | 118.53391703558978, 86 | 119.00110473442648, 87 | 118.22480242913166, 88 | 118.66765922692467, 89 | 119.1133497058388, 90 | 118.25980207026679, 91 | ] 92 | while DSSCapacitors.AvailableSteps > 0: 93 | print('DSSCapacitors.AvailableSteps', DSSCapacitors.AvailableSteps) 94 | i = i + 1 95 | DSSSolution.LoadMult = 1 + i * 0.1 # 10% more each time 96 | DSSSolution.InitSnap() 97 | DSSSolution.SolveNoControl() 98 | DSSSolution.SampleControlDevices() # sample all other controls 99 | 100 | # Emulate the cap control Sample Routine and get the bus voltage 101 | DSSCircuit.SetActiveBus("feedbus") 102 | V = DSSBus.VMagAngle 103 | 104 | # check the first phase magnitude 105 | Vreg = V[0] / PTratio 106 | print("Step", i, "Voltage=", Vreg, "LoadMult=", DSSSolution.LoadMult) 107 | assert np.isclose(Vreg, v_step_up[i - 1]) 108 | if Vreg < ONsetting: # push a message to bump up the number of steps 109 | DSSControlQueue.Push(hour, secDelay, ActionCodeAdd, DeviceHandle) 110 | 111 | DSSSolution.DoControlActions() # this sends actions to the local action list 112 | 113 | print('DSSControlQueue.NumActions', DSSControlQueue.NumActions) 114 | if DSSControlQueue.NumActions > 0: 115 | while DSSControlQueue.PopAction > 0: 116 | devHandle = DSSControlQueue.DeviceHandle 117 | if devHandle == DeviceHandle: 118 | iCap = DSSCapacitors.First # Sets designated capacitor active 119 | 120 | currentActionCode = DSSControlQueue.ActionCode 121 | 122 | if currentActionCode == ActionCodeAdd: 123 | DSSCapacitors.AddStep() 124 | elif currentActionCode == ActionCodeSub: 125 | DSSCapacitors.SubtractStep() 126 | 127 | # Print result 128 | print("Capacitor", DSSCapacitors.Name, "States =", tuple(DSSCapacitors.States)) 129 | 130 | 131 | v_step_down = [ 132 | 121.8764097324052, 133 | 121.1919475267437, 134 | 121.72822436752874, 135 | 121.00188980140766, 136 | 121.51826847553448, 137 | 120.74704094567356, 138 | 121.24330745091815, 139 | 120.41556666716592, 140 | ] 141 | 142 | # Now let's reverse Direction and start removing steps 143 | while DSSCapacitors.AvailableSteps < DSSCapacitors.NumSteps: 144 | i = i - 1 145 | DSSSolution.LoadMult = 1 + i * 0.1 # 10% more each time 146 | DSSSolution.InitSnap() 147 | DSSSolution.SolveNoControl() 148 | DSSSolution.SampleControlDevices() # sample all other controls 149 | 150 | # Emulate the cap control Sample Routine and get the bus voltage 151 | DSSCircuit.SetActiveBus("feedbus") 152 | V = DSSBus.VMagAngle 153 | 154 | # check the first phase magnitude 155 | Vreg = V[0] / PTratio 156 | print("Step", i, "Voltage=", Vreg, "LoadMult=", DSSSolution.LoadMult) 157 | assert np.isclose(Vreg, v_step_down[i + 1]) 158 | if Vreg > OFFsetting: # push a message to bump down the number of steps 159 | DSSControlQueue.Push(hour, secDelay, ActionCodeSub, DeviceHandle) 160 | 161 | DSSSolution.DoControlActions() # this send actions to the local action list 162 | 163 | if DSSControlQueue.NumActions > 0: 164 | while DSSControlQueue.PopAction > 0: 165 | devHandle = DSSControlQueue.DeviceHandle 166 | if devHandle == DeviceHandle: 167 | DSSCapacitors.First # Sets designated capacitor active 168 | 169 | currentActionCode = DSSControlQueue.ActionCode 170 | if currentActionCode == ActionCodeAdd: 171 | DSSCapacitors.AddStep() 172 | elif currentActionCode == ActionCodeSub: 173 | DSSCapacitors.SubtractStep() 174 | 175 | # Print result 176 | print("Capacitor", DSSCapacitors.Name, "States =", tuple(DSSCapacitors.States)) 177 | 178 | 179 | 180 | if __name__ == '__main__': 181 | test_ctrlqueue() 182 | -------------------------------------------------------------------------------- /tests/test_events.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from dss import DSS, DSSException 3 | 4 | try: 5 | from ._settings import BASE_DIR 6 | except ImportError: 7 | from _settings import BASE_DIR 8 | 9 | class EventHandler: 10 | # Note: for real usage, prefer to generate local classes bound to some 11 | # state, use the comtype style (pass an object) to allow more complex 12 | # initialization, or post-initialize the instance somehow. 13 | event_sequence = [] 14 | 15 | def OnInitControls(self): 16 | EventHandler.event_sequence.append('Init') 17 | 18 | def OnStepControls(self): 19 | EventHandler.event_sequence.append('Step') 20 | 21 | def OnCheckControls(self): 22 | EventHandler.event_sequence.append('Check') 23 | 24 | 25 | def test_events_style_win32com(): 26 | EventHandler.event_sequence.clear() 27 | evt_conn = DSS.Events.WithEvents(EventHandler) 28 | 29 | DSS.Text.Command = f'redirect "{BASE_DIR}/Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss"' 30 | DSS.Text.Command = 'solve mode=daily number=1' 31 | assert EventHandler.event_sequence == ['Init', 'Check', 'Check', 'Check', 'Step', 'Init', 'Check', 'Step'] 32 | evt_conn.close() 33 | DSS.Text.Command = 'solve' 34 | assert EventHandler.event_sequence == ['Init', 'Check', 'Check', 'Check', 'Step', 'Init', 'Check', 'Step'] 35 | 36 | with pytest.raises(DSSException): 37 | evt_conn.close() 38 | 39 | 40 | def test_events_style_comtypes(): 41 | EventHandler.event_sequence.clear() 42 | evt_conn = DSS.Events.GetEvents(EventHandler()) 43 | 44 | DSS.Text.Command = f'redirect "{BASE_DIR}/Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss"' 45 | DSS.Text.Command = 'solve mode=daily number=1' 46 | assert EventHandler.event_sequence == ['Init', 'Check', 'Check', 'Check', 'Step', 'Init', 'Check', 'Step'] 47 | evt_conn.close() 48 | DSS.Text.Command = 'solve' 49 | assert EventHandler.event_sequence == ['Init', 'Check', 'Check', 'Check', 'Step', 'Init', 'Check', 'Step'] 50 | 51 | with pytest.raises(DSSException): 52 | evt_conn.close() 53 | -------------------------------------------------------------------------------- /tests/test_obj.py: -------------------------------------------------------------------------------- 1 | # This moved to AltDSS-Python, initially at: 2 | # https://github.com/dss-extensions/altdss-python/blob/master/tests/test_obj.py 3 | -------------------------------------------------------------------------------- /tests/test_past_issues.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | from time import perf_counter 3 | import dss 4 | from dss import DSS, IDSS, DSSException, SparseSolverOptions, SolveModes, set_case_insensitive_attributes 5 | import numpy as np 6 | import pytest 7 | import scipy.sparse as sp 8 | 9 | try: 10 | from ._settings import BASE_DIR, WIN32, ZIP_FN, DSS 11 | except ImportError: 12 | from _settings import BASE_DIR, WIN32, ZIP_FN, DSS 13 | 14 | def setup_function(): 15 | DSS.ClearAll() 16 | DSS.AllowForms = False 17 | if not DSS._api_util._is_odd: 18 | DSS.AllowEditor = False 19 | DSS.AdvancedTypes = False 20 | DSS.AllowChangeDir = True 21 | DSS.COMErrorResults = True # TODO: change to False 22 | DSS.CompatFlags = 0 23 | 24 | 25 | def test_rxmatrix(): 26 | DSS.ClearAll() 27 | DSS.NewCircuit('test_rxmatrix') 28 | for r_or_x in 'rx': 29 | DSS.Text.Command = f'new Line.ourline{r_or_x} phases=3' 30 | DSS.Text.Command = f'~ {r_or_x}matrix=[1,2,3]' 31 | DSS.Text.Command = f'~ {r_or_x}matrix=[1,2,3 | 4,5,6 | 7,8,9]' 32 | DSS.Text.Command = f'? Line.ourline{r_or_x}.{r_or_x}matrix' 33 | assert DSS.Text.Result == '[1 |4 5 |7 8 9 ]' 34 | 35 | with pytest.raises(DSSException): 36 | DSS.Text.Command = f'~ {r_or_x}matrix=[10,20,30,40]' 37 | 38 | DSS.Text.Command = f'? Line.ourline{r_or_x}.{r_or_x}matrix' 39 | assert DSS.Text.Result == '[1 |4 5 |7 8 9 ]' 40 | 41 | with pytest.raises(DSSException): 42 | DSS.Text.Command = f'~ {r_or_x}matrix={list(range(1000))}' 43 | 44 | with pytest.raises(DSSException): 45 | DSS.Text.Command = f'~ {r_or_x}matrix=[1,2,3 | 4,5,6,7]' 46 | 47 | DSS.Text.Command = f'~ {r_or_x}matrix=[11 | 22, 33 | 44, 55, 66]' 48 | DSS.Text.Command = f'? Line.ourline{r_or_x}.{r_or_x}matrix' 49 | assert DSS.Text.Result == '[11 |22 33 |44 55 66 ]' 50 | 51 | 52 | def test_create_no_circuit(): 53 | general_classes = ( 54 | 'CNData', 'DynamicExp', 'GrowthShape', 'LineSpacing', 'LoadShape', 'PriceShape', 'Spectrum', 55 | 'TShape', 'TCC_Curve', 'TSData', 'XfmrCode', 'XYcurve', 'WireData', 56 | ) 57 | for cls in DSS.Classes: 58 | DSS.ClearAll() 59 | 60 | if cls in general_classes: 61 | DSS.Text.Command = f'new {cls}.test' 62 | else: 63 | with pytest.raises(DSSException, match=r'\(#(279)|(265)\)'): 64 | DSS.Text.Command = f'new {cls}.test' 65 | pytest.fail(f'Object of type "{cls}" was allowed to be created without a circuit!') 66 | 67 | 68 | def test_create_with_circuit(): 69 | for cls in DSS.Classes: 70 | DSS.ClearAll() 71 | DSS.NewCircuit(f'test_{cls}') 72 | if cls in ('CapControl', 'RegControl', 'GenDispatcher', 'StorageController', 'Relay', 'Fuse', 'SwtControl', 'ESPVLControl', 'GICsource'): 73 | with pytest.raises(DSSException): 74 | DSS.Text.Command = f'new {cls}.test{cls}' 75 | 76 | DSS.Text.Command = f'new Transformer.testtr' 77 | DSS.Text.Command = f'new Capacitor.testcap' 78 | if cls == 'RegControl': 79 | DSS.Text.Command = f'new {cls}.test{cls}2 transformer=testtr' 80 | elif cls == 'CapControl': 81 | DSS.Text.Command = f'new {cls}.test{cls}2 element=transformer.testtr capacitor=testcap' 82 | elif cls == 'GenDispatcher': 83 | DSS.Text.Command = f'new {cls}.test{cls}2 element=transformer.testtr' 84 | 85 | else: 86 | DSS.Text.Command = f'new {cls}.test{cls}' 87 | 88 | 89 | def test_ymatrix_csc(): 90 | # We accidentally left a np.complex in the ymatrix code before, 91 | # so let's always check if it's working now 92 | 93 | DSS.Text.Command = f'redirect "{BASE_DIR}/Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss"' 94 | DSS.ActiveCircuit.Solution.Solve() 95 | DSS.AdvancedTypes = True 96 | assert np.all(DSS.ActiveCircuit.SystemY == sp.csc_matrix(DSS.YMatrix.GetCompressedYMatrix())) 97 | --------------------------------------------------------------------------------