Skip to content

Add integer capacity extension#344

Closed
idelder wants to merge 17 commits into
TemoaProject:unstablefrom
idelder:extension/integer_capacity
Closed

Add integer capacity extension#344
idelder wants to merge 17 commits into
TemoaProject:unstablefrom
idelder:extension/integer_capacity

Conversation

@idelder

@idelder idelder commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

NOTE currently based on top of PR #343. This is also different from but compatible with a future unit commitment extension, except that unit commitment divides capacity within a technology into subunits (e.g., gas turbines are each divided into 100 MW sub units) whereas this applies integer capacity units at the technology or tech-group/region summation level, for things like chunky investment/retirement.

Integer Capacity

The integer_capacity extension adds optional constraints that force the
capacity of a technology or technology group to be deployed in discrete,
indivisible units rather than as a continuous quantity. This is useful for
modeling lumpy investments or retirements such as a power plant, reactor, or
other facility that can only exist in whole units of a fixed size.

Enabling the extension introduces integer decision variables, which turns the
model into a mixed-integer program (MIP). It is disabled by default and enabled
per run through configuration:

extensions = ["integer_capacity"]

Its parameters live in the extension-owned tables of the same name and are loaded
only when the extension is enabled. See :ref:extensions for how the extension
framework wires these components into the model.

Parameters

image image

Decision Variables

Enabling the extension adds two integer decision variables, one per constraint.
Each counts the number of discrete units, which is multiplied by the
corresponding unit size to recover the capacity quantity.

image

Constraints

image

Summary by CodeRabbit

  • New Features

    • Added support for optional model extensions, including new extension configuration and bundled extension docs.
    • Introduced two built-in extensions: growth rates and integer capacity.
    • Added a sample “template” extension scaffold and tutorial config support for enabling extensions.
  • Bug Fixes

    • Improved handling of existing capacity and myopic scenarios.
    • Strengthened tutorial and test database setup with schema validation and better consistency checks.
  • Documentation

    • Expanded documentation for extensions and updated related model/formulation references.

pre-commit-ci Bot and others added 17 commits June 26, 2026 14:27
updates:
- [github.com/astral-sh/uv-pre-commit: 0.11.19 → 0.11.21](astral-sh/uv-pre-commit@0.11.19...0.11.21)
- [github.com/astral-sh/ruff-pre-commit: v0.15.16 → v0.15.17](astral-sh/ruff-pre-commit@v0.15.16...v0.15.17)
updates:
- [github.com/astral-sh/uv-pre-commit: 0.11.21 → 0.11.23](astral-sh/uv-pre-commit@0.11.21...0.11.23)
- [github.com/astral-sh/ruff-pre-commit: v0.15.17 → v0.15.18](astral-sh/ruff-pre-commit@v0.15.17...v0.15.18)
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
…counting purposes

Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
…ly retirement, and growth rate constraints all together

Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
…capacities

Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
@idelder idelder marked this pull request as draft June 30, 2026 16:22
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dd17695e-1517-4238-b6f1-7126cdd7fe2c

📥 Commits

