Process GP-02 — Triggered by internal initiative to fix, improve, or extend GemsPy. Not driven by a new GEMS Language version.
Deviation from standard workflow — Step 3: extended impact analysis on results is required. You must explicitly state whether solver results are expected to change, and if so, document the reasoning.
Affected Component
Decomposition / optim-config (optim_config/)
Type of Change
Patch: dependency update, code optimisation, or internal refactor with no syntax impact
Description
While auditing docs/user-guide/optim-config.md against the actual Pydantic schema in src/gems_craft/optim_config/parsing.py, I found two places where the documented format doesn't match what load_optim_config() actually accepts. Both were confirmed by loading real YAML through load_optim_config().
1. solver-options.name is documented as a closed enum, but it's a free string.
The docs table (optim-config.md, "solver-options" section) reads:
name | str | "highs" | Solver name: "highs" (default), "xpress", or "gurobi"
SolverOptionsConfig.name (parsing.py:112) is a plain str field with no validator restricting its value. It's forwarded verbatim to linopy's solve(solver_name=...) (gems_runner/simulation/optimization.py:347), which supports additional solvers (e.g. cbc, glpk, scip, mosek, copt...). Confirmed:
load_optim_config(path_with("solver-options:\n name: cbc\n"))
# -> config.solver_options.name == "cbc", no error
The doc should either state that name accepts any solver name supported by the installed linopy backend (with highs/xpress/gurobi as the officially supported/tested ones), or explicitly note that GemsPy itself does not validate this value — validation/failure happens at solve time in the solver backend.
2. The "(default)" annotations for mode and location are misleading — both fields are actually required once an entry is listed.
- In "
out-of-bounds-processing" the table shows: `cyclic` (default) | Wrap around...
- In "
model-decomposition" the table shows: `subproblems` (default) | Element lives in each operational subproblem
Both read as if you can omit the mode / location key on a listed entry and get the default. In reality:
models:
- id: storage
out-of-bounds-processing:
constraints:
- id: soc_balance # no `mode` key
raises pydantic.ValidationError: models.0.out-of-bounds-processing.constraints.0.mode — Field required, because OutOfBoundsConstraintConfig.mode (parsing.py:70) has no default. Same for ElementLocationConfig.location (parsing.py:54) — omitting location on a listed variable/constraint/objective-contribution also raises Field required.
The actual default only applies to elements that are not listed at all in the constraints / variables / constraints / objective-contributions arrays (per the OutOfBoundsFilter docstring in gems_runner/simulation/optimization.py:141-147: "Constraints not listed in the config default to cyclic wrap-around"). The docs should clarify that mode/location are mandatory for any entry that is listed, and that "default" refers only to entirely-omitted elements.
Results Impact
No — this is a documentation-only change. No parsing code, defaults, or solver behaviour are modified; solver output values are unaffected. Confirmed by re-running the existing optim-config unit test suite unchanged.
Validation Strategy
- Existing unit tests in
tests/unittests/gems_craft/optim_config/ already cover the actual (correct) required-field and free-string behaviour described above — the fix only touches prose/tables in docs/user-guide/optim-config.md, so no new tests are needed.
- Manual repro used to confirm both discrepancies (reproducible against
main @ 7fbc3f41):
from pathlib import Path
from gems_craft.optim_config import load_optim_config
# 1) arbitrary solver name accepted
load_optim_config(<yaml with solver-options.name: cbc>) # succeeds
# 2) missing `mode` / `location` on a listed entry raises
load_optim_config(<yaml with out-of-bounds constraint missing `mode`>) # ValidationError: Field required
load_optim_config(<yaml with model-decomposition variable missing `location`>) # ValidationError: Field required
- No E2E/reference-study validation needed since no solver-facing code changes.
Process Checklist
Step 1 — Issue Creation
Step 2 — Triage
Step 3 — Impact Analysis ⚠️ extended results analysis required
Step 4 — Implementation
Step 5 — Testing & Validation
Step 6 — CI Validation
Step 7 — Review & Merge
Step 8 — Versioning
Step 9 — Supporting Files
Step 10 — Release
Process GP-02 — Triggered by internal initiative to fix, improve, or extend GemsPy. Not driven by a new GEMS Language version.
Affected Component
Decomposition / optim-config (optim_config/)
Type of Change
Patch: dependency update, code optimisation, or internal refactor with no syntax impact
Description
While auditing docs/user-guide/optim-config.md against the actual Pydantic schema in src/gems_craft/optim_config/parsing.py, I found two places where the documented format doesn't match what
load_optim_config()actually accepts. Both were confirmed by loading real YAML throughload_optim_config().1.
solver-options.nameis documented as a closed enum, but it's a free string.The docs table (optim-config.md, "
solver-options" section) reads:SolverOptionsConfig.name(parsing.py:112) is a plainstrfield with no validator restricting its value. It's forwarded verbatim tolinopy'ssolve(solver_name=...)(gems_runner/simulation/optimization.py:347), which supports additional solvers (e.g.cbc,glpk,scip,mosek,copt...). Confirmed:The doc should either state that
nameaccepts any solver name supported by the installed linopy backend (withhighs/xpress/gurobias the officially supported/tested ones), or explicitly note that GemsPy itself does not validate this value — validation/failure happens at solve time in the solver backend.2. The "(default)" annotations for
modeandlocationare misleading — both fields are actually required once an entry is listed.out-of-bounds-processing" the table shows:`cyclic` (default) | Wrap around...model-decomposition" the table shows:`subproblems` (default) | Element lives in each operational subproblemBoth read as if you can omit the
mode/locationkey on a listed entry and get the default. In reality:raises
pydantic.ValidationError: models.0.out-of-bounds-processing.constraints.0.mode — Field required, becauseOutOfBoundsConstraintConfig.mode(parsing.py:70) has no default. Same forElementLocationConfig.location(parsing.py:54) — omittinglocationon a listed variable/constraint/objective-contribution also raisesField required.The actual default only applies to elements that are not listed at all in the
constraints/variables/constraints/objective-contributionsarrays (per theOutOfBoundsFilterdocstring in gems_runner/simulation/optimization.py:141-147: "Constraints not listed in the config default to cyclic wrap-around"). The docs should clarify thatmode/locationare mandatory for any entry that is listed, and that "default" refers only to entirely-omitted elements.Results Impact
No — this is a documentation-only change. No parsing code, defaults, or solver behaviour are modified; solver output values are unaffected. Confirmed by re-running the existing optim-config unit test suite unchanged.
Validation Strategy
tests/unittests/gems_craft/optim_config/already cover the actual (correct) required-field and free-string behaviour described above — the fix only touches prose/tables indocs/user-guide/optim-config.md, so no new tests are needed.main@7fbc3f41):Process Checklist
Step 1 — Issue Creation
Step 2 — Triage
Step 3 — Impact Analysis⚠️ extended results analysis required
Step 4 — Implementation
Step 5 — Testing & Validation
Step 6 — CI Validation
mypy)black,isort)pytest)Step 7 — Review & Merge
Step 8 — Versioning
pyproject.tomlversion bumpedStep 9 — Supporting Files
AGENTS.mdreviewed for impact and updated if neededStep 10 — Release