diff --git a/README.md b/README.md index 52e5bd3..6dd2158 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ Use cases live under `interop/core/use_cases/`, each implementing a Protocol por - `docs/translation_mappings/translation-from-pypsa-to-sienna.md` is the authoritative mapping reference for PyPSA / Sienna field translation. - `docs/translation_mappings/translation-from-plexos-to-pypsa.md` states what each part of a PLEXOS model becomes in PyPSA. - `docs/translation_mappings/translation-from-plexos-to-sienna.md` states the same for Sienna, and `plexos-to-sienna-gap-analysis.md` beside it states what that translation loses and what each loss does to a dispatch. +- `docs/translation_mappings/translation-from-plexos-to-sienna-investments.md` states what a PLEXOS expansion plan becomes in a Sienna investments portfolio, which `plexos-to-sienna-investments` writes beside the base system, and `plexos-to-sienna-gap-analysis.md` states what the portfolio leaves out and what each loss does to an expansion. - `docs/developer_documentation/mutation-testing.md` covers the mutmut workflow. - `tests/features/README.md` explains the in-process REPL-driven BDD harness and when subprocess is needed. diff --git a/code-review/ledger.jsonl b/code-review/ledger.jsonl index 34f1d94..70e6cb5 100644 --- a/code-review/ledger.jsonl +++ b/code-review/ledger.jsonl @@ -293,3 +293,77 @@ {"id": "r18-03", "pr": "11", "repo": "transition-zero/tz-oss-interop", "round": 18, "file": "libs/interop-testing/src/interop_testing/steps/sienna_portfolio.py", "line": 49, "severity": "minor", "label": "suggestion", "claim": "The two attribute steps hold the same body, and differ only in how each one finds the component id.", "suggested_fix": "", "rule_created": false, "ts": "2026-09-10T14:55:22Z", "source": "reviewer", "verdict": "accepted", "reason": "it removes lines", "category": null, "promote_now": false, "suppressed_by": null} {"id": "r18-04", "pr": "11", "repo": "transition-zero/tz-oss-interop", "round": 18, "file": "tests/step_defs/test_pypsa_to_sienna_to_pypsa.py", "line": 7, "severity": "nit", "label": "nitpick", "claim": "The import block spans three lines for one name.", "suggested_fix": "", "rule_created": false, "ts": "2026-09-10T14:55:22Z", "source": "reviewer", "verdict": "accepted", "reason": "it removes lines", "category": null, "promote_now": false, "suppressed_by": null} {"id": "r18-05", "pr": "11", "repo": "transition-zero/tz-oss-interop", "round": 18, "file": "libs/interop-testing/src/interop_testing/steps/sienna_portfolio.py", "line": 21, "severity": "minor", "label": "issue", "claim": "assert_portfolio_component_absent gives one document two names, because it reads only the component groups sienna_system.py owns.", "suggested_fix": "", "rule_created": false, "ts": "2026-09-10T14:55:22Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-01", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 5, "severity": "major", "label": "issue", "claim": "The document splits two pipelines by the position of one heading, not by a section of its own.", "suggested_fix": "Delete the pointer sentence and put the split in the heading levels.\n\nThe document now:\n\n```markdown\nThis document lists each thing a `plexos-to-sienna` run loses ... The entries from\n[A `Constraint` over part of the model](#a-constraint-over-part-of-the-model) onwards cover\nthe `plexos-to-sienna-investments` run instead ...\n\n## Reserve requirements\n...\n## A `Constraint` over part of the model\n```\n\nThe document to use:\n\n```markdown\nThis document lists each thing a PLEXOS to Sienna run loses. Each part below names the run\nit covers.\n\n## What `plexos-to-sienna` loses from a dispatch\n\n### Reserve requirements\n...\n\n## What `plexos-to-sienna-investments` loses from an expansion\n\n### A `Constraint` over part of the model\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-02", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 223, "severity": "major", "label": "issue", "claim": "The steps for a base year other than 2020 leave out the carrier mappings file the second leg asks for.", "suggested_fix": "Name the second file the reader must write.\n\nThe text to use:\n\n```markdown\nTo state another one, run the two legs yourself: `plexos-to-pypsa`, then\n`pypsa-to-sienna-investments` over the network and the sidecar it wrote. The second leg asks\nfor a carrier mappings file in PyPSA words, with a `pypsa_carrier` row for each carrier the\nfirst leg wrote, because no mappings pipeline runs beside a single leg. That pipeline is one\nleg, so it prompts for its steps, and the base year is the `base_year` of `step[2]`.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-03", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 187, "severity": "major", "label": "issue", "claim": "The list of types a generator candidate can take leaves out `RenewableNonDispatch`.", "suggested_fix": "Add the fourth type.\n\nThe text to use:\n\n```markdown\n`SupplyTechnology`, and its `sienna_component_type` must be a type a generator becomes:\n`ThermalStandard`, `RenewableDispatch`, `RenewableNonDispatch` or `HydroDispatch`.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-04", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 371, "severity": "major", "label": "issue", "claim": "The entry names one of the three rules that leave a `Constraint` out of the portfolio.", "suggested_fix": "State all three rules, and widen the heading to match.\n\nThe text to use:\n\n```markdown\n## A `Constraint` a cap cannot carry\n\n**What happens to it.** The portfolio writes a `CarbonCaps` only for a constraint that\ncovers every generator and storage object the two documents hold, holds its weighted sum to\n`<=`, and states an `RHS Year` or an `RHS`. The run leaves out every other constraint,\n`decisions.md` names it, and the log warns. Every constraint still reaches the\n`extensions.json` sidecar, whether or not it became a cap.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-05", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 215, "severity": "minor", "label": "issue", "claim": "The new text states the mappings prompt comes last, and the dispatch table above it states the prompt comes fourth.", "suggested_fix": "Move the mappings row of the dispatch table to the end.\n\nThe table to use:\n\n```markdown\n| Pipeline | `plexos-to-sienna` |\n| the PLEXOS `` input XML | `case_study_inputs/aemo-isp-2024/2024 ISP Step Change Model.xml` |\n| which PLEXOS Model to translate | `Step Change` |\n| a four-digit year such as 2026 | `2025` |\n| the SiennaSchemas system.json | `outputs/system.json` |\n| User mappings file | `inputs/plexos_user_mappings.yaml` |\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-06", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 224, "severity": "major", "label": "issue", "claim": "The reader cannot run `pypsa-to-sienna-investments` with the PLEXOS mappings file the section tells them to write.", "suggested_fix": "```markdown\nTo state another one, run the two legs yourself: `plexos-to-pypsa`, then\n`pypsa-to-sienna-investments` over the network and the sidecar it wrote. The second leg is a\nPyPSA run, so it asks for a mappings file in PyPSA words (`pypsa_carrier`), not the PLEXOS\nfile above. The chain derives that file for you, and a lone run does not, so write one\nnaming each carrier the first leg gave a generator. That pipeline is one leg, so it prompts\nfor its steps, and the base year is the `base_year` of `step[2]`.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-07", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 186, "severity": "minor", "label": "issue", "claim": "The list of Sienna types a generator candidate may take leaves out `RenewableNonDispatch`, which the code accepts.", "suggested_fix": "```markdown\n`SupplyTechnology`, and its `sienna_component_type` must be a type a generator becomes:\n`ThermalStandard`, `RenewableDispatch`, `RenewableNonDispatch` or `HydroDispatch`.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-08", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 399, "severity": "minor", "label": "issue", "claim": "PyPSA refuses a network only for a missing discount rate, not for a missing overnight cost or lifetime.", "suggested_fix": "```markdown\n**The cause.** PyPSA annuitises an overnight cost with a discount rate over a lifetime.\nWithout a discount rate it refuses the network; without a build cost nothing prices the\nbuild; without an economic life it annuitises across its default lifetime of infinity, which\nprices the build as a perpetuity. A Sienna technology states its own price and its own\nfinancing, and `TechnologyFinancialData` requires both a return on equity and a capital\nrecovery period.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-09", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 500, "severity": "minor", "label": "issue", "claim": "The PyPSA solve does not dispatch a fixed fleet: it sizes every extendable component the PLEXOS leg writes.", "suggested_fix": "```markdown\nthey belong on the request that solves the portfolio rather than in it. No solve in this\nrepository reads a portfolio: `solve` runs PyPSA over a network and PowerSimulations.jl over\na base system, and neither reads an investment period or a representative day.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-10", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 173, "severity": "nit", "label": "nitpick", "claim": "The dispatch section leaves out seven named categories and their siblings, so the count is higher than seven.", "suggested_fix": "```markdown\nThe dispatch run above leaves seven kinds of category, and the sibling of each, out of its\nfile, because a dispatch system has no place for what they hold.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-11", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 230, "severity": "major", "label": "suggestion", "claim": "This paragraph records a measurement nobody made. It explains an absence and it states a status.", "suggested_fix": "Delete the first two sentences. Keep the sentence that tells the reader where the counts come from.\n\nThe text now:\n\n```markdown\nThe counts for this path are not measured. The sections above give counts from a real run of\nthe PyPSA path and the Sienna path; nobody has yet recorded how many technologies, demand\nrequirements and caps the portfolio holds, or how many candidates each rule leaves out. The\n`decisions.md` of your own run reports both: each component it left out, with the reason, and\neach source field it did not map.\n```\n\nThe text to use:\n\n```markdown\nThe `decisions.md` of your own run gives the counts for this path: each component the run left\nout, with the reason, and each source field it did not map.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-12", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 310, "severity": "minor", "label": "suggestion", "claim": "This paragraph repeats the expansion section above and the gap analysis. It is a third copy of two absences.", "suggested_fix": "Keep the lead sentence and the link. Delete the two sentences that repeat what other text already states.\n\nThe text now:\n\n```markdown\n**The expansion path states no number of its own.** interop writes the portfolio and stops\nthere. No solve in this repository reads one, so nothing here says what a plan would build or\nwhat it would cost. The portfolio also states one expansion problem and no schedule: it\ncarries no investment periods and no representative days, and it holds no transport\ntechnology, so a plan built from it cannot build transmission.\n[The gap analysis](../translation_mappings/plexos-to-sienna-gap-analysis.md) lists everything\nelse the portfolio leaves out.\n```\n\nThe text to use:\n\n```markdown\n**The expansion path states no number of its own.** No solve in this repository reads a\nportfolio, so nothing here says what a plan would build or what it would cost.\n[The gap analysis](../translation_mappings/plexos-to-sienna-gap-analysis.md) states what the\nportfolio leaves out and what each loss does to an expansion.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-13", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 342, "severity": "minor", "label": "suggestion", "claim": "This paragraph gives no cost. It repeats for a third time that the expansion path runs no solve.", "suggested_fix": "Delete the paragraph.\n\nThe text now:\n\n```markdown\nThe result tables are 440 MB on disk. The solve also writes `problem_results.bin`, which is\n330 MB.\n\nThe expansion run reads the same model over the same year as the Sienna path and writes one\nmore document beside the three files. It runs no solve, so none of the solve compute above\napplies to it.\n```\n\nThe text to use:\n\n```markdown\nThe result tables are 440 MB on disk. The solve also writes `problem_results.bin`, which is\n330 MB.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-14", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 173, "severity": "nit", "label": "nitpick", "claim": "The clause \"because those plants are what it is about\" argues for the instruction. It states no fact.", "suggested_fix": "Cut the clause.\n\nThe text now:\n\n```markdown\nThe dispatch run above leaves seven categories out of its file, because a dispatch system has\nno place for what they hold. The expansion run names the ones that hold candidate plants,\nbecause those plants are what it is about. A generator takes the name of its `Fuel` where it\nburns one and its category where it does not, so each of those categories takes one\n`category` row:\n```\n\nThe text to use:\n\n```markdown\nThe dispatch run above leaves seven categories out of its file, because a dispatch system has\nno place for what they hold. The expansion run names the ones that hold candidate plants. A\ngenerator takes the name of its `Fuel` where it burns one and its category where it does not,\nso each of those categories takes one `category` row:\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-15", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 5, "severity": "minor", "label": "suggestion", "claim": "This sentence names the entries by their position. A new dispatch entry makes the pointer wrong.", "suggested_fix": "Put the expansion entries under a heading and point at the heading, not at an order.\n\nThe text now:\n\n```markdown\nThis document lists each thing a `plexos-to-sienna` run loses, and what that loss does to a\ndispatch. It covers the translation and the validation run that proves the system solves in\nPowerSimulations.jl. The entries from\n[A `Constraint` over part of the model](#a-constraint-over-part-of-the-model) onwards cover\nthe `plexos-to-sienna-investments` run instead, which writes an expansion portfolio beside a\nbase system of its own.\n```\n\nThe text to use:\n\n```markdown\nThis document lists each thing a `plexos-to-sienna` run loses, and what that loss does to a\ndispatch. It covers the translation and the validation run that proves the system solves in\nPowerSimulations.jl. The entries under\n[What the expansion portfolio loses](#what-the-expansion-portfolio-loses) cover the\n`plexos-to-sienna-investments` run, which writes an expansion portfolio beside a base system\nof its own.\n```\n\nAdd `## What the expansion portfolio loses` above the `A `Constraint` over part of the model`\nentry, and demote the seven expansion entries to `###`.", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-16", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 404, "severity": "nit", "label": "nitpick", "claim": "The sentence \"The alternative is worse\" defends the design under a heading that states the effect.", "suggested_fix": "Delete the sentence, or state the fact under **The cause**.\n\nThe text now:\n\n```markdown\n**The effect on the expansion.** That candidate is not in the portfolio, so no plan built\nfrom it can build that technology. The alternative is worse: a candidate whose build nothing\nprices would be built for free, at whatever size its limits allow.\n```\n\nThe text to use:\n\n```markdown\n**The effect on the expansion.** That candidate is not in the portfolio, so no plan built\nfrom it can build that technology.\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-17", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "nit", "label": "nitpick", "claim": "The word \"now\" states a status. The same sentence calls the Investments path out of scope.", "suggested_fix": "Drop the word.\n\nThe text now:\n\n```markdown\nA genuine expansion problem is the Investments path, which the `pypsa-to-sienna-investments` pipeline now writes as a portfolio document beside the operations system; ...\n```\n\nThe text to use:\n\n```markdown\nA genuine expansion problem is the Investments path, which the `pypsa-to-sienna-investments` pipeline writes as a portfolio document beside the operations system; ...\n```", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-18", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 230, "severity": "minor", "label": "suggestion", "claim": "This paragraph says the expansion path has no measured counts, which the block at line 310 already says, and it then repeats the pointer to `decisions.md` given twice earlier in the same section.", "suggested_fix": "Delete lines 230 to 234 and one blank line beside them. The block at lines 310 to 316 keeps the statement that the path gives no number, and lines 170 and 187 keep the `decisions.md` pointer. Net: -6 lines.", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-19", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/case_studies/aemo-isp-2024.md", "line": 342, "severity": "minor", "label": "suggestion", "claim": "The last paragraph of \"What it costs\" states no cost and repeats for a third time that the expansion run does no solve.", "suggested_fix": "Delete lines 342 to 344 and the blank line before them. Add a size or a compute figure for the expansion run only if one is measured. Net: -4 lines.", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-20", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 1, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 5, "severity": "minor", "label": "suggestion", "claim": "The intro splits the document by position, so any entry added or moved makes the sentence wrong, and lines 24 to 26 already tell the reader how to recognise an expansion entry.", "suggested_fix": "Delete the sentence from \"The entries from\" on line 5 to \"base system of its own.\" on line 8, and let the fourth-heading rule at lines 24 to 26 mark which run each entry covers. Net: -3 lines.", "rule_created": false, "ts": "2026-09-09T02:36:47Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-01", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "major", "label": "issue", "claim": "This paragraph says the Investments target is out of scope for v1, and then says a pipeline writes it.", "suggested_fix": "The text now:\n\n```md\nIt is **out of scope for v1**. For v1 we translate the **solved** fleet: take `p_nom_opt`\n(post-solve capacities) as fixed Operations capacity and drop the expansion parameters (or\nkeep round-trip crumbs in the `ext` sidecar). A genuine expansion problem is the Investments\npath, which the `pypsa-to-sienna-investments` pipeline now writes as a portfolio document\nbeside the operations system; [Translation from PLEXOS to a Sienna investments portfolio](translation-from-plexos-to-sienna-investments.md)\nstates what that portfolio holds, and that pipeline is its second leg.\n```\n\nThe text to use:\n\n```md\nThis document is the Operations translation, so it drops the expansion parameters: it takes\n`p_nom_opt` (post-solve capacities) as fixed Operations capacity, and keeps round-trip crumbs\nin the `ext` sidecar. The `pypsa-to-sienna-investments` pipeline translates the expansion\nproblem itself, and writes a portfolio document beside the operations system.\n[Translation from PLEXOS to a Sienna investments portfolio](translation-from-plexos-to-sienna-investments.md)\nstates what that portfolio holds; that pipeline is its second leg.\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "accepted", "reason": "it blocks the merge", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-02", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/case_studies/aemo-isp-2024.md", "line": 214, "severity": "minor", "label": "issue", "claim": "The new text puts the mappings prompt last, but the table at line 124 puts it second.", "suggested_fix": "`_run_translate` in `interop/adapters/inbound/interactive_cli/app.py` collects the source, step and sink parameters, and calls `_prompt_user_mappings` after them. Correct the Sienna path table as well.\n\nThe table now:\n\n```md\n| Pipeline | `plexos-to-sienna` |\n| User mappings file | `inputs/plexos_user_mappings.yaml` |\n| the PLEXOS `` input XML | `case_study_inputs/...xml` |\n| which PLEXOS Model to translate | `Step Change` |\n| a four-digit year such as 2026 | `2025` |\n| the SiennaSchemas system.json | `outputs/system.json` |\n```\n\nThe table to use:\n\n```md\n| Pipeline | `plexos-to-sienna` |\n| the PLEXOS `` input XML | `case_study_inputs/...xml` |\n| which PLEXOS Model to translate | `Step Change` |\n| a four-digit year such as 2026 | `2025` |\n| the SiennaSchemas system.json | `outputs/system.json` |\n| User mappings file | `inputs/plexos_user_mappings.yaml` |\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-03", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 399, "severity": "minor", "label": "issue", "claim": "This entry names a `Generator` and a `Battery`, but the same rule also drops a pumped-storage turbine.", "suggested_fix": "`rate_object` in `interop/plugins/shared/plexos_pypsa_translations/_storage_shared.py` calls `find_unpriced_candidate` for a pumped-storage turbine too.\n\nThe text now:\n\n```md\n**The PLEXOS data.** A `Generator` or a `Battery` stating `Max Units Built` but not all of\n`Build Cost`, `WACC` and `Economic Life`.\n```\n\nThe text to use:\n\n```md\n**The PLEXOS data.** A `Generator`, a `Battery` or a pumped-storage turbine stating\n`Max Units Built` but not all of `Build Cost`, `WACC` and `Economic Life`.\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-04", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 445, "severity": "minor", "label": "issue", "claim": "The document says each technology leaves the `requirements` list empty, but the portfolio writes no `requirements` field.", "suggested_fix": "```markdown\n**What happens to it.** The portfolio writes no `requirements` on anything. The field is\nabsent from every `SupplyTechnology`, `StorageTechnology` and `DemandRequirement`, and\n`decisions.md` records it as not mapped.\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-05", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/case_studies/aemo-isp-2024.md", "line": 214, "severity": "nit", "label": "nitpick", "claim": "The dispatch table at line 124 lists the User mappings file prompt fourth, but the REPL asks for it last.", "suggested_fix": "```markdown\n| the SiennaSchemas system.json | `outputs/system.json` |\n| User mappings file | `inputs/plexos_user_mappings.yaml` |\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-06", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "minor", "label": "suggestion", "claim": "The new clause is a status note. The word \"now\" ages badly, and the paragraph still calls the same path out of scope.", "suggested_fix": "Delete the scope claim and the word \"now\", and say plainly which chain the pipeline is a leg of.\n\nThe text now:\n\n```markdown\n**Implication for this translation.** PyPSA's capacity-expansion fields (`p_nom_extendable`, `capital_cost`, `p_nom_min`/`p_nom_max`, `build_year`, `lifetime`) belong to **Investments**, which is a separate translation target (PyPSA expansion → SiennaSchemas Investments), not extra fields on an Operations component. It is **out of scope for v1**. For v1 we translate the **solved** fleet: take `p_nom_opt` (post-solve capacities) as fixed Operations capacity and drop the expansion parameters (or keep round-trip crumbs in the `ext` sidecar). A genuine expansion problem is the Investments path, which the `pypsa-to-sienna-investments` pipeline now writes as a portfolio document beside the operations system; [Translation from PLEXOS to a Sienna investments portfolio](translation-from-plexos-to-sienna-investments.md) states what that portfolio holds, and that pipeline is its second leg.\n```\n\nThe text to use:\n\n```markdown\n**Implication for this translation.** PyPSA's capacity-expansion fields (`p_nom_extendable`, `capital_cost`, `p_nom_min`/`p_nom_max`, `build_year`, `lifetime`) belong to **Investments**, which is a separate translation target (PyPSA expansion → SiennaSchemas Investments), not extra fields on an Operations component. This document translates the **solved** fleet: take `p_nom_opt` (post-solve capacities) as fixed Operations capacity and drop the expansion parameters (or keep round-trip crumbs in the `ext` sidecar). A genuine expansion problem is the Investments path, which the `pypsa-to-sienna-investments` pipeline writes as a portfolio document beside the operations system. [Translation from PLEXOS to a Sienna investments portfolio](translation-from-plexos-to-sienna-investments.md) states what that portfolio holds; that pipeline is the second leg of the PLEXOS chain.\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-07", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/case_studies/aemo-isp-2024.md", "line": 306, "severity": "minor", "label": "suggestion", "claim": "The last two lines duplicate the pointer at the end of the expansion path section, word for word.", "suggested_fix": "Keep the sentence that is new here and delete the repeated pointer.\n\nThe text now:\n\n```markdown\n**The expansion path states no number of its own.** No solve in this repository reads a\nportfolio, so nothing here says what a plan would build or what it would cost.\n[The gap analysis](../translation_mappings/plexos-to-sienna-gap-analysis.md) states what the\nportfolio leaves out and what each loss does to an expansion.\n```\n\nThe text to use:\n\n```markdown\n**The expansion path states no number of its own.** No solve in this repository reads a\nportfolio, so nothing here says what a plan would build or what it would cost.\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-08", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/case_studies/aemo-isp-2024.md", "line": 166, "severity": "nit", "label": "nitpick", "claim": "\"It is a translation only\" repeats the clause beside it, which already says interop runs no expansion solve.", "suggested_fix": "Delete the first of the two sentences.\n\nThe text now:\n\n```markdown\nunder. It is a translation only. interop runs no expansion solve, so this path stops at the\nfiles. This section covers the Step Change scenario. The other two follow the same steps.\n```\n\nThe text to use:\n\n```markdown\nunder. interop runs no expansion solve, so this path stops at the files. This section covers\nthe Step Change scenario. The other two follow the same steps.\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-09", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 20, "severity": "nit", "label": "suggestion", "claim": "This paragraph repeats the heading table above it, so the two copies of the fourth heading can drift apart.", "suggested_fix": "Fold the variant into the table row and delete the paragraph.\n\nThe text now:\n\n```markdown\n| The effect on the dispatch | What the solve then does differently from your PLEXOS model |\n\nWhere the subject is an expansion rather than a dispatch, the fourth heading reads **The\neffect on the expansion**: what a plan built from the portfolio does differently from the\nexpansion your PLEXOS model states.\n```\n\nThe text to use:\n\n```markdown\n| The effect on the dispatch, or on the expansion | What the solve, or a plan built from the portfolio, then does differently from your PLEXOS model |\n```", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-10", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 445, "severity": "minor", "label": "issue", "claim": "The entry says every technology \"leaves the list empty\", but the sink drops a null field, so `requirements` is absent from the document rather than written as an empty list.", "suggested_fix": "Join the two sentences and say what the sink writes: \"The portfolio writes no `requirements`: the field is absent from every `SupplyTechnology`, `StorageTechnology` and `DemandRequirement`, and `decisions.md` records it as not mapped.\" Net: -1 lines.", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-11", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 475, "severity": "minor", "label": "issue", "claim": "The default for `max_tons_mwh` is quoted in \"Mt/MWh\", which contradicts the field's own name and the unit the sibling mapping table gives it.", "suggested_fix": "Drop the third-party default from the sentence, or state the unit the field name gives: \"With `max_tons_mwh` absent, nothing limits the carbon intensity of what it builds.\" Net: -1 lines.", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r2-12", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 2, "file": "docs/case_studies/aemo-isp-2024.md", "line": 308, "severity": "nit", "label": "nitpick", "claim": "The pointer to the gap analysis is written twice in this file, in the same words.", "suggested_fix": "Delete the two closing lines of the expansion paragraph in \"The headline number\", keeping \"**The expansion path states no number of its own.** No solve in this repository reads a portfolio, so nothing here says what a plan would build or what it would cost.\" The identical pointer at line 234 already sends the reader there. Net: -2 lines.", "rule_created": false, "ts": "2026-09-09T02:59:14Z", "source": "reviewer", "verdict": "accepted", "reason": "it takes lines away", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-01", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "major", "label": "issue", "claim": "The diff sends a PyPSA reader to a PLEXOS document, because the `pypsa-to-sienna-investments` pipeline has no document of its own.", "suggested_fix": "Say that the linked document states every field the portfolio holds, and that its source column names the PLEXOS property behind each field, not the PyPSA one.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-02", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/case_studies/aemo-isp-2024.md", "line": 184, "severity": "major", "label": "issue", "claim": "The case study states two general translator rules that `translation-from-plexos-to-sienna-investments.md` already states.", "suggested_fix": "Point at `#across-all-components` for the rule that document holds, and keep the two facts it does not hold.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-03", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 436, "severity": "major", "label": "suggestion", "claim": "Three entries of the new expansion part give the same cause, that a `CarbonCaps` holds the whole portfolio.", "suggested_fix": "State the cause once in the first entry, and let the other two refer to it.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-04", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/case_studies/aemo-isp-2024.md", "line": 178, "severity": "major", "label": "issue", "claim": "No cell of the new category table gives a value the reader can copy into the mappings file.", "suggested_fix": "Replace the table with a list of the categories, each naming the types and prime movers it takes.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-05", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 86, "severity": "major", "label": "issue", "claim": "The rewritten paragraph says the `pypsa-to-sienna-investments` pipeline translates the expansion problem, but the four bullets below it still call that translation unfunded, open and a choice the reader must confirm.", "suggested_fix": "Cut the four bullets to one sentence.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-06", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/case_studies/aemo-isp-2024.md", "line": 175, "severity": "minor", "label": "issue", "claim": "The carrier rule is stated without the heat-rate condition, so a reader writes the wrong mappings row for a generator that names a `Fuel` but states no `Heat Rate`.", "suggested_fix": "Write \"where it burns one at a heat rate\".", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-07", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 380, "severity": "minor", "label": "suggestion", "claim": "The sentence defends the choice not to approximate a cap, which the mapping document already states.", "suggested_fix": "Delete the sentence.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-08", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 505, "severity": "minor", "label": "suggestion", "claim": "The sentence about interop's `solve` command documents an absence of a feature the reader is not looking at.", "suggested_fix": "Delete the sentence.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-09", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/case_studies/aemo-isp-2024.md", "line": 219, "severity": "minor", "label": "suggestion", "claim": "The last sentence copies the opening paragraph of the mapping document this section links two paragraphs later.", "suggested_fix": "Delete the sentence.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-10", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 450, "severity": "minor", "label": "suggestion", "claim": "The last two sentences of the effect paragraph repeat the cause paragraph above them and the effect of the first entry.", "suggested_fix": "Delete both sentences.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-11", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 451, "severity": "nit", "label": "suggestion", "claim": "The middle sentence repeats the cause paragraph three lines above it.", "suggested_fix": "Delete the sentence. The same fix as r3-10.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-12", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/translation-from-plexos-to-sienna.md", "line": 8, "severity": "nit", "label": "suggestion", "claim": "The \"Refer to\" line repeats the document title the sentence before it gives.", "suggested_fix": "Put the link on the pipeline name and delete the \"Refer to\" line.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-13", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 364, "severity": "nit", "label": "nitpick", "claim": "The intro sentence states what the heading above it states.", "suggested_fix": "Delete the sentence.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-14", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/translation_mappings/translation-from-plexos-to-pypsa.md", "line": 165, "severity": "nit", "label": "nitpick", "claim": "The sentence before this pointer already names every component that becomes extendable.", "suggested_fix": "Delete the pointer.", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-15", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 3, "file": "docs/case_studies/aemo-isp-2024.md", "line": 173, "severity": "nit", "label": "issue", "claim": "The count of seven categories contradicts the same document, which says the seven names each have sibling categories per state.", "suggested_fix": "Write \"seven named categories, and their siblings\".", "rule_created": false, "ts": "2026-09-10T09:59:59Z", "source": "reviewer", "verdict": "dropped", "reason": "repeats r1-10, which round 1 already answered", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-01", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 18, "severity": "major", "label": "suggestion", "claim": "This file now holds two pipelines, but its name states one pipeline only, and the four-item table carries both cases in one cell.", "suggested_fix": "Give the table one row per run. Do not split the file.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "thermo", "verdict": "partial", "reason": "The table cell is fixed. The file split is declined: it reverses r1-01 and r1-20, which round 1 accepted, and it needs a new file plus every inbound link.", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-02", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-plexos-to-sienna-investments.md", "line": 6, "severity": "major", "label": "issue", "claim": "The case study states two translator rules that no mapping document states: the base system types a generator candidate may name, and the rows a storage candidate needs.", "suggested_fix": "Add both rules to `Across all components`, and let the case study point at it.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "thermo", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-03", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 224, "severity": "major", "label": "issue", "claim": "One paragraph states seven facts, and it holds the only instructions in the repository for a lone `pypsa-to-sienna-investments` run.", "suggested_fix": "Move the general rules to `The base year` of the mapping document, and leave a pointer.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "thermo", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-04", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 431, "severity": "major", "label": "suggestion", "claim": "The `requirements` entry states nothing that the cap entry above it and the mapping document do not state already.", "suggested_fix": "Delete the entry. The `requirements` row of the mapping document carries the fact.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "thermo", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-05", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 86, "severity": "minor", "label": "suggestion", "claim": "The sentence under **Why Operations and not Investments** repeats the sentence that opens the paragraph above it.", "suggested_fix": "Delete the line and its heading.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "thermo", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-06", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "README.md", "line": 307, "severity": "minor", "label": "suggestion", "claim": "The README names no document for what the expansion path loses.", "suggested_fix": "Name the gap analysis in the investments bullet.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "thermo", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-07", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 441, "severity": "minor", "label": "issue", "claim": "\"the cap above\" points a reader at the entry above, which is the transmission entry, not the cap entry.", "suggested_fix": "Round 3 wrote the wrong cross-reference. Deleting the entry under r4-04 removes it.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "correctness", "verdict": "accepted", "reason": "a repair of an error round 3 of this cycle introduced", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-08", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 463, "severity": "minor", "label": "question", "claim": "\"The cap applies to every year of the run\" contradicts the cap entry, which says a cap states one ceiling over the whole run.", "suggested_fix": "Say the cap holds the whole run, so a yearly right-hand side becomes a budget for the whole run.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "correctness", "verdict": "accepted", "reason": "a repair of an error round 3 of this cycle introduced", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-09", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "minor", "label": "issue", "claim": "The claim that the PLEXOS investments document \"states every field the portfolio holds\" is wrong, because three portfolio financial fields appear in no table there.", "suggested_fix": "Say it states the source of each technology, requirement and attribute field.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "correctness", "verdict": "accepted", "reason": "a repair of an error round 3 of this cycle introduced", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-10", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "nit", "label": "issue", "claim": "The sentence names a \"source column\" that the PLEXOS investments document does not have; its column is headed `From`.", "suggested_fix": "Name the `From` column.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "correctness", "verdict": "accepted", "reason": "a repair of an error round 3 of this cycle introduced", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-11", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 166, "severity": "minor", "label": "issue", "claim": "\"It is a translation only. interop runs no expansion solve\" duplicates the paragraph under \"The headline number\".", "suggested_fix": "Delete one of the two copies.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pruning", "verdict": "superseded", "reason": "r4-19 deletes the other copy. Applying both would delete the fact from the document.", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-12", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 173, "severity": "nit", "label": "suggestion", "claim": "The clause \"because a dispatch system has no place for what they hold\" restates a reason the dispatch section above gives in sharper words.", "suggested_fix": "Delete the clause.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pruning", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-13", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 215, "severity": "nit", "label": "suggestion", "claim": "The list of the other prompts duplicates the REPL the reader is looking at while they answer it.", "suggested_fix": "Keep \"Keep the default at every other prompt\" and the prompt order sentence.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pruning", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-14", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 497, "severity": "minor", "label": "issue", "claim": "\"so they belong on the request that solves the portfolio rather than in it\" argues that the schema is right rather than stating the cause.", "suggested_fix": "Delete the clause.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pruning", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-15", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 82, "severity": "nit", "label": "suggestion", "claim": "\"which is a separate translation target\" is now said twice in one paragraph, because the next sentence names the pipeline that does it.", "suggested_fix": "Delete the clause.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pruning", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-16", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-pypsa-to-sienna.md", "line": 14, "severity": "minor", "label": "issue", "claim": "The revision note still promises the rationale and the open question that round 3 removed.", "suggested_fix": "Say the document targets Operations and that `pypsa-to-sienna-investments` translates the expansion problem.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "accepted", "reason": "a repair of an error round 3 of this cycle introduced", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-17", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/translation-from-plexos-to-pypsa.md", "line": 205, "severity": "minor", "label": "issue", "claim": "The `s_nom_extendable` row still gives \"Version 1 dispatches only\" as the reason, which the sentence at line 164 contradicts.", "suggested_fix": "Write \"The translator writes no expandable line.\"", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-18", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 128, "severity": "minor", "label": "issue", "claim": "`sem-2024-2032.md` and `caiso-sa26.md` still list the User mappings file prompt before the input XML, which this branch moved to last.", "suggested_fix": "Move the row to the end of the table in both case studies.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "deferred", "reason": "an automated run acts only on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-19", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 308, "severity": "minor", "label": "suggestion", "claim": "The paragraph under \"What the number does not cover\" states what the section intro at line 166 already states.", "suggested_fix": "Delete the paragraph.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-20", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/translation_mappings/plexos-to-sienna-gap-analysis.md", "line": 3, "severity": "nit", "label": "nitpick", "claim": "\"Each part below names the run it covers\" only points at the two headings below, which name their run in plain sight.", "suggested_fix": "Delete the sentence.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-21", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 182, "severity": "nit", "label": "nitpick", "claim": "The trailing clause of the third bullet states the instruction the paragraph two lines below gives for every row.", "suggested_fix": "Delete the clause.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-22", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 4, "file": "docs/case_studies/aemo-isp-2024.md", "line": 234, "severity": "nit", "label": "nitpick", "claim": "The closing paragraph points at the mapping document, which the section already links.", "suggested_fix": "Keep the gap analysis pointer only.", "rule_created": false, "ts": "2026-09-10T10:09:20Z", "source": "pointless", "verdict": "accepted", "reason": "an automated run acts on a blocking comment or one that removes lines", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r3-06", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 5, "file": "docs/case_studies/aemo-isp-2024.md", "line": 174, "severity": "minor", "label": "issue", "claim": "The carrier rule omits the heat-rate condition, so a reader writes the wrong mappings row for a generator that names a `Fuel` and states no `Heat Rate`.", "suggested_fix": "Write \"where it burns one at a heat rate\".", "rule_created": false, "ts": "2026-09-10T13:33:36Z", "source": "user", "verdict": "accepted", "reason": "the user asked for it", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-17", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 5, "file": "docs/translation_mappings/translation-from-plexos-to-pypsa.md", "line": 205, "severity": "minor", "label": "issue", "claim": "The `s_nom_extendable` row gives \"Version 1 dispatches only\" as the reason, which line 164 contradicts.", "suggested_fix": "Write \"The translator writes no expandable line.\"", "rule_created": false, "ts": "2026-09-10T13:33:36Z", "source": "user", "verdict": "accepted", "reason": "the user asked for it", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-18", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 5, "file": "docs/case_studies/sem-2024-2032.md", "line": 97, "severity": "minor", "label": "issue", "claim": "`sem-2024-2032.md` and `caiso-sa26.md` list the User mappings file prompt before the input XML, but the REPL asks for it after every source, step and sink prompt.", "suggested_fix": "Move the row to the end of the table in both case studies.", "rule_created": false, "ts": "2026-09-10T13:33:36Z", "source": "user", "verdict": "accepted", "reason": "the user asked for it", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r4-06", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 5, "file": "README.md", "line": 307, "severity": "minor", "label": "issue", "claim": "The investments bullet names no document for what the expansion path loses.", "suggested_fix": "Name `plexos-to-sienna-gap-analysis.md` in the investments bullet.", "rule_created": false, "ts": "2026-09-10T13:33:36Z", "source": "user", "verdict": "accepted", "reason": "the user asked for it", "category": null, "promote_now": false, "suppressed_by": null} +{"id": "r1-10", "pr": "12", "repo": "transition-zero/tz-oss-interop", "round": 5, "file": "docs/case_studies/aemo-isp-2024.md", "line": 173, "severity": "nit", "label": "issue", "claim": "The count of seven categories is lower than the number a reader must leave out, because each named category has sibling categories per state.", "suggested_fix": "Write \"seven named categories, and their siblings\".", "rule_created": false, "ts": "2026-09-10T13:33:36Z", "source": "user", "verdict": "accepted", "reason": "the user asked for it", "category": null, "promote_now": false, "suppressed_by": null} diff --git a/docs/case_studies/aemo-isp-2024.md b/docs/case_studies/aemo-isp-2024.md index 38fcd53..25b3379 100644 --- a/docs/case_studies/aemo-isp-2024.md +++ b/docs/case_studies/aemo-isp-2024.md @@ -121,11 +121,11 @@ Select `translate`. Then give these answers: | Source framework | `plexos` | | Destination framework | `sienna` | | Pipeline | `plexos-to-sienna` | -| User mappings file | `inputs/plexos_user_mappings.yaml` | | the PLEXOS `` input XML | `case_study_inputs/aemo-isp-2024/2024 ISP Step Change Model.xml` | | which PLEXOS Model to translate | `Step Change` | | a four-digit year such as 2026 | `2025` | | the SiennaSchemas system.json | `outputs/system.json` | +| User mappings file | `inputs/plexos_user_mappings.yaml` | Give the year `2025` here, unlike the PyPSA run above, which leaves the year empty. The chronology of the Model is 2024-07-01 to 2025-06-30, so the year 2025 narrows it to @@ -158,6 +158,67 @@ Then select `solve`. Give the model type `sienna` and the system `linearised`, and the HiGHS defaults. Leave the time limit empty. For more data about these prompts, refer to [the solve tutorial](../tutorials/solve.md#sienna-path). +### The expansion path + +The same model also translates to a Sienna investments portfolio, which is what a partner +running an expansion in PowerSystemsInvestments.jl needs. That run writes the expansion +problem: the technologies the plan may build, the demand they meet, and the caps they run +under. It is a translation only. interop runs no expansion solve, so this path stops at the +files. This section covers the Step Change scenario. The other two follow the same steps. + +Write a second mappings file, `inputs/plexos_expansion_mappings.yaml`. It is the dispatch +file plus one row for each carrier a candidate takes. A candidate whose carrier the file does +not name is left out of the portfolio, and `decisions.md` names each one. + +The dispatch run above leaves seven named categories, and their siblings, out of its file. +The expansion run names the ones that hold candidate plants. A generator takes the name of its +`Fuel` where it burns one at a heat rate, and its category where it does not, so add one +`category` row for each of these: + +- `2023 REZ NSW`, and the sibling category of each other state: a wind or a solar candidate, + so `RenewableDispatch` with the prime mover `WT` or `PVe`; +- `New Entrants NSW`, and the sibling category of each other state: a wind, a solar or a gas + candidate, so the prime mover `WT`, `PVe`, `CC` or `CT`; +- `LTESA Projects`, `Policy Projects` and `VRET Projects`. + +The translator reads no meaning from a category name, so give each row the +`sienna_component_type` and the `sienna_prime_mover_type` of the plant that category holds in +your copy of the model. +[Across all components](../translation_mappings/translation-from-plexos-to-sienna-investments.md#across-all-components) +states the types a row may name, and what happens to a row that names another kind's type. + +Leave `REZ Augmentation` and `Group REZ Augmentation` out of this file as well. They are +transmission augmentations written as generators, and the portfolio holds no transport +technology, so a row for one states a plant your model does not mean. + +Select `translate`. Then give these answers: + +| Prompt | Answer | +| --- | --- | +| Source framework | `plexos` | +| Destination framework | `sienna` | +| Pipeline | `plexos-to-sienna-investments` | +| the PLEXOS `` input XML | `case_study_inputs/aemo-isp-2024/2024 ISP Step Change Model.xml` | +| which PLEXOS Model to translate | `Step Change` | +| a four-digit year such as 2026 | `2025` | +| the SiennaSchemas system.json | `outputs/system.json` | +| the SiennaSchemas portfolio document to write | `outputs/portfolio.json` | +| User mappings file | `inputs/plexos_expansion_mappings.yaml` | + +Keep the default at every other prompt. The mappings prompt comes last, after the file +prompts of both sinks. + +That run writes four files: the three the Sienna path writes, and `outputs/portfolio.json` +beside them. The portfolio names `system.json` in its `base_system_file`, so the two are read +together. + +Every cost in a portfolio is quoted in a base year, and no PLEXOS field states one. This run +takes the default base year of 2020. To state another one, refer to +[The base year](../translation_mappings/translation-from-plexos-to-sienna-investments.md#the-base-year). + +[The gap analysis](../translation_mappings/plexos-to-sienna-gap-analysis.md) states what the +portfolio leaves out and what each loss does to an expansion. + ## The headline number **What you can check by yourself.** All three scenarios translate. All three solves give diff --git a/docs/case_studies/caiso-sa26.md b/docs/case_studies/caiso-sa26.md index 9ad8378..3a66221 100644 --- a/docs/case_studies/caiso-sa26.md +++ b/docs/case_studies/caiso-sa26.md @@ -178,12 +178,12 @@ Select `translate`. Then give these answers: | Source framework | `plexos` | | Destination framework | `sienna` | | Pipeline | `plexos-to-sienna-monte-carlo` | -| User mappings file | `inputs/plexos_user_mappings.yaml` | | the PLEXOS `` input XML | `case_study_inputs/caiso-sa26/CAISOSA26 20260429.xml` | | which PLEXOS Model to translate | `M09Y2026 SA26` | | a four-digit year such as 2026 | Leave empty, as for the PyPSA run above. | | directory to hold the ensemble | `outputs/caiso-m09-sienna` | | names each replication's directory | Keep the default, `{sample}` | +| User mappings file | `inputs/plexos_user_mappings.yaml` | That run writes one directory per replication, `1`, `2` and `3`, each holding four files: `system.json`, its HDF5 companion `system_time_series_storage.h5`, `extensions.json`, and the diff --git a/docs/case_studies/sem-2024-2032.md b/docs/case_studies/sem-2024-2032.md index fed7d88..af72352 100644 --- a/docs/case_studies/sem-2024-2032.md +++ b/docs/case_studies/sem-2024-2032.md @@ -94,11 +94,11 @@ Select `translate`. Then give these answers: | Source framework | `plexos` | | Destination framework | `sienna` | | Pipeline | `plexos-to-sienna` | -| User mappings file | `inputs/plexos_user_mappings.yaml` | | the PLEXOS `` input XML | `case_study_inputs/sem-2024-2032/PUBLIC Validation 2024-2032 Model 2025-03-14.xml` | | which PLEXOS Model to translate | `Opt A 24-32 (Avail, Uplift, Wheeling)--MIP` | | a four-digit year such as 2026 | `2026` | | the SiennaSchemas system.json | `outputs/system.json` | +| User mappings file | `inputs/plexos_user_mappings.yaml` | That run writes three files: `outputs/system.json`, its HDF5 companion `outputs/system_time_series_storage.h5`, and `outputs/extensions.json`. Those three files diff --git a/docs/translation_mappings/plexos-to-sienna-gap-analysis.md b/docs/translation_mappings/plexos-to-sienna-gap-analysis.md index bb13c66..37f831d 100644 --- a/docs/translation_mappings/plexos-to-sienna-gap-analysis.md +++ b/docs/translation_mappings/plexos-to-sienna-gap-analysis.md @@ -1,11 +1,11 @@ # What a PLEXOS to Sienna translation loses -This document lists each thing a `plexos-to-sienna` run loses, and what that loss does to a -dispatch. It covers the translation and the validation run that proves the system solves in -PowerSimulations.jl. +This document lists each thing a PLEXOS to Sienna run loses. For what the translation keeps, refer to -[Translation from PLEXOS to Sienna](translation-from-plexos-to-sienna.md). +[Translation from PLEXOS to Sienna](translation-from-plexos-to-sienna.md), and for the +portfolio to +[Translation from PLEXOS to a Sienna investments portfolio](translation-from-plexos-to-sienna-investments.md). Each entry gives four things: @@ -15,10 +15,16 @@ Each entry gives four things: | What happens to it | Where it goes, or that it goes nowhere | | The cause | Why | | The effect on the dispatch | What the solve then does differently from your PLEXOS model | +| The effect on the expansion | What a plan built from the portfolio then does differently from your PLEXOS model | --- -## Reserve requirements +## What `plexos-to-sienna` loses from a dispatch + +These entries cover the translation and the validation run that proves the system solves in +PowerSimulations.jl. + +### Reserve requirements **The PLEXOS data.** Each `Reserve` object, its type, its requirement in MW or as a share of a profile, and the generators that can provide it. @@ -37,7 +43,7 @@ model, and a scarcity price your model shows does not appear. --- -## Load shedding, on a plain run +### Load shedding, on a plain run **The PLEXOS data.** Each Region states a `VoLL`, the value of lost load. @@ -55,7 +61,7 @@ than a shortfall in MWh. Run the reliability chain to get the shortfall in MWh i --- -## Unit commitment, relaxed +### Unit commitment, relaxed **The PLEXOS data.** A start cost, a minimum up time and a minimum down time on each thermal generator. @@ -78,7 +84,7 @@ two paths do not compare under that answer. --- -## Thermal availability where the model states none +### Thermal availability where the model states none **The PLEXOS data.** An `Outage Factor`, an `Outage Rating`, a `Rating` profile or a `Units Out` profile on some of the thermal fleet, and nothing on the rest. @@ -98,7 +104,7 @@ number. --- -## A hydro unit whose inflow is water, not power +### A hydro unit whose inflow is water, not power **The PLEXOS data.** A reservoir hydro turbine with a `Natural Inflow` stated in cumec or in m³/day, or with no `Natural Inflow` at all. @@ -117,7 +123,7 @@ The AEMO 2024 ISP states every inflow in cumec, so its whole reservoir fleet is --- -## A storage unit that states no energy +### A storage unit that states no energy **The PLEXOS data.** A `Battery` with no `Capacity` and no `Duration`, or a pumped storage head reservoir whose `Max Volume` is in water. @@ -133,7 +139,7 @@ across the hours it would have covered. --- -## A generator that is not a power plant +### A generator that is not a power plant **The PLEXOS data.** A generator whose category names a transmission augmentation, a policy project or another pseudo-object rather than a technology. @@ -150,7 +156,7 @@ each run. --- -## Heat rate bands +### Heat rate bands **The PLEXOS data.** A `Heat Rate` stated as several bands, so the efficiency changes with output. @@ -168,7 +174,7 @@ differently. --- -## A generator that burns more than one fuel +### A generator that burns more than one fuel **The PLEXOS data.** Several `Fuels` memberships on one generator. @@ -183,7 +189,7 @@ price. A dual-fuel unit that your model switches to a cheaper fuel does not swit --- -## A Fuel and a generator category of one name +### A Fuel and a generator category of one name **The PLEXOS data.** A `Fuel` and a generator category that share a name, for example a fuel `HVO` and a category `HVO`. @@ -201,7 +207,7 @@ groups of generators then take that type. --- -## Zones, interfaces and custom constraints +### Zones, interfaces and custom constraints **The PLEXOS data.** `Zone` objects, `Interface` flow limits and `Constraint` objects, which include energy budgets, running hour limits, RPS targets and emission caps. @@ -224,7 +230,7 @@ the dispatch. --- -## Hydro cascades and volumes in water +### Hydro cascades and volumes in water **The PLEXOS data.** `Waterway` objects joining reservoirs, and `Max Volume` and `Initial Volume` stated in 1000 m³ or a `Natural Inflow` stated in cumec. @@ -243,7 +249,7 @@ number that depends on hydro. --- -## One solve, one window +### One solve, one window **The PLEXOS data.** A Horizon, which can be many years long. @@ -261,7 +267,7 @@ shorter year. --- -## Region Price of Dump Energy +### Region Price of Dump Energy **The PLEXOS data.** A Region `Price of Dump Energy`, the price of energy the system spills. @@ -276,7 +282,7 @@ objective. The Region `VoLL` beside it does reach a reliability run: refer to --- -## Sienna holds no Monte Carlo forecast a solve reads +### Sienna holds no Monte Carlo forecast a solve reads **The PLEXOS data.** A pre-sampled model states many values for one property at one snapshot, one per replication, and a run over it draws a distribution of outcomes. @@ -294,7 +300,7 @@ counts how many of them lose load. Solve the replications and count the outcomes --- -## A Sienna objective and a PyPSA objective do not compare +### A Sienna objective and a PyPSA objective do not compare **The PLEXOS data.** A Region `VoLL`, in a reliability run on both sides of the PyPSA hub. @@ -313,7 +319,7 @@ not the same quantity and must not be compared or subtracted. --- -## A profile that reaches only some replications +### A profile that reaches only some replications **The PLEXOS data.** An outage draw that takes a unit out in one replication and leaves it available through the whole window in another. @@ -334,7 +340,7 @@ holds. --- -## A reliability solve reports its unserved energy in the results files +### A reliability solve reports its unserved energy in the results files **The PLEXOS data.** The energy a window cannot serve, which a reliability run prices at the Region `VoLL`. @@ -350,3 +356,139 @@ does not read yet. **The effect on the dispatch.** None. The number is in the solve output; no report collects it for you. + +--- + +## What `plexos-to-sienna-investments` loses from an expansion + +### A `Constraint` a cap cannot carry + +**The PLEXOS data.** A `Constraint` holding a weighted sum over the objects it names to a +right-hand side: an emission cap over a group of plants, a target over one region, a budget +over one technology. + +**What happens to it.** The portfolio writes a `CarbonCaps` only for a constraint that meets +all five of these conditions: + +- it names every generator and storage object the base system and the portfolio hold; +- it holds its weighted sum to `<=`; +- it states a right-hand side for a year or for the whole horizon; +- that right-hand side is a finite number; +- its `Include in LT Plan` is not false, and a constraint that states nothing stays in. + +Every other constraint is left out, `decisions.md` names it, and the log warns. Every +constraint still reaches the `extensions.json` sidecar, whether or not it became a cap. + +**The cause.** `CarbonCaps` names no members and no region: a cap in a portfolio holds the +whole portfolio. A cap also states one ceiling over the whole run, so a constraint held to +`>=` or to `==`, and a constraint whose only right-hand side bounds a repeating window inside +the run, give it nothing to carry. A cap states a number of million tonnes, and neither NaN +nor Infinity is one, so a right-hand side that is not a finite number gives it nothing either. +A constraint the expansion plan does not have to meet would bound a problem your model leaves +free. + +**The effect on the expansion.** Nothing bounds what the constraint names, so the plan may +build and run those objects up to their own limits. A model whose targets are all regional, +technology-scoped, or written as a floor reaches the portfolio with no cap at all. Read the +constraints in the sidecar before you trust what the plan builds. + +--- + +### A candidate whose build nothing prices + +**The PLEXOS data.** A `Generator`, a `Battery` or a pumped-storage turbine stating `Max Units +Built` but not all of `Build Cost`, `WACC` and `Economic Life`. + +**What happens to it.** The PLEXOS leg leaves the build out. An object with nothing running +yet is its build and nothing else, so the whole object goes and `decisions.md` names it. An +object that already runs keeps the capacity it runs, and only the build it may add is +dropped. The second leg drops a candidate that reaches it with no finite upper bound on +capacity, a capacity floor above that upper bound, no finite lifetime, a lifetime below one +year, no overnight cost or no discount rate. It drops a storage candidate that holds no +energy, or that puts no upper bound on the energy a build may add. It names each one the same +way. + +**The cause.** PyPSA annuitises an overnight cost with a discount rate over a lifetime. It +refuses a network that states an overnight cost and no discount rate, it prices a build with +no lifetime as a perpetuity, and it builds for free a candidate that states no overnight cost +at all. A Sienna technology states its own price and its own financing, and +`TechnologyFinancialData` requires both a return on equity and a capital recovery period. + +**The effect on the expansion.** That candidate is not in the portfolio, so no plan built +from it can build that technology. + +--- + +### Transmission a plan may build + +**The PLEXOS data.** A `Line` or a transformer the plan may expand, and the `Line.Type` that +says which technology LT Plan expands it with. + +**What happens to it.** Neither leg writes an expandable branch. The base system holds each +line and each link at the rating it already has, and the portfolio holds no technology for a +corridor. + +**The cause.** SiennaSchemas states transport technologies of its own, and this translation +writes none of them. The PLEXOS leg fixes the capacity of every line and link, so nothing +reaches the hub for a second leg to read either. + +**The effect on the expansion.** The network is fixed. A plan can put new capacity only where +the corridors that already exist can carry it, so it builds nearer to the demand than your +model would, and an expansion your model meets by reinforcing a corridor is met by generation +or not at all. + +--- + +### The year a cap applies in, and a cap on carbon intensity + +**The PLEXOS data.** A `Constraint` right-hand side, stated for a year or over the whole +horizon, and the span it applies over. + +**What happens to it.** The yearly right-hand side becomes `max_mtons`, the cap's limit in +million tonnes, and the horizon-wide one is read where the constraint states no yearly limit. +`CarbonCaps.target_year` and `CarbonCaps.max_tons_mwh` are left unmapped and are absent from +the document; `decisions.md` records both. + +**The cause.** PLEXOS states the span a right-hand side applies over, not the year it applies +in, and it has no rate-based right-hand side for `max_tons_mwh` to carry. + +**The effect on the expansion.** The cap holds the whole run, whichever year your model +stated it for, so a yearly right-hand side becomes a budget for the whole run. With +`max_tons_mwh` absent, nothing limits the carbon intensity of what it builds. + +--- + +### A storage build that prices only its discharge + +**The PLEXOS data.** The `Build Cost` of a `Battery`, or of a pumped-storage turbine, which +prices the unit by its power. + +**What happens to it.** It becomes the technology's `capital_costs.discharge_capital_cost`. +The `charge_capital_cost` and the `energy_capital_cost` beside it are written as zero curves. + +**The cause.** A Sienna storage technology adds charge power, discharge power and energy +independently and prices each of the three. PLEXOS prices the object by its power alone, and +PyPSA carries one overnight cost for a storage unit, so neither states the other two prices. + +**The effect on the expansion.** A consumer that sizes the three parts separately takes the +largest energy and the largest charging power its limits allow for nothing, because nothing +prices either. The whole price of a storage build sits on its discharge capacity, which is +the right number only if you read the source's build cost as the price of a whole unit. + +--- + +### The years a plan steps through + +**The PLEXOS data.** The investment periods of an LT Plan, the years it steps through, and +the representative days and weights it samples each year with. + +**What happens to it.** Neither reaches the portfolio. The portfolio states one expansion +problem, with no schedule of periods and no representative-day weighting. + +**The cause.** A portfolio document holds the technologies, the requirements and the regions +of an expansion problem. The periods and the representative days are terms of the solve. + +**The effect on the expansion.** Whoever solves the portfolio chooses the periods and the +sampling. A plan built over a different set of years, or against a different set of +representative days, from the ones your PLEXOS model uses builds a different fleet, so its +result and your model's LT Plan result are not the same quantity. diff --git a/docs/translation_mappings/translation-from-plexos-to-pypsa.md b/docs/translation_mappings/translation-from-plexos-to-pypsa.md index 9c1b6ea..c8ba8aa 100644 --- a/docs/translation_mappings/translation-from-plexos-to-pypsa.md +++ b/docs/translation_mappings/translation-from-plexos-to-pypsa.md @@ -160,7 +160,8 @@ mark of an HVDC line or of a trade path. The translator does **not** read the `Line.Type` property. That property sets the technology that LT Plan uses when it expands a line: `0` for AC and `1` for DC. It does not -set the operation of a line that exists. Version 1 translates dispatch only. +set the operation of a line that exists. The translator writes no expandable line either: +only a `Generator`, a `Battery` and a pumped-storage turbine become extendable components. A line must have a `Node From` membership and a `Node To` membership. If a line does not have both, it connects nothing. The translator does not write it, and it records this. @@ -201,7 +202,7 @@ PyPSA `Line` has no cost for its flow. Thus for such a line the charge is `not m | `length` | km | `Length` | `direct` | | `active` | | `True` | `default` | | `carrier` | | `AC` | `default` | -| `s_nom_extendable` | | `False`. Version 1 dispatches only. | `default` | +| `s_nom_extendable` | | `False`. The translator writes no expandable line. | `default` | | `v_ang_min` / `v_ang_max` | | PLEXOS states no voltage-angle limit. | `not mapped` | `s_nom` is one rating for the two directions. Thus if the forward limit and the reverse diff --git a/docs/translation_mappings/translation-from-plexos-to-sienna-investments.md b/docs/translation_mappings/translation-from-plexos-to-sienna-investments.md index c590247..c9d2729 100644 --- a/docs/translation_mappings/translation-from-plexos-to-sienna-investments.md +++ b/docs/translation_mappings/translation-from-plexos-to-sienna-investments.md @@ -7,7 +7,9 @@ portfolio. It gives the source of each field. > build, not when it builds it: it does not carry the investment periods, the representative > days or their weights. It does not translate an expansion of a transmission line, and it > writes no aggregation of candidates by category. Refer to -> [Not translated](#not-translated). +> [Not translated](#not-translated) and to +> [the gap analysis](plexos-to-sienna-gap-analysis.md), which states what each loss does to an +> expansion. One run of `plexos-to-sienna-investments` writes **two documents**. `system.json` is the base power system: the fleet that already runs. `portfolio.json` is the expansion problem: the @@ -58,6 +60,11 @@ Where the intermediate form loses something, this document says so. `capacity_limits.min` is that same capacity, which a build cannot take away. - **A candidate whose carrier your mappings file does not name is left out.** The run completes and `decisions.md` names each one. +- **A generator candidate names one of four base system types.** Your mappings file must send + its carrier to `ThermalStandard`, `RenewableDispatch`, `RenewableNonDispatch` or + `HydroDispatch`, and a `ThermalStandard` row also states the `sienna_fuel_type`. A `Battery` + candidate and a pumped-storage candidate need no row of their own, because the mappings + pipeline supplies a `storage_kind` row for all three storage kinds. - **A candidate whose carrier your mappings file sends to another kind's type is left out.** A generator becomes a `SupplyTechnology` and a battery or a pumped-storage turbine becomes a `StorageTechnology`, so a carrier sent to a base system type the other kind holds names a @@ -216,6 +223,15 @@ both come from the `base_year` parameter of the `pypsa_to_sienna_investments_map step. It defaults to **2020**, which is the year SiennaSchemas itself defaults a construction year to. Set it to the dollar year your `Build Cost` and `FO&M Charge` are quoted in. +A chained pipeline prompts for the source of its first leg and the sinks of its last, and for +no step in between, so a `plexos-to-sienna-investments` run always takes the default. To state +another year, run the two legs yourself: `plexos-to-pypsa`, then `pypsa-to-sienna-investments` +over the network and the sidecar it wrote. The second leg prompts for its steps, so the base +year is the `base_year` of `step[2]`. That leg also asks for a mappings file in PyPSA words, +which the chain derives from your PLEXOS file and a lone run cannot, so write a `carriers` +file with one `pypsa_carrier` row for each carrier the first leg wrote, the storage carriers +included. + ### What the PyPSA hub cannot carry The route runs through a PyPSA network, and three values have no PyPSA column: diff --git a/docs/translation_mappings/translation-from-plexos-to-sienna.md b/docs/translation_mappings/translation-from-plexos-to-sienna.md index 30aa452..9358510 100644 --- a/docs/translation_mappings/translation-from-plexos-to-sienna.md +++ b/docs/translation_mappings/translation-from-plexos-to-sienna.md @@ -3,12 +3,14 @@ This document tells you what each part of your PLEXOS model becomes in the Sienna system. It gives the source of each field. -> **Scope:** the translator accepts electricity-only models, and it translates them for -> dispatch. It does not translate capacity expansion, custom constraints or hydro cascades. -> It does not carry the reserves to a file you keep. Refer to -> [Not translated](#not-translated) and to +> **Scope:** the translator accepts electricity-only models, and the `plexos-to-sienna` +> pipeline translates them for dispatch. It writes no capacity expansion: what your plan may +> build becomes a Sienna investments portfolio instead, which +> [the `plexos-to-sienna-investments` pipeline](translation-from-plexos-to-sienna-investments.md) +> writes. This pipeline does not translate custom constraints or hydro cascades, and it does +> not carry the reserves to a file you keep. Refer to [Not translated](#not-translated) and to > [the gap analysis](plexos-to-sienna-gap-analysis.md), which states what each loss does to a -> dispatch. +> dispatch and what the portfolio leaves out of an expansion. The `plexos-to-sienna` pipeline runs through a PyPSA network on the way. This document does not describe that network. It states the mapping as one step, because that is what you give diff --git a/docs/translation_mappings/translation-from-pypsa-to-sienna.md b/docs/translation_mappings/translation-from-pypsa-to-sienna.md index b759cc5..77cc9fd 100644 --- a/docs/translation_mappings/translation-from-pypsa-to-sienna.md +++ b/docs/translation_mappings/translation-from-pypsa-to-sienna.md @@ -11,7 +11,7 @@ Concretely, versus `main` (≈280 insertions / 294 deletions): - **Time series.** Replaced the `SingleTimeSeries`/`add_time_series!` framing with SiennaSchemas `TimeSeriesAssociation` records (integer `owner_id`); value arrays are **retained in HDF5** keyed by `time_series_uuid` (the schema's named external store). Standardised on the **per-unit shape + `scaling_factor_multiplier`** convention. - **Component extensions.** PyPSA fields with no SiennaSchemas home now go in a separate `extensions.json` sidecar, a document keyed by kind whose records are identified by `name`, replacing the PowerSystems.jl `ext` dict. - **References & ordering.** `get_component(...)` / "added to the `System`" became integer-`id` references with an id-assignment ordering (the lists themselves are unordered). -- **Investments boundary.** Added that SiennaSchemas' `Investments/` namespace is a separate, parallel model (the home for capacity expansion); v1 targets **Operations** (PowerSimulations.jl), with the rationale and the open question of whether expansion interoperability is intended. +- **Investments boundary.** Added that SiennaSchemas' `Investments/` namespace is a separate, parallel model (the home for capacity expansion); this document targets **Operations** (PowerSimulations.jl), and `pypsa-to-sienna-investments` translates the expansion problem. --- @@ -79,17 +79,10 @@ This document targets the SiennaSchemas **Operations** namespace (the dispatchab - It adds policy `Requirements` (`CarbonCaps`, `CarbonTax`, `CapacityReserveMargin`, `EnergyShareRequirements`, …) and `Financials`. - It bridges to Operations through each technology's `power_systems_type` (the Operations type it realises into) and `TopologyMapping` (region → buses). -**Implication for this translation.** PyPSA's capacity-expansion fields (`p_nom_extendable`, `capital_cost`, `p_nom_min`/`p_nom_max`, `build_year`, `lifetime`) belong to **Investments**, which is a separate translation target (PyPSA expansion → SiennaSchemas Investments), not extra fields on an Operations component. It is **out of scope for v1**. For v1 we translate the **solved** fleet: take `p_nom_opt` (post-solve capacities) as fixed Operations capacity and drop the expansion parameters (or keep round-trip crumbs in the `ext` sidecar). A genuine expansion problem is the future Investments path. +**Implication for this translation.** PyPSA's capacity-expansion fields (`p_nom_extendable`, `capital_cost`, `p_nom_min`/`p_nom_max`, `build_year`, `lifetime`) belong to **Investments**, not extra fields on an Operations component. This document is the Operations translation, so it drops the expansion parameters: it takes `p_nom_opt` (post-solve capacities) as fixed Operations capacity, and keeps round-trip crumbs in the `ext` sidecar. The `pypsa-to-sienna-investments` pipeline translates the expansion problem itself, and writes a portfolio document beside the operations system. It is the second leg of `plexos-to-sienna-investments`, so [Translation from PLEXOS to a Sienna investments portfolio](translation-from-plexos-to-sienna-investments.md) states the source of each technology, requirement and attribute field the portfolio holds. The `From` column of that document names the PLEXOS property behind each field, not the PyPSA one. Throughout this document, **`effective_p_nom`** denotes the capacity an operations model may dispatch: `p_nom_opt` where an extendable component states one, `p_nom_min` where it states a capacity a build cannot take away, and `p_nom` otherwise. All capacity-derived fields — `base_power`, `active_power`, `active_power_limits`, `ramp_limits`, and the hydro energy budget scaling factor — use `effective_p_nom`. For a non-extendable component `effective_p_nom = p_nom`. A solve that builds none of an extendable component writes `p_nom_opt` 0, and that 0 is the capacity to translate: the plan rejected the build. A network no solve has touched leaves `p_nom_opt` out of the file altogether, and an extendable component there is rated at the `p_nom_min` it already runs. An extendable component with neither a `p_nom_opt` nor a `p_nom_min` is a candidate rather than a plant: the generator, renewable, hydro and storage mappings leave it out and record it as skipped. -**Why Operations and not Investments.** This is a deliberate scope choice tied to the deliverable, not a claim that Operations is the more natural fit: - -- **The deliverable targets PowerSimulations.jl** — the *operations* solver (dispatch, UC, PCM). The Investments schemas are consumed by PowerSystemsInvestments.jl, a separate and newer solver that is not the funded target. The acceptance test ("it runs in PowerSimulations.jl") pins us to Operations. -- **Maturity.** Operations + PowerSimulations.jl is the mature, well-exercised path; the `Investments/` schemas and PowerSystemsInvestments.jl are newer. -- **Investments is a larger, separate translation** with its own open mappings: aggregating PyPSA buses into `Node`/`Zone` regions, reconciling PyPSA's (often pre-annuitised) `capital_cost` with `discount_rate`/`capital_recovery_period`/`interest_rate`, and mapping PyPSA `GlobalConstraints` (e.g. CO₂ limits) to `Requirements` (`CarbonCaps`/`CarbonTax`). -- **Counterpoint (kept honest):** a PyPSA-Eur network *is* natively a capacity-expansion model, so Investments is the faithful target if the goal is **expansion interoperability** rather than **dispatching a solved system**. If that is the intent, the target flips to Investments. Confirm the deliverable's intent (dispatch-in-PowerSimulations vs expansion-in-Sienna) before assuming Operations is sufficient. - ### System container SiennaSchemas defines per-component schemas but not how a whole system is packaged, so we define the container: a **JSON object mapping each Sienna type name to a list of that type's component objects**.