Skip to content

Add economies of scale extension#349

Open
idelder wants to merge 28 commits into
TemoaProject:unstablefrom
idelder:extension/economies_of_scale
Open

Add economies of scale extension#349
idelder wants to merge 28 commits into
TemoaProject:unstablefrom
idelder:extension/economies_of_scale

Conversation

@idelder

@idelder idelder commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

NOTE currently based on top of PR #345

Add eos extension — piecewise-linear economies-of-scale cost curves

Documentation

Economies of Scale (EOS).pdf

Summary

This PR introduces the Economies of Scale (EOS) extension, which replaces or supplements flat per-unit costs with piecewise-linear cost curves driven by deployment or usage quantities. Three cost types are supported:

Feature Table Cost type Curve driven by
cost_invest_eos cost_invest_eos Investment Cumulative installed capacity (learning-curve effect)
cost_fixed_eos cost_fixed_eos Fixed O&M Available capacity per period
cost_variable_eos cost_variable_eos Variable O&M Total activity (output flow) per period

The extension is disabled by default and enabled per run with extensions = ["eos"] in the config.

Formulation

All three features share a common binary-integer piecewise-linear formulation. For each technology cluster (r, t) (or (r, p, t) for period-specific curves):

  • The curve is divided into contiguous segments, each defined by quantity bounds and corresponding cost bounds.
  • A binary variable selects exactly one active segment per cluster per period (enforced by an equality constraint on the sum of binaries).
  • Big-M-style lower and upper bound constraints pin the quantity variable (capacity or activity) within the active segment's range.
  • A linear interpolation within the active segment gives the total cost.

cost_invest_eos differs from the other two in that its curve is global across time: each period inherits all previously installed capacity. The period cost is the incremental cumulative cost — the curve evaluated at end-of-period cumulative capacity minus the curve evaluated at the previous period's cumulative capacity (or at pre-existing capacity in the first period). The incremental cost is then discounted and amortised using the same loan_cost/loan_cost_survival_curve functions as the base cost_invest.

cost_fixed_eos and cost_variable_eos are period-specific: the curve parameters can change from period to period. Their costs are discounted and amortised using the same fixed_or_variable_cost function as the base model.

Files changed

New source files:

  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py — investment learning-curve component
  • temoa/extensions/economies_of_scale/components/cost_variable_eos.py — variable O&M activity-curve component

Modified source files:

  • temoa/extensions/economies_of_scale/components/cost_fixed_eos.py — docstrings updated to EOS terminology
  • temoa/extensions/economies_of_scale/core/model.py — type hints and component registration for all three features; new register_early_eos_components function for early-stage set/param construction
  • temoa/extensions/economies_of_scale/data_manifest.pyLoadItem for cost_invest_eos
  • temoa/extensions/economies_of_scale/extension.pyowned_tables and regional_group_tables updated
  • temoa/extensions/economies_of_scale/tables.sqlcost_invest_eos table schema added
  • temoa/core/model.py — calls register_early_eos_components during model construction

Documentation:

  • docs/source/extensions/eos.rst — comprehensive new reference page covering all three features: concept, table schema, sets, variables, constraints (via autofunction), and objective contribution
  • docs/source/extensions.rst — toctree updated (replaces etl entry with eos)
  • docs/source/images/eos_cost_curve.png / .svg — new figure showing the piecewise-linear cost curve with labelled segment bounds, active-segment shading, and quantity/cost axis ticks
  • scripts/generate_etl_figure.py — rewritten as the EOS figure generator

Notes

  • Technologies can be assigned to groups via tech_or_group: all members share the same curve and their contributions to the driving quantity are summed.
  • EOS invest processes may carry an additional flat cost_invest in the base tables alongside the piecewise curve.
  • Pre-existing capacity (from existing_capacity) is included in the cumulative total for cost_invest_eos but does not incur an investment charge — it is subtracted out when computing the incremental period cost.
  • All technologies in a cost_invest_eos cluster must share the same lifetime_process, loan_lifetime_process, and loan_rate in every period they appear together; the extension validates this at model build time.

