Skip to content

Split gems into gems_craft/gems_runner (alternative to #243)#244

Open
aoustry wants to merge 2 commits into
mainfrom
refacto/splitting_option_two
Open

Split gems into gems_craft/gems_runner (alternative to #243)#244
aoustry wants to merge 2 commits into
mainfrom
refacto/splitting_option_two

Conversation

@aoustry

@aoustry aoustry commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Process ID

Process: GP-01

Description

Alternative split of the monolithic gems package into gems_craft and gems_runner, proposed as a counter-option to #243.

Both PRs agree on the goal: separate the solver-independent parts of GEMS from solve-time execution. They disagree on where the line goes.

#243's boundary is parsing vs. everything else. gems_craft there contains only the Pydantic YAML-schema layer (*Schema classes). The resolved domain objects — Model, Library, System, Component, PortsConnection — and the entire expression/ package (AST, parsing, linearity/indexing analysis) all move to gems_runner, bundled with the solver.

This PR's boundary is solver-dependency, not parsing. gems_craft contains everything with no dependency on linopy/xarray/highspy:

  • The full domain model: model/ (Model, Library, Port*, Constraint, Parameter, Variable), study/ minus runner.py (System, Component, PortsConnection, Study, DataBase, resolve_components.py, folder.py, scenario_builder.py)
  • YAML schema/parsing (as in Feature/submodule gems craft #243)
  • optim_config/ (pure Pydantic + TYPE_CHECKING-only refs, no solver import)
  • The structural half of expression/ — AST, parsing, degree.py/is_linear, indexing.py, uses_sum_connections_on.py — everything except evaluate.py

gems_runner is left with only genuinely solve-time code: expression/evaluate.py (its dual()/reduced_cost()/variable() handlers are solver-output-shaped, not general-purpose math — the one deliberate exception), simulation/*, session/*, study/runner.py, and the gemspy CLI (main/).

Why this matters: a System/Component CRUD-and-query API (editing systems, filtering components by properties, walking peer components through port connections) needs the resolved domain model, not the raw parsed schema. Under #243's split, building that API on gems_craft would require depending on gems_runner — pulling in the solver stack just to read/edit a system. Under this split, gems_craft is a complete, self-contained base for that API.

Packaging: kept as a single distribution (one pyproject.toml), not a two-wheel monorepo — lower risk, no CI/tooling restructuring. linopy/xarray/highspy move to a new runner optional-dependency extra so pip install gemspy alone never pulls in the solver stack, verified by installing gems_craft in a clean venv with zero extras and importing it successfully.

Impact Analysis

  • expression/, model/, study/, optim_config/, simulation/, session/, main/ are all affected — file moves only, no logic changes. Every gems.* import across src/ and tests/ was mechanically rewritten to gems_craft.* / gems_runner.*.
  • gems_runner.expression.evaluate gained two absolute imports (gems_craft.expression.expression, .indexing, .visitor) in place of what were same-package relative imports in the monolith, since evaluate.py now lives in a different package than the rest of expression/.
  • pyproject.toml: dependencies trimmed to gems_craft's actual deps (numpy, pandas, PyYAML, pydantic, anytree, antlr4-python3-runtime); added [project.optional-dependencies].runner for linopy/xarray/highspy; gemspy script entry point repointed to gems_runner.main.main:main_cli; [tool.mypy] packages/overrides updated.
  • .github/workflows/ci.yml: install step now requests --extra runner; coverage flags updated from --cov gems to --cov gems_craft --cov gems_runner.
  • Solver output values: no change — this is a pure module-boundary refactor, no expression/model/simulation logic was touched.

Checklist

  • Unit tests pass (pytest) — 547 passed, 1 xfailed (full suite incl. --group solvers)
  • Type checking passes (mypy) — 60 source files, clean
  • Formatting passes (black, isort)
  • pyproject.toml version bumped if applicable — left alone per docs/agents/python-convention.md (version bumps are handled via the feat(release): commit workflow, not manual edits); changelog entry added under [Unreleased]
  • AGENTS.md reviewed for impact and updated if needed — architecture section rewritten for the two-package structure

Generated by Claude Code

claude added 2 commits July 3, 2026 16:09
Splits the monolithic gems package along a solver-dependency boundary
instead of a parsing-vs-everything boundary:

- gems_craft: the resolved domain model (Model, Library, System,
  Component, PortsConnection), YAML schema/parsing, and the structural
  half of the expression language (AST, parsing, linearity/indexing
  analysis). No solver dependency - installable and importable with
  only numpy, pandas, PyYAML, pydantic, anytree, and
  antlr4-python3-runtime.
- gems_runner: solve-time execution - expression evaluation
  (evaluate.py), session, simulation, study.runner, and the gemspy CLI.
  Depends on gems_craft plus a new `runner` extra (linopy, xarray,
  highspy).

This keeps gems_craft usable as a standalone base for editing,
validating, and querying systems (e.g. an API layer) without pulling
in the optimization solver stack, which the prior split (PR #243)
did not allow since it placed System/Component/Model and the whole
expression package under the solver-dependent side.

Verified: gems_craft imports cleanly in a clean venv with no runner
extra installed; mypy clean; full pytest suite passes unchanged
(541 passed, 6 skipped, 1 xfailed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C635KKMQurDjEBS3qXBqpA
CI was still installing deps without the new `runner` optional-dependency
extra, so mypy failed resolving gems_runner's linopy/xarray/highspy
imports. The pytest step also still referenced the old `gems` package
name for coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C635KKMQurDjEBS3qXBqpA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants