DiTTo#
DiTTo is the Distribution Transformation Tool. It is an open source tool to convert and modify electrical distribution system models. DiTTo implements a many-to-one-to-many parsing framework, making it modular and robust. The reader modules parse data files of distribution system format (e.g. OpenDSS) and create an object for each electrical component. These objects are stored in a GDM DistributionSystem instance. The writer modules are then used to export the data stored in memory to a selected output distribution system format (e.g. OpenDSS) which are written to disk.
For model conversion. We will need to install DiTto. To do that, use the command below
pip install NREL-ditto opendssdirect.py
Requirement already satisfied: NREL-ditto in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (0.0.9)
Requirement already satisfied: opendssdirect.py in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (0.9.4)
Requirement already satisfied: grid-data-models==2.0.1 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from NREL-ditto) (2.0.1)
Requirement already satisfied: nrel-altdss-schema==0.0.1 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from NREL-ditto) (0.0.1)
Requirement already satisfied: rdflib in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from NREL-ditto) (7.1.4)
Requirement already satisfied: click~=8.1.7 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (8.1.8)
Requirement already satisfied: geopandas in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (1.0.1)
Requirement already satisfied: importlib-metadata in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (8.6.1)
Requirement already satisfied: infrasys~=0.4.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (0.4.0)
Requirement already satisfied: networkx in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (3.4.2)
Requirement already satisfied: pandas~=2.2.3 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (2.2.3)
Requirement already satisfied: plotly in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (6.0.1)
Requirement already satisfied: pydantic~=2.10.6 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from grid-data-models==2.0.1->NREL-ditto) (2.10.6)
Requirement already satisfied: dss-python==0.15.7 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from opendssdirect.py) (0.15.7)
Requirement already satisfied: dss-python-backend==0.14.5 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from dss-python==0.15.7->opendssdirect.py) (0.14.5)
Requirement already satisfied: numpy>=1.21.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from dss-python==0.15.7->opendssdirect.py) (2.2.5)
Requirement already satisfied: typing-extensions<5,>=4.5 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from dss-python==0.15.7->opendssdirect.py) (4.13.2)
Requirement already satisfied: cffi>=1.11.2 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from dss-python-backend==0.14.5->dss-python==0.15.7->opendssdirect.py) (1.17.1)
Requirement already satisfied: pyparsing<4,>=2.1.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from rdflib->NREL-ditto) (3.2.3)
Requirement already satisfied: loguru~=0.7.2 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (0.7.3)
Requirement already satisfied: pint~=0.23 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (0.24.4)
Requirement already satisfied: pyarrow~=19.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (19.0.1)
Requirement already satisfied: rich~=13.7.1 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (13.7.1)
Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pandas~=2.2.3->grid-data-models==2.0.1->NREL-ditto) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pandas~=2.2.3->grid-data-models==2.0.1->NREL-ditto) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pandas~=2.2.3->grid-data-models==2.0.1->NREL-ditto) (2025.2)
Requirement already satisfied: annotated-types>=0.6.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pydantic~=2.10.6->grid-data-models==2.0.1->NREL-ditto) (0.7.0)
Requirement already satisfied: pydantic-core==2.27.2 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pydantic~=2.10.6->grid-data-models==2.0.1->NREL-ditto) (2.27.2)
Requirement already satisfied: pyogrio>=0.7.2 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from geopandas->grid-data-models==2.0.1->NREL-ditto) (0.10.0)
Requirement already satisfied: packaging in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from geopandas->grid-data-models==2.0.1->NREL-ditto) (25.0)
Requirement already satisfied: pyproj>=3.3.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from geopandas->grid-data-models==2.0.1->NREL-ditto) (3.7.1)
Requirement already satisfied: shapely>=2.0.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from geopandas->grid-data-models==2.0.1->NREL-ditto) (2.1.0)
Requirement already satisfied: zipp>=3.20 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from importlib-metadata->grid-data-models==2.0.1->NREL-ditto) (3.21.0)
Requirement already satisfied: narwhals>=1.15.1 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from plotly->grid-data-models==2.0.1->NREL-ditto) (1.36.0)
Requirement already satisfied: pycparser in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from cffi>=1.11.2->dss-python-backend==0.14.5->dss-python==0.15.7->opendssdirect.py) (2.22)
Requirement already satisfied: platformdirs>=2.1.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pint~=0.23->infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (4.3.7)
Requirement already satisfied: flexcache>=0.3 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pint~=0.23->infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (0.3)
Requirement already satisfied: flexparser>=0.4 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pint~=0.23->infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (0.4)
Requirement already satisfied: certifi in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from pyogrio>=0.7.2->geopandas->grid-data-models==2.0.1->NREL-ditto) (2025.4.26)
Requirement already satisfied: six>=1.5 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas~=2.2.3->grid-data-models==2.0.1->NREL-ditto) (1.17.0)
Requirement already satisfied: markdown-it-py>=2.2.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from rich~=13.7.1->infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (3.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from rich~=13.7.1->infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (2.19.1)
Requirement already satisfied: mdurl~=0.1 in /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich~=13.7.1->infrasys~=0.4.0->grid-data-models==2.0.1->NREL-ditto) (0.1.2)
Note: you may need to restart the kernel to use updated packages.
We start by loading a sample GDM system using the gdmloader.
from gdm.distribution import DistributionSystem
from gdmloader.constants import GCS_CASE_SOURCE
from gdmloader.source import SystemLoader
gdm_loader = SystemLoader()
gdm_loader.add_source(GCS_CASE_SOURCE)
distribution_system: DistributionSystem = gdm_loader.load_dataset(
source_name=GCS_CASE_SOURCE.name,
system_type=DistributionSystem,
dataset_name="p5r",
)
distribution_system.name = "P5R"
/opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py:502: UserWarning: Ellipsis is not a Python type (it may be an instance of an object), Pydantic will allow any object with no validation since we cannot even enforce that the input is an instance of the given type. To get rid of this error wrap the type with `pydantic.SkipValidation`.
warn(
---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
Cell In[2], line 2
1 from gdm.distribution import DistributionSystem
----> 2 from gdmloader.constants import GCS_CASE_SOURCE
3 from gdmloader.source import SystemLoader
5 gdm_loader = SystemLoader()
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/gdmloader/constants.py:13
3 import fsspec
5 GDM_CASE_SOURCE = SourceModel(
6 fs=fsspec.filesystem("github", org="NREL-Distribution-Suites", repo="gdm-cases", branch="main"),
7 name="gdm-cases",
8 url="https://github.com/NREL-Distribution-Suites/gdm-cases",
9 folder="data",
10 )
12 GCS_CASE_SOURCE = SourceModel(
---> 13 fs=fsspec.filesystem("gcs"),
14 name="gdm_data",
15 url="https://storage.googleapis.com/gdm_data",
16 folder="data",
17 )
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/fsspec/registry.py:310, in filesystem(protocol, **storage_options)
303 warnings.warn(
304 "The 'arrow_hdfs' protocol has been deprecated and will be "
305 "removed in the future. Specify it as 'hdfs'.",
306 DeprecationWarning,
307 )
309 cls = get_filesystem_class(protocol)
--> 310 return cls(**storage_options)
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/fsspec/spec.py:81, in _Cached.__call__(cls, *args, **kwargs)
79 return cls._cache[token]
80 else:
---> 81 obj = super().__call__(*args, **kwargs)
82 # Setting _fs_token here causes some static linters to complain.
83 obj._fs_token_ = token
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/gcsfs/core.py:333, in GCSFileSystem.__init__(self, project, access, token, block_size, consistency, cache_timeout, secure_serialize, check_connection, requests_timeout, requester_pays, asynchronous, session_kwargs, loop, timeout, endpoint_url, default_location, version_aware, **kwargs)
327 if check_connection:
328 warnings.warn(
329 "The `check_connection` argument is deprecated and will be removed in a future release.",
330 DeprecationWarning,
331 )
--> 333 self.credentials = GoogleCredentials(project, access, token)
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/gcsfs/credentials.py:53, in GoogleCredentials.__init__(self, project, access, token, check_credentials)
51 self.lock = threading.Lock()
52 self.token = token
---> 53 self.connect(method=token)
55 if check_credentials:
56 warnings.warn(
57 "The `check_credentials` argument is deprecated and will be removed in a future release.",
58 DeprecationWarning,
59 )
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/gcsfs/credentials.py:262, in GoogleCredentials.connect(self, method)
260 for meth in ["google_default", "cache", "cloud", "anon"]:
261 try:
--> 262 self.connect(method=meth)
263 logger.debug("Connected with method %s", meth)
264 break
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/gcsfs/credentials.py:279, in GoogleCredentials.connect(self, method)
277 raise RuntimeError("All connection methods have failed!")
278 else:
--> 279 self.__getattribute__("_connect_" + method)()
280 self.method = method
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/gcsfs/credentials.py:100, in GoogleCredentials._connect_cloud(self)
98 with requests.Session() as session:
99 req = Request(session)
--> 100 self.credentials.refresh(req)
101 except gauth.exceptions.RefreshError as error:
102 raise ValueError("Invalid gcloud credentials") from error
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/google/auth/compute_engine/credentials.py:126, in Credentials.refresh(self, request)
124 scopes = self._scopes if self._scopes is not None else self._default_scopes
125 try:
--> 126 self._retrieve_info(request)
127 self.token, self.expiry = _metadata.get_service_account_token(
128 request, service_account=self._service_account_email, scopes=scopes
129 )
130 except exceptions.TransportError as caught_exc:
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/google/auth/compute_engine/credentials.py:99, in Credentials._retrieve_info(self, request)
90 def _retrieve_info(self, request):
91 """Retrieve information about the service account.
92
93 Updates the scopes and retrieves the full service account email.
(...) 97 HTTP requests.
98 """
---> 99 info = _metadata.get_service_account_info(
100 request, service_account=self._service_account_email
101 )
103 self._service_account_email = info["email"]
105 # Don't override scopes requested by the user.
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/google/auth/compute_engine/_metadata.py:342, in get_service_account_info(request, service_account)
339 path = "instance/service-accounts/{0}/".format(service_account)
340 # See https://cloud.google.com/compute/docs/metadata#aggcontents
341 # for more on the use of 'recursive'.
--> 342 return get(request, path, params={"recursive": "true"})
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/google/auth/compute_engine/_metadata.py:207, in get(request, path, root, params, recursive, retry_count, headers, return_none_for_not_found_error, timeout)
205 backoff = ExponentialBackoff(total_attempts=retry_count)
206 failure_reason = None
--> 207 for attempt in backoff:
208 try:
209 response = request(
210 url=url, method="GET", headers=headers_to_use, timeout=timeout
211 )
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/google/auth/_exponential_backoff.py:133, in ExponentialBackoff.__next__(self)
129 return self._backoff_count
131 jitter = self._calculate_jitter()
--> 133 time.sleep(jitter)
135 self._current_wait_in_seconds *= self._multiplier
136 return self._backoff_count
KeyboardInterrupt:
Once installed, a GDM model can be loaded into memory and converted to any of the supported formats using one of ditto writers
from pathlib import Path
from ditto.writers.opendss.write import Writer
writer = Writer(distribution_system)
writer.write(output_path=Path("."), separate_substations=False, separate_feeders=False)
2025-05-02 11:06:28.929 | WARNING | ditto.writers.opendss.write:write:80 - Mapper VoltageLimitSetMapper not found. Skipping
2025-05-02 11:06:28.929 | WARNING | ditto.writers.opendss.write:write:80 - Mapper LocationMapper not found. Skipping
2025-05-02 11:06:28.929 | WARNING | ditto.writers.opendss.write:write:80 - Mapper PhaseVoltageSourceEquipmentMapper not found. Skipping
2025-05-02 11:06:28.930 | WARNING | ditto.writers.opendss.write:write:80 - Mapper PhaseLoadEquipmentMapper not found. Skipping
2025-05-02 11:06:28.930 | WARNING | ditto.writers.opendss.write:write:80 - Mapper InverterEquipmentMapper not found. Skipping
2025-05-02 11:06:28.930 | WARNING | ditto.writers.opendss.write:write:80 - Mapper SolarEquipmentMapper not found. Skipping
2025-05-02 11:06:28.930 | WARNING | ditto.writers.opendss.write:write:80 - Mapper WindingEquipmentMapper not found. Skipping
2025-05-02 11:06:28.931 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in MatrixImpedanceSwitchEquipmentMapper...
2025-05-02 11:06:28.931 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in MatrixImpedanceBranchEquipmentMapper...
2025-05-02 11:06:28.931 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in DistributionBusMapper...
2025-05-02 11:06:28.935 | WARNING | ditto.writers.opendss.write:write:80 - Mapper VoltageSourceEquipmentMapper not found. Skipping
2025-05-02 11:06:28.935 | WARNING | ditto.writers.opendss.write:write:80 - Mapper LoadEquipmentMapper not found. Skipping
2025-05-02 11:06:28.935 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in DistributionTransformerEquipmentMapper...
2025-05-02 11:06:28.935 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in DistributionVoltageSourceMapper...
2025-05-02 11:06:28.936 | WARNING | ditto.writers.opendss.write:write:109 - Equipment Mapper VoltageSourceEquipmentMapper not found. Skipping
2025-05-02 11:06:28.936 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in DistributionLoadMapper...
2025-05-02 11:06:28.937 | WARNING | ditto.writers.opendss.write:write:109 - Equipment Mapper LoadEquipmentMapper not found. Skipping
2025-05-02 11:06:28.939 | WARNING | ditto.writers.opendss.write:write:109 - Equipment Mapper LoadEquipmentMapper not found. Skipping
2025-05-02 11:06:28.939 | WARNING | ditto.writers.opendss.write:write:80 - Mapper DistributionSolarMapper not found. Skipping
2025-05-02 11:06:28.939 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in DistributionTransformerMapper...
2025-05-02 11:06:28.941 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in MatrixImpedanceBranchMapper...
2025-05-02 11:06:28.949 | DEBUG | ditto.writers.opendss.write:write:83 - Mapping components in MatrixImpedanceSwitchMapper...
In this example, the OpenDSS model contains intentional errors such as missing bus definitions, incorrect syntax for loads, and incorrect capacitor connections. The Python script uses GDM’s GDMReader to attempt to read the model, which should identify and report these issues.
from ditto.readers.opendss.reader import Reader
from opendssdirect import dss
dss("""
clear
new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
new line.line_1 bus1=bus_1.1 bus2=bus_2.2 phases=1
solve
""")
dss.Circuit.Save("test_circuit", options=0)
reader = Reader("test_circuit/Master.dss")
gdm_system = reader.get_system()
2025-05-02 11:06:29.176 | DEBUG | ditto.readers.opendss.reader:_read:61 - Loading OpenDSS model.
2025-05-02 11:06:29.180 | DEBUG | ditto.readers.opendss.reader:_read:70 - Model loaded from test_circuit/Master.dss.
2025-05-02 11:06:29.181 | DEBUG | ditto.readers.opendss.components.buses:get_buses:22 - parsing bus components...
2025-05-02 11:06:29.182 | DEBUG | ditto.readers.opendss.components.sources:get_voltage_sources:88 - parsing voltage sources components...
2025-05-02 11:06:29.183 | DEBUG | ditto.readers.opendss.components.loadshapes:build_profiles:103 - parsing timeseries components...
2025-05-02 11:06:29.183 | DEBUG | ditto.readers.opendss.components.capacitors:get_capacitors:89 - parsing capacitor components...
2025-05-02 11:06:29.184 | DEBUG | ditto.readers.opendss.components.loads:get_loads:95 - parsing load components...
2025-05-02 11:06:29.184 | DEBUG | ditto.readers.opendss.components.pv_systems:get_pvsystems:83 - parsing pvsystem components...
2025-05-02 11:06:29.185 | DEBUG | ditto.readers.opendss.components.transformers:get_transformer_equipments:149 - parsing transformer equipment...
2025-05-02 11:06:29.185 | DEBUG | ditto.readers.opendss.components.transformers:get_transformers:182 - parsing transformer components...
2025-05-02 11:06:29.185 | DEBUG | ditto.readers.opendss.components.conductors:get_conductors_equipment:16 - parsing conductor components...
2025-05-02 11:06:29.186 | DEBUG | ditto.readers.opendss.components.cables:get_cables_equipment:22 - parsing cable components...
2025-05-02 11:06:29.186 | DEBUG | ditto.readers.opendss.components.branches:get_matrix_branch_equipments:195 - parsing matrix branch equipment...
2025-05-02 11:06:29.189 | DEBUG | ditto.readers.opendss.components.branches:get_geometry_branch_equipments:67 - parsing geometry branch equipment...
2025-05-02 11:06:29.189 | DEBUG | ditto.readers.opendss.components.branches:get_branches:263 - parsing branch components...
2025-05-02 11:06:29.190 | DEBUG | ditto.readers.opendss.components.branches:get_branches:269 - building line Line.line_1...
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[3], line 12
4 dss("""
5 clear
6 new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
7 new line.line_1 bus1=bus_1.1 bus2=bus_2.2 phases=1
8 solve
9 """)
10 dss.Circuit.Save("test_circuit", options=0)
---> 12 reader = Reader("test_circuit/Master.dss")
13 gdm_system = reader.get_system()
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/reader.py:47, in Reader.__init__(self, Opendss_master_file, crs)
45 self.Opendss_master_file = Path(Opendss_master_file)
46 self.crs = crs
---> 47 self._read()
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/reader.py:99, in Reader._read(self)
95 geometry_branch_equipment_catalog, mapped_geometry = get_geometry_branch_equipments(
96 self.system
97 )
98 self._add_components(geometry_branch_equipment_catalog.values())
---> 99 branches = get_branches(
100 self.system,
101 mapped_geometry,
102 geometry_branch_equipment_catalog,
103 matrix_branch_equipments_catalog,
104 thermal_limit_catalog,
105 )
106 self._add_components(branches)
108 logger.debug("parsing complete...")
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/components/branches.py:344, in get_branches(system, mapping, geometry_branch_equipment_catalog, matrix_branch_equipments_catalog, thermal_limit_catalog)
339 if model_class in [MatrixImpedanceSwitch, MatrixImpedanceFuse]:
340 model_dict["is_closed"] = [
341 not (odd.CktElement.IsOpen(1, node + 1) or odd.CktElement.IsOpen(2, node + 1))
342 for node in range(len(nodes))
343 ]
--> 344 matrix_branch = model_class(**model_dict)
345 branches.append(matrix_branch)
346 flag = odd.Lines.Next()
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/pydantic/main.py:214, in BaseModel.__init__(self, **data)
212 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
213 __tracebackhide__ = True
--> 214 validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
215 if self is not validated_self:
216 warnings.warn(
217 'A custom validator is returning a value other than `self`.\n'
218 "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"
219 'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',
220 stacklevel=2,
221 )
ValidationError: 1 validation error for MatrixImpedanceBranch
Value error, Conductor phase (phase=<Phase.A: 'A'>) does not match bus phases (bus.phases=[<Phase.B: 'B'>]) [type=value_error, input_value={'name': 'line_1', 'buses...tity(400.0, 'ampere')>)}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.10/v/value_error
from ditto.readers.opendss.reader import Reader
from opendssdirect import dss
dss("""
clear
new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
new line.line_1 bus1=bus_1.1 bus2=bus_2.1 phases=1 length=-10
solve
""")
dss.Circuit.Save("test_circuit", options=0)
reader = Reader("test_circuit/Master.dss")
gdm_system = reader.get_system()
2025-05-02 11:07:47.071 | DEBUG | ditto.readers.opendss.reader:_read:61 - Loading OpenDSS model.
2025-05-02 11:07:47.075 | DEBUG | ditto.readers.opendss.reader:_read:70 - Model loaded from test_circuit/Master.dss.
2025-05-02 11:07:47.076 | DEBUG | ditto.readers.opendss.components.buses:get_buses:22 - parsing bus components...
2025-05-02 11:07:47.077 | DEBUG | ditto.readers.opendss.components.sources:get_voltage_sources:88 - parsing voltage sources components...
2025-05-02 11:07:47.078 | DEBUG | ditto.readers.opendss.components.loadshapes:build_profiles:103 - parsing timeseries components...
2025-05-02 11:07:47.078 | DEBUG | ditto.readers.opendss.components.capacitors:get_capacitors:89 - parsing capacitor components...
2025-05-02 11:07:47.078 | DEBUG | ditto.readers.opendss.components.loads:get_loads:95 - parsing load components...
2025-05-02 11:07:47.079 | DEBUG | ditto.readers.opendss.components.pv_systems:get_pvsystems:83 - parsing pvsystem components...
2025-05-02 11:07:47.079 | DEBUG | ditto.readers.opendss.components.transformers:get_transformer_equipments:149 - parsing transformer equipment...
2025-05-02 11:07:47.079 | DEBUG | ditto.readers.opendss.components.transformers:get_transformers:182 - parsing transformer components...
2025-05-02 11:07:47.079 | DEBUG | ditto.readers.opendss.components.conductors:get_conductors_equipment:16 - parsing conductor components...
2025-05-02 11:07:47.083 | DEBUG | ditto.readers.opendss.components.cables:get_cables_equipment:22 - parsing cable components...
2025-05-02 11:07:47.084 | DEBUG | ditto.readers.opendss.components.branches:get_matrix_branch_equipments:195 - parsing matrix branch equipment...
2025-05-02 11:07:47.090 | DEBUG | ditto.readers.opendss.components.branches:get_geometry_branch_equipments:67 - parsing geometry branch equipment...
2025-05-02 11:07:47.091 | DEBUG | ditto.readers.opendss.components.branches:get_branches:263 - parsing branch components...
2025-05-02 11:07:47.093 | DEBUG | ditto.readers.opendss.components.branches:get_branches:269 - building line Line.line_1...
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Cell In[4], line 12
4 dss("""
5 clear
6 new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
7 new line.line_1 bus1=bus_1.1 bus2=bus_2.1 phases=1 length=-10
8 solve
9 """)
10 dss.Circuit.Save("test_circuit", options=0)
---> 12 reader = Reader("test_circuit/Master.dss")
13 gdm_system = reader.get_system()
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/reader.py:47, in Reader.__init__(self, Opendss_master_file, crs)
45 self.Opendss_master_file = Path(Opendss_master_file)
46 self.crs = crs
---> 47 self._read()
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/reader.py:99, in Reader._read(self)
95 geometry_branch_equipment_catalog, mapped_geometry = get_geometry_branch_equipments(
96 self.system
97 )
98 self._add_components(geometry_branch_equipment_catalog.values())
---> 99 branches = get_branches(
100 self.system,
101 mapped_geometry,
102 geometry_branch_equipment_catalog,
103 matrix_branch_equipments_catalog,
104 thermal_limit_catalog,
105 )
106 self._add_components(branches)
108 logger.debug("parsing complete...")
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/components/branches.py:335, in get_branches(system, mapping, geometry_branch_equipment_catalog, matrix_branch_equipments_catalog, thermal_limit_catalog)
318 equipment = _build_matrix_branch(
319 MatrixBranchTypes.LINE.value,
320 matrix_branch_equipments_catalog,
(...) 324 recloser,
325 )
326 equipment = get_equipment_from_catalog(
327 equipment, matrix_branch_equipments_catalog, equipment_class.__name__
328 )
329 model_dict = {
330 "name": odd.Lines.Name().lower(),
331 "buses": [
332 system.get_component(DistributionBus, bus1),
333 system.get_component(DistributionBus, bus2),
334 ],
--> 335 "length": PositiveDistance(odd.Lines.Length(), UNIT_MAPPER[odd.Lines.Units()]),
336 "phases": [PHASE_MAPPER[node] for node in nodes],
337 "equipment": equipment,
338 }
339 if model_class in [MatrixImpedanceSwitch, MatrixImpedanceFuse]:
340 model_dict["is_closed"] = [
341 not (odd.CktElement.IsOpen(1, node + 1) or odd.CktElement.IsOpen(2, node + 1))
342 for node in range(len(nodes))
343 ]
File ~/Documents/GitHub/grid-data-models/src/gdm/quantities.py:180, in PositiveDistance.__init__(self, value, units, **kwargs)
179 def __init__(self, value, units, **kwargs):
--> 180 assert all(
181 np.array(value).flatten() >= 0
182 ), f"Distance ({value}, {units}) must be positive."
AssertionError: Distance (-10.0, m) must be positive.
from ditto.readers.opendss.reader import Reader
from opendssdirect import dss
dss("""
clear
new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
new line.line_1 bus1=bus_1.1 bus2=bus_2.1 phases=1
new load.load_1 bus=bus_2.1.2.3 phases=1
solve
""")
dss.Circuit.Save("test_circuit", options=0)
reader = Reader("test_circuit/Master.dss")
/opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py:502: UserWarning: Ellipsis is not a Python type (it may be an instance of an object), Pydantic will allow any object with no validation since we cannot even enforce that the input is an instance of the given type. To get rid of this error wrap the type with `pydantic.SkipValidation`.
warn(
2025-05-01 15:23:51.732 | DEBUG | ditto.readers.opendss.reader:_read:61 - Loading OpenDSS model.
2025-05-01 15:23:51.737 | DEBUG | ditto.readers.opendss.reader:_read:70 - Model loaded from test_circuit/Master.dss.
2025-05-01 15:23:51.738 | DEBUG | ditto.readers.opendss.components.buses:get_buses:22 - parsing bus components...
2025-05-01 15:23:51.739 | DEBUG | ditto.readers.opendss.components.sources:get_voltage_sources:88 - parsing voltage sources components...
2025-05-01 15:23:51.740 | DEBUG | ditto.readers.opendss.components.loadshapes:build_profiles:103 - parsing timeseries components...
2025-05-01 15:23:51.741 | DEBUG | ditto.readers.opendss.components.capacitors:get_capacitors:89 - parsing capacitor components...
2025-05-01 15:23:51.741 | DEBUG | ditto.readers.opendss.components.loads:get_loads:95 - parsing load components...
2025-05-01 15:23:51.741 | DEBUG | ditto.readers.opendss.components.loadshapes:build_profiles:103 - parsing timeseries components...
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[1], line 13
4 dss("""
5 clear
6 new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
(...) 9 solve
10 """)
11 dss.Circuit.Save("test_circuit", options=0)
---> 13 reader = Reader("test_circuit/Master.dss")
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/reader.py:47, in Reader.__init__(self, Opendss_master_file, crs)
45 self.Opendss_master_file = Path(Opendss_master_file)
46 self.crs = crs
---> 47 self._read()
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/reader.py:77, in Reader._read(self)
75 self._add_components(get_voltage_sources(self.system))
76 self._add_components(get_capacitors(self.system))
---> 77 self._add_components(get_loads(self.system))
78 self._add_components(get_pvsystems(self.system))
79 (
80 distribution_transformer_equipment_catalog,
81 winding_equipment_catalog,
82 ) = get_transformer_equipments(self.system)
File ~/Documents/GitHub/ditto/src/ditto/readers/opendss/components/loads.py:106, in get_loads(system)
104 profile_names = [odd.Loads.Daily(), odd.Loads.Yearly(), odd.Loads.Duty()]
105 profiles = build_profiles(profile_names, ObjectsWithProfile.LOAD, profile_catalog)
--> 106 distribution_load = DistributionLoad(
107 name=load_name,
108 bus=system.get_component(DistributionBus, bus1),
109 phases=[PHASE_MAPPER[el] for el in nodes],
110 equipment=LoadEquipment,
111 )
112 for profile_name in profile_names:
113 if profile_name in profiles:
File /opt/homebrew/Caskroom/miniconda/base/envs/gdm2/lib/python3.12/site-packages/pydantic/main.py:214, in BaseModel.__init__(self, **data)
212 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
213 __tracebackhide__ = True
--> 214 validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
215 if self is not validated_self:
216 warnings.warn(
217 'A custom validator is returning a value other than `self`.\n'
218 "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"
219 'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',
220 stacklevel=2,
221 )
ValidationError: 1 validation error for DistributionLoad
Value error, Loads phases self.phases=[<Phase.A: 'A'>, <Phase.B: 'B'>, <Phase.C: 'C'>] must be subset of bus phases. [<Phase.A: 'A'>, <Phase.B: 'B'>] [type=value_error, input_value={'name': 'load_1', 'bus':...tionType.STAR: 'STAR'>)}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.10/v/value_error
Validation Summary Via Monkey Patching#
from pydantic import ValidationError
from rich.console import Console
from infrasys import Component
from rich.table import Table
validation_errors = []
_original_init = Component.__init__
def patched_init(self, *args, **kwargs):
try:
_original_init(self, *args, **kwargs)
except ValidationError as e:
for error in e.errors():
validation_errors.append([
kwargs['name'],
self.__class__.__name__,
error['loc'] if error['loc'] else "On model validation",
error['type'],
error['msg']
])
Component.__init__ = patched_init
from ditto.readers.opendss.reader import Reader
from opendssdirect import dss
dss("""
clear
new Circuit.test bus1=bus_1.1 BasekV=7.2 pu=1.03 Angle=0.0 Phases=1 Z1=[1e-05, 1e-05] Z0=[1e-05, 1e-05]
new line.line_1 bus1=bus_1.1 bus2=bus_2.2 phases=3
solve
""")
dss.Circuit.Save("test_circuit", options=0)
reader = Reader("test_circuit/Master.dss")
if validation_errors:
error_table = Table(title="Validation warning summary")
error_table.add_column("Model", justify="right", style="cyan", no_wrap=True)
error_table.add_column("Type", style="green")
error_table.add_column("Field", justify="right", style="bright_magenta")
error_table.add_column("Error", style="bright_red")
error_table.add_column("Message", justify="right", style="turquoise2")
for row in validation_errors:
error_table.add_row(*row)
console = Console()
console.print(error_table)
raise Exception(f"Validations errors occured when running the script. See the table above")
2025-05-02 11:07:58.868 | DEBUG | ditto.readers.opendss.reader:_read:61 - Loading OpenDSS model.
2025-05-02 11:07:58.873 | DEBUG | ditto.readers.opendss.reader:_read:70 - Model loaded from test_circuit/Master.dss.
2025-05-02 11:07:58.873 | DEBUG | ditto.readers.opendss.components.buses:get_buses:22 - parsing bus components...
2025-05-02 11:07:58.875 | DEBUG | ditto.readers.opendss.components.sources:get_voltage_sources:88 - parsing voltage sources components...
2025-05-02 11:07:58.875 | DEBUG | ditto.readers.opendss.components.loadshapes:build_profiles:103 - parsing timeseries components...
2025-05-02 11:07:58.877 | DEBUG | ditto.readers.opendss.components.capacitors:get_capacitors:89 - parsing capacitor components...
2025-05-02 11:07:58.877 | DEBUG | ditto.readers.opendss.components.loads:get_loads:95 - parsing load components...
2025-05-02 11:07:58.877 | DEBUG | ditto.readers.opendss.components.pv_systems:get_pvsystems:83 - parsing pvsystem components...
2025-05-02 11:07:58.877 | DEBUG | ditto.readers.opendss.components.transformers:get_transformer_equipments:149 - parsing transformer equipment...
2025-05-02 11:07:58.877 | DEBUG | ditto.readers.opendss.components.transformers:get_transformers:182 - parsing transformer components...
2025-05-02 11:07:58.877 | DEBUG | ditto.readers.opendss.components.conductors:get_conductors_equipment:16 - parsing conductor components...
2025-05-02 11:07:58.878 | DEBUG | ditto.readers.opendss.components.cables:get_cables_equipment:22 - parsing cable components...
2025-05-02 11:07:58.878 | DEBUG | ditto.readers.opendss.components.branches:get_matrix_branch_equipments:195 - parsing matrix branch equipment...
2025-05-02 11:07:58.886 | DEBUG | ditto.readers.opendss.components.branches:get_geometry_branch_equipments:67 - parsing geometry branch equipment...
2025-05-02 11:07:58.886 | DEBUG | ditto.readers.opendss.components.branches:get_branches:263 - parsing branch components...
2025-05-02 11:07:58.891 | DEBUG | ditto.readers.opendss.components.branches:get_branches:269 - building line Line.line_1...
2025-05-02 11:07:58.893 | DEBUG | ditto.readers.opendss.reader:_read:108 - parsing complete...
System ┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ ┃ Property ┃ Value ┃ ┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩ │ System name │ │ │ Data format version │ 2.0.0 │ │ Components attached │ 10 │ │ Time Series attached │ 0 │ │ Description │ │ └──────────────────────┴───────┘
Component Information ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ ┃ Type ┃ Count ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩ │ DistributionBus │ 2 │ │ DistributionVoltageSource │ 1 │ │ Location │ 1 │ │ MatrixImpedanceBranch │ 1 │ │ MatrixImpedanceBranchEquipment │ 1 │ │ PhaseVoltageSourceEquipment │ 1 │ │ VoltageLimitSet │ 2 │ │ VoltageSourceEquipment │ 1 │ └────────────────────────────────┴───────┘
2025-05-02 11:07:58.900 | DEBUG | ditto.readers.opendss.reader:_read:109 -
None
2025-05-02 11:07:58.901 | DEBUG | ditto.readers.opendss.reader:_read:110 - Building graph...
2025-05-02 11:07:58.901 | DEBUG | ditto.readers.opendss.reader:_read:112 - Graph with 2 nodes and 1 edges
2025-05-02 11:07:58.902 | DEBUG | ditto.readers.opendss.reader:_read:113 - Graph build complete...
2025-05-02 11:07:58.902 | DEBUG | ditto.readers.opendss.reader:_read:114 - Updating graph to fix split phase representation...
2025-05-02 11:07:58.903 | DEBUG | ditto.readers.opendss.reader:_read:116 - System update complete...
Validation warning summary ┏━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Model ┃ Type ┃ Field ┃ Error ┃ Message ┃ ┡━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ line_1 │ MatrixImpedanceBranch │ On model validation │ value_error │ Value error, Conductor phase │ │ │ │ │ │ (phase=<Phase.A: 'A'>) does not match bus │ │ │ │ │ │ phases (bus.phases=[<Phase.B: 'B'>, │ │ │ │ │ │ <Phase.C: 'C'>]) │ └────────┴───────────────────────┴─────────────────────┴─────────────┴────────────────────────────────────────────┘
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
Cell In[5], line 51
48 console = Console()
49 console.print(error_table)
---> 51 raise Exception(f"Validations errors occured when running the script. See the table above")
Exception: Validations errors occured when running the script. See the table above