mambo-power 0.1.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.
Files changed (174) hide show
  1. mambo_power-0.1.0/.gitignore +19 -0
  2. mambo_power-0.1.0/LICENSE +21 -0
  3. mambo_power-0.1.0/PKG-INFO +141 -0
  4. mambo_power-0.1.0/README.md +128 -0
  5. mambo_power-0.1.0/fixtures/PROVENANCE-raw.md +242 -0
  6. mambo_power-0.1.0/fixtures/case14_v33.raw +85 -0
  7. mambo_power-0.1.0/fixtures/matpower/PROVENANCE.md +208 -0
  8. mambo_power-0.1.0/fixtures/matpower/SOURCES.md +36 -0
  9. mambo_power-0.1.0/fixtures/matpower/case118.m +787 -0
  10. mambo_power-0.1.0/fixtures/matpower/case14.m +129 -0
  11. mambo_power-0.1.0/fixtures/matpower/case30.m +130 -0
  12. mambo_power-0.1.0/fixtures/matpower/case300.m +1322 -0
  13. mambo_power-0.1.0/fixtures/matpower/case57.m +342 -0
  14. mambo_power-0.1.0/fixtures/matpower/case_ieee30.m +211 -0
  15. mambo_power-0.1.0/fixtures/matpower/derived/PROVENANCE.md +84 -0
  16. mambo_power-0.1.0/fixtures/matpower/derived/case14_island.m +145 -0
  17. mambo_power-0.1.0/fixtures/matpower/derived/case14_noslackgen.m +142 -0
  18. mambo_power-0.1.0/fixtures/matpower/derived/case14_pwl.m +163 -0
  19. mambo_power-0.1.0/fixtures/matpower/derived/case14_roles.m +149 -0
  20. mambo_power-0.1.0/fixtures/synthetic_quirks_v33.raw +55 -0
  21. mambo_power-0.1.0/pyproject.toml +132 -0
  22. mambo_power-0.1.0/src/mambo_power/__init__.py +10 -0
  23. mambo_power-0.1.0/src/mambo_power/contingency/__init__.py +60 -0
  24. mambo_power-0.1.0/src/mambo_power/contingency/n1.py +160 -0
  25. mambo_power-0.1.0/src/mambo_power/io/__init__.py +32 -0
  26. mambo_power-0.1.0/src/mambo_power/io/csv_bundle.py +713 -0
  27. mambo_power-0.1.0/src/mambo_power/io/limitations.py +27 -0
  28. mambo_power-0.1.0/src/mambo_power/io/matpower.py +465 -0
  29. mambo_power-0.1.0/src/mambo_power/io/native.py +32 -0
  30. mambo_power-0.1.0/src/mambo_power/io/pandapower_json.py +1166 -0
  31. mambo_power-0.1.0/src/mambo_power/io/psse_raw.py +776 -0
  32. mambo_power-0.1.0/src/mambo_power/io/pypsa.py +376 -0
  33. mambo_power-0.1.0/src/mambo_power/io/report.py +77 -0
  34. mambo_power-0.1.0/src/mambo_power/jobs/__init__.py +44 -0
  35. mambo_power-0.1.0/src/mambo_power/jobs/models.py +216 -0
  36. mambo_power-0.1.0/src/mambo_power/jobs/registry.py +323 -0
  37. mambo_power-0.1.0/src/mambo_power/jobs/run.py +355 -0
  38. mambo_power-0.1.0/src/mambo_power/market/__init__.py +40 -0
  39. mambo_power-0.1.0/src/mambo_power/market/_clearing.py +131 -0
  40. mambo_power-0.1.0/src/mambo_power/market/agents.py +673 -0
  41. mambo_power-0.1.0/src/mambo_power/market/multiperiod.py +329 -0
  42. mambo_power-0.1.0/src/mambo_power/market/nodal.py +178 -0
  43. mambo_power-0.1.0/src/mambo_power/market/strategy.py +371 -0
  44. mambo_power-0.1.0/src/mambo_power/market/zonal.py +706 -0
  45. mambo_power-0.1.0/src/mambo_power/model/__init__.py +56 -0
  46. mambo_power-0.1.0/src/mambo_power/model/entities.py +249 -0
  47. mambo_power-0.1.0/src/mambo_power/model/errors.py +66 -0
  48. mambo_power-0.1.0/src/mambo_power/model/islands.py +196 -0
  49. mambo_power-0.1.0/src/mambo_power/model/network.py +308 -0
  50. mambo_power-0.1.0/src/mambo_power/model/scenario.py +98 -0
  51. mambo_power-0.1.0/src/mambo_power/model/warnings.py +127 -0
  52. mambo_power-0.1.0/src/mambo_power/numerics/__init__.py +36 -0
  53. mambo_power-0.1.0/src/mambo_power/numerics/arrays.py +265 -0
  54. mambo_power-0.1.0/src/mambo_power/numerics/bbus.py +97 -0
  55. mambo_power-0.1.0/src/mambo_power/numerics/errors.py +48 -0
  56. mambo_power-0.1.0/src/mambo_power/numerics/lodf.py +89 -0
  57. mambo_power-0.1.0/src/mambo_power/numerics/ptdf.py +36 -0
  58. mambo_power-0.1.0/src/mambo_power/numerics/roles.py +107 -0
  59. mambo_power-0.1.0/src/mambo_power/numerics/ybus.py +69 -0
  60. mambo_power-0.1.0/src/mambo_power/opf/__init__.py +259 -0
  61. mambo_power-0.1.0/src/mambo_power/opf/dc_opf.py +1014 -0
  62. mambo_power-0.1.0/src/mambo_power/opf/multiperiod.py +680 -0
  63. mambo_power-0.1.0/src/mambo_power/opf/redispatch.py +579 -0
  64. mambo_power-0.1.0/src/mambo_power/opf/zonal.py +522 -0
  65. mambo_power-0.1.0/src/mambo_power/pf/__init__.py +160 -0
  66. mambo_power-0.1.0/src/mambo_power/pf/_common.py +41 -0
  67. mambo_power-0.1.0/src/mambo_power/pf/ac_newton.py +337 -0
  68. mambo_power-0.1.0/src/mambo_power/pf/dc.py +107 -0
  69. mambo_power-0.1.0/src/mambo_power/py.typed +0 -0
  70. mambo_power-0.1.0/src/mambo_power/results/__init__.py +93 -0
  71. mambo_power-0.1.0/src/mambo_power/results/agents.py +182 -0
  72. mambo_power-0.1.0/src/mambo_power/results/feasibility.py +93 -0
  73. mambo_power-0.1.0/src/mambo_power/results/from_arrays.py +196 -0
  74. mambo_power-0.1.0/src/mambo_power/results/market.py +92 -0
  75. mambo_power-0.1.0/src/mambo_power/results/multiperiod.py +188 -0
  76. mambo_power-0.1.0/src/mambo_power/results/n1.py +75 -0
  77. mambo_power-0.1.0/src/mambo_power/results/opf.py +82 -0
  78. mambo_power-0.1.0/src/mambo_power/results/power_flow.py +100 -0
  79. mambo_power-0.1.0/src/mambo_power/results/provenance.py +43 -0
  80. mambo_power-0.1.0/src/mambo_power/results/tables.py +70 -0
  81. mambo_power-0.1.0/src/mambo_power/results/zonal.py +243 -0
  82. mambo_power-0.1.0/tests/_agents.py +194 -0
  83. mambo_power-0.1.0/tests/_bids.py +225 -0
  84. mambo_power-0.1.0/tests/_brute_force_lodf.py +44 -0
  85. mambo_power-0.1.0/tests/_brute_force_n1.py +44 -0
  86. mambo_power-0.1.0/tests/_degeneracy.py +355 -0
  87. mambo_power-0.1.0/tests/_fixtures.py +12 -0
  88. mambo_power-0.1.0/tests/_periods.py +97 -0
  89. mambo_power-0.1.0/tests/_rated.py +66 -0
  90. mambo_power-0.1.0/tests/_shifter.py +113 -0
  91. mambo_power-0.1.0/tests/_storage.py +125 -0
  92. mambo_power-0.1.0/tests/_zones.py +112 -0
  93. mambo_power-0.1.0/tests/conftest.py +24 -0
  94. mambo_power-0.1.0/tests/parity/_mpc_reader.py +37 -0
  95. mambo_power-0.1.0/tests/parity/test_ac_timing.py +58 -0
  96. mambo_power-0.1.0/tests/parity/test_ac_vs_matpower_stored.py +175 -0
  97. mambo_power-0.1.0/tests/parity/test_ac_vs_pandapower.py +316 -0
  98. mambo_power-0.1.0/tests/parity/test_ac_vs_pandapower_island.py +80 -0
  99. mambo_power-0.1.0/tests/parity/test_dc_vs_pandapower.py +214 -0
  100. mambo_power-0.1.0/tests/parity/test_market_multiperiod_vs_pypsa.py +472 -0
  101. mambo_power-0.1.0/tests/parity/test_market_nodal_vs_pandapower.py +219 -0
  102. mambo_power-0.1.0/tests/parity/test_market_zonal_vs_pypsa.py +664 -0
  103. mambo_power-0.1.0/tests/parity/test_matpower_vs_pandapower.py +425 -0
  104. mambo_power-0.1.0/tests/parity/test_opf_vs_pandapower.py +124 -0
  105. mambo_power-0.1.0/tests/parity/test_opf_vs_pypsa.py +194 -0
  106. mambo_power-0.1.0/tests/parity/test_oracles_import.py +15 -0
  107. mambo_power-0.1.0/tests/parity/test_pandapower_json_vs_pandapower.py +242 -0
  108. mambo_power-0.1.0/tests/parity/test_pypsa_export_vs_pypsa.py +304 -0
  109. mambo_power-0.1.0/tests/parity/test_roles_vs_pandapower.py +80 -0
  110. mambo_power-0.1.0/tests/parity/test_shifter_pf_vs_pypsa.py +54 -0
  111. mambo_power-0.1.0/tests/parity/test_ybus_vs_pandapower.py +175 -0
  112. mambo_power-0.1.0/tests/property/test_numerics_properties.py +146 -0
  113. mambo_power-0.1.0/tests/unit/snapshots/network.schema.json +779 -0
  114. mambo_power-0.1.0/tests/unit/test_agents_fixtures.py +332 -0
  115. mambo_power-0.1.0/tests/unit/test_api_docs_coverage.py +83 -0
  116. mambo_power-0.1.0/tests/unit/test_bids.py +162 -0
  117. mambo_power-0.1.0/tests/unit/test_branch_kind.py +120 -0
  118. mambo_power-0.1.0/tests/unit/test_contingency_n1.py +211 -0
  119. mambo_power-0.1.0/tests/unit/test_contingency_n1_brute_force.py +48 -0
  120. mambo_power-0.1.0/tests/unit/test_docs_registry_listing.py +59 -0
  121. mambo_power-0.1.0/tests/unit/test_docstrings.py +105 -0
  122. mambo_power-0.1.0/tests/unit/test_effective_roles.py +131 -0
  123. mambo_power-0.1.0/tests/unit/test_examples_run.py +61 -0
  124. mambo_power-0.1.0/tests/unit/test_export_report.py +51 -0
  125. mambo_power-0.1.0/tests/unit/test_feasibility.py +114 -0
  126. mambo_power-0.1.0/tests/unit/test_fixture_agreement.py +43 -0
  127. mambo_power-0.1.0/tests/unit/test_fixture_case300.py +57 -0
  128. mambo_power-0.1.0/tests/unit/test_fixtures_derived.py +124 -0
  129. mambo_power-0.1.0/tests/unit/test_fixtures_pwl.py +95 -0
  130. mambo_power-0.1.0/tests/unit/test_io_csv_bundle.py +605 -0
  131. mambo_power-0.1.0/tests/unit/test_io_limitations.py +92 -0
  132. mambo_power-0.1.0/tests/unit/test_io_pandapower_json.py +1044 -0
  133. mambo_power-0.1.0/tests/unit/test_io_psse_raw.py +512 -0
  134. mambo_power-0.1.0/tests/unit/test_io_pypsa.py +386 -0
  135. mambo_power-0.1.0/tests/unit/test_islands.py +229 -0
  136. mambo_power-0.1.0/tests/unit/test_jobs.py +1649 -0
  137. mambo_power-0.1.0/tests/unit/test_json_schema_snapshot.py +54 -0
  138. mambo_power-0.1.0/tests/unit/test_load_bid_scenario.py +190 -0
  139. mambo_power-0.1.0/tests/unit/test_market_agents.py +978 -0
  140. mambo_power-0.1.0/tests/unit/test_market_agents_economics.py +544 -0
  141. mambo_power-0.1.0/tests/unit/test_market_multiperiod.py +925 -0
  142. mambo_power-0.1.0/tests/unit/test_market_nodal.py +314 -0
  143. mambo_power-0.1.0/tests/unit/test_market_strategy.py +538 -0
  144. mambo_power-0.1.0/tests/unit/test_market_zonal.py +1571 -0
  145. mambo_power-0.1.0/tests/unit/test_matpower_parser.py +420 -0
  146. mambo_power-0.1.0/tests/unit/test_model_examples.py +66 -0
  147. mambo_power-0.1.0/tests/unit/test_model_invariants.py +348 -0
  148. mambo_power-0.1.0/tests/unit/test_model_roundtrip.py +153 -0
  149. mambo_power-0.1.0/tests/unit/test_native_roundtrip_fixtures.py +42 -0
  150. mambo_power-0.1.0/tests/unit/test_numerics_arrays.py +511 -0
  151. mambo_power-0.1.0/tests/unit/test_numerics_dense.py +329 -0
  152. mambo_power-0.1.0/tests/unit/test_numerics_guards.py +80 -0
  153. mambo_power-0.1.0/tests/unit/test_opf_dc.py +236 -0
  154. mambo_power-0.1.0/tests/unit/test_opf_dc_case14_pwl.py +111 -0
  155. mambo_power-0.1.0/tests/unit/test_opf_dc_demand.py +308 -0
  156. mambo_power-0.1.0/tests/unit/test_opf_dc_pwl.py +146 -0
  157. mambo_power-0.1.0/tests/unit/test_opf_multiperiod.py +1125 -0
  158. mambo_power-0.1.0/tests/unit/test_opf_overlap_guard.py +209 -0
  159. mambo_power-0.1.0/tests/unit/test_opf_pwl_guard.py +60 -0
  160. mambo_power-0.1.0/tests/unit/test_opf_redispatch.py +755 -0
  161. mambo_power-0.1.0/tests/unit/test_opf_solve_dc_opf.py +264 -0
  162. mambo_power-0.1.0/tests/unit/test_opf_zonal.py +642 -0
  163. mambo_power-0.1.0/tests/unit/test_packaging_metadata.py +40 -0
  164. mambo_power-0.1.0/tests/unit/test_period_scenario.py +307 -0
  165. mambo_power-0.1.0/tests/unit/test_periods_helper.py +103 -0
  166. mambo_power-0.1.0/tests/unit/test_pf_ac_newton.py +567 -0
  167. mambo_power-0.1.0/tests/unit/test_pf_dc.py +234 -0
  168. mambo_power-0.1.0/tests/unit/test_pypi_sequencing_guard.py +210 -0
  169. mambo_power-0.1.0/tests/unit/test_rated_helper.py +60 -0
  170. mambo_power-0.1.0/tests/unit/test_results_models.py +288 -0
  171. mambo_power-0.1.0/tests/unit/test_shifter_flow_fix.py +147 -0
  172. mambo_power-0.1.0/tests/unit/test_shifter_flow_fix_multiperiod_redispatch.py +315 -0
  173. mambo_power-0.1.0/tests/unit/test_storage_helper.py +96 -0
  174. mambo_power-0.1.0/tests/unit/test_zones_helper.py +188 -0
