cim-tables 0.1.0a1__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.
Files changed (39) hide show
  1. cim_tables-0.1.0a1/.gitignore +222 -0
  2. cim_tables-0.1.0a1/DISCLAIMER.md +24 -0
  3. cim_tables-0.1.0a1/LICENSE +28 -0
  4. cim_tables-0.1.0a1/PKG-INFO +243 -0
  5. cim_tables-0.1.0a1/README.md +213 -0
  6. cim_tables-0.1.0a1/cimtables/__init__.py +115 -0
  7. cim_tables-0.1.0a1/cimtables/binder.py +191 -0
  8. cim_tables-0.1.0a1/cimtables/connectivity.py +285 -0
  9. cim_tables-0.1.0a1/cimtables/diff.py +64 -0
  10. cim_tables-0.1.0a1/cimtables/document.py +191 -0
  11. cim_tables-0.1.0a1/cimtables/grammar.lark +69 -0
  12. cim_tables-0.1.0a1/cimtables/graph_write.py +320 -0
  13. cim_tables-0.1.0a1/cimtables/impedance.py +80 -0
  14. cim_tables-0.1.0a1/cimtables/parser.py +252 -0
  15. cim_tables-0.1.0a1/cimtables/py.typed +0 -0
  16. cim_tables-0.1.0a1/cimtables/records.py +29 -0
  17. cim_tables-0.1.0a1/cimtables/synthetic.py +22 -0
  18. cim_tables-0.1.0a1/cimtables/units.py +25 -0
  19. cim_tables-0.1.0a1/cimtables/validate.py +294 -0
  20. cim_tables-0.1.0a1/cimtables/writer.py +498 -0
  21. cim_tables-0.1.0a1/docs/CIMTBL_DESIGN.md +466 -0
  22. cim_tables-0.1.0a1/pyproject.toml +68 -0
  23. cim_tables-0.1.0a1/tests/fixtures/ieee13.cimtbl +146 -0
  24. cim_tables-0.1.0a1/tests/fixtures/ieee14.cimtbl +19 -0
  25. cim_tables-0.1.0a1/tests/fixtures/wire_infos.cimtbl +6 -0
  26. cim_tables-0.1.0a1/tests/test_bind_cim18v17.py +226 -0
  27. cim_tables-0.1.0a1/tests/test_binder.py +264 -0
  28. cim_tables-0.1.0a1/tests/test_connectivity.py +339 -0
  29. cim_tables-0.1.0a1/tests/test_diff.py +79 -0
  30. cim_tables-0.1.0a1/tests/test_document.py +212 -0
  31. cim_tables-0.1.0a1/tests/test_get_qty.py +163 -0
  32. cim_tables-0.1.0a1/tests/test_graph_write.py +198 -0
  33. cim_tables-0.1.0a1/tests/test_impedance.py +95 -0
  34. cim_tables-0.1.0a1/tests/test_load_model.py +175 -0
  35. cim_tables-0.1.0a1/tests/test_parser.py +446 -0
  36. cim_tables-0.1.0a1/tests/test_units.py +42 -0
  37. cim_tables-0.1.0a1/tests/test_update.py +141 -0
  38. cim_tables-0.1.0a1/tests/test_validate.py +130 -0
  39. cim_tables-0.1.0a1/tests/test_writer.py +195 -0