Reviewing files that changed from the base of the PR and between 8fb9aaf and bff1c69.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (71)
  • .pre-commit-config.yaml
  • docs/source/computational_implementation.rst
  • docs/source/extensions.rst
  • docs/source/extensions/growth_rates.rst
  • docs/source/extensions/integer_capacity.rst
  • docs/source/index.rst
  • docs/source/mathematical_formulation.rst
  • docs/source/param_desc_and_tables.rst
  • requirements-dev.txt
  • requirements.txt
  • temoa/_internal/run_actions.py
  • temoa/_internal/temoa_sequencer.py
  • temoa/cli.py
  • temoa/components/capacity.py
  • temoa/components/limits.py
  • temoa/components/technology.py
  • temoa/components/time.py
  • temoa/components/utils.py
  • temoa/core/config.py
  • temoa/core/model.py
  • temoa/data_io/component_manifest.py
  • temoa/data_io/hybrid_loader.py
  • temoa/data_io/loader_manifest.py
  • temoa/db_schema/temoa_schema_v4.sql
  • temoa/extensions/framework.py
  • temoa/extensions/growth_rates/__init__.py
  • temoa/extensions/growth_rates/components/__init__.py
  • temoa/extensions/growth_rates/components/growth_capacity.py
  • temoa/extensions/growth_rates/components/growth_new_capacity.py
  • temoa/extensions/growth_rates/components/growth_new_capacity_delta.py
  • temoa/extensions/growth_rates/core/__init__.py
  • temoa/extensions/growth_rates/core/model.py
  • temoa/extensions/growth_rates/data_manifest.py
  • temoa/extensions/growth_rates/extension.py
  • temoa/extensions/growth_rates/tables.sql
  • temoa/extensions/integer_capacity/__init__.py
  • temoa/extensions/integer_capacity/components/__init__.py
  • temoa/extensions/integer_capacity/components/integer_capacity.py
  • temoa/extensions/integer_capacity/core/__init__.py
  • temoa/extensions/integer_capacity/core/model.py
  • temoa/extensions/integer_capacity/data_manifest.py
  • temoa/extensions/integer_capacity/extension.py
  • temoa/extensions/integer_capacity/tables.sql
  • temoa/extensions/method_of_morris/morris.py
  • temoa/extensions/method_of_morris/morris_evaluate.py
  • temoa/extensions/modeling_to_generate_alternatives/mga_sequencer.py
  • temoa/extensions/myopic/myopic_sequencer.py
  • temoa/extensions/single_vector_mga/sv_mga_sequencer.py
  • temoa/extensions/stochastics/scenario_creator.py
  • temoa/extensions/template/__init__.py
  • temoa/extensions/template/components/__init__.py
  • temoa/extensions/template/components/example_limit.py
  • temoa/extensions/template/core/__init__.py
  • temoa/extensions/template/core/model.py
  • temoa/extensions/template/data_manifest.py
  • temoa/extensions/template/extension.py
  • temoa/extensions/template/tables.sql
  • temoa/tutorial_assets/config_sample.toml
  • temoa/tutorial_assets/utopia.sql
  • tests/conftest.py
  • tests/legacy_test_values.py
  • tests/test_cli.py
  • tests/test_extensions.py
  • tests/test_full_runs.py
  • tests/testing_configs/config_myopic_capacities.toml
  • tests/testing_data/mediumville.sql
  • tests/testing_data/mediumville_sets.json
  • tests/testing_data/myopic_capacities.sql
  • tests/testing_data/test_system_sets.json
  • tests/testing_data/utopia_sets.json
  • tests/testing_data/utopia_v3.sql

Walkthrough

Introduces a pluggable ExtensionSpec-based extension framework that lets optional modeling features register Pyomo components and data-loading manifests without modifying core code. Growth/degrowth capacity constraints are migrated from core into a growth_rates extension; an integer_capacity extension and unregistered template scaffold are added. Separately, myopic capacity retirement is fixed via a new get_adjusted_existing_capacity utility and retired_existing_capacity model parameter. Dependencies and pre-commit tooling are bumped.

Changes

Extension Framework and Bundled Extensions