Summary by CodeRabbit

  • New Features

    • Added optional extension support for discrete capacity, growth rates, and economies of scale (EOS).
    • EOS enables piecewise-linear cost-curve modeling; growth-rate limits and discrete-capacity constraints can be enabled via configuration.
    • Database schema v4 updated with a storage level fraction table.
  • Documentation

    • Expanded the Extensions guide, including extension lifecycle and authoring steps, plus new pages for EOS, growth rates, and discrete capacity.
  • Bug Fixes

    • Improved existing-capacity adjustments, loan-cost polling, and cost-legend accumulation; strengthened CLI database foreign-key validation.
  • Tests

    • Added coverage for extension loading and added myopic stress/capacity validations.

pre-commit-ci Bot and others added 25 commits July 6, 2026 11:30
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>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
…P models

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>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: de1117db-040d-4785-be85-949dec1b3e35

📥 Commits

Reviewing files that changed from the base of the PR and between 9bc80fd and 35e928b.

📒 Files selected for processing (14)
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/get_comm_tech.py
  • temoa/extensions/method_of_morris/morris_evaluate.py
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/modeling_to_generate_alternatives/tech_activity_vector_manager.py
  • temoa/extensions/modeling_to_generate_alternatives/vector_manager.py
  • temoa/extensions/modeling_to_generate_alternatives/worker.py
  • temoa/extensions/monte_carlo/mc_worker.py
  • temoa/extensions/single_vector_mga/output_summary.py
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/template/core/model.py
  • tests/test_full_runs.py
  • tests/testing_configs/config_myopic_capacities.toml
  • tests/testing_data/myopic_capacities.sql
💤 Files with no reviewable changes (3)
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py

Walkthrough

This PR adds an optional extension framework, moves growth-rate constraints into a new extension, adds discrete-capacity and economies-of-scale extensions, and updates model loading, docs, tests, and myopic database handling. It also refreshes schema generation, fixture data, and several behavior-preserving formatting changes.

Changes

Extension Framework and Feature Extensions

Layer / File(s) Summary
Framework, config, and model wiring
temoa/extensions/framework.py, temoa/core/config.py, temoa/core/model.py, temoa/_internal/run_actions.py, temoa/_internal/temoa_sequencer.py, temoa/extensions/method_of_morris/..., temoa/extensions/modeling_to_generate_alternatives/..., temoa/extensions/myopic/..., temoa/extensions/single_vector_mga/..., temoa/extensions/stochastics/..., temoa/extensions/get_comm_tech.py, docs/source/extensions.rst, docs/source/computational_implementation.rst, docs/source/index.rst, temoa/tutorial_assets/config_sample.toml, scripts/generate_etl_figure.py, .pre-commit-config.yaml, pyproject.toml
Adds extension normalization, spec resolution, hook application, and extension propagation through model construction; documents the framework; refreshes lint hooks; and adds the EOS figure generator.
Extension-aware manifests and loader
temoa/data_io/component_manifest.py, temoa/data_io/hybrid_loader.py, temoa/data_io/loader_manifest.py, tests/test_extensions.py, tests/conftest.py
Extends manifest metadata, appends extension LoadItems, loads extension-owned schema SQL, reshapes indexed rows, and derives sparse sets from manifest metadata.
Template extension scaffold
temoa/extensions/template/*
Adds the inert template extension package with example constraint wiring, manifest, SQL schema, and extension spec.
Discrete-capacity extension
temoa/extensions/discrete_capacity/*, docs/source/extensions/discrete_capacity.rst
Adds the discrete-capacity extension components, manifest, SQL schema, package exports, and documentation.
Growth-rates extension and core removal
temoa/extensions/growth_rates/*, 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, docs/source/extensions/growth_rates.rst, tests/testing_data/*_sets.json
Moves growth/degrowth capacity limits out of core into the growth-rates extension, adds extension-specific tables, model wiring, manifest entries, docs, and test-set hashes, and removes the core limit declarations and schema/docs references.
Economies-of-scale extension and loan-cost refactor
temoa/extensions/economies_of_scale/*, temoa/components/costs.py, temoa/_internal/table_data_puller.py, temoa/_internal/exchange_tech_cost_ledger.py, temoa/__init__.py, docs/source/extensions/eos.rst, tests/test_table_writer.py
Adds the EOS extension with piecewise-linear cost curves, widens cost helpers to accept expressions, adds loan-cost polling helpers, and wires EOS polling into cost aggregation.

Myopic Capacity, Schema, and Fixture Updates

Layer / File(s) Summary
Adjusted existing capacity and core consumers
temoa/components/utils.py, temoa/components/capacity.py, temoa/components/technology.py, temoa/components/time.py, temoa/core/model.py
Adds the adjusted-existing-capacity helper and uses it in capacity and technology logic; updates time and lifetime indexing for retired-capacity handling.
Retired-capacity manifest and loader updates
temoa/data_io/component_manifest.py, temoa/data_io/hybrid_loader.py, temoa/data_io/loader_manifest.py
Adds retired-capacity loading, custom lifetime loaders, extension-aware table metadata, and manifest-driven sparse-set derivation.
Schema-first database generation and solver suffix handling
temoa/db_schema/temoa_schema_v4.sql, temoa/cli.py, temoa/_internal/run_actions.py
Updates the canonical SQLite schema and tutorial-db generation flow, and adjusts appsi_highs dual-suffix handling in solve_instance.
Myopic and full-run test fixtures
tests/conftest.py, tests/test_full_runs.py, tests/testing_configs/config_myopic_capacities.toml, tests/testing_data/myopic_capacities.sql, tests/testing_data/mediumville.sql, tests/testing_data/utopia_v3.sql, tests/test_cli.py, tests/legacy_test_values.py
Adds myopic stress-run scenarios, a new myopic test config/database, updates fixture SQL and set hashes, swaps the CLI Utopia fixture, and adjusts legacy expected values.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: Feature, database-schema, docs, refactor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the economies of scale extension.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
temoa/db_schema/temoa_schema_v4.sql (1)

926-937: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant vintage foreign key definition.

The vintage column has an inline FK at line 926 (REFERENCES time_period (period)) and a duplicate table-level FK at line 937 (FOREIGN KEY (vintage) REFERENCES time_period (period)). Since this line was already modified to remove the tech FK, this is a good opportunity to drop the redundant table-level vintage FK as well.

♻️ Proposed cleanup
     PRIMARY KEY (scenario, region, period, tech, vintage),
-    FOREIGN KEY (vintage) REFERENCES time_period (period)
 );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@temoa/db_schema/temoa_schema_v4.sql` around lines 926 - 937, The schema still
defines `vintage` twice as a foreign key in `temoa_schema_v4.sql`, once inline
on the `vintage` column and again in the table constraint. Remove the redundant
table-level `FOREIGN KEY (vintage) REFERENCES time_period (period)` from the
same CREATE TABLE block that includes `PRIMARY KEY (scenario, region, period,
tech, vintage)`, while keeping the inline `vintage INTEGER REFERENCES
time_period (period)` definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/source/extensions/discrete_capacity.rst`:
- Around line 47-56: Update the `limit_discrete_capacity` documentation to
explicitly warn that it constrains total retirement-adjusted available capacity,
including existing fleet capacity, not just new builds. Add a brief caveat near
the `limit_discrete_capacity` description stating that the unit size must evenly
divide any pre-existing capacity for the affected technology/group, otherwise
the model may become infeasible. Refer to the `limit_discrete_capacity` and
`limit_discrete_new_capacity` sections to clearly distinguish the two behaviors.

In `@scripts/generate_etl_figure.py`:
- Around line 112-117: The mypy failures come from `dash_kw` in
`generate_etl_figure.py` being inferred as `dict[str, object]`, which breaks the
`ax.plot()` `**kwargs` calls inside the `segments` loop. Update the `dash_kw`
definition to use an explicit `dict[str, Any]` annotation so the keyword
arguments match `matplotlib`’s overloaded `ax.plot` signature and the four plot
calls in that loop type-check cleanly.

In `@temoa/_internal/run_actions.py`:
- Around line 247-250: The dual suffix handling in the solver branch needs to be
made safe and use the public API. In the `run_actions` logic where `solver_name
== 'appsi_highs'`, guard the result of `instance.component('dual')` before using
it since it may be `None`, and update the suffix direction via the public
`direction` property on `dual_suffix` instead of assigning to the private
`_direction` field.

In `@temoa/cli.py`:
- Around line 587-601: Extract the duplicated database निर्माण flow in
temoa/cli.py into a shared helper such as
_build_tutorial_database(target_database, schema_sql, tutorial_sql), and move
the common sequence from the current production path and fallback path there:
connect, execute schema, temporarily disable foreign keys, load tutorial SQL,
re-enable foreign keys, run PRAGMA foreign_key_check, and commit. Update both
callers to only read their respective SQL sources and delegate to the helper,
keeping the fallback IntegrityError behavior with from None unchanged.

In `@temoa/components/technology.py`:
- Around line 451-459: In the existing-capacity handling branch in
technology-related processing, replace the warning-only path with a hard
validation failure when surviving_cap is positive and (r, t, v) is not in
model.process_periods. Keep any myopic tolerance behavior only behind an
explicit mode/threshold check, and use the same logger context around the
lifetime_survival_curve and process_periods check to raise a ValueError instead
of continuing.

In `@temoa/components/utils.py`:
- Around line 105-109: The capacity_adjustment calculation in the helper
currently sums retired_existing_capacity for every model.time_exist period,
which can include periods at or before the vintage and lead to incorrect
capacity subtraction. Update the logic in the capacity_adjustment expression to
only include retired_existing_capacity entries for post-vintage periods,
matching the same vintage check used by adjusted_capacity_constraint and the
capacity formula, and keep the lifetime_survival_curve denominator handling
unchanged.

In `@temoa/core/model.py`:
- Around line 42-45: The core model is directly importing and calling the
EOS-specific early registration hook, which creates an unnecessary hard
dependency on a single extension. Remove the `register_early_eos_components`
import from `temoa.core.model` and move early registration behind the generic
extension framework by adding an optional early hook (for example on
`ExtensionSpec`) and dispatching it from `apply_model_extension_hooks` or a new
`apply_early_model_extension_hooks`. Update the core model call site to iterate
enabled extensions and invoke the generic early hook only when present, so the
EOS logic is loaded only if `self.enabled_extensions` includes it.

In `@temoa/data_io/hybrid_loader.py`:
- Around line 697-764: The custom loaders for lifetime tables are querying
optional tables directly, which can still raise OperationalError on older
schemas. Update _load_lifetime_tech, _load_lifetime_process, and
_load_lifetime_survival_curve in HybridLoader to follow the same optional-table
guard used elsewhere before running the SELECT. If the table is absent, skip
loading instead of re-querying it, while keeping the existing filtering logic
for viable_rt, viable_rtv, and myopic_index.

In `@temoa/extensions/economies_of_scale/components/cost_fixed_eos.py`:
- Around line 37-60: The fixed-cost EOS validation in cost_fixed_eos.py needs an
extra guard that the first segment starts at zero. Update the segment checks in
the loop over model.cost_fixed_eos_segments (using the cap_lower/cap_upper and
cost_lower/cost_upper values from model.cost_fixed_eos[r, p, t, n]) so the
earliest segment for each (r, p, t) requires cap_lower == 0 and cost_lower == 0
before proceeding. Keep the existing nonnegative and increasing-bound checks,
and raise the same ValueError/log path when the first segment violates this
zero-origin requirement.

In `@temoa/extensions/economies_of_scale/components/cost_invest_eos.py`:
- Around line 73-96: The validation in cost_invest_eos should also ensure the
first investment segment starts at zero, since a positive cap_lower or
cost_lower on the initial segment makes zero deployment infeasible and
pre-charges cost; update the guard in the loop over
model.cost_invest_eos_segments to special-case the first sorted segment for each
(r, t) and require its lower capacity and cost bounds to be zero, while keeping
the existing nonnegative and increasing checks for all segments.

In `@temoa/extensions/economies_of_scale/components/cost_variable_eos.py`:
- Around line 37-60: The validation in cost_variable_eos.py only checks for
nonnegative, increasing segment bounds, but it must also ensure the first
segment starts at zero. Update the guard in the loop over
model.cost_variable_eos_segments (and the cost_variable_eos table lookup) so the
segment with the lowest index for each r, p, t requires both activity_lower and
cost_lower to be 0. Keep the existing monotonicity checks for the other bounds,
and raise the same ValueError with a clear message if the first segment is not
anchored at zero.

In `@temoa/extensions/economies_of_scale/core/data_puller.py`:
- Around line 101-107: The EOS cost accumulation in the data_puller logic
assumes entries[key] already exists and that .update() can safely add EOS
values, which can fail for replacement cases and overwrite existing flat costs.
Update the accumulation flow in the affected branches of the EOS cost handling
code so it safely initializes or fetches the per-key entry before adding to
CostType.D_INVEST and CostType.INVEST, and merge EOS costs without replacing any
existing base fixed/variable cost fields. Apply the same fix consistently in the
repeated accumulation paths in the data_puller module.
- Around line 149-150: The EOS variable-cost extraction in data_puller.py is
iterating the fixed cluster set instead of the variable period set, so update
the loop in the period-cost collection logic to use the variable EOS rows source
and keep cost_variable_eos.period_cost() aligned with variable-only clusters.
Locate the iteration around the cost extraction that currently walks
model.cost_fixed_eos_period_rpt and switch it to the corresponding variable EOS
period set so fixed-only clusters are not passed through and variable-only costs
are included in the output.

In `@temoa/extensions/economies_of_scale/core/model.py`:
- Around line 142-170: The fixed EOS cost component is defined and constrained
in model setup, but its objective contribution is never registered with the
model. Update the setup around initialize_cost_fixed_eos and
cost_fixed_eos.total_cost() so the total fixed EOS cost is attached as a
BuildAction and incorporated into model.total_cost, using the existing
cost_fixed_eos module and the model initialization block to place the
registration alongside the other EOS component hooks.

In `@temoa/extensions/framework.py`:
- Around line 119-141: The guard in assert_disabled_extension_tables_are_empty
currently only logs a warning when disabled extension-owned tables contain rows,
but it should fail fast instead. Update the populated-table branch in
assert_disabled_extension_tables_are_empty to raise an exception rather than
calling logger.warning, keeping the existing message content so callers cannot
proceed with disabled extension data silently ignored.

In `@temoa/extensions/growth_rates/components/growth_new_capacity_delta.py`:
- Around line 78-88: Update the docstring in growth_new_capacity_delta to use
the correct Theta set names: replace the growth and degrowth references from
limit_growth_capacityDelta and limit_degrowth_capacityDelta to
limit_growth_new_capacity_delta and limit_degrowth_new_capacity_delta. Keep the
terminology consistent with the surrounding function, parameter, and constraint
names in growth_new_capacity_delta and the model registration so the documented
math matches the implemented identifiers.

In `@temoa/extensions/growth_rates/components/growth_new_capacity.py`:
- Around line 76-84: The docstring in growth_new_capacity should use the correct
Theta set names for this module. Update the two copied references from
growth_capacity.py so they match the growth_new_capacity and
degrowth_new_capacity parameter sets used by the corresponding constraint logic,
keeping the rest of the math text unchanged.

In `@temoa/extensions/method_of_morris/morris.py`:
- Line 50: The in-function import of contextlib is redundant because contextlib
is already imported at the module level. Remove the extra import from the
function that contains it in morris.py, keeping only the module-level import so
the function body stays clean and consistent with the rest of the file.

In `@temoa/extensions/monte_carlo/mc_sequencer.py`:
- Around line 153-156: Remove the commented-out kwargs dead code in MCSequencer:
the unused commented dictionary block in mc_sequencer.py should be deleted since
MCWorker already takes solver_name and solver_options directly in its
constructor. Update the surrounding code near the MCSequencer logic to keep only
the active argument passing and eliminate the stale commented lines.

In `@temoa/tutorial_assets/config_sample.toml`:
- Around line 21-25: The sample in config_sample.toml uses an extension ID that
is not registered, so the example can trigger an unknown-extension config error.
Update the example extension list to use only IDs returned by
get_known_extension_specs(), and adjust the surrounding comment/example text so
it only shows registered extension names for the extensions setting.

---

Outside diff comments:
In `@temoa/db_schema/temoa_schema_v4.sql`:
- Around line 926-937: The schema still defines `vintage` twice as a foreign key
in `temoa_schema_v4.sql`, once inline on the `vintage` column and again in the
table constraint. Remove the redundant table-level `FOREIGN KEY (vintage)
REFERENCES time_period (period)` from the same CREATE TABLE block that includes
`PRIMARY KEY (scenario, region, period, tech, vintage)`, while keeping the
inline `vintage INTEGER REFERENCES time_period (period)` definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5e28e099-879c-4bc2-b2ef-e8e8e93e2589

📥 Commits

Reviewing files that changed from the base of the PR and between 2bde478 and 09e9003.

⛔ Files ignored due to path filters (4)
  • docs/source/images/eos_cost_curve.png is excluded by !**/*.png
  • docs/source/images/eos_cost_curve.svg is excluded by !**/*.svg
  • docs/source/images/etl_cost_curve.png is excluded by !**/*.png
  • docs/source/images/etl_cost_curve.svg is excluded by !**/*.svg
📒 Files selected for processing (105)
  • .pre-commit-config.yaml
  • docs/source/computational_implementation.rst
  • docs/source/extensions.rst
  • docs/source/extensions/discrete_capacity.rst
  • docs/source/extensions/eos.rst
  • docs/source/extensions/growth_rates.rst
  • docs/source/index.rst
  • docs/source/mathematical_formulation.rst
  • docs/source/param_desc_and_tables.rst
  • pyproject.toml
  • scripts/generate_etl_figure.py
  • temoa/__init__.py
  • temoa/_internal/exchange_tech_cost_ledger.py
  • temoa/_internal/run_actions.py
  • temoa/_internal/table_data_puller.py
  • temoa/_internal/temoa_sequencer.py
  • temoa/cli.py
  • temoa/components/capacity.py
  • temoa/components/costs.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/discrete_capacity/__init__.py
  • temoa/extensions/discrete_capacity/components/__init__.py
  • temoa/extensions/discrete_capacity/components/discrete_capacity.py
  • temoa/extensions/discrete_capacity/core/__init__.py
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/discrete_capacity/data_manifest.py
  • temoa/extensions/discrete_capacity/extension.py
  • temoa/extensions/discrete_capacity/tables.sql
  • temoa/extensions/economies_of_scale/__init__.py
  • temoa/extensions/economies_of_scale/components/__init__.py
  • temoa/extensions/economies_of_scale/components/cost_fixed_eos.py
  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py
  • temoa/extensions/economies_of_scale/components/cost_variable_eos.py
  • temoa/extensions/economies_of_scale/core/__init__.py
  • temoa/extensions/economies_of_scale/core/data_puller.py
  • temoa/extensions/economies_of_scale/core/model.py
  • temoa/extensions/economies_of_scale/data_manifest.py
  • temoa/extensions/economies_of_scale/extension.py
  • temoa/extensions/economies_of_scale/tables.sql
  • temoa/extensions/framework.py
  • temoa/extensions/get_comm_tech.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/method_of_morris/morris.py
  • temoa/extensions/method_of_morris/morris_evaluate.py
  • temoa/extensions/method_of_morris/morris_sequencer.py
  • temoa/extensions/modeling_to_generate_alternatives/hull.py
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/modeling_to_generate_alternatives/mga_sequencer.py
  • temoa/extensions/modeling_to_generate_alternatives/tech_activity_vector_manager.py
  • temoa/extensions/modeling_to_generate_alternatives/vector_manager.py
  • temoa/extensions/modeling_to_generate_alternatives/worker.py
  • temoa/extensions/monte_carlo/example_builds/scenario_analyzer.py
  • temoa/extensions/monte_carlo/example_builds/scenario_maker.py
  • temoa/extensions/monte_carlo/mc_run.py
  • temoa/extensions/monte_carlo/mc_sequencer.py
  • temoa/extensions/monte_carlo/mc_worker.py
  • temoa/extensions/myopic/evolution_updater.py
  • temoa/extensions/myopic/myopic_progress_mapper.py
  • temoa/extensions/myopic/myopic_sequencer.py
  • temoa/extensions/single_vector_mga/output_summary.py
  • temoa/extensions/single_vector_mga/sv_mga_sequencer.py
  • temoa/extensions/stochastics/scenario_creator.py
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/stochastics/stochastic_sequencer.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/test_table_writer.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
💤 Files with no reviewable changes (5)
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • tests/testing_data/mediumville.sql
  • docs/source/param_desc_and_tables.rst
  • docs/source/mathematical_formulation.rst
  • temoa/extensions/stochastics/stochastic_config.py

Comment thread docs/source/extensions/discrete_capacity.rst
Comment thread scripts/generate_etl_figure.py Outdated
Comment thread temoa/_internal/run_actions.py Outdated
Comment thread temoa/cli.py
Comment thread temoa/components/technology.py
Comment thread temoa/extensions/growth_rates/components/growth_new_capacity.py
Comment thread temoa/extensions/method_of_morris/morris.py Outdated
Comment thread temoa/extensions/monte_carlo/mc_sequencer.py
Comment thread temoa/tutorial_assets/config_sample.toml
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
@idelder idelder force-pushed the extension/economies_of_scale branch from 09e9003 to 7afdefd Compare July 8, 2026 21:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
temoa/extensions/method_of_morris/morris_evaluate.py (1)

85-87: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

if not status: never triggers — status is a tuple (always truthy).

run_actions.check_solve_status() returns tuple[bool, str]. A non-empty tuple is always truthy in Python, so if not status: is always False — the RuntimeError is never raised, even for non-optimal solves. This is pre-existing, but now more relevant since extensions=config.extensions (line 80) activates the EOS extension in Method of Morris runs, adding solver complexity that could produce non-optimal terminations.

Proposed fix
-    status = run_actions.check_solve_status(res)
-    if not status:
-        raise RuntimeError('Bad solve during Method of Morris')
+    ok, msg = run_actions.check_solve_status(res)
+    if not ok:
+        raise RuntimeError(f'Bad solve during Method of Morris: {msg}')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@temoa/extensions/method_of_morris/morris_evaluate.py` around lines 85 - 87,
The Method of Morris solve check in morris_evaluate.py is testing the whole
return value from run_actions.check_solve_status(), but that function returns a
tuple[bool, str] so the condition never fails. Update the status handling around
check_solve_status and the subsequent RuntimeError in the Method of Morris
evaluation flow to inspect the boolean element explicitly and raise when the
solve is not acceptable, using the existing res/status variables in
morris_evaluate rather than the tuple object itself.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_full_runs.py`:
- Around line 184-192: The assertions in the `output_cost` queries are using the
raw cost fields instead of the discounted ones, so update the SQL in this test
to read from the `d_*` columns. In the `test_full_runs` checks for
`tech_discrete_cap` and `tech_discrete_new_cap`, replace the `fixed`, `var`, and
`invest` sums with `d_fixed`, `d_var`, and `d_invest` so the values match the
“discounted” wording used in the test expectations.

---

Outside diff comments:
In `@temoa/extensions/method_of_morris/morris_evaluate.py`:
- Around line 85-87: The Method of Morris solve check in morris_evaluate.py is
testing the whole return value from run_actions.check_solve_status(), but that
function returns a tuple[bool, str] so the condition never fails. Update the
status handling around check_solve_status and the subsequent RuntimeError in the
Method of Morris evaluation flow to inspect the boolean element explicitly and
raise when the solve is not acceptable, using the existing res/status variables
in morris_evaluate rather than the tuple object itself.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 380e402b-f9a6-4103-816c-f6f57a1b8851

📥 Commits

Reviewing files that changed from the base of the PR and between 09e9003 and 9bc80fd.

📒 Files selected for processing (20)
  • scripts/generate_etl_figure.py
  • temoa/_internal/run_actions.py
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/economies_of_scale/core/data_puller.py
  • temoa/extensions/economies_of_scale/core/model.py
  • temoa/extensions/get_comm_tech.py
  • temoa/extensions/method_of_morris/morris.py
  • temoa/extensions/method_of_morris/morris_evaluate.py
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/modeling_to_generate_alternatives/tech_activity_vector_manager.py
  • temoa/extensions/modeling_to_generate_alternatives/vector_manager.py
  • temoa/extensions/modeling_to_generate_alternatives/worker.py
  • temoa/extensions/monte_carlo/mc_worker.py
  • temoa/extensions/single_vector_mga/output_summary.py
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/template/core/model.py
  • tests/test_full_runs.py
  • tests/test_table_writer.py
  • tests/testing_configs/config_myopic_capacities.toml
  • tests/testing_data/myopic_capacities.sql
💤 Files with no reviewable changes (4)
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/method_of_morris/morris.py

Comment thread tests/test_full_runs.py
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
@idelder idelder force-pushed the extension/economies_of_scale branch from 9bc80fd to 04f9a16 Compare July 9, 2026 01:00
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