@@ -0,0 +1,19 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .hypothesis/
11
+ .cache/
12
+ site/
13
+ .env
14
+ .env.*
15
+
16
+ # Windows/git-bash crash dumps and local tool state — recur in the main checkout each wave
17
+ # (M3, M4 and M5 each swept these by hand; M4's continuation record asked for this entry).
18
+ *.stackdump
19
+ .playwright-cli/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Manho Joung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.5
2
+ Name: mambo-power
3
+ Version: 0.1.0
4
+ Summary: Fundamental Python package for power system analysis and electricity market modelling
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: highspy
9
+ Requires-Dist: numpy
10
+ Requires-Dist: pydantic>=2
11
+ Requires-Dist: scipy
12
+ Description-Content-Type: text/markdown
13
+
14
+ # mambo-power
15
+
16
+ A fundamental Python package for power system analysis and electricity market modelling.
17
+ It owns its network data model and implements its own solvers on numpy, scipy and HiGHS;
18
+ pandapower and PyPSA serve only as test oracles.
19
+
20
+ Documentation: **https://mambo10005.github.io/mambo-power/**
21
+
22
+ ## What it is
23
+
24
+ - A JSON-native network model (pydantic v2): `Network` with buses, branches, generators
25
+ (with cost curves), loads, shunts, storage and zones; physical units, stable string ids,
26
+ all-issues validation with named error codes, JSON schema generated from the model.
27
+ - Importers that speak only the model: MATPOWER `.m` cases and the native JSON format, plus
28
+ pandapower JSON, PyPSA, PSS/E RAW and CSV bundles.
29
+ - Network matrices over scipy.sparse: `NetworkArrays` (the single per-unit conversion site),
30
+ Ybus, Bbus, PTDF, LODF with bridge detection.
31
+ - Solvers: DC power flow and AC Newton-Raphson with Q-limit enforcement; DC optimal power flow
32
+ with duals on HiGHS; N-1 contingency screening; market clearing (nodal LMP, multiperiod with
33
+ storage, zonal with redispatch, agent-based bidding).
34
+ - Typed, id-keyed results stamped with provenance (engine version, solver, timings), never
35
+ stored on the network; a stateless, JSON-serialisable `jobs.run(SolveRequest)` surface
36
+ designed to sit behind a service.
37
+ - Narrative tutorial notebooks (execution-tested in CI), an automated changelog, and PyPI
38
+ trusted publishing.
39
+
40
+ Free in both senses: an open-source stack end to end with no paid solvers or licences, and
41
+ built, tested, documented and published entirely on free infrastructure (GitHub Actions,
42
+ GitHub Pages, PyPI trusted publishing).
43
+
44
+ ## Status
45
+
46
+ | Wave | Scope | State |
47
+ | --- | --- | --- |
48
+ | M1 | Installable package, `Network` model, MATPOWER import, Ybus/Bbus/PTDF/LODF, CI matrix | merged |
49
+ | M2 | DC + AC Newton-Raphson power flow, typed results, `jobs` API, docs site, examples | merged |
50
+ | M3 | DC optimal power flow with duals on HiGHS, N-1 branch-contingency screening | merged |
51
+ | M4 | Nodal market: elastic-demand DC-OPF, LMP clearing, settlement | merged |
52
+ | M5 | Multiperiod market: 24-period horizon, ramp coupling, storage SoC, per-period settlement | merged |
53
+ | M6 | Zonal market: zonal clearing, min-cost redispatch, nodal-vs-zonal comparison | merged |
54
+ | M7 | Agent-based bidding: strategies, offered-vs-true cost overlay, fixed-point loop | merged |
55
+ | M8 | Interchange: pandapower JSON, PyPSA, PSS/E RAW, CSV bundle | merged |
56
+ | M9 | Tutorials, semantic-release changelog, PyPI 0.1.0 trusted publishing | merged |
57
+
58
+ Not yet on PyPI — this changes in the same action as the `v0.1.0` tag; see
59
+ [Getting started](https://mambo10005.github.io/mambo-power/getting-started/) for the current
60
+ install instructions, which is the live source of truth if this file is ever stale (this table
61
+ is not covered by any automated freshness check — see the wave M9 continuation record if you're
62
+ reading this after a release and it still says otherwise). Runtime dependencies are exactly
63
+ `numpy`, `scipy`, `highspy`, `pydantic`; Python 3.11 or newer.
64
+
65
+ ## Install from source
66
+
67
+ ```bash
68
+ git clone https://github.com/mambo10005/mambo-power.git
69
+ cd mambo-power
70
+ uv sync # runtime deps only; add --all-groups for dev + docs tooling
71
+ ```
72
+
73
+ Without [uv](https://docs.astral.sh/uv/): `pip install -e .` in any Python >= 3.11 environment.
74
+
75
+ ## Quick start
76
+
77
+ ```python
78
+ from mambo_power import pf
79
+ from mambo_power.io import matpower
80
+
81
+ net = matpower.load("fixtures/matpower/case14.m") # validated Network, physical units
82
+ result = pf.solve_dc(net) # typed result keyed by ids, MW
83
+ print(result.generators[0].p_mw) # 219.0 -- slack generator balance
84
+ print(result.branches[0].p_from_mw) # 147.84 -- flow bus-1 -> bus-2
85
+ print(result.provenance.version, result.provenance.solver)
86
+ text = result.model_dump_json() # exact JSON round-trip
87
+ again = type(result).model_validate_json(text)
88
+ assert again == result
89
+ ```
90
+
91
+ Then: [Getting started](https://mambo10005.github.io/mambo-power/getting-started/) walks
92
+ through loading, validating, solving and reading results with real output.
93
+
94
+ ## Tutorials and manual
95
+
96
+ [Tutorials](https://mambo10005.github.io/mambo-power/tutorials/) are prose-heavy, narrative
97
+ walkthroughs (a first power flow, DC-OPF + N-1, a nodal market, where to go next) — start there
98
+ if you're new. The manual is the reference:
99
+
100
+ - [Network model](https://mambo10005.github.io/mambo-power/manual/model/) — every entity,
101
+ field, unit, and validation code
102
+ - [File formats](https://mambo10005.github.io/mambo-power/manual/formats/) — native JSON,
103
+ MATPOWER, pandapower JSON, PyPSA, PSS/E RAW, CSV bundles
104
+ - [Numerics](https://mambo10005.github.io/mambo-power/manual/numerics/) — `NetworkArrays`,
105
+ Ybus, Bbus, PTDF, LODF and bridges
106
+ - [Power flow](https://mambo10005.github.io/mambo-power/manual/power-flow/) — the DC
107
+ formulation and the AC solver's contract
108
+ - [DC-OPF](https://mambo10005.github.io/mambo-power/manual/opf/) and
109
+ [N-1 screening](https://mambo10005.github.io/mambo-power/manual/n1/)
110
+ - Market clearing: [nodal](https://mambo10005.github.io/mambo-power/manual/market/),
111
+ [multiperiod](https://mambo10005.github.io/mambo-power/manual/multiperiod/),
112
+ [zonal](https://mambo10005.github.io/mambo-power/manual/zonal/)
113
+ - [Agent-based bidding](https://mambo10005.github.io/mambo-power/manual/agents/) — strategies,
114
+ offered-vs-true cost, the fixed-point loop
115
+ - [Results](https://mambo10005.github.io/mambo-power/manual/results/) — result tables,
116
+ provenance, JSON round-trip, `to_arrays()`
117
+ - [Jobs API](https://mambo10005.github.io/mambo-power/manual/jobs/) — the stateless
118
+ `SolveRequest` / `SolveResult` surface
119
+ - [API reference](https://mambo10005.github.io/mambo-power/api/model/),
120
+ [Design](https://mambo10005.github.io/mambo-power/design/architecture/),
121
+ [Changelog](https://mambo10005.github.io/mambo-power/changelog/),
122
+ [Contributing](https://mambo10005.github.io/mambo-power/contributing/)
123
+
124
+ ## Development
125
+
126
+ ```bash
127
+ uv sync --all-groups
128
+ uv run ruff check . && uv run ruff format --check . && uv run mypy
129
+ uv run pytest # tiers: -m unit | parity | property
130
+ uv run mkdocs serve # docs at http://127.0.0.1:8000
131
+ ```
132
+
133
+ Tests are tiered (`tests/unit`, `tests/parity` against pandapower/PyPSA/MATPOWER,
134
+ `tests/property` with hypothesis). Every public symbol must carry a docstring; a unit test
135
+ enforces it. The docs build with `mkdocs build --strict` in CI and deploy to GitHub Pages on
136
+ pushes to `epic/01-foundation` and `main`.
137
+
138
+ ## Licence
139
+
140
+ MIT — see [LICENSE](LICENSE). Bundled MATPOWER cases under `fixtures/matpower/` are public
141
+ IEEE test data as distributed by MATPOWER; see `fixtures/matpower/PROVENANCE.md`.
@@ -0,0 +1,128 @@
1
+ # mambo-power
2
+
3
+ A fundamental Python package for power system analysis and electricity market modelling.
4
+ It owns its network data model and implements its own solvers on numpy, scipy and HiGHS;
5
+ pandapower and PyPSA serve only as test oracles.
6
+
7
+ Documentation: **https://mambo10005.github.io/mambo-power/**
8
+
9
+ ## What it is
10
+
11
+ - A JSON-native network model (pydantic v2): `Network` with buses, branches, generators
12
+ (with cost curves), loads, shunts, storage and zones; physical units, stable string ids,
13
+ all-issues validation with named error codes, JSON schema generated from the model.
14
+ - Importers that speak only the model: MATPOWER `.m` cases and the native JSON format, plus
15
+ pandapower JSON, PyPSA, PSS/E RAW and CSV bundles.
16
+ - Network matrices over scipy.sparse: `NetworkArrays` (the single per-unit conversion site),
17
+ Ybus, Bbus, PTDF, LODF with bridge detection.
18
+ - Solvers: DC power flow and AC Newton-Raphson with Q-limit enforcement; DC optimal power flow
19
+ with duals on HiGHS; N-1 contingency screening; market clearing (nodal LMP, multiperiod with
20
+ storage, zonal with redispatch, agent-based bidding).
21
+ - Typed, id-keyed results stamped with provenance (engine version, solver, timings), never
22
+ stored on the network; a stateless, JSON-serialisable `jobs.run(SolveRequest)` surface
23
+ designed to sit behind a service.
24
+ - Narrative tutorial notebooks (execution-tested in CI), an automated changelog, and PyPI
25
+ trusted publishing.
26
+
27
+ Free in both senses: an open-source stack end to end with no paid solvers or licences, and
28
+ built, tested, documented and published entirely on free infrastructure (GitHub Actions,
29
+ GitHub Pages, PyPI trusted publishing).
30
+
31
+ ## Status
32
+
33
+ | Wave | Scope | State |
34
+ | --- | --- | --- |
35
+ | M1 | Installable package, `Network` model, MATPOWER import, Ybus/Bbus/PTDF/LODF, CI matrix | merged |
36
+ | M2 | DC + AC Newton-Raphson power flow, typed results, `jobs` API, docs site, examples | merged |
37
+ | M3 | DC optimal power flow with duals on HiGHS, N-1 branch-contingency screening | merged |
38
+ | M4 | Nodal market: elastic-demand DC-OPF, LMP clearing, settlement | merged |
39
+ | M5 | Multiperiod market: 24-period horizon, ramp coupling, storage SoC, per-period settlement | merged |
40
+ | M6 | Zonal market: zonal clearing, min-cost redispatch, nodal-vs-zonal comparison | merged |
41
+ | M7 | Agent-based bidding: strategies, offered-vs-true cost overlay, fixed-point loop | merged |
42
+ | M8 | Interchange: pandapower JSON, PyPSA, PSS/E RAW, CSV bundle | merged |
43
+ | M9 | Tutorials, semantic-release changelog, PyPI 0.1.0 trusted publishing | merged |
44
+
45
+ Not yet on PyPI — this changes in the same action as the `v0.1.0` tag; see
46
+ [Getting started](https://mambo10005.github.io/mambo-power/getting-started/) for the current
47
+ install instructions, which is the live source of truth if this file is ever stale (this table
48
+ is not covered by any automated freshness check — see the wave M9 continuation record if you're
49
+ reading this after a release and it still says otherwise). Runtime dependencies are exactly
50
+ `numpy`, `scipy`, `highspy`, `pydantic`; Python 3.11 or newer.
51
+
52
+ ## Install from source
53
+
54
+ ```bash
55
+ git clone https://github.com/mambo10005/mambo-power.git
56
+ cd mambo-power
57
+ uv sync # runtime deps only; add --all-groups for dev + docs tooling
58
+ ```
59
+
60
+ Without [uv](https://docs.astral.sh/uv/): `pip install -e .` in any Python >= 3.11 environment.
61
+
62
+ ## Quick start
63
+
64
+ ```python
65
+ from mambo_power import pf
66
+ from mambo_power.io import matpower
67
+
68
+ net = matpower.load("fixtures/matpower/case14.m") # validated Network, physical units
69
+ result = pf.solve_dc(net) # typed result keyed by ids, MW
70
+ print(result.generators[0].p_mw) # 219.0 -- slack generator balance
71
+ print(result.branches[0].p_from_mw) # 147.84 -- flow bus-1 -> bus-2
72
+ print(result.provenance.version, result.provenance.solver)
73
+ text = result.model_dump_json() # exact JSON round-trip
74
+ again = type(result).model_validate_json(text)
75
+ assert again == result
76
+ ```
77
+
78
+ Then: [Getting started](https://mambo10005.github.io/mambo-power/getting-started/) walks
79
+ through loading, validating, solving and reading results with real output.
80
+
81
+ ## Tutorials and manual
82
+
83
+ [Tutorials](https://mambo10005.github.io/mambo-power/tutorials/) are prose-heavy, narrative
84
+ walkthroughs (a first power flow, DC-OPF + N-1, a nodal market, where to go next) — start there
85
+ if you're new. The manual is the reference:
86
+
87
+ - [Network model](https://mambo10005.github.io/mambo-power/manual/model/) — every entity,
88
+ field, unit, and validation code
89
+ - [File formats](https://mambo10005.github.io/mambo-power/manual/formats/) — native JSON,
90
+ MATPOWER, pandapower JSON, PyPSA, PSS/E RAW, CSV bundles
91
+ - [Numerics](https://mambo10005.github.io/mambo-power/manual/numerics/) — `NetworkArrays`,
92
+ Ybus, Bbus, PTDF, LODF and bridges
93
+ - [Power flow](https://mambo10005.github.io/mambo-power/manual/power-flow/) — the DC
94
+ formulation and the AC solver's contract
95
+ - [DC-OPF](https://mambo10005.github.io/mambo-power/manual/opf/) and
96
+ [N-1 screening](https://mambo10005.github.io/mambo-power/manual/n1/)
97
+ - Market clearing: [nodal](https://mambo10005.github.io/mambo-power/manual/market/),
98
+ [multiperiod](https://mambo10005.github.io/mambo-power/manual/multiperiod/),
99
+ [zonal](https://mambo10005.github.io/mambo-power/manual/zonal/)
100
+ - [Agent-based bidding](https://mambo10005.github.io/mambo-power/manual/agents/) — strategies,
101
+ offered-vs-true cost, the fixed-point loop
102
+ - [Results](https://mambo10005.github.io/mambo-power/manual/results/) — result tables,
103
+ provenance, JSON round-trip, `to_arrays()`
104
+ - [Jobs API](https://mambo10005.github.io/mambo-power/manual/jobs/) — the stateless
105
+ `SolveRequest` / `SolveResult` surface
106
+ - [API reference](https://mambo10005.github.io/mambo-power/api/model/),
107
+ [Design](https://mambo10005.github.io/mambo-power/design/architecture/),
108
+ [Changelog](https://mambo10005.github.io/mambo-power/changelog/),
109
+ [Contributing](https://mambo10005.github.io/mambo-power/contributing/)
110
+
111
+ ## Development
112
+
113
+ ```bash
114
+ uv sync --all-groups
115
+ uv run ruff check . && uv run ruff format --check . && uv run mypy
116
+ uv run pytest # tiers: -m unit | parity | property
117
+ uv run mkdocs serve # docs at http://127.0.0.1:8000
118
+ ```
119
+
120
+ Tests are tiered (`tests/unit`, `tests/parity` against pandapower/PyPSA/MATPOWER,
121
+ `tests/property` with hypothesis). Every public symbol must carry a docstring; a unit test
122
+ enforces it. The docs build with `mkdocs build --strict` in CI and deploy to GitHub Pages on
123
+ pushes to `epic/01-foundation` and `main`.
124
+
125
+ ## Licence
126
+
127
+ MIT — see [LICENSE](LICENSE). Bundled MATPOWER cases under `fixtures/matpower/` are public
128
+ IEEE test data as distributed by MATPOWER; see `fixtures/matpower/PROVENANCE.md`.
@@ -0,0 +1,242 @@
1
+ # PSS/E RAW v33 fixtures — provenance and hand derivations (M8 W4, AC-4)
2
+
3
+ Two hand-authored files, both NOT upstream bytes and both consumed only by
4
+ `tests/unit/test_io_psse_raw.py` through `mambo_power.io.psse_raw`. Neither
5
+ carries a reference power-flow solution. The record layouts follow the v33
6
+ field order recorded in `.bionic/docs/record/m8-research.md` §3 (from
7
+ grg-pssedata `struct.py`, BSD-3, and MATPOWER `psse_convert.m` /
8
+ `psse_convert_xfmr.m`, BSD-3); no upstream RAW file was copied (the only
9
+ public IEEE-14 RAW found carries no detectable licence — spec "Rejected
10
+ alternatives").
11
+
12
+ Layout common to both: line 1 `IC, SBASE, REV, XFRRAT, NXFRAT, BASFRQ`
13
+ (`IC=0`, `REV=33`), two title lines, then the sections in v33 order — bus,
14
+ load, fixed shunt, generator, non-transformer branch, transformer, area,
15
+ two-terminal DC, VSC DC, impedance correction, multi-terminal DC,
16
+ multi-section line, zone, inter-area transfer, owner, FACTS, switched shunt,
17
+ GNE, induction machine — each closed by a line whose first field is `0`, and
18
+ `Q` at the end. Text after `/` outside quotes is a comment.
19
+
20
+ ## case14_v33.raw — transcription of `matpower/case14.m`
21
+
22
+ Authored 2026-08-29 field by field from `fixtures/matpower/case14.m` (the
23
+ verbatim MATPOWER file; its own provenance is in `fixtures/matpower/PROVENANCE.md`).
24
+ `io.psse_raw.load(case14_v33.raw)` must equal `io.matpower.load(case14.m)` on
25
+ every bus, branch (including `kind`), generator-limit and load field to 1e-9
26
+ (AC-4). Every field of every record is one of: a `case14.m` cell (mapping
27
+ below), a v33 default that the importer ignores, or a derived id.
28
+
29
+ ### Bus records (14) — `mpc.bus` row → bus record
30
+
31
+ | RAW field | source | unit |
32
+ | --- | --- | --- |
33
+ | `I` | `BUS_I` | — |
34
+ | `NAME` | `mpc.bus_name` row (informational; the model has no bus name) | — |
35
+ | `BASKV` | `BASE_KV` = **0** in `case14.m` (CDF "unknown"); kept as 0 so that both importers apply the same `BASE_KV_REPLACED` repair to 1.0 — writing 138 kV would make `base_kv` differ from the `.m` import | kV |
36
+ | `IDE` | `BUS_TYPE` (3 slack, 2 PV, 1 PQ) — same code table | — |
37
+ | `AREA`, `ZONE` | `AREA`, `ZONE` (all 1) | — |
38
+ | `OWNER` | 1 (default; ignored) | — |
39
+ | `VM`, `VA` | `VM`, `VA` | pu, deg |
40
+ | `NVHI`, `NVLO` | `VMAX`, `VMIN` (1.06 / 0.94) | pu |
41
+ | `EVHI`, `EVLO` | copies of `VMAX`, `VMIN` (ignored by the importer) | pu |
42
+
43
+ ### Load records (11) — `PD`/`QD` of each bus row with a non-zero pair
44
+
45
+ `I` = `BUS_I`, `ID` = `'1 '`, `STATUS` = 1, `AREA`/`ZONE` = the bus's,
46
+ `PL` = `PD` (MW), `QL` = `QD` (MVAr), `IP IQ YP YQ` = 0 (no current- or
47
+ admittance-type load in MATPOWER), `OWNER` 1, `SCALE` 1, `INTRPT` 0. Buses
48
+ 1, 7 and 8 have `PD = QD = 0` and get no record, exactly as `io.matpower`
49
+ emits no `Load` for them. Importer id `load-<I>-<ID>` (`load-2-1`, …) versus
50
+ matpower's `load-<I>`; the AC-4 test matches loads by bus.
51
+
52
+ ### Fixed shunt (1) — `GS`/`BS` of bus 9
53
+
54
+ `9,'1 ',1, GL=0.000 (GS, MW), BL=19.000 (BS, MVAr)`; same sign convention as
55
+ MATPOWER (positive `BL` injects). Only bus 9 has a non-zero `GS`/`BS` pair.
56
+
57
+ ### Generator records (5) — `mpc.gen` row → generator record
58
+
59
+ | RAW field | source |
60
+ | --- | --- |
61
+ | `I`, `ID` | `GEN_BUS`, `'1 '` |
62
+ | `PG`, `QG` | `PG`, `QG` (MW, MVAr) |
63
+ | `QT`, `QB` | `QMAX`, `QMIN` |
64
+ | `VS` | `VG` |
65
+ | `IREG` | 0 (default; ignored) |
66
+ | `MBASE` | `MBASE` = 100 (ignored by the model) |
67
+ | `ZR ZX RT XT GTAP` | 0, 1, 0, 0, 1 (v33 defaults; ignored) |
68
+ | `STAT` | `GEN_STATUS` |
69
+ | `RMPCT` | 100 (default; ignored) |
70
+ | `PT`, `PB` | `PMAX`, `PMIN` |
71
+ | `O1..F4`, `WMOD`, `WPF` | 1,1.0,0,1.0,0,1.0,0,1.0,0,1.0 (defaults; ignored) |
72
+
73
+ Costs: `mpc.gencost` has **no RAW representation** — no v33 section carries
74
+ economic data (research §3) — so every imported generator has `cost=None` and
75
+ the importer reports `RAW_NO_COSTS` once. `PC1..APF` of `mpc.gen` are not
76
+ read by `io.matpower` either.
77
+
78
+ ### Non-transformer branch records (17) — `mpc.branch` rows with `TAP = 0`
79
+
80
+ `I`=`F_BUS`, `J`=`T_BUS`, `CKT`=`'1 '`, `R`=`BR_R`, `X`=`BR_X`, `B`=`BR_B`
81
+ (pu on SBASE = baseMVA = 100), `RATEA RATEB RATEC` = `RATE_A RATE_B RATE_C`
82
+ = 0 (unrated → `rating_mva=None` in both importers), `GI BI GJ BJ` = 0,
83
+ `ST` = `BR_STATUS`, `MET` 1, `LEN` 0, owners default. Importer id
84
+ `branch-<I>-<J>-<CKT>`; matched by `(from, to)` in the test.
85
+
86
+ ### Two-winding transformer records (3) — `mpc.branch` rows 8, 9, 10 (`TAP ≠ 0`)
87
+
88
+ case14's branches 4-7 (tap 0.978), 4-9 (0.969) and 5-6 (0.932) are written as
89
+ four-line transformer records with **CW=1, CZ=1, CM=1** so the round-trip is
90
+ exact by construction:
91
+
92
+ - line 1: `I, J, K=0 (two-winding), CKT='1 ', CW=1, CZ=1, CM=1, MAG1=0, MAG2=0,
93
+ NMETR=2, NAME='', STAT=BR_STATUS, owners default, VECGRP=''`
94
+ - line 2: `R1-2 = BR_R, X1-2 = BR_X` — with CZ=1 these are pu on system
95
+ SBASE, i.e. the `.m` values verbatim; `SBASE1-2 = 100` (unused for CZ=1)
96
+ - line 3: `WINDV1 = TAP` — with CW=1 a pu turns ratio on the bus base, so
97
+ `tap = WINDV1 / WINDV2 = TAP / 1.0`; `NOMV1 = 0` (= bus base), `ANG1 = SHIFT
98
+ = 0`, `RATA1 = RATE_A = 0` (→ `None`), `RATB1 RATC1` 0, `COD1 = 0` (no
99
+ control), `CONT1 0`, `RMA1 RMI1 VMA1 VMI1` 1.1/0.9 (control limits; ignored),
100
+ `NTP1 33`, `TAB1 0`, `CR1 CX1 CNXA1` 0
101
+ - line 4: `WINDV2 = 1.0, NOMV2 = 0`
102
+
103
+ `BR_B` is 0 on all three rows, matching the transformer model (`b = 0`).
104
+ `Branch.kind` is `"transformer"` for these records (set from the record type,
105
+ not inferred from the tap) and `"line"` for the 17 branch records — equal to
106
+ what `io.matpower` derives from the `TAP` column on this file.
107
+
108
+ ### Area, zone
109
+
110
+ `1, ISW=1, PDES=0, PTOL=999.99, ARNAME='IEEE14'` (the importer keeps only the
111
+ bus `AREA` label); zone `1,'1'` → `Zone(id="1", name="1")` (matpower yields
112
+ `name=None`; zones are outside the AC-4 field comparison).
113
+
114
+ ## synthetic_quirks_v33.raw — hand-derived expected network
115
+
116
+ Authored 2026-08-29; 4 buses, SBASE 100 MVA, 50 Hz header. Purpose: every
117
+ conversion branch of the importer that `case14_v33.raw` does not reach.
118
+ Quirks present: CZ=2 and CZ=3, CW=2 and CW=3, CM=2, a neutral-tap (1.0 / 0 deg)
119
+ transformer, the four-line transformer
120
+ record continuation, two circuits between one bus pair (`CKT '1'` / `'2'`),
121
+ a ZIP load, two loads (plus one out-of-service) on one bus, a fixed shunt,
122
+ branch end shunts (`BI`, `GJ`), a 9-field bus record (no `NVHI..EVLO`), a
123
+ short generator record (no owner fields), a quoted name containing a comma,
124
+ trailing `/` comments on data lines, an out-of-service generator, and ignored
125
+ records: one owner, one switched shunt.
126
+
127
+ ### Buses
128
+
129
+ | id | base_kv | type | vm | va | v_min | v_max | area | zone |
130
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
131
+ | `bus-1` | 138 | slack | 1.00 | 0.0 | 0.9 | 1.1 | `"1"` | `"1"` |
132
+ | `bus-2` | 138 | pq | 0.95 | -2.5 | 0.9 | 1.1 | `"1"` | `"1"` |
133
+ | `bus-3` | 13.8 | pv | 1.02 | -1.0 | 0.95 | 1.05 | `"2"` | `"1"` |
134
+ | `bus-4` | 13.8 | pq | 1.00 | -3.0 | `None` | `None` | `"2"` | `"2"` |
135
+
136
+ Zones: `Zone("1", name="ZONE-A")`, `Zone("2", name="ZONE-B")`.
137
+
138
+ ### Loads (MATPOWER rule: `P = PL + IP·VM + YP·VM²`, same for Q, at the bus's VM)
139
+
140
+ - `load-2-1` at `bus-2`, VM = 0.95: `p = 40 + 10·0.95 + 20·0.95² = 40 + 9.5 +
141
+ 18.05 = 67.55` MW; `q = 10 + 2·0.95 + 4·0.9025 = 10 + 1.9 + 3.61 = 15.51`
142
+ MVAr; reported `RAW_LOAD_ZIP_FOLDED`.
143
+ - `load-4-1`: 20 / 5; `load-4-2`: 5 / 1; `load-4-3`: 99 / 99 with
144
+ `in_service=False` (STATUS 0).
145
+
146
+ ### Shunts
147
+
148
+ - `shunt-2-1` (fixed shunt): `g_mw = 1.0`, `b_mvar = 15.0`.
149
+ - `shunt-branch-1-2-2-i` at `bus-1` from branch `1-2 '2'` end shunt `BI = 0.01`
150
+ pu: `b_mvar = 0.01 · 100 = 1.0`, `g_mw = 0`; `shunt-branch-1-2-2-j` at
151
+ `bus-2` from `GJ = 0.002` pu: `g_mw = 0.2`, `b_mvar = 0`. Reported
152
+ `RAW_BRANCH_END_SHUNT_FOLDED` (one entry per end).
153
+ - `shunt-xfmr-2-3-1` at `bus-2` from T1's magnetising branch (CM=2, below):
154
+ `g_mw = 0.02`, `b_mvar = -0.9997999799959989`. Reported
155
+ `RAW_XFMR_MAGNETISING_FOLDED`.
156
+
157
+ ### Generators
158
+
159
+ - `gen-1-1` at `bus-1`: p 50, q 0, q_max 100, q_min -100, v_set 1.0, p_max 200,
160
+ p_min 0, in service.
161
+ - `gen-3-1` at `bus-3`: p 30, q 5, q_max 50, q_min -20, v_set 1.02, p_max 80,
162
+ p_min 10, in service.
163
+ - `gen-3-2` at `bus-3` (18-field record): p 0, q 0, q_max 10, q_min -10, v_set
164
+ 1.02, p_max 20, p_min 0, `in_service=False` (STAT 0).
165
+ - All `cost=None`; `RAW_NO_COSTS` reported once.
166
+
167
+ ### Lines (non-transformer branch records)
168
+
169
+ - `branch-1-2-1`: r 0.01, x 0.1, b 0.02, rating 150, kind `line`, no tap/shift.
170
+ - `branch-1-2-2`: r 0.02, x 0.2, b 0.04, rating 100, kind `line`.
171
+
172
+ ### T1 — `branch-2-3-1`, CW=2, CZ=2, CM=2 (kind `transformer`)
173
+
174
+ Record: `R1-2 = 0.005, X1-2 = 0.08, SBASE1-2 = 50`; `WINDV1 = 144.9` kV,
175
+ `NOMV1 = 0` (→ bus 2 base 138 kV), `ANG1 = 0`, `RATA1 = 60`; `WINDV2 = 13.8`
176
+ kV, `NOMV2 = 0`; `MAG1 = 20000` W, `MAG2 = 0.02`.
177
+
178
+ - CZ=2: `R, X` are pu on `SBASE1-2` and `NOMV1`. Impedance base conversion
179
+ (`psse_convert_xfmr.m`): `factor = Zb_winding / Zb_system =
180
+ (NOMV1² / SBASE1-2) / (BASKV_I² / SBASE) = (138/138)² · 100/50 = 2`.
181
+ `r = 0.005 · 2 = 0.01`, `x = 0.08 · 2 = 0.16`.
182
+ - CW=2: winding voltages in kV, each divided by its bus base:
183
+ `t1 = 144.9 / 138 = 1.05`, `t2 = 13.8 / 13.8 = 1.0`; `tap_ratio = t1 / t2
184
+ = 1.05`; `shift_deg = None` (ANG1 = 0).
185
+ - CM=2: `MAG1` no-load loss in W, `MAG2` exciting current pu on `SBASE1-2`
186
+ and `NOMV1`. `G_w = 20000 / (1e6 · 50) = 0.0004`; `B_w = -sqrt(MAG2² −
187
+ G_w²) = -sqrt(0.0004 − 0.00000016) = -sqrt(0.00039984) =
188
+ -0.01999599959991998` (inductive, negative). To system base admittance
189
+ scales by `SBASE1-2 / SBASE = 0.5`, then to physical: `g_mw = 0.0004 · 0.5 ·
190
+ 100 = 0.02` MW, `b_mvar = -0.01999599959991998 · 0.5 · 100 =
191
+ -0.9997999799959989` MVAr, placed at the from bus `bus-2` as
192
+ `shunt-xfmr-2-3-1`.
193
+ - `rating_mva = 60`, `b = 0`, in service.
194
+
195
+ ### T2 — `branch-3-4-1`, CW=3, CZ=3, CM=1 (kind `transformer`)
196
+
197
+ Record: `R1-2 = 10000` (W, load loss), `X1-2 = 0.12` (|Z| pu on `SBASE1-2`),
198
+ `SBASE1-2 = 25`; `WINDV1 = 0.98` (pu of `NOMV1`), `NOMV1 = 14.49` kV,
199
+ `ANG1 = 5.0`, `RATA1 = 30`; `WINDV2 = 1.0`, `NOMV2 = 0`; `MAG1 = MAG2 = 0`.
200
+
201
+ - CZ=3: `R_w = 10000 / (1e6 · 25) = 0.0004` pu on the winding base;
202
+ `X_w = sqrt(0.12² − 0.0004²) = sqrt(0.0144 − 0.00000016) =
203
+ sqrt(0.01439984) = 0.11999933333148147`.
204
+ `factor = (NOMV1 / BASKV_3)² · SBASE / SBASE1-2 = (14.49/13.8)² · 100/25 =
205
+ 1.05² · 4 = 1.1025 · 4 = 4.41`.
206
+ `r = 0.0004 · 4.41 = 0.001764`, `x = 0.11999933333148147 · 4.41 =
207
+ 0.5291970599918333`.
208
+ - CW=3: winding voltages in pu of nominal, converted to pu of bus base by
209
+ `NOMV / BASKV` (`NOMV = 0` → 1): `t1 = 0.98 · 14.49 / 13.8 = 0.98 · 1.05 =
210
+ 1.029`, `t2 = 1.0`; `tap_ratio = 1.029`; `shift_deg = 5.0`.
211
+ - CM=1 with `MAG1 = MAG2 = 0`: no magnetising shunt, no report entry.
212
+ - `rating_mva = 30`, `b = 0`, in service.
213
+
214
+ ### T3 — `branch-2-4-1`, CW=1, CZ=1, CM=1, neutral tap (kind `transformer`; spec A7 / AC-6)
215
+
216
+ Record: `R1-2 = 0.002, X1-2 = 0.05, SBASE1-2 = 100`; `WINDV1 = 1.0`, `NOMV1 = 0`,
217
+ `ANG1 = 0`, `RATA1 = 40`; `WINDV2 = 1.0`, `NOMV2 = 0`; `MAG1 = MAG2 = 0`.
218
+
219
+ - CZ=1: `r = 0.002`, `x = 0.05` verbatim (pu on SBASE).
220
+ - CW=1: `t1 = 1.0`, `t2 = 1.0`; `tap_ratio = 1.0 / 1.0 = 1.0` — nominal; `shift_deg =
221
+ None` (ANG1 = 0). Nothing about the parameters distinguishes this branch from a
222
+ line: `kind = "transformer"` comes only from the record type, which is what the
223
+ fixture exists to prove (`Branch._default_kind` would infer `"line"` here).
224
+ - `rating_mva = 40`, `b = 0`, in service; no magnetising shunt, no report entry.
225
+ - Adds the loop 2-3-4-2; the network stays one island.
226
+
227
+ ### Ignored records (one report entry each)
228
+
229
+ - owner `1,'OWNER ONE'` → `RAW_SECTION_IGNORED` naming section `owner`,
230
+ record `1`.
231
+ - switched shunt at bus 4 (`BINIT = 5.0`) → `RAW_SWITCHED_SHUNT_IGNORED`
232
+ naming bus 4 (`BINIT` is **not** folded into a shunt; the entry says so).
233
+
234
+ Areas (`AREA HV`, `AREA MV`) contribute nothing beyond the bus labels and are
235
+ not reported (they are read, not ignored).
236
+
237
+ ### Not in this file
238
+
239
+ A three-winding transformer (five-line record, `K ≠ 0`) is exercised by an
240
+ inline text in `tests/unit/test_io_psse_raw.py` (each such record →
241
+ `RAW_THREE_WINDING_IGNORED`, one entry), so this fixture stays a network the
242
+ model accepts unchanged.
@@ -0,0 +1,85 @@
1
+ 0, 100.00, 33, 0, 0, 60.00 / hand-authored from fixtures/matpower/case14.m (see fixtures/PROVENANCE-raw.md)
2
+ IEEE 14 BUS TEST CASE -- transcription of MATPOWER case14.m, PSS/E RAW v33 layout
3
+ mambo-power fixture; BASKV 0 kept as in the source; no cost data exists in RAW
4
+ 1,'Bus 1 HV', 0.0000,3, 1, 1, 1,1.06000, 0.0000,1.06000,0.94000,1.06000,0.94000
5
+ 2,'Bus 2 HV', 0.0000,2, 1, 1, 1,1.04500, -4.9800,1.06000,0.94000,1.06000,0.94000
6
+ 3,'Bus 3 HV', 0.0000,2, 1, 1, 1,1.01000, -12.7200,1.06000,0.94000,1.06000,0.94000
7
+ 4,'Bus 4 HV', 0.0000,1, 1, 1, 1,1.01900, -10.3300,1.06000,0.94000,1.06000,0.94000
8
+ 5,'Bus 5 HV', 0.0000,1, 1, 1, 1,1.02000, -8.7800,1.06000,0.94000,1.06000,0.94000
9
+ 6,'Bus 6 LV', 0.0000,2, 1, 1, 1,1.07000, -14.2200,1.06000,0.94000,1.06000,0.94000
10
+ 7,'Bus 7 ZV', 0.0000,1, 1, 1, 1,1.06200, -13.3700,1.06000,0.94000,1.06000,0.94000
11
+ 8,'Bus 8 TV', 0.0000,2, 1, 1, 1,1.09000, -13.3600,1.06000,0.94000,1.06000,0.94000
12
+ 9,'Bus 9 LV', 0.0000,1, 1, 1, 1,1.05600, -14.9400,1.06000,0.94000,1.06000,0.94000
13
+ 10,'Bus 10 LV', 0.0000,1, 1, 1, 1,1.05100, -15.1000,1.06000,0.94000,1.06000,0.94000
14
+ 11,'Bus 11 LV', 0.0000,1, 1, 1, 1,1.05700, -14.7900,1.06000,0.94000,1.06000,0.94000
15
+ 12,'Bus 12 LV', 0.0000,1, 1, 1, 1,1.05500, -15.0700,1.06000,0.94000,1.06000,0.94000
16
+ 13,'Bus 13 LV', 0.0000,1, 1, 1, 1,1.05000, -15.1600,1.06000,0.94000,1.06000,0.94000
17
+ 14,'Bus 14 LV', 0.0000,1, 1, 1, 1,1.03600, -16.0400,1.06000,0.94000,1.06000,0.94000
18
+ 0 / END OF BUS DATA, BEGIN LOAD DATA
19
+ 2,'1 ',1, 1, 1, 21.700, 12.700, 0.000, 0.000, 0.000, 0.000, 1,1,0
20
+ 3,'1 ',1, 1, 1, 94.200, 19.000, 0.000, 0.000, 0.000, 0.000, 1,1,0
21
+ 4,'1 ',1, 1, 1, 47.800, -3.900, 0.000, 0.000, 0.000, 0.000, 1,1,0
22
+ 5,'1 ',1, 1, 1, 7.600, 1.600, 0.000, 0.000, 0.000, 0.000, 1,1,0
23
+ 6,'1 ',1, 1, 1, 11.200, 7.500, 0.000, 0.000, 0.000, 0.000, 1,1,0
24
+ 9,'1 ',1, 1, 1, 29.500, 16.600, 0.000, 0.000, 0.000, 0.000, 1,1,0
25
+ 10,'1 ',1, 1, 1, 9.000, 5.800, 0.000, 0.000, 0.000, 0.000, 1,1,0
26
+ 11,'1 ',1, 1, 1, 3.500, 1.800, 0.000, 0.000, 0.000, 0.000, 1,1,0
27
+ 12,'1 ',1, 1, 1, 6.100, 1.600, 0.000, 0.000, 0.000, 0.000, 1,1,0
28
+ 13,'1 ',1, 1, 1, 13.500, 5.800, 0.000, 0.000, 0.000, 0.000, 1,1,0
29
+ 14,'1 ',1, 1, 1, 14.900, 5.000, 0.000, 0.000, 0.000, 0.000, 1,1,0
30
+ 0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA
31
+ 9,'1 ',1, 0.000, 19.000
32
+ 0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA
33
+ 1,'1 ', 232.400, -16.900, 10.000, 0.000,1.06000, 0, 100.000, 0.00000, 1.00000, 0.00000, 0.00000,1.00000,1, 100.0, 332.400, 0.000, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,0,1.0000
34
+ 2,'1 ', 40.000, 42.400, 50.000, -40.000,1.04500, 0, 100.000, 0.00000, 1.00000, 0.00000, 0.00000,1.00000,1, 100.0, 140.000, 0.000, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,0,1.0000
35
+ 3,'1 ', 0.000, 23.400, 40.000, 0.000,1.01000, 0, 100.000, 0.00000, 1.00000, 0.00000, 0.00000,1.00000,1, 100.0, 100.000, 0.000, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,0,1.0000
36
+ 6,'1 ', 0.000, 12.200, 24.000, -6.000,1.07000, 0, 100.000, 0.00000, 1.00000, 0.00000, 0.00000,1.00000,1, 100.0, 100.000, 0.000, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,0,1.0000
37
+ 8,'1 ', 0.000, 17.400, 24.000, -6.000,1.09000, 0, 100.000, 0.00000, 1.00000, 0.00000, 0.00000,1.00000,1, 100.0, 100.000, 0.000, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,0,1.0000
38
+ 0 / END OF GENERATOR DATA, BEGIN BRANCH DATA
39
+ 1, 2,'1 ', 0.01938, 0.05917, 0.05280, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
40
+ 1, 5,'1 ', 0.05403, 0.22304, 0.04920, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
41
+ 2, 3,'1 ', 0.04699, 0.19797, 0.04380, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
42
+ 2, 4,'1 ', 0.05811, 0.17632, 0.03400, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
43
+ 2, 5,'1 ', 0.05695, 0.17388, 0.03460, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
44
+ 3, 4,'1 ', 0.06701, 0.17103, 0.01280, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
45
+ 4, 5,'1 ', 0.01335, 0.04211, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
46
+ 6, 11,'1 ', 0.09498, 0.19890, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
47
+ 6, 12,'1 ', 0.12291, 0.25581, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
48
+ 6, 13,'1 ', 0.06615, 0.13027, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
49
+ 7, 8,'1 ', 0.00000, 0.17615, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
50
+ 7, 9,'1 ', 0.00000, 0.11001, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
51
+ 9, 10,'1 ', 0.03181, 0.08450, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
52
+ 9, 14,'1 ', 0.12711, 0.27038, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
53
+ 10, 11,'1 ', 0.08205, 0.19207, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
54
+ 12, 13,'1 ', 0.22092, 0.19988, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
55
+ 13, 14,'1 ', 0.17093, 0.34802, 0.00000, 0.00, 0.00, 0.00, 0.00000, 0.00000, 0.00000, 0.00000,1,1, 0.00, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000
56
+ 0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA
57
+ 4, 7, 0,'1 ',1,1,1, 0.00000, 0.00000,2,' ',1, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,' '
58
+ 0.00000, 0.20912, 100.00
59
+ 0.97800, 0.000, 0.000, 0.00, 0.00, 0.00,0, 0, 1.10000, 0.90000, 1.10000, 0.90000, 33, 0, 0.00000, 0.00000, 0.000
60
+ 1.00000, 0.000
61
+ 4, 9, 0,'1 ',1,1,1, 0.00000, 0.00000,2,' ',1, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,' '
62
+ 0.00000, 0.55618, 100.00
63
+ 0.96900, 0.000, 0.000, 0.00, 0.00, 0.00,0, 0, 1.10000, 0.90000, 1.10000, 0.90000, 33, 0, 0.00000, 0.00000, 0.000
64
+ 1.00000, 0.000
65
+ 5, 6, 0,'1 ',1,1,1, 0.00000, 0.00000,2,' ',1, 1,1.0000, 0,1.0000, 0,1.0000, 0,1.0000,' '
66
+ 0.00000, 0.25202, 100.00
67
+ 0.93200, 0.000, 0.000, 0.00, 0.00, 0.00,0, 0, 1.10000, 0.90000, 1.10000, 0.90000, 33, 0, 0.00000, 0.00000, 0.000
68
+ 1.00000, 0.000
69
+ 0 / END OF TRANSFORMER DATA, BEGIN AREA DATA
70
+ 1, 1, 0.000, 999.990,'IEEE14 '
71
+ 0 / END OF AREA DATA, BEGIN TWO-TERMINAL DC DATA
72
+ 0 / END OF TWO-TERMINAL DC DATA, BEGIN VSC DC LINE DATA
73
+ 0 / END OF VSC DC LINE DATA, BEGIN IMPEDANCE CORRECTION DATA
74
+ 0 / END OF IMPEDANCE CORRECTION DATA, BEGIN MULTI-TERMINAL DC DATA
75
+ 0 / END OF MULTI-TERMINAL DC DATA, BEGIN MULTI-SECTION LINE DATA
76
+ 0 / END OF MULTI-SECTION LINE DATA, BEGIN ZONE DATA
77
+ 1,'1 '
78
+ 0 / END OF ZONE DATA, BEGIN INTER-AREA TRANSFER DATA
79
+ 0 / END OF INTER-AREA TRANSFER DATA, BEGIN OWNER DATA
80
+ 0 / END OF OWNER DATA, BEGIN FACTS DEVICE DATA
81
+ 0 / END OF FACTS DEVICE DATA, BEGIN SWITCHED SHUNT DATA
82
+ 0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DATA
83
+ 0 / END OF GNE DATA, BEGIN INDUCTION MACHINE DATA
84
+ 0 / END OF INDUCTION MACHINE DATA
85
+ Q