r2x-sienna 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- r2x_sienna-0.0.0/LICENSE.txt +28 -0
- r2x_sienna-0.0.0/PKG-INFO +88 -0
- r2x_sienna-0.0.0/README.md +26 -0
- r2x_sienna-0.0.0/pyproject.toml +97 -0
- r2x_sienna-0.0.0/src/r2x_sienna/__init__.py +26 -0
- r2x_sienna-0.0.0/src/r2x_sienna/config.py +70 -0
- r2x_sienna-0.0.0/src/r2x_sienna/exporter.py +152 -0
- r2x_sienna-0.0.0/src/r2x_sienna/logger.py +16 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/__init__.py +180 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/attributes.py +68 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/base.py +134 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/branch.py +964 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/core.py +39 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/costs.py +203 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/enums.py +206 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/generators.py +1526 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/getters.py +93 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/load.py +228 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/named_tuples.py +54 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/py.typed +0 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/services.py +130 -0
- r2x_sienna-0.0.0/src/r2x_sienna/models/topology.py +108 -0
- r2x_sienna-0.0.0/src/r2x_sienna/parser.py +456 -0
- r2x_sienna-0.0.0/src/r2x_sienna/plugin.py +23 -0
- r2x_sienna-0.0.0/src/r2x_sienna/py.pyted +2 -0
- r2x_sienna-0.0.0/src/r2x_sienna/serialization.py +188 -0
- r2x_sienna-0.0.0/src/r2x_sienna/units.py +75 -0
- r2x_sienna-0.0.0/src/r2x_sienna/upgrader/__init__.py +13 -0
- r2x_sienna-0.0.0/src/r2x_sienna/upgrader/data_upgrader.py +76 -0
- r2x_sienna-0.0.0/src/r2x_sienna/upgrader/upgrade_steps.py +316 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, PCM
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: r2x-sienna
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Sienna model plugin
|
|
5
|
+
Keywords: Sienna
|
|
6
|
+
Author: mcllerena, pesap
|
|
7
|
+
Author-email: mcllerena <mcllerena@users.noreply.github.com>, pesap <pesap@users.noreply.github.com>
|
|
8
|
+
License: BSD 3-Clause License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2025, PCM
|
|
11
|
+
|
|
12
|
+
Redistribution and use in source and binary forms, with or without
|
|
13
|
+
modification, are permitted provided that the following conditions are met:
|
|
14
|
+
|
|
15
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
16
|
+
list of conditions and the following disclaimer.
|
|
17
|
+
|
|
18
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
19
|
+
this list of conditions and the following disclaimer in the documentation
|
|
20
|
+
and/or other materials provided with the distribution.
|
|
21
|
+
|
|
22
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
23
|
+
contributors may be used to endorse or promote products derived from
|
|
24
|
+
this software without specific prior written permission.
|
|
25
|
+
|
|
26
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
27
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
28
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
29
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
30
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
31
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
32
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
33
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
34
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
35
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
36
|
+
Classifier: Development Status :: 4 - Beta
|
|
37
|
+
Classifier: Intended Audience :: Developers
|
|
38
|
+
Classifier: Intended Audience :: Science/Research
|
|
39
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
40
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
41
|
+
Classifier: Programming Language :: Python
|
|
42
|
+
Classifier: Programming Language :: Python :: 3
|
|
43
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
46
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
47
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
48
|
+
Classifier: Natural Language :: English
|
|
49
|
+
Classifier: Operating System :: OS Independent
|
|
50
|
+
Classifier: Topic :: File Formats :: JSON
|
|
51
|
+
Classifier: Topic :: Scientific/Engineering
|
|
52
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
53
|
+
Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
|
|
54
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
55
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
56
|
+
Classifier: Typing :: Typed
|
|
57
|
+
Requires-Dist: r2x-core>=0.1.1,<1.0.0
|
|
58
|
+
Maintainer: mcllerena, pesap
|
|
59
|
+
Maintainer-email: mcllerena <mcllerena@users.noreply.github.com>, pesap <pesap@users.noreply.github.com>
|
|
60
|
+
Requires-Python: >=3.11, <3.14
|
|
61
|
+
Description-Content-Type: text/markdown
|
|
62
|
+
|
|
63
|
+
# R2X-Sienna
|
|
64
|
+
|
|
65
|
+
> A plugin for the R2X framework that enables translation from Sienna (PowerSystems.jl) data formats to PLEXOS models.
|
|
66
|
+
>
|
|
67
|
+
> [](https://pypi.python.org/pypi/r2x-sienna)
|
|
68
|
+
> [](https://pypi.python.org/pypi/r2x-sienna)
|
|
69
|
+
> [](https://github.com/astral-sh/ruff)
|
|
70
|
+
|
|
71
|
+
## Overview
|
|
72
|
+
|
|
73
|
+
R2X-Sienna provides seamless integration between Sienna and PLEXOS through the R2X translation framework. This plugin allows users to:
|
|
74
|
+
|
|
75
|
+
- Parse Sienna JSON/h5 system files
|
|
76
|
+
- Convert Sienna components to PLEXOS format
|
|
77
|
+
- Export complete PLEXOS XML databases
|
|
78
|
+
- Maintain data integrity during translation
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install r2x-sienna
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
This project is licensed under a BSD 3-Clause License.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# R2X-Sienna
|
|
2
|
+
|
|
3
|
+
> A plugin for the R2X framework that enables translation from Sienna (PowerSystems.jl) data formats to PLEXOS models.
|
|
4
|
+
>
|
|
5
|
+
> [](https://pypi.python.org/pypi/r2x-sienna)
|
|
6
|
+
> [](https://pypi.python.org/pypi/r2x-sienna)
|
|
7
|
+
> [](https://github.com/astral-sh/ruff)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
R2X-Sienna provides seamless integration between Sienna and PLEXOS through the R2X translation framework. This plugin allows users to:
|
|
12
|
+
|
|
13
|
+
- Parse Sienna JSON/h5 system files
|
|
14
|
+
- Convert Sienna components to PLEXOS format
|
|
15
|
+
- Export complete PLEXOS XML databases
|
|
16
|
+
- Maintain data integrity during translation
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install r2x-sienna
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
This project is licensed under a BSD 3-Clause License.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "r2x-sienna"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
description = "Sienna model plugin"
|
|
6
|
+
license = {file = "LICENSE.txt"}
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "mcllerena", email = "mcllerena@users.noreply.github.com" },
|
|
9
|
+
{ name = "pesap", email = "pesap@users.noreply.github.com" },
|
|
10
|
+
]
|
|
11
|
+
maintainers = [
|
|
12
|
+
{ name = "mcllerena", email = "mcllerena@users.noreply.github.com" },
|
|
13
|
+
{ name = "pesap", email = "pesap@users.noreply.github.com" },
|
|
14
|
+
]
|
|
15
|
+
requires-python = ">=3.11, <3.14"
|
|
16
|
+
dependencies = [
|
|
17
|
+
"r2x-core>=0.1.1,<1.0.0",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
keywords=["Sienna"]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Intended Audience :: Science/Research",
|
|
25
|
+
"License :: OSI Approved :: BSD License",
|
|
26
|
+
"Topic :: Software Development :: Build Tools",
|
|
27
|
+
"Programming Language :: Python",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"License :: OSI Approved :: BSD License",
|
|
34
|
+
"Natural Language :: English",
|
|
35
|
+
"Operating System :: OS Independent",
|
|
36
|
+
"Topic :: File Formats :: JSON",
|
|
37
|
+
"Topic :: Scientific/Engineering",
|
|
38
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
39
|
+
"Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator",
|
|
40
|
+
"Topic :: Software Development :: Build Tools",
|
|
41
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
42
|
+
"Typing :: Typed",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.entry-points."r2x_plugin"]
|
|
46
|
+
sienna = "r2x_sienna.plugin:manifest"
|
|
47
|
+
|
|
48
|
+
[build-system]
|
|
49
|
+
requires = ["uv_build>=0.8.22,<0.9.0"]
|
|
50
|
+
build-backend = "uv_build"
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
exclude = [
|
|
54
|
+
'plugins',
|
|
55
|
+
]
|
|
56
|
+
ignore_missing_imports=true
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
[dependency-groups]
|
|
60
|
+
dev = [
|
|
61
|
+
"ipython>=9.2.0",
|
|
62
|
+
"mypy>=1.15.0",
|
|
63
|
+
"pre-commit>=4.2.0",
|
|
64
|
+
"pytest>=8.3.5",
|
|
65
|
+
"pytest-coverage>=0.0",
|
|
66
|
+
"ruff>=0.11.5",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.ruff]
|
|
70
|
+
line-length = 110
|
|
71
|
+
extend-exclude = [
|
|
72
|
+
".venv",
|
|
73
|
+
"venv",
|
|
74
|
+
"build",
|
|
75
|
+
"dist",
|
|
76
|
+
"*.egg-info",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.pytest.ini_options]
|
|
80
|
+
pythonpath = [
|
|
81
|
+
"src"
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
filterwarnings = [
|
|
85
|
+
"ignore::DeprecationWarning:infrasys.*",
|
|
86
|
+
"ignore::pydantic._internal._generate_schema.UnsupportedFieldAttributeWarning",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.commitizen]
|
|
90
|
+
name = "cz_conventional_commits"
|
|
91
|
+
tag_format = "$version"
|
|
92
|
+
version_scheme = "pep440"
|
|
93
|
+
version_provider = "uv"
|
|
94
|
+
major_version_zero = true
|
|
95
|
+
|
|
96
|
+
[tool.uv]
|
|
97
|
+
prerelease = "allow"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""R2X Sienna Plugin.
|
|
2
|
+
A plugin for parsing Sienna model data into the R2X framework using infrasys components.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import version
|
|
6
|
+
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from .config import SiennaConfig
|
|
10
|
+
from .exporter import SiennaExporter
|
|
11
|
+
from .parser import SiennaParser
|
|
12
|
+
|
|
13
|
+
__version__ = version("r2x_sienna")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Disable default loguru handler for library usage
|
|
17
|
+
# Applications using this library should configure their own handlers
|
|
18
|
+
logger.disable("r2x_sienna")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"SiennaConfig",
|
|
23
|
+
"SiennaParser",
|
|
24
|
+
"SiennaExporter",
|
|
25
|
+
"__version__",
|
|
26
|
+
]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Configuration for Sienna parser."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
from r2x_core.plugin_config import PluginConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SiennaConfig(PluginConfig):
|
|
12
|
+
"""Configuration for Sienna model parser.
|
|
13
|
+
|
|
14
|
+
This configuration class defines all parameters needed to parse
|
|
15
|
+
Sienna model data, including year information and model-specific settings.
|
|
16
|
+
Model-specific defaults and constants should be loaded using the
|
|
17
|
+
`load_defaults()` class method and used in parser logic.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
model_year : int | list[int]
|
|
22
|
+
Model solve year(s) (e.g., 2030, [2030, 2040, 2050])
|
|
23
|
+
system_name : str, optional
|
|
24
|
+
Name of the power system
|
|
25
|
+
scenario : str, optional
|
|
26
|
+
Scenario identifier
|
|
27
|
+
system_base_power : float, optional
|
|
28
|
+
System base power in MVA for per-unit calculations
|
|
29
|
+
skip_validation : bool, optional
|
|
30
|
+
Whether to skip validation during parsing
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
--------
|
|
34
|
+
Single year with custom base power:
|
|
35
|
+
|
|
36
|
+
>>> config = SiennaConfig(
|
|
37
|
+
... model_year=2030,
|
|
38
|
+
... system_name="EI_Sys",
|
|
39
|
+
... system_base_power=100.0,
|
|
40
|
+
... skip_validation=True,
|
|
41
|
+
... )
|
|
42
|
+
|
|
43
|
+
Multiple years:
|
|
44
|
+
|
|
45
|
+
>>> config = SiennaConfig(
|
|
46
|
+
... model_year=[2030, 2040, 2050],
|
|
47
|
+
... system_name="Case5_PJM",
|
|
48
|
+
... skip_validation=False,
|
|
49
|
+
... )
|
|
50
|
+
|
|
51
|
+
See Also
|
|
52
|
+
--------
|
|
53
|
+
r2x_core.plugin_config.PluginConfig : Base configuration class
|
|
54
|
+
r2x_sienna.parser.SiennaParser : Parser that uses this configuration
|
|
55
|
+
load_defaults : Class method to load default constants from JSON
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
model_year: Annotated[
|
|
59
|
+
int | list[int] | None,
|
|
60
|
+
Field(description="Model solve year(s) - automatically converted to list"),
|
|
61
|
+
] = None
|
|
62
|
+
system_name: Annotated[str | None, Field(default=None, description="Power system name")] = None
|
|
63
|
+
json_path: Annotated[str | None, Field(default=None, description="Path to JSON data file")] = None
|
|
64
|
+
scenario: Annotated[str, Field(default="base", description="Scenario identifier")] = "base"
|
|
65
|
+
system_base_power: Annotated[
|
|
66
|
+
float, Field(default=100.0, description="System base power in MVA for per-unit calculations")
|
|
67
|
+
] = 100.0
|
|
68
|
+
skip_validation: Annotated[
|
|
69
|
+
bool, Field(default=False, description="Whether to skip validation during parsing")
|
|
70
|
+
] = False
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
import orjson
|
|
6
|
+
from infrasys import TimeSeriesStorageType
|
|
7
|
+
from loguru import logger
|
|
8
|
+
from r2x_core.exceptions import ExporterError
|
|
9
|
+
from r2x_core.exporter import BaseExporter
|
|
10
|
+
from r2x_core.result import Err, Ok, Result
|
|
11
|
+
|
|
12
|
+
from r2x_sienna.serialization import serialize_component_to_psy
|
|
13
|
+
|
|
14
|
+
PARAMETRIZED_OUTPUT_TYPES = {"value_curve", "function_data", "loss"}
|
|
15
|
+
OUTPUT_METADATA = {"__metadata__", "internal"}
|
|
16
|
+
PARAMETRIZED_FIELDS = {"direction"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SiennaExporter(BaseExporter):
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
config,
|
|
23
|
+
system,
|
|
24
|
+
/,
|
|
25
|
+
*,
|
|
26
|
+
data_store=None,
|
|
27
|
+
system_data=None,
|
|
28
|
+
output_path=None,
|
|
29
|
+
export_time_series=True,
|
|
30
|
+
**kwargs,
|
|
31
|
+
):
|
|
32
|
+
self.should_export_time_series = export_time_series
|
|
33
|
+
self.system_data = system_data or {}
|
|
34
|
+
self.output_path = output_path
|
|
35
|
+
self.output_json = {}
|
|
36
|
+
|
|
37
|
+
# Pass filtered kwargs to parent to avoid overwriting methods
|
|
38
|
+
# Remove any kwargs that might conflict with our methods or attributes
|
|
39
|
+
filtered_kwargs = {
|
|
40
|
+
k: v for k, v in kwargs.items() if k not in {"export_time_series", "system_data", "output_path"}
|
|
41
|
+
}
|
|
42
|
+
super().__init__(config, system, data_store=data_store, **filtered_kwargs)
|
|
43
|
+
|
|
44
|
+
def setup_configuration(self) -> Result[None, ExporterError]:
|
|
45
|
+
if self.output_path is None:
|
|
46
|
+
return Err(ExporterError("output_path is required"))
|
|
47
|
+
|
|
48
|
+
if not isinstance(self.output_path, Path):
|
|
49
|
+
self.output_path = Path(self.output_path)
|
|
50
|
+
|
|
51
|
+
if not self.output_path.parent.exists():
|
|
52
|
+
try:
|
|
53
|
+
self.output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
return Err(ExporterError(f"Failed to create output directory: {e}"))
|
|
56
|
+
|
|
57
|
+
return Ok(None)
|
|
58
|
+
|
|
59
|
+
def prepare_export(self) -> Result[None, ExporterError]:
|
|
60
|
+
try:
|
|
61
|
+
system_information = self.system_data.get(
|
|
62
|
+
"system_information", self._default_system_information()
|
|
63
|
+
)
|
|
64
|
+
self.output_json = {**system_information}
|
|
65
|
+
self.output_json["data"] = self.system_data.get("data_information", {})
|
|
66
|
+
|
|
67
|
+
components = []
|
|
68
|
+
for component in self.system._component_mgr.iter_all():
|
|
69
|
+
serialized = serialize_component_to_psy(component)
|
|
70
|
+
if serialized is not None:
|
|
71
|
+
components.append(serialized)
|
|
72
|
+
|
|
73
|
+
self.output_json["data"]["components"] = components
|
|
74
|
+
self.output_json["data"]["subsystems"] = {}
|
|
75
|
+
self.output_json["data"]["masked_components"] = {}
|
|
76
|
+
|
|
77
|
+
dumped_data = orjson.dumps(self.output_json)
|
|
78
|
+
with open(self.output_path, "wb") as f:
|
|
79
|
+
f.write(dumped_data)
|
|
80
|
+
|
|
81
|
+
return Ok(None)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error("Failed to export system: {}", e)
|
|
84
|
+
return Err(ExporterError(f"Export failed: {e}"))
|
|
85
|
+
|
|
86
|
+
def export_time_series(self) -> Result[None, ExporterError]:
|
|
87
|
+
if not self.should_export_time_series:
|
|
88
|
+
logger.debug("Time series export disabled, skipping")
|
|
89
|
+
return Ok(None)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
self.system.convert_storage(time_series_storage_type=TimeSeriesStorageType.HDF5)
|
|
93
|
+
|
|
94
|
+
storage_file_path = f"{self.output_path.stem}_time_series_storage.h5"
|
|
95
|
+
full_storage_path = self.output_path.parent / storage_file_path
|
|
96
|
+
|
|
97
|
+
import os
|
|
98
|
+
|
|
99
|
+
if os.path.exists(full_storage_path):
|
|
100
|
+
os.remove(full_storage_path)
|
|
101
|
+
logger.debug(f"Removed existing storage file: {full_storage_path}")
|
|
102
|
+
|
|
103
|
+
self.system._time_series_mgr.serialize({}, full_storage_path, db_name=self.system.DB_FILENAME)
|
|
104
|
+
|
|
105
|
+
self.output_json["data"]["time_series_storage_type"] = (
|
|
106
|
+
"InfrastructureSystems.Hdf5TimeSeriesStorage"
|
|
107
|
+
)
|
|
108
|
+
self.output_json["data"]["time_series_storage_file"] = storage_file_path
|
|
109
|
+
self.output_json["data"]["internal"] = {
|
|
110
|
+
"uuid": {"value": str(uuid4())},
|
|
111
|
+
"ext": {},
|
|
112
|
+
"units_info": None,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
dumped_data = orjson.dumps(self.output_json)
|
|
116
|
+
with open(self.output_path, "wb") as f:
|
|
117
|
+
f.write(dumped_data)
|
|
118
|
+
|
|
119
|
+
return Ok(None)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error("Failed to export time series: {}", e)
|
|
122
|
+
return Err(ExporterError(f"Time series export failed: {e}"))
|
|
123
|
+
|
|
124
|
+
def _default_system_information(self) -> dict[str, Any]:
|
|
125
|
+
return {
|
|
126
|
+
"internal": {
|
|
127
|
+
"uuid": {"value": str(uuid4())},
|
|
128
|
+
"ext": {},
|
|
129
|
+
"units_info": None,
|
|
130
|
+
},
|
|
131
|
+
"units_settings": {
|
|
132
|
+
"base_value": 100.0,
|
|
133
|
+
"unit_system": "NATURAL_UNITS",
|
|
134
|
+
"__metadata__": {"module": "InfrastructureSystems", "type": "SystemUnitsSettings"},
|
|
135
|
+
},
|
|
136
|
+
"frequency": 60.0,
|
|
137
|
+
"runchecks": True,
|
|
138
|
+
"metadata": {
|
|
139
|
+
"name": None,
|
|
140
|
+
"description": None,
|
|
141
|
+
"__metadata__": {"module": "PowerSystems", "type": "SystemMetadata"},
|
|
142
|
+
},
|
|
143
|
+
"data_format_version": "4.0.0",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def to_psy(config, system, system_data, filename, /, *, write_year=None, **kwargs):
|
|
148
|
+
exporter = SiennaExporter(config, system, system_data=system_data, output_path=filename, **kwargs)
|
|
149
|
+
result = exporter.export()
|
|
150
|
+
if result.is_err():
|
|
151
|
+
raise Exception(f"Export failed: {result.error}")
|
|
152
|
+
return result
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def timeit(func):
|
|
7
|
+
"""Time function."""
|
|
8
|
+
|
|
9
|
+
def wrapper(*args, **kwargs):
|
|
10
|
+
start_time = time.time()
|
|
11
|
+
result = func(*args, **kwargs)
|
|
12
|
+
end_time = time.time()
|
|
13
|
+
logger.debug(f"Function {func.__name__} executed in {end_time - start_time:.4f} seconds")
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
return wrapper
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from .attributes import GeographicInfo, GeometricDistributionForcedOutage, ImpedanceCorrectionData
|
|
2
|
+
from .branch import (
|
|
3
|
+
ACBranch,
|
|
4
|
+
AreaInterchange,
|
|
5
|
+
Branch,
|
|
6
|
+
DCBranch,
|
|
7
|
+
DiscreteControlledACBranch,
|
|
8
|
+
Line,
|
|
9
|
+
MonitoredLine,
|
|
10
|
+
PhaseShiftingTransformer,
|
|
11
|
+
PhaseShiftingTransformer3W,
|
|
12
|
+
TapTransformer,
|
|
13
|
+
ThreeWindingTransformer,
|
|
14
|
+
TModelHVDCLine,
|
|
15
|
+
Transformer2W,
|
|
16
|
+
Transformer3W,
|
|
17
|
+
TwoTerminalGenericHVDCLine,
|
|
18
|
+
TwoTerminalLCCLine,
|
|
19
|
+
TwoTerminalVSCLine,
|
|
20
|
+
TwoWindingTransformer,
|
|
21
|
+
)
|
|
22
|
+
from .core import ReserveMap, Service, TransmissionInterfaceMap
|
|
23
|
+
from .costs import (
|
|
24
|
+
HydroGenerationCost,
|
|
25
|
+
HydroReservoirCost,
|
|
26
|
+
RenewableGenerationCost,
|
|
27
|
+
StorageCost,
|
|
28
|
+
ThermalGenerationCost,
|
|
29
|
+
)
|
|
30
|
+
from .enums import (
|
|
31
|
+
DiscreteControlledBranchStatus,
|
|
32
|
+
DiscreteControlledBranchType,
|
|
33
|
+
FACTSOperationModes,
|
|
34
|
+
HydroTurbineType,
|
|
35
|
+
ImpedanceCorrectionTransformerControlMode,
|
|
36
|
+
PrimeMoversType,
|
|
37
|
+
PumpHydroStatus,
|
|
38
|
+
ReserveDirection,
|
|
39
|
+
ReserveType,
|
|
40
|
+
ReservoirDataType,
|
|
41
|
+
ReservoirLocation,
|
|
42
|
+
ThermalFuels,
|
|
43
|
+
TransformerControlObjective,
|
|
44
|
+
WindingCategory,
|
|
45
|
+
WindingGroupNumber,
|
|
46
|
+
)
|
|
47
|
+
from .generators import (
|
|
48
|
+
EnergyReservoirStorage,
|
|
49
|
+
HybridSystem,
|
|
50
|
+
HydroDispatch,
|
|
51
|
+
HydroEnergyReservoir,
|
|
52
|
+
HydroGen,
|
|
53
|
+
HydroPumpedStorage,
|
|
54
|
+
HydroPumpTurbine,
|
|
55
|
+
HydroReservoir,
|
|
56
|
+
HydroTurbine,
|
|
57
|
+
RenewableDispatch,
|
|
58
|
+
RenewableGen,
|
|
59
|
+
RenewableNonDispatch,
|
|
60
|
+
SynchronousCondenser,
|
|
61
|
+
ThermalMultiStart,
|
|
62
|
+
ThermalStandard,
|
|
63
|
+
)
|
|
64
|
+
from .load import (
|
|
65
|
+
FACTSControlDevice,
|
|
66
|
+
FixedAdmittance,
|
|
67
|
+
InterruptiblePowerLoad,
|
|
68
|
+
PowerLoad,
|
|
69
|
+
StandardLoad,
|
|
70
|
+
SwitchedAdmittance,
|
|
71
|
+
)
|
|
72
|
+
from .named_tuples import (
|
|
73
|
+
Complex,
|
|
74
|
+
FromTo_ToFrom,
|
|
75
|
+
GeoLocation,
|
|
76
|
+
InputOutput,
|
|
77
|
+
MinMax,
|
|
78
|
+
StartShut,
|
|
79
|
+
StartTimeLimits,
|
|
80
|
+
StartUpStages,
|
|
81
|
+
UpDown,
|
|
82
|
+
)
|
|
83
|
+
from .services import Reserve, TransmissionInterface, VariableReserve
|
|
84
|
+
from .topology import ACBus, Arc, Area, Bus, DCBus, LoadZone
|
|
85
|
+
|
|
86
|
+
__all__ = [
|
|
87
|
+
# enums
|
|
88
|
+
"DiscreteControlledBranchStatus",
|
|
89
|
+
"DiscreteControlledBranchType",
|
|
90
|
+
"TransformerControlObjective",
|
|
91
|
+
"FACTSOperationModes",
|
|
92
|
+
"WindingGroupNumber",
|
|
93
|
+
"ImpedanceCorrectionTransformerControlMode",
|
|
94
|
+
"WindingCategory",
|
|
95
|
+
"PumpHydroStatus",
|
|
96
|
+
"ReservoirLocation",
|
|
97
|
+
"ReservoirDataType",
|
|
98
|
+
"HydroTurbineType",
|
|
99
|
+
"PrimeMoversType",
|
|
100
|
+
"ThermalFuels",
|
|
101
|
+
# attributes
|
|
102
|
+
"GeographicInfo",
|
|
103
|
+
"ImpedanceCorrectionData",
|
|
104
|
+
"GeometricDistributionForcedOutage",
|
|
105
|
+
# branch
|
|
106
|
+
"ACBranch",
|
|
107
|
+
"AreaInterchange",
|
|
108
|
+
"Branch",
|
|
109
|
+
"DCBranch",
|
|
110
|
+
"Line",
|
|
111
|
+
"MonitoredLine",
|
|
112
|
+
"PhaseShiftingTransformer",
|
|
113
|
+
"DiscreteControlledACBranch",
|
|
114
|
+
"TapTransformer",
|
|
115
|
+
"TModelHVDCLine",
|
|
116
|
+
"Transformer3W",
|
|
117
|
+
"ThreeWindingTransformer",
|
|
118
|
+
"PhaseShiftingTransformer3W",
|
|
119
|
+
"TwoWindingTransformer",
|
|
120
|
+
"Transformer2W",
|
|
121
|
+
"TwoTerminalGenericHVDCLine",
|
|
122
|
+
"TwoTerminalLCCLine",
|
|
123
|
+
"TwoTerminalVSCLine",
|
|
124
|
+
# core
|
|
125
|
+
"ReserveType",
|
|
126
|
+
"ReserveDirection",
|
|
127
|
+
"ReserveMap",
|
|
128
|
+
"Service",
|
|
129
|
+
"TransmissionInterfaceMap",
|
|
130
|
+
# costs
|
|
131
|
+
"HydroGenerationCost",
|
|
132
|
+
"HydroReservoirCost",
|
|
133
|
+
"RenewableGenerationCost",
|
|
134
|
+
"StorageCost",
|
|
135
|
+
"ThermalGenerationCost",
|
|
136
|
+
# generators
|
|
137
|
+
"SynchronousCondenser",
|
|
138
|
+
"EnergyReservoirStorage",
|
|
139
|
+
"HybridSystem",
|
|
140
|
+
"HydroDispatch",
|
|
141
|
+
"HydroEnergyReservoir",
|
|
142
|
+
"HydroGen",
|
|
143
|
+
"HydroPumpedStorage",
|
|
144
|
+
"HydroReservoir",
|
|
145
|
+
"HydroTurbine",
|
|
146
|
+
"RenewableDispatch",
|
|
147
|
+
"RenewableGen",
|
|
148
|
+
"RenewableNonDispatch",
|
|
149
|
+
"ThermalMultiStart",
|
|
150
|
+
"ThermalStandard",
|
|
151
|
+
"HydroPumpTurbine",
|
|
152
|
+
# load
|
|
153
|
+
"FixedAdmittance",
|
|
154
|
+
"SwitchedAdmittance",
|
|
155
|
+
"InterruptiblePowerLoad",
|
|
156
|
+
"FACTSControlDevice",
|
|
157
|
+
"PowerLoad",
|
|
158
|
+
"StandardLoad",
|
|
159
|
+
# named_tuples
|
|
160
|
+
"Complex",
|
|
161
|
+
"FromTo_ToFrom",
|
|
162
|
+
"GeoLocation",
|
|
163
|
+
"InputOutput",
|
|
164
|
+
"MinMax",
|
|
165
|
+
"StartShut",
|
|
166
|
+
"StartUpStages",
|
|
167
|
+
"StartTimeLimits",
|
|
168
|
+
"UpDown",
|
|
169
|
+
# services
|
|
170
|
+
"Reserve",
|
|
171
|
+
"TransmissionInterface",
|
|
172
|
+
"VariableReserve",
|
|
173
|
+
# topology
|
|
174
|
+
"ACBus",
|
|
175
|
+
"Arc",
|
|
176
|
+
"Area",
|
|
177
|
+
"Bus",
|
|
178
|
+
"DCBus",
|
|
179
|
+
"LoadZone",
|
|
180
|
+
]
|