DSS-Extensions — JSON exports#
Updated for DSS C-API version 0.13.2
You can open and then run this notebook on Google Colab for a quick overview if you don’t want to set up a local environment: Open in Colab.
Preparation
To rerun this notebook:
Install Python; we recommend using miniforge from conda-forge – after installation, either create a dedicated environment or make sure the basics are installed (
conda install jupyterlab pandas
)Install the DSS-Extensions (
python -m pip install dss-python[plot] opendssdirect.py
)Download the example files:
If you have Git installed (recommended), you can run the following cell.
Otherwise, download dss-extensions/electricdss-tst and extract the
electricdss-tst-master
folder in the same path as this notebook. Rename it toelectricdss-tst
.
! git clone --depth=1 -q https://github.com/dss-extensions/electricdss-tst
fatal: destination path 'electricdss-tst' already exists and is not an empty directory.
If you are running under Google Colab, running the following cell should install the packages.
import os, subprocess
if os.getenv("COLAB_RELEASE_TAG"):
print(subprocess.check_output('pip install dss-python[plot] opendssdirect.py[extras] pandas==2', shell=True).decode())
Introduction#
Although this is a Python notebook and uses DSS-Python, the same functions and behavior described here apply to all DSS-Extensions, even if they use different API layouts: OpenDSSDirect.py, OpenDSSDirect.jl, DSS Sharp, DSS MATLAB.
Our alternative OpenDSS engine used in DSS-Extensions, implemented in the DSS C-API library, extends the API to provide some JSON export functions since version 0.12.0. Provided some constraints, most classes and properties could be exported as JSON, but a few properties didn’t work correctly even with these contraints. Warning: This feature is an API Extension and is not available in the official OpenDSS.
Note that manually creating JSON through the classic DSS API (as implemented in the official COM DLL API) is possible but cumbersome, requiring manually tracking and converting strings to the types and several other details. We expect the DSS C-API implementation, being shared across all DSS-Extensions, can achieve better performance and remove this extra processing work.
We will use Pandas to show tables more easily in this document, but it is not required for exporting. In the near future, we plan to release integrated dataframe support for using with Pandas and similar workloads in Python and other languages.
For general interactive use of DSS objects, the work-in-progress Obj and Batch APIs (working names) provides more direct manipulation of objects. Some initial examples are available on DSS-Python’s tests: test_obj.py, test_batch.py. This new API will later be published as a separate module.
We invite users to provide feedback, including feature requests, to better guide future development. The Discussions or Issues features here are good places to give us feedback: dss-extensions/dss-extensions
See also: DSS-Extensions — OpenDSS: Overview of Python APIs
How it works#
The DSS engine maintains an order of each property of each object when they are created and populated. Our DSS engine walks through this list and export each of the properties, applying disambiguation and some other transforms. For exporting a whole class, the results for each object are collected in a JSON array.
Some redundant properties are skipped, exporting the primary value when possible instead.
For classes related to transformers and LineGeometry
, there are a few auxiliary properties in DSS (wdg
and cond
) that are used to iterate the substructures and fill/read the values. On the JSON export, this properties are skipped, and the values that usually refer to the substructure are represented as arrays, effectively returning the values for all substructures. For example, transformers have the kVA
property, which in the DSS language and general property usage refer to the kVA
of the active winding of a certain transformer. In our JSON export, kVA
is an array of all kVA
values of the windings. For this example, kVA
is turned into the kVAs
property, but some properties do not have array-style options:
Transformer
andXfmrCode
:MaxTap
,MinTap
,RdcOhms
,NumTaps
,Rneut
,Xneut
AutoTrans
:MaxTap
,MinTap
,RdcOhms
,NumTaps
LineGeometry
:x
,h
(note that these are already arrays inLineSpacing
in the DSS language)
All these are consequently exported as arrays (lists) in the JSON document.
Only native JSON types are used. Complex numbers are represented as pairs of floating-point numbers.
The redundant properties at marked Redundant with
in DSS-Extensions: OpenDSS Commands and Properties.
See a special note about LineGeometry
and Line
at the end of this document.
Future expectations#
Although the basics of the JSON export feature are done, we may still adjust some aspects for better user experience and uniformity. We do not want to distance the output too far from the official OpenDSS format, but introducing a few extra properties (especially for Lines and Transformers) may lessen some concerns of usability.
Naming convention:
As expected for the internal property names, the JSON exports and Obj/Batch APIs will be updated to match the new internal names. Only the case of the characters will change since that doesn’t affect OpenDSS (OpenDSS is mostly case insensitive). For example
nconds
may be updated toNConds
without negative effects. Since we already have an option to export lower-case names, users could use that if they do not want to worry about this specific change in the future.Since OpenDSS have some properties which are invalid as identifiers on the most popular programming languages, we may add an option to transform the related keys. For example,
%LoadLoss
could be replaced withpctLoadLoss
, as already done to implement the Obj/Batch APIs in Python and C++. This is not a priority.
Alternative layouts:
For transformers and related objects, we may provide an option to output and input windings as a list of objects. For lines and related, conductor position objects could be used.
For array outputs, when the arrays represent matrices, add an option to use array of arrays (
[[1,2], [3,4]]
) instead of plain arrays ([1,2,3,4]
).
Export and import: Currently, only exporting is supported due to the various ways some classes can be populated, optional fields/properties, and so on.
There are plan to provide a more restricted alternative for importing from various formats to allow easier validation. If the data is uniform, roundtrip as dataframes (export JSON and reimporting it later as dataframes) through batch_new(df=df)
, e.g. dss.Obj.Line.batch_new(df=df_lines)
.
GeoJSON: GeoJSON export of the whole circuit, including buses, is expected in a future version. We didn’t want to add too much complexity before confirming the output is correct and reasonable.
Basic usage#
In the classic API, the active element or a whole active class can be exported.
from dss import dss
dss.Text.Command = 'redirect electricdss-tst/Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss'
# Select a load
dss.ActiveCircuit.Loads.Name = '670a'
dss.ActiveCircuit.ActiveDSSElement.Name
'Load.670a'
dss.ActiveCircuit.ActiveDSSElement.ToJSON()
'{"Name":"670a","Bus1":"670.1","Phases":1,"Conn":"wye","Model":1,"kV":2.3999999999999999E+000,"kW":1.7000000000000000E+001,"kvar":1.0000000000000000E+001}'
For inspection or post-processing, we could load the JSON string in Python for better formatting:
import json
json.loads(dss.ActiveCircuit.ActiveDSSElement.ToJSON())
{'Name': '670a',
'Bus1': '670.1',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 17.0,
'kvar': 10.0}
dss.SetActiveClass('Load')
json.loads(dss.ActiveCircuit.ActiveClass.ToJSON())
[{'Name': '671',
'Bus1': '671.1.2.3',
'Phases': 3,
'Conn': 'delta',
'Model': 1,
'kV': 4.16,
'kW': 1155.0,
'kvar': 660.0},
{'Name': '634a',
'Bus1': '634.1',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 0.277,
'kW': 160.0,
'kvar': 110.0},
{'Name': '634b',
'Bus1': '634.2',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 0.277,
'kW': 120.0,
'kvar': 90.0},
{'Name': '634c',
'Bus1': '634.3',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 0.277,
'kW': 120.0,
'kvar': 90.0},
{'Name': '645',
'Bus1': '645.2',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 170.0,
'kvar': 125.0},
{'Name': '646',
'Bus1': '646.2.3',
'Phases': 1,
'Conn': 'delta',
'Model': 2,
'kV': 4.16,
'kW': 230.0,
'kvar': 132.0},
{'Name': '692',
'Bus1': '692.3.1',
'Phases': 1,
'Conn': 'delta',
'Model': 5,
'kV': 4.16,
'kW': 170.0,
'kvar': 151.0},
{'Name': '675a',
'Bus1': '675.1',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 485.0,
'kvar': 190.0},
{'Name': '675b',
'Bus1': '675.2',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 68.0,
'kvar': 60.0},
{'Name': '675c',
'Bus1': '675.3',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 290.0,
'kvar': 212.0},
{'Name': '611',
'Bus1': '611.3',
'Phases': 1,
'Conn': 'wye',
'Model': 5,
'kV': 2.4,
'kW': 170.0,
'kvar': 80.0},
{'Name': '652',
'Bus1': '652.1',
'Phases': 1,
'Conn': 'wye',
'Model': 2,
'kV': 2.4,
'kW': 128.0,
'kvar': 86.0},
{'Name': '670a',
'Bus1': '670.1',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 17.0,
'kvar': 10.0},
{'Name': '670b',
'Bus1': '670.2',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 66.0,
'kvar': 38.0},
{'Name': '670c',
'Bus1': '670.3',
'Phases': 1,
'Conn': 'wye',
'Model': 1,
'kV': 2.4,
'kW': 117.0,
'kvar': 68.0}]
Or we could load into a dataframe. Some classes fit dataframes well, some not so much.
Users should be aware that the order of properties can change the final result for some classes. Transformers and lines use indexed structures that don’t fit the tabular format by default.
import pandas as pd
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
/tmp/ipykernel_17431/4005930840.py:2: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
Name | Bus1 | Phases | Conn | Model | kV | kW | kvar | |
---|---|---|---|---|---|---|---|---|
0 | 671 | 671.1.2.3 | 3 | delta | 1 | 4.160 | 1155.0 | 660.0 |
1 | 634a | 634.1 | 1 | wye | 1 | 0.277 | 160.0 | 110.0 |
2 | 634b | 634.2 | 1 | wye | 1 | 0.277 | 120.0 | 90.0 |
3 | 634c | 634.3 | 1 | wye | 1 | 0.277 | 120.0 | 90.0 |
4 | 645 | 645.2 | 1 | wye | 1 | 2.400 | 170.0 | 125.0 |
5 | 646 | 646.2.3 | 1 | delta | 2 | 4.160 | 230.0 | 132.0 |
6 | 692 | 692.3.1 | 1 | delta | 5 | 4.160 | 170.0 | 151.0 |
7 | 675a | 675.1 | 1 | wye | 1 | 2.400 | 485.0 | 190.0 |
8 | 675b | 675.2 | 1 | wye | 1 | 2.400 | 68.0 | 60.0 |
9 | 675c | 675.3 | 1 | wye | 1 | 2.400 | 290.0 | 212.0 |
10 | 611 | 611.3 | 1 | wye | 5 | 2.400 | 170.0 | 80.0 |
11 | 652 | 652.1 | 1 | wye | 2 | 2.400 | 128.0 | 86.0 |
12 | 670a | 670.1 | 1 | wye | 1 | 2.400 | 17.0 | 10.0 |
13 | 670b | 670.2 | 1 | wye | 1 | 2.400 | 66.0 | 38.0 |
14 | 670c | 670.3 | 1 | wye | 1 | 2.400 | 117.0 | 68.0 |
dss.SetActiveClass('Transformer')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
/tmp/ipykernel_17431/303292550.py:2: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
Name | Phases | X12 | Bus | Conn | kV | kVA | pctR | Bank | pctLoadLoss | |
---|---|---|---|---|---|---|---|---|---|---|
0 | sub | 3 | 0.008 | [sourcebus, 650] | [delta, wye] | [114.99999999999999, 4.16] | [5000.0, 5000.0] | [0.0005, 0.0005] | NaN | NaN |
1 | reg1 | 1 | 0.010 | [650.1, rg60.1] | NaN | [2.399999999999999, 2.399999999999999] | [1666.0, 1666.0] | NaN | reg1 | 0.01 |
2 | reg2 | 1 | 0.010 | [650.2, rg60.2] | NaN | [2.399999999999999, 2.399999999999999] | [1666.0, 1666.0] | NaN | reg1 | 0.01 |
3 | reg3 | 1 | 0.010 | [650.3, rg60.3] | NaN | [2.399999999999999, 2.399999999999999] | [1666.0, 1666.0] | NaN | reg1 | 0.01 |
4 | xfm1 | 3 | 2.000 | [633, 634] | [wye, wye] | [4.16, 0.4799999999999999] | [500.0, 500.0] | [0.55, 0.55] | NaN | NaN |
This NaN values above should be the empty/non-filled properties. For example, check the JSON for reg1
directly:
dss.ActiveCircuit.Transformers.Name = 'reg1'
json.loads(dss.ActiveCircuit.ActiveDSSElement.ToJSON())
{'Name': 'reg1',
'Phases': 1,
'Bank': 'reg1',
'X12': 0.01,
'kVA': [1666.0, 1666.0],
'Bus': ['650.1', 'rg60.1'],
'kV': [2.4, 2.4],
'pctLoadLoss': 0.01}
For reg1
, the properties windings
, conn
and %R
were not provided. The order of the properties in this previous cell output are in the same order as the original DSS script.
We can check how the properties were used in the input file for comparison:
with open('electricdss-tst/Version8/Distrib/IEEETestCases/13Bus/IEEE13Nodeckt.dss', 'r') as ckt_file:
for text_line in ckt_file:
if 'transformer.reg1' in text_line.lower():
# Found the transformer
print(text_line, end='')
#...but need to check for the MORE command:
text_line = next(ckt_file)
while text_line.startswith('~'):
print(text_line, end='')
text_line = next(ckt_file)
break
New Transformer.Reg1 phases=1 bank=reg1 XHL=0.01 kVAs=[1666 1666]
~ Buses=[650.1 RG60.1] kVs=[2.4 2.4] %LoadLoss=0.01
From this, note that Buses
was reported in the bus
field as an array, as well as kVAs
and kVs
were exported as a kVA
and kV
arrays, respectively. This is done by disambiguation process.
As a sidenote, the dataframe can be a little better with Pandas 2.0+, using NA instead of NaN (which forces converting integers to floats):
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(), dtype_backend='pyarrow')
/tmp/ipykernel_17431/2594549408.py:1: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(), dtype_backend='pyarrow')
Name | Phases | X12 | Bus | Conn | kV | kVA | pctR | Bank | pctLoadLoss | |
---|---|---|---|---|---|---|---|---|---|---|
0 | sub | 3 | 0.008 | [sourcebus, 650] | [delta, wye] | [114.99999999999999, 4.16] | [5000.0, 5000.0] | [0.0005, 0.0005] | <NA> | <NA> |
1 | reg1 | 1 | 0.01 | [650.1, rg60.1] | NaN | [2.399999999999999, 2.399999999999999] | [1666.0, 1666.0] | NaN | reg1 | 0.01 |
2 | reg2 | 1 | 0.01 | [650.2, rg60.2] | NaN | [2.399999999999999, 2.399999999999999] | [1666.0, 1666.0] | NaN | reg1 | 0.01 |
3 | reg3 | 1 | 0.01 | [650.3, rg60.3] | NaN | [2.399999999999999, 2.399999999999999] | [1666.0, 1666.0] | NaN | reg1 | 0.01 |
4 | xfm1 | 3 | 2.0 | [633, 634] | [wye, wye] | [4.16, 0.4799999999999999] | [500.0, 500.0] | [0.55, 0.55] | <NA> | <NA> |
Export option flags#
Some flags are currently implemented to adjust the output:
Full
: Exports all properties, even if not filled by the user. This can be useful for debugging or just checking some default properties.EnumAsInt
: Try to use integer representation for enums. Note that the integer values may be specific to a version of the engine — that is, don’t use the integer values for long-term storage. The integer values may be useful to avoid the work of converting the strings to integer on user code and so on. The enums should be available in the DSS.Obj classes, and are exported from theDSS_ExtractSchema
low-level function.SkipRedundant
: Avoid outputting redundant properties.FullNames
: Prefer listing object full names, including the class name, even where it is not required.Pretty
: Pretty-format the output (as much as the default implementation of fpjson in Free Pascal allows). The output is a bit more readable without requiring loading the JSON.ExcludeDisabled
: When exporting a whole class, skip disabled elements.SkipDSSClass
: By default, a JSON field “DSSClass” is added with the name of the DSS class for each element. This may not always be required or useful, so we can skip it sometimes.LowercaseKeys
: Use all lowercase keys instead of the internal variants for all keys. “DSSClass” is also converted to “dssclass”.
These are bit flags, so you have to bitwise-or them together when multiple flags are used.
from dss import DSSJSONFlags
?? DSSJSONFlags
Let’s print the Pretty
, LowercaseKeys
and Full
outputs with for reg1
:
dss.ActiveCircuit.Transformers.Name = 'reg1'
print(dss.ActiveCircuit.ActiveDSSElement.ToJSON())
{"Name":"reg1","Phases":1,"Bank":"reg1","X12":1.0000000000000000E-002,"kVA":[1.6660000000000000E+003,1.6660000000000000E+003],"Bus":["650.1","rg60.1"],"kV":[2.3999999999999999E+000,2.3999999999999999E+000],"pctLoadLoss":1.0000000000000000E-002}
dss.ActiveCircuit.Transformers.Name = 'reg1'
print(dss.ActiveCircuit.ActiveDSSElement.ToJSON(DSSJSONFlags.Pretty))
{
"Name" : "reg1",
"Phases" : 1,
"Bank" : "reg1",
"X12" : 1.0000000000000000E-002,
"kVA" : [
1.6660000000000000E+003,
1.6660000000000000E+003
],
"Bus" : [
"650.1",
"rg60.1"
],
"kV" : [
2.3999999999999999E+000,
2.3999999999999999E+000
],
"pctLoadLoss" : 1.0000000000000000E-002
}
dss.ActiveCircuit.Transformers.Name = 'reg1'
print(dss.ActiveCircuit.ActiveDSSElement.ToJSON(DSSJSONFlags.LowercaseKeys))
{"Name":"reg1","phases":1,"bank":"reg1","x12":1.0000000000000000E-002,"kva":[1.6660000000000000E+003,1.6660000000000000E+003],"bus":["650.1","rg60.1"],"kv":[2.3999999999999999E+000,2.3999999999999999E+000],"%loadloss":1.0000000000000000E-002}
dss.ActiveCircuit.Transformers.Name = 'reg1'
json.loads(dss.ActiveCircuit.ActiveDSSElement.ToJSON(DSSJSONFlags.Full))
{'Name': 'reg1',
'Phases': 1,
'Bus': ['650.1', 'rg60.1'],
'Conn': ['wye', 'wye'],
'kV': [2.4, 2.4],
'kVA': [1666.0, 1666.0],
'Tap': [1.0, 1.05625],
'pctR': [0.005, 0.005],
'RNeut': [-1.0, -1.0],
'XNeut': [0.0, 0.0],
'Buses': ['650.1', 'rg60.1'],
'Conns': ['wye', 'wye'],
'kVs': [2.4, 2.4],
'kVAs': [1666.0, 1666.0],
'Taps': [1.0, 1.05625],
'XHL': 0.01,
'XHT': 35.0,
'XLT': 30.0,
'XSCArray': [0.01],
'Thermal': 2.0,
'n': 0.8,
'm': 0.8,
'FLRise': 65.0,
'HSRise': 15.0,
'pctLoadLoss': 0.01,
'pctNoLoadLoss': 0.0,
'NormHkVA': 1832.6,
'EmergHkVA': 2499.0,
'Sub': False,
'MaxTap': [1.1, 1.1],
'MinTap': [0.9, 0.9],
'NumTaps': [32, 32],
'SubName': '',
'pctIMag': 0.0,
'ppm_Antifloat': 1.0,
'pctRs': [0.005, 0.005],
'Bank': 'reg1',
'XfmrCode': None,
'XRConst': False,
'X12': 0.01,
'X13': 35.0,
'X23': 30.0,
'LeadLag': 'Lag',
'WdgCurrents': '594.2557, (-28.695), 562.6086, (151.31), ',
'Core': 'shell',
'RDCOhms': [0.0001469387755102041, 0.0001469387755102041],
'Ratings': [1100.0],
'FaultRate': 0.007,
'pctPerm': 0.0,
'Repair': 0.0,
'BaseFreq': 60.0,
'Enabled': True,
'Like': ''}
dss.SetActiveClass('Transformer')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass | DSSJSONFlags.EnumAsInt))
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[19], line 2
1 dss.SetActiveClass('Transformer')
----> 2 pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass | DSSJSONFlags.EnumAsInt))
AttributeError: type object 'DSSJSONFlags' has no attribute 'SkipDSSClass'
dss.SetActiveClass('Line')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass))
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[20], line 2
1 dss.SetActiveClass('Line')
----> 2 pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass))
AttributeError: type object 'DSSJSONFlags' has no attribute 'SkipDSSClass'
dss.SetActiveClass('LineCode')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass)).head().T
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[21], line 2
1 dss.SetActiveClass('LineCode')
----> 2 pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass)).head().T
AttributeError: type object 'DSSJSONFlags' has no attribute 'SkipDSSClass'
dss.SetActiveClass('LineCode')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass | DSSJSONFlags.SkipRedundant | DSSJSONFlags.Full)).head().T
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[22], line 2
1 dss.SetActiveClass('LineCode')
----> 2 pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.SkipDSSClass | DSSJSONFlags.SkipRedundant | DSSJSONFlags.Full)).head().T
AttributeError: type object 'DSSJSONFlags' has no attribute 'SkipDSSClass'
Using OpenDSSDirect.py#
from opendssdirect import dss as odd
odd.Basic.SetActiveClass('Capacitor')
odd.ActiveClass.ToJSON()
'[{"Name":"cap1","Bus1":"675","Phases":3,"kvar":[6.0000000000000000E+002],"kV":4.1600000000000001E+000},{"Name":"cap2","Bus1":"611.3","Phases":1,"kvar":[1.0000000000000000E+002],"kV":2.3999999999999999E+000}]'
odd.Loads.First()
odd.Element.ToJSON()
'{"Name":"671","Bus1":"671.1.2.3","Phases":3,"Conn":"delta","Model":1,"kV":4.1600000000000001E+000,"kW":1.1550000000000000E+003,"kvar":6.6000000000000000E+002}'
json.loads(odd.Element.ToJSON(DSSJSONFlags.Full))
{'Name': '671',
'Phases': 3,
'Bus1': '671.1.2.3',
'kV': 4.16,
'kW': 1155.0,
'PF': 0.8682431421244591,
'Model': 1,
'Yearly': None,
'Daily': None,
'Duty': None,
'Growth': None,
'Conn': 'delta',
'kvar': 660.0,
'RNeut': -1.0,
'XNeut': 0.0,
'Status': 'Variable',
'Class': 1,
'VMinpu': 0.95,
'VMaxpu': 1.05,
'VMinNorm': 0.0,
'VMinEmerg': 0.0,
'XfkVA': 0.0,
'AllocationFactor': 0.5,
'kVA': 1330.2725284692608,
'pctMean': 50.0,
'pctStdDev': 10.0,
'CVRWatts': 1.0,
'CVRVars': 2.0,
'kWh': 0.0,
'kWhDays': 30.0,
'CFactor': 4.0,
'CVRCurve': None,
'NumCust': 1,
'ZIPV': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
'pctSeriesRL': 50.0,
'RelWeight': 1.0,
'VLowpu': 0.5,
'puXHarm': 0.0,
'XRHarm': 6.0,
'Spectrum': 'defaultload',
'BaseFreq': 60.0,
'Enabled': True,
'Like': ''}
Using the Obj and Batch APIs#
Both simple objects and batch collections have the to_json()
method. The same option flags are accepted.
Load = dss.Obj.Load
Load[1].to_json()
/tmp/ipykernel_17431/120103453.py:1: DeprecationWarning: Obj identifier is deprecated; use the `to_altdss()` method instead, or import AltDSS directly ("from altdss import altdss") when using it stand-alone.
Load = dss.Obj.Load
'{"Name":"634a","Bus1":"634.1","Phases":1,"Conn":"wye","Model":1,"kV":2.7700000000000002E-001,"kW":1.6000000000000000E+002,"kvar":1.1000000000000000E+002}'
For the following cell, a batch of loads that use delta connections are selected and then exported to JSON.
from dss.IObj import Connection
print(Load.batch(conn=Connection.delta).to_json(DSSJSONFlags.Pretty))
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[27], line 1
----> 1 from dss.IObj import Connection
2 print(Load.batch(conn=Connection.delta).to_json(DSSJSONFlags.Pretty))
ModuleNotFoundError: No module named 'dss.IObj'
Line
and LineGeometry
: special cases for conductors#
LineGeometry
objects present some difficulties even in the official OpenDSS in DSS scripts, when using save circuit
.
To workaround it, we expose all conductors in the wire
property, and leave wires
as the original wires
array. That is, we can interpret wire
in the JSON output as the full array of conductors. The class names are included, so there is no ambiguity of what it actually contains.
For Line
, cncables
and tscables
are suppressed, and we use wires
for all data instead.
dss.AllowEditor = False
dss.Text.Command = 'redirect ./electricdss-tst/Test/IEEE13_Assets.dss'
dss.SetActiveClass('WireData')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
/tmp/ipykernel_17431/3701825667.py:2: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
Name | NormAmps | Radius | GMRAC | RDC | RUnits | RadUnits | GMRUnits | RAC | |
---|---|---|---|---|---|---|---|---|---|
0 | acsr_556_5 | 730.0 | 0.4635 | 0.37320 | 0.035227 | kft | in | in | NaN |
1 | acsr_4/0 | 340.0 | 0.2815 | 0.09768 | 0.112121 | kft | in | in | NaN |
2 | acsr_1/0 | 230.0 | 0.1990 | 0.05352 | 0.212121 | kft | in | in | NaN |
3 | cu_1/0 | 100.0 | 0.1840 | 0.13356 | NaN | mi | in | in | 0.607 |
dss.SetActiveClass('LineSpacing')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
/tmp/ipykernel_17431/24329383.py:2: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON())
Name | NPhases | Units | X | H | |
---|---|---|---|---|---|
0 | 500 | 3 | ft | [-4.0, -1.0, 3.0, 0.0] | [28.0, 28.0, 28.0, 24.0] |
1 | 505 | 2 | ft | [-4.0, 3.0, 0.0] | [28.0, 28.0, 24.0] |
2 | 510 | 1 | ft | [0.5, 0.0] | [29.0, 24.0] |
dss.SetActiveClass('LineGeometry')
pd.set_option('display.max_colwidth', None)
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(), dtype_backend='pyarrow')
/tmp/ipykernel_17431/4129479670.py:3: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(), dtype_backend='pyarrow')
Name | NConds | NPhases | Reduce | Spacing | Conductors | X | H | Units | |
---|---|---|---|---|---|---|---|---|---|
0 | 601 | 4 | 3 | True | 500 | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | NaN | NaN | NaN |
1 | 602 | 4 | 3 | True | 500 | [WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0] | NaN | NaN | NaN |
2 | 603 | 3 | 2 | True | 505 | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | NaN | NaN | NaN |
3 | 604 | 3 | 2 | True | 505 | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | NaN | NaN | NaN |
4 | 605 | 2 | 1 | True | 510 | [WireData.acsr_1/0, WireData.acsr_1/0] | NaN | NaN | NaN |
5 | 606 | 3 | 3 | True | <NA> | [CNData.cn_250, CNData.cn_250, CNData.cn_250] | [-0.5, 0.0, 0.5] | [-4.0, -4.0, -4.0] | [ft, ft, ft] |
6 | 607 | 2 | 1 | True | <NA> | [TSData.ts_1/0, WireData.cu_1/0] | [0.0, 0.25] | [-4.0, -4.0] | [ft, ft] |
Since some of the LineGeometry objects above use LineSpacing objects to fill x
and h
, we would need to inspect the data from the LineSpacing objects to get all the info.
In this case, using DSSJSONFlags.Full
can help. Notice in the next cell output that all LineGeometry have x
and h
filled.
Manually inspecting the properties through the classic DSS property API or our new Obj API would also return the correct values.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.Full | DSSJSONFlags.FullNames), dtype_backend='pyarrow')
/tmp/ipykernel_17431/205243968.py:1: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(DSSJSONFlags.Full | DSSJSONFlags.FullNames), dtype_backend='pyarrow')
Name | NConds | NPhases | Wire | X | H | Units | NormAmps | EmergAmps | Reduce | Spacing | Conductors | Ratings | LineType | Like | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 601 | 4 | 3 | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | [-4.0, -1.0, 3.0, 0.0] | [28.0, 28.0, 28.0, 24.0] | [ft, ft, ft, ft] | 730.0 | 1095.0 | True | LineSpacing.500 | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | [0.0] | oh | |
1 | 602 | 4 | 3 | [WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0] | [-4.0, -1.0, 3.0, 0.0] | [28.0, 28.0, 28.0, 24.0] | [ft, ft, ft, ft] | 340.0 | 510.0 | True | LineSpacing.500 | [WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0] | [0.0] | oh | |
2 | 603 | 3 | 2 | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [-4.0, 3.0, 0.0] | [28.0, 28.0, 24.0] | [ft, ft, ft] | 230.0 | 345.0 | True | LineSpacing.505 | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [0.0] | oh | |
3 | 604 | 3 | 2 | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [-4.0, 3.0, 0.0] | [28.0, 28.0, 24.0] | [ft, ft, ft] | 230.0 | 345.0 | True | LineSpacing.505 | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [0.0] | oh | |
4 | 605 | 2 | 1 | [WireData.acsr_1/0, WireData.acsr_1/0] | [0.5, 0.0] | [29.0, 24.0] | [ft, ft] | 230.0 | 345.0 | True | LineSpacing.510 | [WireData.acsr_1/0, WireData.acsr_1/0] | [0.0] | oh | |
5 | 606 | 3 | 3 | [CNData.cn_250, CNData.cn_250, CNData.cn_250] | [-0.5, 0.0, 0.5] | [-4.0, -4.0, -4.0] | [ft, ft, ft] | 260.0 | 390.0 | True | <NA> | [CNData.cn_250, CNData.cn_250, CNData.cn_250] | [0.0] | oh | |
6 | 607 | 2 | 1 | [TSData.ts_1/0, WireData.cu_1/0] | [0.0, 0.25] | [-4.0, -4.0] | [ft, ft] | 165.0 | 247.5 | True | <NA> | [TSData.ts_1/0, WireData.cu_1/0] | [0.0] | oh |
dss.ActiveCircuit.Lines.Name = '684611'
json.loads(dss.ActiveCircuit.ActiveDSSElement.ToJSON())
{'Name': '684611',
'Phases': 1,
'Bus1': '684.3',
'Bus2': '611.3',
'Spacing': '510',
'Conductors': ['WireData.acsr_1/0', 'WireData.acsr_1/0'],
'Length': 300.0,
'Units': 'ft',
'Ratings': [400.0],
'NormAmps': 230.0,
'EmergAmps': 345.0}
dss.Text.Command = 'redirect electricdss-tst/Test/IEEE13_LineAndCableSpacing.dss'
dss.SetActiveClass('Line')
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(), dtype_backend='pyarrow').T
/tmp/ipykernel_17431/776559013.py:2: FutureWarning: Passing literal json to 'read_json' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
pd.read_json(dss.ActiveCircuit.ActiveClass.ToJSON(), dtype_backend='pyarrow').T
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Name | 650632 | 632670 | 670671 | 671680 | 632633 | 632645 | 645646 | 671684 | 684611 | 692675 | 684652 | 671692 |
Bus1 | rg60.1.2.3 | 632.1.2.3 | 670.1.2.3 | 671.1.2.3 | 632.1.2.3 | 632.3.2 | 645.3.2 | 671.1.3 | 684.3 | 692.1.2.3 | 684.1 | 671 |
Bus2 | 632.1.2.3 | 670.1.2.3 | 671.1.2.3 | 680.1.2.3 | 633.1.2.3 | 645.3.2 | 646.3.2 | 684.1.3 | 611.3 | 675.1.2.3 | 652.1 | 692 |
Spacing | 500 | 500 | 500 | 500 | 500 | 505 | 505 | 505 | 510 | 515 | 520 | <NA> |
Conductors | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | [WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_556_5, WireData.acsr_4/0] | [WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0, WireData.acsr_4/0] | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [WireData.acsr_1/0, WireData.acsr_1/0, WireData.acsr_1/0] | [WireData.acsr_1/0, WireData.acsr_1/0] | [CNData.cn_250, CNData.cn_250, CNData.cn_250] | [TSData.ts_1/0, WireData.cu_1/0] | NaN |
Length | 2000.0 | 667.0 | 1333.0 | 1000.0 | 500.0 | 500.0 | 300.0 | 300.0 | 300.0 | 500.0 | 800.0 | 0.001 |
Units | ft | ft | ft | ft | ft | ft | ft | ft | ft | ft | ft | none |
Ratings | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | [400.0] | NaN |
NormAmps | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | 260 | 165 | <NA> |
EmergAmps | -1.0 | -1.0 | -1.0 | -1.0 | -1.0 | -1.0 | -1.0 | -1.0 | -1.0 | 390.0 | 247.5 | <NA> |
Phases | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 3 | 1 | 3 |
Switch | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | True |
R1 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 0.0001 |
R0 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 0.0001 |
X1 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 0 |
X0 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 0 |
C1 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 0 |
C0 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | 0 |