@@ -0,0 +1,222 @@
1
+ .vscode/*
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[codz]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py.cover
52
+ *.lcov
53
+ .hypothesis/
54
+ .pytest_cache/
55
+ cover/
56
+
57
+ # Translations
58
+ *.mo
59
+ *.pot
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+ db.sqlite3
65
+ db.sqlite3-journal
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ .pybuilder/
79
+ target/
80
+
81
+ # Jupyter Notebook
82
+ .ipynb_checkpoints
83
+
84
+ # IPython
85
+ profile_default/
86
+ ipython_config.py
87
+
88
+ # pyenv
89
+ # For a library or package, you might want to ignore these files since the code is
90
+ # intended to run in multiple environments; otherwise, check them in:
91
+ # .python-version
92
+
93
+ # pipenv
94
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
96
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
97
+ # install all needed dependencies.
98
+ # Pipfile.lock
99
+
100
+ # uv
101
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
102
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
103
+ # commonly ignored for libraries.
104
+ # uv.lock
105
+
106
+ # poetry
107
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
109
+ # commonly ignored for libraries.
110
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111
+ # poetry.lock
112
+ # poetry.toml
113
+
114
+ # pdm
115
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
116
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
117
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
118
+ # pdm.lock
119
+ # pdm.toml
120
+ .pdm-python
121
+ .pdm-build/
122
+
123
+ # pixi
124
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
125
+ # pixi.lock
126
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
127
+ # in the .venv directory. It is recommended not to include this directory in version control.
128
+ .pixi/*
129
+ !.pixi/config.toml
130
+
131
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
132
+ __pypackages__/
133
+
134
+ # Celery stuff
135
+ celerybeat-schedule*
136
+ celerybeat.pid
137
+
138
+ # Redis
139
+ *.rdb
140
+ *.aof
141
+ *.pid
142
+
143
+ # RabbitMQ
144
+ mnesia/
145
+ rabbitmq/
146
+ rabbitmq-data/
147
+
148
+ # ActiveMQ
149
+ activemq-data/
150
+
151
+ # SageMath parsed files
152
+ *.sage.py
153
+
154
+ # Environments
155
+ .env
156
+ .envrc
157
+ .venv
158
+ env/
159
+ venv/
160
+ ENV/
161
+ env.bak/
162
+ venv.bak/
163
+
164
+ # Spyder project settings
165
+ .spyderproject
166
+ .spyproject
167
+
168
+ # Rope project settings
169
+ .ropeproject
170
+
171
+ # mkdocs/Zensical documentation
172
+ /site
173
+
174
+ # mypy
175
+ .mypy_cache/
176
+ .dmypy.json
177
+ dmypy.json
178
+
179
+ # Pyre type checker
180
+ .pyre/
181
+
182
+ # pytype static type analyzer
183
+ .pytype/
184
+
185
+ # Cython debug symbols
186
+ cython_debug/
187
+
188
+ # PyCharm
189
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
190
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
191
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
192
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
193
+ # .idea/
194
+
195
+ # Abstra
196
+ # Abstra is an AI-powered process automation framework.
197
+ # Ignore directories containing user credentials, local state, and settings.
198
+ # Learn more at https://abstra.io/docs
199
+ .abstra/
200
+
201
+ # Visual Studio Code
202
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
203
+ # can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
204
+ # and can be added to the global gitignore or merged into this file. However, if you prefer, you
205
+ # could uncomment the following to ignore the entire vscode folder
206
+ # .vscode/
207
+ # Temporary file for partial code execution
208
+ tempCodeRunnerFile.py
209
+
210
+ # Ruff stuff:
211
+ .ruff_cache/
212
+
213
+ # PyPI configuration file
214
+ .pypirc
215
+
216
+ # Marimo
217
+ marimo/_static/
218
+ marimo/_lsp/
219
+ __marimo__/
220
+
221
+ # Streamlit
222
+ .streamlit/secrets.toml
@@ -0,0 +1,24 @@
1
+ # Disclaimer
2
+
3
+ This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the
4
+ United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any
5
+ jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or
6
+ implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any
7
+ information, apparatus, product, software, or process disclosed, or represents that its use would not infringe
8
+ privately owned rights.
9
+
10
+ Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or
11
+ otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States
12
+ Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do
13
+ not necessarily state or reflect those of the United States Government or any agency thereof.
14
+
15
+ <div align="center">
16
+
17
+ PACIFIC NORTHWEST NATIONAL LABORATORY<br>
18
+ operated by<br>
19
+ BATTELLE<br>
20
+ for the<br>
21
+ UNITED STATES DEPARTMENT OF ENERGY<br>
22
+ under Contract DE-AC05-76RL01830
23
+
24
+ </div>
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Battelle Memorial Institute; All rights reserved.
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,243 @@
1
+ Metadata-Version: 2.5
2
+ Name: cim-tables
3
+ Version: 0.1.0a1
4
+ Summary: The .cimtbl DSL: parser, profile-reflective validator, and graph-write/binder core for cim-graph
5
+ Project-URL: Homepage, https://github.com/PNNL-CIM-Tools/CIM-Tables
6
+ Project-URL: Documentation, https://github.com/PNNL-CIM-Tools/CIM-Tables/blob/main/docs/CIMTBL_DESIGN.md
7
+ Project-URL: Repository, https://github.com/PNNL-CIM-Tools/CIM-Tables
8
+ Project-URL: Issues, https://github.com/PNNL-CIM-Tools/CIM-Tables/issues
9
+ Project-URL: Models, https://github.com/PNNL-CIM-Tools/CIMTBL-Models
10
+ Author-email: "A. Anderson" <19935503+AAndersn@users.noreply.github.com>
11
+ License-Expression: BSD-3-Clause
12
+ License-File: DISCLAIMER.md
13
+ License-File: LICENSE
14
+ Keywords: cim,cim-graph,dsl,grid-models,power-systems
15
+ Classifier: Development Status :: 3 - Alpha
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Science/Research
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: cim-graph<0.7,>=0.6.0a2
27
+ Requires-Dist: cim-profiles<0.2,>=0.1.0a1
28
+ Requires-Dist: lark>=1.1.9
29
+ Description-Content-Type: text/markdown
30
+
31
+ # CIM-Tables
32
+
33
+ **A CIM input file you can write in Notepad.**
34
+
35
+ `cimtables` reads `.cimtbl`, a plain-text table format for building power-system models in the IEC Common
36
+ Information Model (CIM), and turns it into live [cim-graph](https://github.com/PNNL-CIM-Tools/CIM-Graph)
37
+ objects that can be saved as standard CIM RDF/XML or JSON-LD.
38
+
39
+ ## Why this exists
40
+
41
+ CIM is the industry's common language for describing a power grid, but it has never had a format people can
42
+ *write*. Its exchange formats, RDF/XML and JSON-LD, are produced and consumed by software: every object is a
43
+ block of namespaced XML, every link between objects is a UUID, and adding one load to a feeder means hand-editing
44
+ dozens of lines whose meaning is spread across the file. Nobody opens a CIM XML file in a text editor to build a
45
+ model.
46
+
47
+ So in practice, CIM models are built some other way. A model is drawn in a vendor tool, or written in a
48
+ simulator's native format (an OpenDSS `.dss` file, a PSS/E `.raw` file) and then run through a converter. Each
49
+ converter carries its own mapping from the native format's ideas to CIM's, and the CIM model you end up with is
50
+ only as faithful as that mapping. When something is wrong, you debug the converter, not the model.
51
+
52
+ `.cimtbl` removes the middle step. It is to CIM what a `.dss` file is to OpenDSS, except that its vocabulary
53
+ **is** CIM:
54
+
55
+ - every table is a CIM class (`Table ACLineSegment:`),
56
+ - every column header is a CIM attribute or association name (`length`, `BaseVoltage`, `PerLengthImpedance`),
57
+ - every row is one CIM object,
58
+ - units are written once, on the column header (`length (ft)`, `p (kW)`, `r0 (pu)`), and converted for you.
59
+
60
+ There is no mapping to maintain because there is nothing to map. If the CIM profile has a class or attribute,
61
+ you can write it; if it doesn't, you get an error pointing at the line. The result is a file you can create in
62
+ Notepad, read top to bottom, review in a pull request, diff in git, and ask an LLM to edit.
63
+
64
+ ## What it looks like
65
+
66
+ A small radial feeder: one source, two line segments and two loads.
67
+
68
+ ```
69
+ # tiny_feeder.cimtbl
70
+
71
+ Profile = cim18v17
72
+ Authority = https://example.com/models
73
+ Model = tiny_feeder
74
+
75
+ Object BasePower: mRID=sbase, basePower (MVA)=100
76
+ Object Substation: mRID=sub
77
+ Object Feeder: mRID=fdr, NormalEnergizingSubstation=sub
78
+
79
+ Table BaseVoltage: mRID, nominalVoltage (kV)
80
+ bv_12kv, 12.47
81
+
82
+ Object EnergySource: mRID=source, node=sourcebus, phases=ABC, nominalVoltage (kV)=12.47, BaseVoltage=bv_12kv, EquipmentContainer=fdr
83
+
84
+ Table ACLineSegment: mRID, node1, node2, phases, length (ft), r (ohm), x (ohm), BaseVoltage, EquipmentContainer=fdr
85
+ ln_trunk, sourcebus, n1, ABC, 2000, 0.38, 0.75, bv_12kv
86
+ ln_branch, n1, n2, C, 500, 0.12, 0.20, bv_12kv
87
+
88
+ Table EnergyConsumer: mRID, node, phases, p (kW), q (kVAr), BaseVoltage, EquipmentContainer=fdr
89
+ ld_n1, n1, ABC, 1155, 660, bv_12kv
90
+ ld_n2, n2, C, 170, 80, bv_12kv
91
+ ```
92
+
93
+ Load it, and save it as CIM:
94
+
95
+ ```python
96
+ import cimtables
97
+ from cimgraph.utils import write_xml
98
+
99
+ network = cimtables.load_model('tiny_feeder.cimtbl') # a cim-graph FeederModel for Feeder `fdr`
100
+ write_xml(network, 'tiny_feeder.xml') # standard CIM RDF/XML
101
+ ```
102
+
103
+ That is the whole model. Besides the objects you wrote, the loader made what CIM requires
104
+ but no person wants to type: two `Terminal`s per line (`ln_trunk_T1`, `ln_trunk_T2`), one per load and source,
105
+ the `ConnectivityNode`s they meet at (`sourcebus`, `n1`, `n2`), the per-phase children (`ln_trunk_A`,
106
+ `ln_trunk_B`, `ln_trunk_C`, `ln_branch_C`) and each Terminal's `PhaseCode`. The line lengths are stored as
107
+ 609.6 m and 152.4 m, and the loads as 1,155,000 W, because CIM holds SI units; you wrote feet and kilowatts.
108
+
109
+ Larger examples, including the IEEE 13-bus feeder and transmission test cases, are in
110
+ [CIMTBL-Models](https://github.com/PNNL-CIM-Tools/CIMTBL-Models).
111
+
112
+ ## How it stays true to CIM
113
+
114
+ `.cimtbl` is a different *spelling* of a CIM model, not a different model. The rules that keep it that way:
115
+
116
+ - **The profile is the schema.** `cimtables` has no list of supported classes. It reads the CIM profile itself
117
+ (from [cimprofiles](https://github.com/PNNL-CIM-Tools/CIM-Profiles), through cim-graph) and accepts exactly
118
+ what that profile defines: its classes, attributes, associations and enumeration values. A new class in a new
119
+ profile works with no change here, and nothing outside the profile gets in. `Profile = cim18v17` names which
120
+ profile a file is written against.
121
+ - **One row is one CIM object.** A row is what an `rdf:about` block is in CIM XML; a cell is one property; a
122
+ reference cell names its target, as `rdf:resource` does.
123
+ - **Identity is CIM identity.** Objects are named by `mRID`, unique across the whole model as CIM 18 requires.
124
+ Each object's UUID is derived from its mRID and the model's `Authority` and `Model`, so loading the same file
125
+ twice gives the same UUIDs, two models can share a bus called `671` without colliding, and renaming an object
126
+ (`name`) never changes its identity.
127
+ - **Units are CIM units.** Values are stored as CIM's own unit types in SI, exactly as CIM XML holds them. The
128
+ `(unit)` on a column header only says what you typed. `pu` and `percent` impedances are converted to ohms
129
+ against the equipment's own rating (a transformer winding) or the system base (a line), and the system base is
130
+ the model's `BasePower` object, not a hidden default.
131
+ - **Define, then add.** `Object` and `Table` define objects; `Update` adds properties to an object defined
132
+ earlier, possibly in another file. That is CIM's formative and additive split, and how RDF adds properties to
133
+ an existing `rdf:about`.
134
+ - **The shorthand expands into normative CIM.** The only columns that aren't CIM names are the connectivity and
135
+ phase conveniences: `node`/`node1`/`node2` (ConnectivityNode), `bus`/`bus1`/`bus2` (TopologicalNode) and
136
+ `phases`. They generate standard Terminals, nodes and per-phase objects following the CIM rules (delta loads
137
+ included). They are optional: you can write `Terminal` and `ConnectivityNode` rows yourself, and a real CIM
138
+ attribute with the same name always wins.
139
+ - **Nothing hidden.** Everything that affects the model is in the text: the profile, the model identity, the
140
+ base power. The same file gives the same model on any machine.
141
+
142
+ ## Writing a `.cimtbl` file
143
+
144
+ The whole format, briefly:
145
+
146
+ - **`Object <Class>: attribute=value, ...`** for a single object; **`Table <Class>: column, column, ...`**
147
+ followed by indented rows for many.
148
+ - **`#`** starts a comment. Blank lines don't matter.
149
+ - **A blank cell is unset.** Cells are trimmed. A value containing `,` `#` `(` or `)`, or with leading or
150
+ trailing spaces, is quoted as in CSV: `"#2_ACSR_6/1"`, `"DEFAULT (DC)"`. Source names are kept exactly as
151
+ written.
152
+ - **Units go on the column header**, `length (ft)`. A unit-bearing column with no unit is read in SI.
153
+ - **A header column can carry a default** for every row, `EquipmentContainer=fdr`. A row may override it by
154
+ adding a cell at the end.
155
+ - **References are by mRID, and the target must come first.** Declare `BaseVoltage bv_12kv` before the lines
156
+ that use it. Order your file the way you'd explain the model: bases and catalogs, then equipment.
157
+ - **`Import other.cimtbl`** splices another file in at that point, relative to the importing file. Use it to
158
+ share wire and transformer catalogs between models.
159
+ - **The same class can appear in several tables** with different columns, for example lines given by
160
+ impedance in one table and by catalog reference in another.
161
+ - **Phases are written in conductor order**: `ABCN`, `CB`, `s1s2N`. Leave `phases` blank for a balanced
162
+ three-phase device: CIM reads a Terminal with no phases as ABC.
163
+
164
+ The full format, including connectivity, phases, delta connections, relative impedance bases and the error
165
+ rules, is in [docs/CIMTBL_DESIGN.md](https://github.com/PNNL-CIM-Tools/CIM-Tables/blob/main/docs/CIMTBL_DESIGN.md).
166
+
167
+ ## When something is wrong
168
+
169
+ Each row is checked against the profile before it touches the model, and every error names the file and line:
170
+
171
+ ```
172
+ tiny_feeder.cimtbl:21: EnergyConsumerRow has no attribute 'qq' - did you mean 'q'?
173
+ tiny_feeder.cimtbl:14: 'source': BaseVoltage 'bv_13kv' (BaseVoltage) not found in graph (declare it before rows that reference it)
174
+ ```
175
+
176
+ Syntax errors, unknown classes and columns, values of the wrong type, references to undeclared objects,
177
+ duplicate mRIDs and missing ratings all fail the same way, at the line that caused them. Loading stops at the
178
+ first error: nothing is skipped and nothing is guessed.
179
+
180
+ ## Installing
181
+
182
+ ```
183
+ pip install --pre cim-tables
184
+ ```
185
+
186
+ `cim-tables` (imported as `cimtables`) is an alpha release and depends on pre-release versions of `cim-graph` and `cim-profiles`, hence
187
+ `--pre`. It needs Python 3.10 or newer.
188
+
189
+ ## Using it from Python
190
+
191
+ - **`cimtables.load_model(path)`** builds a new model from a file: a `FeederModel` if the file has one
192
+ `Feeder`, else a `NodeBreakerModel` (for a single `Substation`, or no container at all).
193
+ `load_model(path, model_cls=BusBranchModel)` builds a bus-branch model instead.
194
+ - **`cimtables.load(path, network)`** adds a file's objects to a cim-graph model you already have, and
195
+ `load_text(text, network)` does the same for a string.
196
+ - **`cimtables.to_documents(model, name)`** turns any cim-graph model (one read from CIM XML, say) into
197
+ `.cimtbl`; `cimtables.document.write_documents(documents, directory)` writes the files. Loading them back
198
+ gives an equal model. By default an equipment's Terminals and per-phase objects are folded into its `node1`,
199
+ `node2` and `phases` columns, which renames them to `<mRID>_T1` and `<mRID>_A` (their UUIDs change too);
200
+ `terminals='explicit'` keeps every Terminal as its own row with its mRID. Units are picked per column (kV,
201
+ kW, ohm/km, ...); `units={'ACLineSegment.length': 'ft', 'PowerTransformerEnd.x': 'percent'}` (or a JSON
202
+ file of the same) fixes them, and `parts={'catalog': ['WireInfo', ...]}` splits classes into imported files.
203
+ - **`cimtables.document`** writes `.cimtbl` files from Python with stable, aligned formatting. Model converters
204
+ use it to produce `.cimtbl` that a person can then read and edit.
205
+ - Once loaded, the model is ordinary cim-graph: query it, modify it, and write it out with cim-graph's
206
+ `write_xml` or `write_json_ld`.
207
+
208
+ Two settings are process-wide, as they are in cim-graph: `load_model` sets the active CIM profile
209
+ (`CIMG_CIM_PROFILE`) from the file's `Profile =`, and both `load` and `load_model` set cim-graph's model scope
210
+ from `Authority =` and `Model =`.
211
+
212
+ ## Where it fits
213
+
214
+ ```
215
+ cimprofiles → cim-graph → cimtables → CIM-Builder, CIMHub
216
+ (the CIM (the model (.cimtbl in) (model builders and
217
+ classes) in memory) format converters)
218
+ ```
219
+
220
+ - [CIM-Profiles](https://github.com/PNNL-CIM-Tools/CIM-Profiles) provides the CIM classes and units.
221
+ - [CIM-Graph](https://github.com/PNNL-CIM-Tools/CIM-Graph) holds the model and reads and writes CIM XML and
222
+ JSON-LD.
223
+ - [CIM-Builder](https://github.com/PNNL-CIM-Tools/CIM-Builder) builds models programmatically on top of
224
+ `cimtables`.
225
+ - CIMHub's importers (OpenDSS and CYME so far) translate their native format into `.cimtbl` first, so the
226
+ converted model is something a person can read and check.
227
+ - [CIMTBL-Models](https://github.com/PNNL-CIM-Tools/CIMTBL-Models) is a library of grid models written in
228
+ `.cimtbl`.
229
+
230
+ ## Status
231
+
232
+ This is an early release.
233
+
234
+ - The CIM 18 profile `cim18v17` is the one that is tested. Other profiles work in principle, because nothing
235
+ here is profile-specific, but they are not yet covered by tests.
236
+ - References must point backwards in the file. Forward references are an open question.
237
+ - A written model's UUIDs are seeded from its mRIDs, as for any `.cimtbl`: a CIM XML object whose UUID isn't
238
+ its mRID's gets a new one.
239
+
240
+ ## License
241
+
242
+ BSD 3-Clause; see [LICENSE](https://github.com/PNNL-CIM-Tools/CIM-Tables/blob/main/LICENSE) and [DISCLAIMER.md](https://github.com/PNNL-CIM-Tools/CIM-Tables/blob/main/DISCLAIMER.md). Copyright Battelle Memorial Institute.
243
+ Developed at Pacific Northwest National Laboratory.