Layer / File(s) Summary
ExtensionSpec dataclass and framework helpers
temoa/extensions/framework.py
Defines ExtensionSpec frozen dataclass and public helpers: normalize_extension_ids, resolve_extension_specs, apply_model_extension_hooks, append_extension_manifest_items, merge_regional_group_tables, SQLite guards ensure_enabled_extension_tables_exist / assert_disabled_extension_tables_are_empty, and internal _append_extension_schema.
Config, TemoaModel, sequencers, and callsites accept extensions
temoa/core/config.py, temoa/core/model.py, temoa/_internal/run_actions.py, temoa/_internal/temoa_sequencer.py, temoa/extensions/*/...sequencer*.py, temoa/extensions/stochastics/scenario_creator.py, temoa/tutorial_assets/config_sample.toml
TemoaConfig gains extensions parameter with eager spec resolution. TemoaModel.__init__ accepts extensions, stores extension_specs, applies hooks at construction end, and removes growth/degrowth parameter and constraint declarations. build_instance and all sequencer run methods forward extensions=self.config.extensions.
Extension-aware data loading: LoadItem.index_length, manifest, HybridLoader init
temoa/data_io/loader_manifest.py, temoa/data_io/component_manifest.py, temoa/data_io/hybrid_loader.py
LoadItem gains optional index_length. build_manifest accepts extension_ids and appends extension LoadItems. HybridLoader.__init__ resolves specs, builds extension-aware tables_with_regional_groups, validates extension tables, and constructs the manifest with extension IDs. create_data_dict and regional-global index loading use self.model instead of a fresh TemoaModel.
Remove growth/degrowth from core; add growth_rates extension
temoa/components/limits.py, temoa/core/model.py, temoa/db_schema/temoa_schema_v4.sql, docs/source/mathematical_formulation.rst, docs/source/param_desc_and_tables.rst, temoa/extensions/growth_rates/...
Deletes growth/degrowth index helpers, constraint rules, parameter declarations, and the six DB tables from core. Adds the growth_rates extension package: GrowthRatesModel subtype, component modules (growth_capacity, growth_new_capacity, growth_new_capacity_delta), data manifest, GROWTH_RATES_EXTENSION spec, and tables.sql.
integer_capacity extension: integer new/net capacity constraints
temoa/extensions/integer_capacity/...
Adds IntegerCapacityModel subtype, constraint index/rule functions for integer new and net capacity enforcement, register_model_components hook, build_manifest_items, INTEGER_CAPACITY_EXTENSION spec, and tables.sql defining limit_integer_new_capacity and limit_integer_net_capacity.
Template extension scaffold
temoa/extensions/template/...
Adds an intentionally unregistered template extension with ExampleModel subtype, example_new_capacity_limit constraint, build_manifest_items, EXAMPLE_EXTENSION spec, and tables.sql as a scaffold for new extension authors.
CLI tutorial DB: execute schema before data; add utopia_v3 fixture
temoa/cli.py, tests/testing_data/utopia_v3.sql, tests/test_cli.py
_copy_tutorial_resources now executes temoa_schema_v4.sql before utopia.sql in both resource paths and adds foreign-key integrity checks. utopia_v3.sql is a new complete v3 SQLite fixture. UTOPIA_SQL_FIXTURE in test_cli.py is updated to point to it.
Extension framework documentation
docs/source/extensions.rst, docs/source/extensions/growth_rates.rst, docs/source/extensions/integer_capacity.rst, docs/source/index.rst, docs/source/computational_implementation.rst
New extensions.rst covers ExtensionSpec reference, package layout, authoring guide, and verification checklist. New per-extension pages for growth_rates and integer_capacity. Added to index.rst Advanced Topics toctree and a seealso reference in computational_implementation.rst.
Extension test suite and test-set JSON updates
tests/test_extensions.py, tests/conftest.py, tests/testing_data/test_system_sets.json, tests/testing_data/utopia_sets.json, tests/testing_data/mediumville_sets.json
Adds test_extensions.py parameterized over all known extension ids, verifying spec consistency, schema file presence, and component attachment. conftest._build_test_db loads extension schema SQLs. Test-set JSON files add lifetime_tech_rt, remove growth/degrowth constraint entries, and update hashes for changed vintage/process sets.

Myopic Capacity Retirement Adjustments

Layer / File(s) Summary
Model parameter, vintage sets, period_length, and lifetime indices
temoa/components/time.py, temoa/components/utils.py, temoa/core/model.py
TemoaModel adds retired_existing_capacity Param. vintage_exist becomes unsorted, vintage_all becomes vintage_exist | time_optimize, period_length extends to time_optimize | time_exist. lifetime_tech uses a new 2D lifetime_tech_rt set; lifetime_survival_curve domain extends to tech_all | tech_exist. time.py adds param_period_length logic for the last time_exist period. utils.py adds get_adjusted_existing_capacity.
Capacity and technology constraints use adjusted existing capacity
temoa/components/capacity.py, temoa/components/technology.py
annual_retirement_constraint and adjusted_capacity_constraint replace model.existing_capacity[r,t,v] with get_adjusted_existing_capacity. model_process_life_indices and lifetime_process_indices are updated to union existing_capacity keys. check_existing_capacity uses adjusted_cap and logs a warning instead of raising ValueError.
HybridLoader loads retired capacity and filters lifetimes
temoa/data_io/component_manifest.py, temoa/data_io/hybrid_loader.py
Manifest adds LoadItem for retired_existing_capacity from output_retired_capacity. HybridLoader existing-capacity loaders preload vintage_exist and populate viable_existing_rt/rtv. _load_retired_existing_capacity loads only in myopic mode when the table exists. Lifetime loaders filter rows using the union of source-trace and existing viable sets.
Myopic stress tests and test data
tests/test_full_runs.py, tests/conftest.py, tests/testing_configs/config_myopic_capacities.toml, tests/testing_data/myopic_capacities.sql, tests/testing_data/mediumville.sql, tests/legacy_test_values.py
test_full_runs.py adds MyopicStressCase types and a parametrized stress test asserting SUM(total_system_cost) within tolerance. conftest.system_test_run merges per-case myopic overrides. New myopic_capacities.sql test dataset added. mediumville.sql removes unused 2020 existing period. legacy_test_values.py halves mediumville EFF_DOMAIN_SIZE to 1400.

Dependency and Tooling Updates

Layer / File(s) Summary
Pre-commit and pinned dependency version bumps
.pre-commit-config.yaml, requirements.txt, requirements-dev.txt
uv-pre-commit bumped to 0.11.23, ruff-pre-commit to v0.15.18. requirements.txt upgrades fonttools, matplotlib, numpy, platformdirs, pyomo, pytest, scipy, typer, wrapt. requirements-dev.txt additionally upgrades ast-serialize, coverage, distlib, filelock, idna, myst-parser, pandas-stubs, python-discovery, requests, ruff, snowballstemmer, soupsieve, types-deprecated, types-networkx, virtualenv.

Sequence Diagram

sequenceDiagram
  participant User
  participant TemoaConfig
  participant framework as extensions/framework.py
  participant HybridLoader
  participant TemoaModel
  participant SQLite

  User->>TemoaConfig: extensions=["growth_rates"]
  TemoaConfig->>framework: normalize_extension_ids(["growth_rates"])
  TemoaConfig->>framework: resolve_extension_specs(ids)
  framework-->>TemoaConfig: tuple[ExtensionSpec]

  User->>HybridLoader: __init__(config)
  HybridLoader->>framework: resolve_extension_specs(config.extensions)
  HybridLoader->>TemoaModel: TemoaModel(extensions=config.extensions)
  TemoaModel->>framework: apply_model_extension_hooks(self, specs)
  note over TemoaModel: growth_rates Params, Sets, Constraints attached
  HybridLoader->>framework: merge_regional_group_tables(base, specs)
  HybridLoader->>framework: ensure_enabled_extension_tables_exist(con, specs)
  framework->>SQLite: _table_exists / _append_extension_schema
  HybridLoader->>framework: assert_disabled_extension_tables_are_empty(con, all_specs)
  HybridLoader->>HybridLoader: build_manifest(model, extension_ids=config.extensions)
  framework-->>HybridLoader: manifest with extension LoadItems appended
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • TemoaProject/temoa#164: The extension-aware changes to hybrid_loader.py, component_manifest.py, and loader_manifest.py build directly on the declarative manifest-based HybridLoader refactor from this PR, extending the existing manifest pipeline with extension-driven LoadItems.
  • TemoaProject/temoa#190: This PR renames the same limit_growth_*/limit_degrowth_* identifiers and tables to snake_case in the same area that this PR migrates out of core and into the growth_rates extension.
  • TemoaProject/temoa#279: Both PRs modify myopic existing-capacity/lifecycle/retirement logic in temoa/components/capacity.py and temoa/components/technology.py.

Suggested labels

Feature, database-schema, refactor

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@idelder idelder closed this Jun 30, 2026
@idelder idelder reopened this Jun 30, 2026
@idelder idelder changed the base branch from main to unstable June 30, 2026 16:26
@idelder idelder closed this Jun 30, 2026
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.

1 participant