Skip to content

Add unit commitment extension#350

Open
idelder wants to merge 23 commits into
TemoaProject:unstablefrom
idelder:extension/unit_commitment
Open

Add unit commitment extension#350
idelder wants to merge 23 commits into
TemoaProject:unstablefrom
idelder:extension/unit_commitment

Conversation

@idelder

@idelder idelder commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Adds integer and linearised unit commitment as an optional extension.

Docs:
Unit Commitment.pdf

Summary by CodeRabbit

Summary

  • New Features
    • Added an extensions framework with a new extensions configuration option.
    • Enabled optional extensions: discrete capacity, economies of scale, growth rates, and unit commitment (with their table loading and model contributions).
  • Bug Fixes
    • Fixed cost ledger behavior to accumulate repeated cost entries.
    • Improved unit-commitment startup reporting and startup-related inputs/emissions handling.
    • Updated tutorial database generation to load schema before tutorial data.
  • Documentation
    • Added extension framework and per-extension documentation; refreshed mathematical formulation and schema references for ramping and limits.
  • Other
    • Consolidated ramp-up/ramp-down constraint definitions and updated related database schema/tests.

idelder added 4 commits July 10, 2026 09:48
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 14, 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: 632b92fd-fe2b-469b-88cc-1aba8ce903aa

📥 Commits

Reviewing files that changed from the base of the PR and between b36a6d9 and 4a7dbc1.

📒 Files selected for processing (22)
  • docs/source/extensions/unit_commitment.rst
  • temoa/components/limits.py
  • temoa/data_io/hybrid_loader.py
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py
  • temoa/extensions/economies_of_scale/core/model.py
  • temoa/extensions/framework.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
  • temoa/extensions/unit_commitment/components/startup.py
  • temoa/extensions/unit_commitment/core/data_puller.py
  • temoa/extensions/unit_commitment/core/model.py
  • tests/test_full_runs.py
  • tests/testing_data/test_week.sql
💤 Files with no reviewable changes (3)
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/discrete_capacity/core/model.py

Walkthrough

Introduces a configurable extension framework with discrete-capacity, economies-of-scale, growth-rates, and unit-commitment extensions. It consolidates ramp constraints, updates model and loader schemas, revises tutorial assets, and adds extension, sequencing, cost, and integration coverage.

Changes

Extension framework and model integration

Layer / File(s) Summary
Extension registration and loading
temoa/extensions/framework.py, temoa/core/*, temoa/data_io/*
Extension IDs, hooks, schemas, regional mappings, manifests, and sparse index sets are resolved through a common framework.
Core modeling and result integration
temoa/core/model.py, temoa/components/*, temoa/_internal/*
Capacity grouping, generalized ramp constraints, process-based limits, startup accounting, extension result polling, and symbolic cost helpers are integrated.
Built-in extensions
temoa/extensions/{discrete_capacity,economies_of_scale,growth_rates,unit_commitment,template}/*
Extension schemas, manifests, model registration, constraints, objective contributions, data pullers, and template scaffolding are added.
Documentation, schemas, and tests
docs/source/*, temoa/db_schema/*, temoa/tutorial_assets/*, tests/*
Extension documentation and navigation are added, legacy limit and ramp references are updated, tutorial SQL becomes schema-independent data loading, and integration fixtures are expanded.

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

Sequence Diagram(s)

sequenceDiagram
  participant TemoaConfig
  participant ExtensionFramework
  participant HybridLoader
  participant TemoaModel
  participant ExtensionHooks

  TemoaConfig->>ExtensionFramework: normalize and resolve extension IDs
  HybridLoader->>ExtensionFramework: ensure extension tables and build manifest
  HybridLoader->>TemoaModel: construct model with enabled extensions
  TemoaModel->>ExtensionHooks: register model components
  HybridLoader->>ExtensionHooks: load extension manifest items
Loading

Possibly related PRs

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

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.14% 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 accurately summarizes the main change: adding the unit commitment 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/extensions/monte_carlo/example_builds/scenario_analyzer.py (1)

17-36: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Close the SQLite connection explicitly. with Connection(db_resource) as conn: only handles commit/rollback; it does not guarantee conn.close(). Use contextlib.closing(...) or a try/finally to avoid leaking the database handle.

🤖 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/monte_carlo/example_builds/scenario_analyzer.py` around
lines 17 - 36, Update the database access block around the Connection context
manager to explicitly close the SQLite connection, using contextlib.closing or
an equivalent try/finally cleanup while preserving the existing query and error
behavior.

Source: Learnings

🤖 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/unit_commitment.rst`:
- Around line 106-110: The parameter descriptions for min_up_time_hours and
min_down_time_hours incorrectly impose a one-day maximum; update those entries
to describe only their supported non-negative duration requirement, removing the
“must be less than one full day” wording.

In `@scripts/generate_etl_figure.py`:
- Around line 177-187: Update the active-segment label in the ax.text call to
derive its displayed n value from the ACTIVE constant instead of hardcoding
“n=1”, while preserving the existing formatting and highlighting behavior.

In `@temoa/components/operations.py`:
- Around line 195-261: Extract the shared ramping-prefix logic from
_ramp_constraint in temoa/components/operations.py (lines 195-261) into a
reusable helper that returns Constraint.Skip or the computed activity_increase
and rampable-activity base, including parameter lookup, hourly normalization,
ramp_fraction validation, and warning behavior. Update _ramp_constraint to use
it while preserving ramp-up/down inequality construction. In
temoa/extensions/unit_commitment/components/commitment.py (lines 448-557),
replace the duplicated calculations and skip branch with the helper call, then
apply the existing capacity_ramp-specific terms to the returned values.

In `@temoa/data_io/hybrid_loader.py`:
- Around line 659-694: Replace the local TemoaModel() instances in
_load_retired_existing_capacity, _load_lifetime_tech, _load_lifetime_process,
and _load_lifetime_survival_curve with the existing extension-aware self.model
instance. Apply the same replacement to every other model = TemoaModel()
occurrence in this file, reusing self.model when accessing component names.

In `@temoa/extensions/discrete_capacity/components/discrete_capacity.py`:
- Around line 17-26: The set comprehensions are unnecessarily assigned to local
variables before returning. In
temoa/extensions/discrete_capacity/components/discrete_capacity.py lines 17-26,
update limit_discrete_new_capacity_indices to return its comprehension directly;
apply the same change to limit_discrete_capacity_indices at lines 55-64,
preserving both comprehensions’ existing filters and contents.

In `@temoa/extensions/economies_of_scale/components/cost_fixed_eos.py`:
- Around line 44-53: Update the relational checks in the cost_fixed_eos
validation block to wrap each Pyomo component value—cap_lower, cap_upper,
cost_lower, and cost_upper—in value() before applying >= or > comparisons.
Preserve the existing validation conditions and align this evaluation with the
later handling of these variables.
- Around line 69-74: Update the mathematical alignment checks in the cost fixed
EOS validation to evaluate cap_lower, prev_cap_upper, cost_lower, and
prev_cost_upper with Pyomo’s value() before applying abs() and the tolerance
comparisons. Preserve the existing 0.001 threshold and all() structure while
ensuring the comparisons produce Python booleans.

In `@temoa/extensions/economies_of_scale/core/model.py`:
- Around line 17-19: Replace the redundant aliases in
temoa/extensions/economies_of_scale/core/model.py with a grouped import from the
components package for cost_fixed_eos, cost_invest_eos, and cost_variable_eos;
also update temoa/extensions/economies_of_scale/components/cost_invest_eos.py to
import geography and technology directly from temoa.components, preserving all
existing symbol usage.

In `@temoa/extensions/framework.py`:
- Around line 43-46: In the extension ID validation block, update the exception
raised by the type check from ValueError to TypeError while preserving the
existing message and logging behavior.
- Around line 136-139: Replace the explicit loop building populated in the
surrounding function with a list comprehension that retains each table from
spec.owned_tables when _table_has_rows(con, table) is true.

In `@temoa/extensions/growth_rates/components/growth_capacity.py`:
- Around line 40-46: Update both limit_growth_capacity calls in
temoa/extensions/growth_rates/components/growth_capacity.py:40-46 to pass
degrowth=False and degrowth=True as keyword arguments. Apply the same change to
the limit_growth_new_capacity_delta calls in
temoa/extensions/growth_rates/components/growth_new_capacity_delta.py:40-46,
preserving the existing behavior.
- Around line 7-8: Replace the aliased imports with direct imports of geography
and technology from temoa.components in
temoa/extensions/growth_rates/components/growth_capacity.py lines 7-8 and
temoa/extensions/growth_rates/components/growth_new_capacity_delta.py lines 7-8;
preserve all existing module references.

In `@temoa/extensions/monte_carlo/mc_sequencer.py`:
- Around line 153-156: Remove the commented-out kwargs dictionary near the
MCWorker construction in the Monte Carlo sequencer, including its solver_name
and solver_options entries, while leaving the current direct argument passing to
MCWorker unchanged.

In `@temoa/extensions/myopic/myopic_sequencer.py`:
- Around line 316-320: Update the SQL insertion in the sequencer method
containing output_con.execute to use parameterized query arguments instead of
interpolating self.config.scenario and total_cost into the SQL string. Preserve
insertion into output_objective with the existing scenario, total_cost objective
name, and calculated total_cost values.

In `@temoa/extensions/template/data_manifest.py`:
- Around line 19-44: Remove the no-op index_length argument from
build_manifest_items’ LoadItem definition, while leaving the columns,
validation_map, and other loader configuration unchanged.

In `@temoa/extensions/unit_commitment/components/startup.py`:
- Around line 151-160: Update the uc_startup_emissions_rpe call in the
fixed_or_variable_cost comprehension to pass the region as a single-element
collection [r] rather than the string r, ensuring region membership uses exact
element matching instead of substring matching.

In `@temoa/extensions/unit_commitment/core/data_puller.py`:
- Around line 41-51: Restore near-zero filtering in poll_commitment_results and
write_uc_results: only retain commitment rows when online, started, or stopped
exceeds the existing epsilon threshold, matching the filtering pattern used by
poll_startup_input_results and poll_startup_cost_results. Reactivate the
relevant guards and ensure write_uc_results uses its epsilon parameter; remove
the parameter only if filtering is intentionally not required.

In `@temoa/extensions/unit_commitment/core/model.py`:
- Around line 17-18: Replace the aliased module imports for commitment and
startup in model.py with direct from-imports, then update any references in the
module to use the imported symbols without the commitment or startup module
prefixes.

In `@temoa/extensions/unit_commitment/tables.sql`:
- Around line 21-52: Remove the DEFAULT 0.0 clauses from
unit_commitment_startup_emissions.emis_per_cap and
unit_commitment_startup_input.input_per_cap so their declarations no longer
provide values that violate CHECK (... > 0); keep both columns NOT NULL and
preserve the existing positive-value constraints.

In `@tests/testing_data/utopia_v3.sql`:
- Around line 1163-1184: Update the season foreign-key declaration in the
OutputCurtailment table to reference SeasonLabel(season) instead of
TimePeriod(period), matching the schema used by the other season columns in this
file.

---

Outside diff comments:
In `@temoa/extensions/monte_carlo/example_builds/scenario_analyzer.py`:
- Around line 17-36: Update the database access block around the Connection
context manager to explicitly close the SQLite connection, using
contextlib.closing or an equivalent try/finally cleanup while preserving the
existing query and error behavior.
🪄 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: 8840024c-072f-41b8-b623-c3dc45a10db4

📥 Commits

Reviewing files that changed from the base of the PR and between 9d26bd5 and f85e940.

⛔ 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 (116)
  • .pre-commit-config.yaml
  • docs/source/computational_implementation.rst
  • docs/source/database_schema.mmd
  • docs/source/extensions.rst
  • docs/source/extensions/discrete_capacity.rst
  • docs/source/extensions/eos.rst
  • docs/source/extensions/growth_rates.rst
  • docs/source/extensions/unit_commitment.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/table_writer.py
  • temoa/_internal/temoa_sequencer.py
  • temoa/cli.py
  • temoa/components/capacity.py
  • temoa/components/commodities.py
  • temoa/components/costs.py
  • temoa/components/limits.py
  • temoa/components/operations.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/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/mga_sequencer.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/myopic/evolution_updater.py
  • temoa/extensions/myopic/myopic_progress_mapper.py
  • temoa/extensions/myopic/myopic_sequencer.py
  • temoa/extensions/single_vector_mga/sv_mga_sequencer.py
  • temoa/extensions/stochastics/scenario_creator.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/extensions/unit_commitment/__init__.py
  • temoa/extensions/unit_commitment/components/__init__.py
  • temoa/extensions/unit_commitment/components/commitment.py
  • temoa/extensions/unit_commitment/components/startup.py
  • temoa/extensions/unit_commitment/core/__init__.py
  • temoa/extensions/unit_commitment/core/data_puller.py
  • temoa/extensions/unit_commitment/core/model.py
  • temoa/extensions/unit_commitment/data_manifest.py
  • temoa/extensions/unit_commitment/extension.py
  • temoa/extensions/unit_commitment/tables.sql
  • temoa/tutorial_assets/config_sample.toml
  • temoa/tutorial_assets/utopia.sql
  • temoa/types/__init__.py
  • temoa/types/dict_types.py
  • 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_configs/config_test_week.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/test_week.sql
  • tests/testing_data/utopia_sets.json
  • tests/testing_data/utopia_v3.sql
💤 Files with no reviewable changes (3)
  • tests/testing_data/mediumville.sql
  • docs/source/param_desc_and_tables.rst
  • docs/source/database_schema.mmd

Comment thread docs/source/extensions/unit_commitment.rst
Comment thread scripts/generate_etl_figure.py
Comment thread temoa/components/operations.py Outdated
Comment thread temoa/data_io/hybrid_loader.py
Comment thread temoa/extensions/discrete_capacity/components/discrete_capacity.py
Comment thread temoa/extensions/unit_commitment/components/startup.py
Comment thread temoa/extensions/unit_commitment/core/data_puller.py
Comment thread temoa/extensions/unit_commitment/core/model.py Outdated
Comment thread temoa/extensions/unit_commitment/tables.sql
Comment thread tests/testing_data/utopia_v3.sql
@idelder
idelder force-pushed the extension/unit_commitment branch from f85e940 to 9390c47 Compare July 14, 2026 14:51
idelder added 10 commits July 14, 2026 10:51
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>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
idelder added 7 commits July 14, 2026 10:54
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 force-pushed the extension/unit_commitment branch 2 times, most recently from a3a78b1 to 9047c39 Compare July 14, 2026 14:58

@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: 12

Caution

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

⚠️ Outside diff range comments (2)
temoa/components/limits.py (1)

741-757: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Docstring math not updated for the new unit-commitment startup emissions term.

The .. math:: block for limit_emission still lists only flow, embodied, and end-of-life emission terms; the newly added startup.uc_startup_emissions_rpe contribution (when unit_commitment is enabled) isn't reflected in the formula.

Also applies to: 798-804

🤖 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/components/limits.py` around lines 741 - 757, Update the limit_emission
docstring math block to include the startup.uc_startup_emissions_rpe
contribution when unit_commitment is enabled, alongside the existing emission
terms and before the comparison to LE. Apply the same formula update to the
corresponding math block around the additionally referenced section.
temoa/db_schema/temoa_schema_v4.sql (1)

919-938: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Restore the output_cost.tech foreign key — this table is populated from Technology-typed cost keys, and the other tech-bearing output tables still enforce technology(tech). Dropping the constraint only weakens integrity checks on persisted results.

🤖 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 919 - 938, Restore the
foreign key constraint for output_cost.tech in the output_cost table definition,
referencing technology(tech), consistent with the other tech-bearing output
tables. Keep the existing columns and constraints unchanged.
♻️ Duplicate comments (2)
temoa/extensions/monte_carlo/mc_sequencer.py (1)

153-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out dead code.

This dictionary is commented out and no longer used, as the arguments are now passed directly to MCWorker. Removing it improves code readability.

♻️ Proposed refactor
-        # kwargs = {
-        #     'solver_name': self.config.solver_name,
-        #     'solver_options': self.worker_solver_options,
-        # }
🤖 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/monte_carlo/mc_sequencer.py` around lines 153 - 156, Remove
the commented-out kwargs dictionary near the MCWorker construction in the Monte
Carlo sequencer, including the obsolete solver_name and solver_options entries;
leave the current direct argument passing unchanged.
temoa/extensions/framework.py (1)

136-139: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a list comprehension to create the transformed list.

This loop can be simplified for readability and slight performance gains using a list comprehension.

♻️ Proposed refactor
-        populated: list[str] = []
-        for table in spec.owned_tables:
-            if _table_has_rows(con, table):
-                populated.append(table)
+        populated = [table for table in spec.owned_tables if _table_has_rows(con, table)]
🤖 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/framework.py` around lines 136 - 139, Replace the explicit
populated-list initialization and loop in the surrounding function with a list
comprehension that includes each table from spec.owned_tables only when
_table_has_rows(con, table) returns true, preserving the existing order and
result.
🤖 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 `@temoa/components/operations.py`:
- Around line 261-283: The ramp-constraint skip/branch wrapper is duplicated
across both implementations. In temoa/components/operations.py lines 261-283,
extract the shared logic around _ramp_constraint so it accepts a domain-specific
rampable-activity callback or precomputed value, then use it for the
capacity-based _rampable_activity path; in
temoa/extensions/unit_commitment/components/commitment.py lines 472-493, replace
the copied None/Constraint.Skip and ramp_up branching with that shared wrapper
while passing the online-units-based _rampable_activity computation.

In `@temoa/components/time.py`:
- Around line 194-198: In the period-length logic, replace the `elif p in
model.time_exist` following the returning branch with an independent `if`,
preserving its existing condition and `return -1` behavior.
- Around line 339-340: Update the adjacent string literals in the relevant error
message so implicit concatenation includes a space between “or” and
“unit_commitment”, preserving the existing message content otherwise.
- Line 413: Update the return expression in the time-of-day calculation to apply
Pyomo’s value() extractor to both model.time_of_day_hours[d] and
model.time_of_day_hours[d_next] before averaging, ensuring the function returns
a native float rather than a Pyomo expression.

In `@temoa/data_io/hybrid_loader.py`:
- Around line 110-116: Initialize self.viable_existing_techs in __init__
alongside viable_existing_rt and viable_existing_rtv, using the same collection
type and empty-value pattern assigned when it is populated during loading.
Remove or avoid relying on its later dynamic creation so early access cannot
raise AttributeError.

In `@temoa/extensions/discrete_capacity/tables.sql`:
- Around line 1-16: Remove the DEFAULT 0 clause from the capacity column in both
limit_discrete_new_capacity and limit_discrete_capacity, keeping capacity
required via NOT NULL so inserts must provide an explicit value and existing
schema constraints remain effective.

In `@temoa/extensions/economies_of_scale/components/cost_fixed_eos.py`:
- Around line 26-81: Extract the shared segment collection and validation
algorithm from cost_fixed_eos.py into a reusable helper, preserving nonnegative
values, strict monotonicity, and 0.001 boundary alignment checks. Update
temoa/extensions/economies_of_scale/components/cost_fixed_eos.py:26-81 to use or
define the helper,
temoa/extensions/economies_of_scale/components/cost_variable_eos.py:26-81 to
call it with cost_variable_eos, cost_variable_eos_segments, and the “activity”
label, and
temoa/extensions/economies_of_scale/components/cost_invest_eos.py:66-110 to call
it with cost_invest_eos, cost_invest_eos_segments, and the “capacity” label;
leave the invest-specific reference-process checks at lines 112-145 unchanged.

In `@temoa/extensions/growth_rates/components/growth_capacity.py`:
- Around line 87-111: Move the gather_group_regions and gather_group_techs calls
for regions and techs from the top of the constraint rule into the nested if p
== model.time_optimize.first() and model.time_exist branch, immediately before
they are used in the existing-capacity filter; leave all other calculations
unchanged.

In `@temoa/extensions/unit_commitment/components/commitment.py`:
- Line 19: The cross-module dependency on _ramp_activity_increase exposes a
private symbol from operations.py. Rename it to a public symbol and update its
definition, imports, and usages in commitment.py and the referenced call sites,
or add a public wrapper and route commitment.py through that API.
- Around line 79-121: Update the hours_back construction in the min up/down
back-slice initialization to iterate over model.uc_unit_capacity.sparse_keys()
rather than the sparse keys of uc_min_down_time_hours and uc_min_up_time_hours.
Include each UC technology’s min up/down hour values, including defaulted zeros,
so uc_backslices entries for hour 0 are created for every capacity-enabled
technology; leave the backtracking logic unchanged.

In `@temoa/extensions/unit_commitment/core/model.py`:
- Around line 89-90: Update the parameter declarations for uc_min_up_time_hours
and uc_min_down_time_hours to use the NonNegativeIntegers domain instead of
Integers, while preserving their existing defaults and indexing.

In `@temoa/extensions/unit_commitment/tables.sql`:
- Around line 31-52: Remove the DEFAULT '' clauses from the emis_comm and
input_comm primary-key columns in unit_commitment_startup_emissions and
unit_commitment_startup_input, leaving both columns as required TEXT values
while preserving their NOT NULL constraints and table keys.

---

Outside diff comments:
In `@temoa/components/limits.py`:
- Around line 741-757: Update the limit_emission docstring math block to include
the startup.uc_startup_emissions_rpe contribution when unit_commitment is
enabled, alongside the existing emission terms and before the comparison to LE.
Apply the same formula update to the corresponding math block around the
additionally referenced section.

In `@temoa/db_schema/temoa_schema_v4.sql`:
- Around line 919-938: Restore the foreign key constraint for output_cost.tech
in the output_cost table definition, referencing technology(tech), consistent
with the other tech-bearing output tables. Keep the existing columns and
constraints unchanged.

---

Duplicate comments:
In `@temoa/extensions/framework.py`:
- Around line 136-139: Replace the explicit populated-list initialization and
loop in the surrounding function with a list comprehension that includes each
table from spec.owned_tables only when _table_has_rows(con, table) returns true,
preserving the existing order and result.

In `@temoa/extensions/monte_carlo/mc_sequencer.py`:
- Around line 153-156: Remove the commented-out kwargs dictionary near the
MCWorker construction in the Monte Carlo sequencer, including the obsolete
solver_name and solver_options entries; leave the current direct argument
passing unchanged.
🪄 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: 160d0b00-cbe1-4d7e-afb3-03e523d4ed23

📥 Commits

Reviewing files that changed from the base of the PR and between f85e940 and a3a78b1.

⛔ 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 (120)
  • docs/source/computational_implementation.rst
  • docs/source/database_schema.mmd
  • docs/source/extensions.rst
  • docs/source/extensions/discrete_capacity.rst
  • docs/source/extensions/eos.rst
  • docs/source/extensions/growth_rates.rst
  • docs/source/extensions/unit_commitment.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/table_writer.py
  • temoa/_internal/temoa_sequencer.py
  • temoa/cli.py
  • temoa/components/capacity.py
  • temoa/components/commodities.py
  • temoa/components/costs.py
  • temoa/components/limits.py
  • temoa/components/operations.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/extensions/unit_commitment/__init__.py
  • temoa/extensions/unit_commitment/components/__init__.py
  • temoa/extensions/unit_commitment/components/commitment.py
  • temoa/extensions/unit_commitment/components/startup.py
  • temoa/extensions/unit_commitment/core/__init__.py
  • temoa/extensions/unit_commitment/core/data_puller.py
  • temoa/extensions/unit_commitment/core/model.py
  • temoa/extensions/unit_commitment/data_manifest.py
  • temoa/extensions/unit_commitment/extension.py
  • temoa/extensions/unit_commitment/tables.sql
  • temoa/tutorial_assets/config_sample.toml
  • temoa/tutorial_assets/utopia.sql
  • temoa/types/__init__.py
  • temoa/types/dict_types.py
  • tests/conftest.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_configs/config_test_week.toml
  • tests/testing_data/mediumville_sets.json
  • tests/testing_data/myopic_capacities.sql
  • tests/testing_data/test_system_sets.json
  • tests/testing_data/test_week.sql
  • tests/testing_data/utopia_sets.json
  • tests/testing_data/utopia_v3.sql
💤 Files with no reviewable changes (4)
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • docs/source/database_schema.mmd
  • docs/source/param_desc_and_tables.rst
  • temoa/extensions/stochastics/stochastic_config.py

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 12

Caution

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

⚠️ Outside diff range comments (2)
temoa/components/limits.py (1)

741-757: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Docstring math not updated for the new unit-commitment startup emissions term.

The .. math:: block for limit_emission still lists only flow, embodied, and end-of-life emission terms; the newly added startup.uc_startup_emissions_rpe contribution (when unit_commitment is enabled) isn't reflected in the formula.

Also applies to: 798-804

🤖 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/components/limits.py` around lines 741 - 757, Update the limit_emission
docstring math block to include the startup.uc_startup_emissions_rpe
contribution when unit_commitment is enabled, alongside the existing emission
terms and before the comparison to LE. Apply the same formula update to the
corresponding math block around the additionally referenced section.
temoa/db_schema/temoa_schema_v4.sql (1)

919-938: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Restore the output_cost.tech foreign key — this table is populated from Technology-typed cost keys, and the other tech-bearing output tables still enforce technology(tech). Dropping the constraint only weakens integrity checks on persisted results.

🤖 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 919 - 938, Restore the
foreign key constraint for output_cost.tech in the output_cost table definition,
referencing technology(tech), consistent with the other tech-bearing output
tables. Keep the existing columns and constraints unchanged.
♻️ Duplicate comments (2)
temoa/extensions/monte_carlo/mc_sequencer.py (1)

153-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out dead code.

This dictionary is commented out and no longer used, as the arguments are now passed directly to MCWorker. Removing it improves code readability.

♻️ Proposed refactor
-        # kwargs = {
-        #     'solver_name': self.config.solver_name,
-        #     'solver_options': self.worker_solver_options,
-        # }
🤖 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/monte_carlo/mc_sequencer.py` around lines 153 - 156, Remove
the commented-out kwargs dictionary near the MCWorker construction in the Monte
Carlo sequencer, including the obsolete solver_name and solver_options entries;
leave the current direct argument passing unchanged.
temoa/extensions/framework.py (1)

136-139: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a list comprehension to create the transformed list.

This loop can be simplified for readability and slight performance gains using a list comprehension.

♻️ Proposed refactor
-        populated: list[str] = []
-        for table in spec.owned_tables:
-            if _table_has_rows(con, table):
-                populated.append(table)
+        populated = [table for table in spec.owned_tables if _table_has_rows(con, table)]
🤖 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/framework.py` around lines 136 - 139, Replace the explicit
populated-list initialization and loop in the surrounding function with a list
comprehension that includes each table from spec.owned_tables only when
_table_has_rows(con, table) returns true, preserving the existing order and
result.
🤖 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 `@temoa/components/operations.py`:
- Around line 261-283: The ramp-constraint skip/branch wrapper is duplicated
across both implementations. In temoa/components/operations.py lines 261-283,
extract the shared logic around _ramp_constraint so it accepts a domain-specific
rampable-activity callback or precomputed value, then use it for the
capacity-based _rampable_activity path; in
temoa/extensions/unit_commitment/components/commitment.py lines 472-493, replace
the copied None/Constraint.Skip and ramp_up branching with that shared wrapper
while passing the online-units-based _rampable_activity computation.

In `@temoa/components/time.py`:
- Around line 194-198: In the period-length logic, replace the `elif p in
model.time_exist` following the returning branch with an independent `if`,
preserving its existing condition and `return -1` behavior.
- Around line 339-340: Update the adjacent string literals in the relevant error
message so implicit concatenation includes a space between “or” and
“unit_commitment”, preserving the existing message content otherwise.
- Line 413: Update the return expression in the time-of-day calculation to apply
Pyomo’s value() extractor to both model.time_of_day_hours[d] and
model.time_of_day_hours[d_next] before averaging, ensuring the function returns
a native float rather than a Pyomo expression.

In `@temoa/data_io/hybrid_loader.py`:
- Around line 110-116: Initialize self.viable_existing_techs in __init__
alongside viable_existing_rt and viable_existing_rtv, using the same collection
type and empty-value pattern assigned when it is populated during loading.
Remove or avoid relying on its later dynamic creation so early access cannot
raise AttributeError.

In `@temoa/extensions/discrete_capacity/tables.sql`:
- Around line 1-16: Remove the DEFAULT 0 clause from the capacity column in both
limit_discrete_new_capacity and limit_discrete_capacity, keeping capacity
required via NOT NULL so inserts must provide an explicit value and existing
schema constraints remain effective.

In `@temoa/extensions/economies_of_scale/components/cost_fixed_eos.py`:
- Around line 26-81: Extract the shared segment collection and validation
algorithm from cost_fixed_eos.py into a reusable helper, preserving nonnegative
values, strict monotonicity, and 0.001 boundary alignment checks. Update
temoa/extensions/economies_of_scale/components/cost_fixed_eos.py:26-81 to use or
define the helper,
temoa/extensions/economies_of_scale/components/cost_variable_eos.py:26-81 to
call it with cost_variable_eos, cost_variable_eos_segments, and the “activity”
label, and
temoa/extensions/economies_of_scale/components/cost_invest_eos.py:66-110 to call
it with cost_invest_eos, cost_invest_eos_segments, and the “capacity” label;
leave the invest-specific reference-process checks at lines 112-145 unchanged.

In `@temoa/extensions/growth_rates/components/growth_capacity.py`:
- Around line 87-111: Move the gather_group_regions and gather_group_techs calls
for regions and techs from the top of the constraint rule into the nested if p
== model.time_optimize.first() and model.time_exist branch, immediately before
they are used in the existing-capacity filter; leave all other calculations
unchanged.

In `@temoa/extensions/unit_commitment/components/commitment.py`:
- Line 19: The cross-module dependency on _ramp_activity_increase exposes a
private symbol from operations.py. Rename it to a public symbol and update its
definition, imports, and usages in commitment.py and the referenced call sites,
or add a public wrapper and route commitment.py through that API.
- Around line 79-121: Update the hours_back construction in the min up/down
back-slice initialization to iterate over model.uc_unit_capacity.sparse_keys()
rather than the sparse keys of uc_min_down_time_hours and uc_min_up_time_hours.
Include each UC technology’s min up/down hour values, including defaulted zeros,
so uc_backslices entries for hour 0 are created for every capacity-enabled
technology; leave the backtracking logic unchanged.

In `@temoa/extensions/unit_commitment/core/model.py`:
- Around line 89-90: Update the parameter declarations for uc_min_up_time_hours
and uc_min_down_time_hours to use the NonNegativeIntegers domain instead of
Integers, while preserving their existing defaults and indexing.

In `@temoa/extensions/unit_commitment/tables.sql`:
- Around line 31-52: Remove the DEFAULT '' clauses from the emis_comm and
input_comm primary-key columns in unit_commitment_startup_emissions and
unit_commitment_startup_input, leaving both columns as required TEXT values
while preserving their NOT NULL constraints and table keys.

---

Outside diff comments:
In `@temoa/components/limits.py`:
- Around line 741-757: Update the limit_emission docstring math block to include
the startup.uc_startup_emissions_rpe contribution when unit_commitment is
enabled, alongside the existing emission terms and before the comparison to LE.
Apply the same formula update to the corresponding math block around the
additionally referenced section.

In `@temoa/db_schema/temoa_schema_v4.sql`:
- Around line 919-938: Restore the foreign key constraint for output_cost.tech
in the output_cost table definition, referencing technology(tech), consistent
with the other tech-bearing output tables. Keep the existing columns and
constraints unchanged.

---

Duplicate comments:
In `@temoa/extensions/framework.py`:
- Around line 136-139: Replace the explicit populated-list initialization and
loop in the surrounding function with a list comprehension that includes each
table from spec.owned_tables only when _table_has_rows(con, table) returns true,
preserving the existing order and result.

In `@temoa/extensions/monte_carlo/mc_sequencer.py`:
- Around line 153-156: Remove the commented-out kwargs dictionary near the
MCWorker construction in the Monte Carlo sequencer, including the obsolete
solver_name and solver_options entries; leave the current direct argument
passing unchanged.
🪄 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: 160d0b00-cbe1-4d7e-afb3-03e523d4ed23

📥 Commits

Reviewing files that changed from the base of the PR and between f85e940 and a3a78b1.

⛔ 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 (120)
  • docs/source/computational_implementation.rst
  • docs/source/database_schema.mmd
  • docs/source/extensions.rst
  • docs/source/extensions/discrete_capacity.rst
  • docs/source/extensions/eos.rst
  • docs/source/extensions/growth_rates.rst
  • docs/source/extensions/unit_commitment.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/table_writer.py
  • temoa/_internal/temoa_sequencer.py
  • temoa/cli.py
  • temoa/components/capacity.py
  • temoa/components/commodities.py
  • temoa/components/costs.py
  • temoa/components/limits.py
  • temoa/components/operations.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/extensions/unit_commitment/__init__.py
  • temoa/extensions/unit_commitment/components/__init__.py
  • temoa/extensions/unit_commitment/components/commitment.py
  • temoa/extensions/unit_commitment/components/startup.py
  • temoa/extensions/unit_commitment/core/__init__.py
  • temoa/extensions/unit_commitment/core/data_puller.py
  • temoa/extensions/unit_commitment/core/model.py
  • temoa/extensions/unit_commitment/data_manifest.py
  • temoa/extensions/unit_commitment/extension.py
  • temoa/extensions/unit_commitment/tables.sql
  • temoa/tutorial_assets/config_sample.toml
  • temoa/tutorial_assets/utopia.sql
  • temoa/types/__init__.py
  • temoa/types/dict_types.py
  • tests/conftest.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_configs/config_test_week.toml
  • tests/testing_data/mediumville_sets.json
  • tests/testing_data/myopic_capacities.sql
  • tests/testing_data/test_system_sets.json
  • tests/testing_data/test_week.sql
  • tests/testing_data/utopia_sets.json
  • tests/testing_data/utopia_v3.sql
💤 Files with no reviewable changes (4)
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • docs/source/database_schema.mmd
  • docs/source/param_desc_and_tables.rst
  • temoa/extensions/stochastics/stochastic_config.py
🛑 Comments failed to post (12)
temoa/components/operations.py (1)

261-283: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Ramp-constraint skip/branch wrapper still duplicated across core and UC. The shared-prefix extraction (_ramp_activity_increase) resolves most of the previously flagged duplication, but the thin wrapper — call the helper, return Constraint.Skip on None, then branch on ramp_up to build activity_increase <= rampable or -activity_increase <= rampable — is copy-pasted in both files.

  • temoa/components/operations.py#L261-L283: this is the original/simpler version (capacity-based _rampable_activity); a shared wrapper here would need to accept the domain-specific "rampable activity" computation as a callback or pre-computed value.
  • temoa/extensions/unit_commitment/components/commitment.py#L472-L493: mirrors the same skip/branch structure with its own online-units-based _rampable_activity; consolidate by having this call the same shared wrapper, passing its own rampable-activity computation.
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 263-263: Boolean-typed positional argument in function definition

(FBT001)


[warning] 282-282: Unnecessary else after return statement

Remove unnecessary else

(RET505)

📍 Affects 2 files
  • temoa/components/operations.py#L261-L283 (this comment)
  • temoa/extensions/unit_commitment/components/commitment.py#L472-L493
🤖 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/components/operations.py` around lines 261 - 283, The ramp-constraint
skip/branch wrapper is duplicated across both implementations. In
temoa/components/operations.py lines 261-283, extract the shared logic around
_ramp_constraint so it accepts a domain-specific rampable-activity callback or
precomputed value, then use it for the capacity-based _rampable_activity path;
in temoa/extensions/unit_commitment/components/commitment.py lines 472-493,
replace the copied None/Constraint.Skip and ramp_up branching with that shared
wrapper while passing the online-units-based _rampable_activity computation.
temoa/components/time.py (3)

194-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unnecessary elif after a return statement.

Since the preceding if block ends with a return, the elif can be simplified to a simple if to reduce nested branching.

✏️ Proposed fix
     if model.time_exist and p == model.time_exist.last():
         # Need this for one specific use case (capacity growth constraints)
         return model.time_future.first() - model.time_exist.last()
-    elif p in model.time_exist:
+    if p in model.time_exist:
         return -1  # Period length is not defined for existing periods except the last
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    if model.time_exist and p == model.time_exist.last():
        # Need this for one specific use case (capacity growth constraints)
        return model.time_future.first() - model.time_exist.last()
    if p in model.time_exist:
        return -1  # Period length is not defined for existing periods except the last
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 197-197: Unnecessary elif after return statement

Remove unnecessary elif

(RET505)

🤖 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/components/time.py` around lines 194 - 198, In the period-length logic,
replace the `elif p in model.time_exist` following the returning branch with an
independent `if`, preserving its existing condition and `return -1` behavior.

Source: Linters/SAST tools


339-340: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a missing space for string concatenation.

Due to implicit string concatenation, the current strings will evaluate to ... ramp_up/ramp_down orunit_commitment ... with a missing space.

✏️ Proposed fix
-                'seasonal storage or inter-season constraints like ramp_up/ramp_down or'
-                'unit_commitment are used. Check the config file.'
+                'seasonal storage or inter-season constraints like ramp_up/ramp_down or '
+                'unit_commitment are used. Check the config file.'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

                'seasonal storage or inter-season constraints like ramp_up/ramp_down or '
                'unit_commitment are used. Check the config file.'
🤖 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/components/time.py` around lines 339 - 340, Update the adjacent string
literals in the relevant error message so implicit concatenation includes a
space between “or” and “unit_commitment”, preserving the existing message
content otherwise.

413-413: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use value() to return a native float instead of a Pyomo expression.

model.time_of_day_hours[...] accesses a Pyomo component data object. Performing arithmetic on it without extracting its value will yield a Pyomo expression tree rather than a native float. When evaluated in boolean contexts (like inequality comparisons) later during unit commitment initialization, Pyomo will raise a PyomoException. Wrapping the parameters in value() guarantees the function returns a native float as indicated by the type hint.

✏️ Proposed fix
-    return (model.time_of_day_hours[d] + model.time_of_day_hours[d_next]) / 2
+    return (value(model.time_of_day_hours[d]) + value(model.time_of_day_hours[d_next])) / 2
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    return (value(model.time_of_day_hours[d]) + value(model.time_of_day_hours[d_next])) / 2
🤖 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/components/time.py` at line 413, Update the return expression in the
time-of-day calculation to apply Pyomo’s value() extractor to both
model.time_of_day_hours[d] and model.time_of_day_hours[d_next] before averaging,
ensuring the function returns a native float rather than a Pyomo expression.
temoa/data_io/hybrid_loader.py (1)

110-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Initialize viable_existing_techs in __init__.

The attribute self.viable_existing_techs is created dynamically here during data loading. For consistency with viable_existing_rt and viable_existing_rtv, and to follow standard Python best practices (preventing potential AttributeErrors on early access), please initialize it in __init__.

♻️ Proposed refactor

In __init__:

         self.viable_existing_rt: set[tuple[Region, Technology]] = set()
         self.viable_existing_rtv: set[tuple[Region, Technology, Vintage]] = set()
+        self.viable_existing_techs: set[Technology] = set()

Also applies to: 654-658

🤖 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/data_io/hybrid_loader.py` around lines 110 - 116, Initialize
self.viable_existing_techs in __init__ alongside viable_existing_rt and
viable_existing_rtv, using the same collection type and empty-value pattern
assigned when it is populated during loading. Remove or avoid relying on its
later dynamic creation so early access cannot raise AttributeError.
temoa/extensions/discrete_capacity/tables.sql (1)

1-16: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== tables.sql ==\n'
cat -n temoa/extensions/discrete_capacity/tables.sql

printf '\n== discrete_capacity.py outline ==\n'
ast-grep outline temoa/extensions/discrete_capacity/discrete_capacity.py --view expanded || true

printf '\n== search for capacity usage/loaders ==\n'
rg -n "limit_discrete_(new_)?capacity|capacity\s*==\s*discrete_cap|v_discrete_new_capacity|v_discrete_capacity|CHECK \(capacity > 0\)|NonNegativeReals|DEFAULT 0" temoa/extensions/discrete_capacity -S

Repository: TemoaProject/temoa

Length of output: 6896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== data manifest / loader outline ==\n'
fd -a "data_manifest.py" temoa || true
fd -a "*.py" temoa/extensions/discrete_capacity || true

Repository: TemoaProject/temoa

Length of output: 818


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('temoa/extensions/discrete_capacity/tables.sql')
print(p.read_text())
PY

Repository: TemoaProject/temoa

Length of output: 665


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== data_manifest.py ==\n'
cat -n temoa/extensions/discrete_capacity/data_manifest.py

printf '\n== core/model.py ==\n'
cat -n temoa/extensions/discrete_capacity/core/model.py

printf '\n== extension.py ==\n'
cat -n temoa/extensions/discrete_capacity/extension.py

Repository: TemoaProject/temoa

Length of output: 4815


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== search for discrete_capacity data files ==\n'
fd -a --type f 'temoa/extensions/discrete_capacity' | sed 's#^\./##' | sort

printf '\n== search for table schemas / csv headers referencing capacity ==\n'
rg -n "limit_discrete_(new_)?capacity|capacity" temoa/extensions/discrete_capacity -g '*.csv' -g '*.sql' -g '*.py' -S

Repository: TemoaProject/temoa

Length of output: 645


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== loader_manifest.py outline ==\n'
ast-grep outline temoa/data_io/loader_manifest.py --view expanded || true

printf '\n== loader_manifest.py relevant lines ==\n'
rg -n "class LoadItem|columns|validator_name|validation_map|INSERT|executemany|DEFAULT|NULL|missing" temoa/data_io/loader_manifest.py temoa/data_io -S

printf '\n== discrete capacity component loader/search ==\n'
rg -n "LoadItem|is_table_required|columns=\['region', 'tech_or_group', 'capacity'\]" temoa/extensions/discrete_capacity temoa/data_io -S

Repository: TemoaProject/temoa

Length of output: 33579


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== data_io/loader_manifest.py ==\n'
cat -n temoa/data_io/loader_manifest.py

printf '\n== data_io search for table insertion logic ==\n'
rg -n "LoadItem|INSERT INTO|insert into|to_sql|sqlite|default 0|missing" temoa/data_io -S

Repository: TemoaProject/temoa

Length of output: 11853


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== hybrid_loader optional-table handling ==\n'
sed -n '300,380p' temoa/data_io/hybrid_loader.py

printf '\n== search for direct inserts / seed data for discrete_capacity ==\n'
rg -n "INSERT INTO limit_discrete_(new_)?capacity|limit_discrete_(new_)?capacity.*VALUES|fallback_data=.*limit_discrete_(new_)?capacity|discrete_capacity" temoa -S

printf '\n== discrete_capacity docs/files ==\n'
fd -a . temoa/extensions/discrete_capacity -t f | sort

Repository: TemoaProject/temoa

Length of output: 7793


Avoid DEFAULT 0 on capacity. If a row is inserted without an explicit value, SQLite stores 0, and the discrete-capacity constraint collapses to == 0 for that region/tech-or-group. CHECK (capacity > 0) would fail fast instead of silently disabling the limit.

🤖 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/discrete_capacity/tables.sql` around lines 1 - 16, Remove
the DEFAULT 0 clause from the capacity column in both
limit_discrete_new_capacity and limit_discrete_capacity, keeping capacity
required via NOT NULL so inserts must provide an explicit value and existing
schema constraints remain effective.
temoa/extensions/economies_of_scale/components/cost_fixed_eos.py (1)

26-81: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Extract the shared EOS segment-validation logic into one helper. All three EOS cost-curve modules implement the same "collect segments, then validate nonnegativity, strict monotonicity, and 0.001 boundary alignment" algorithm almost verbatim, differing only in which param/table and index tuple they read.

  • temoa/extensions/economies_of_scale/components/cost_fixed_eos.py#L26-L81: keep as the reference implementation, or convert to a thin wrapper around the extracted helper.
  • temoa/extensions/economies_of_scale/components/cost_variable_eos.py#L26-L81: replace this near-identical block with a call to the same shared helper, passing cost_variable_eos/cost_variable_eos_segments and an "activity" label for error messages.
  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py#L66-L110: replace the segment-validation portion (excluding the invest-specific reference-process checks at lines 112-145) with a call to the same shared helper, passing cost_invest_eos/cost_invest_eos_segments and a "capacity" label.
📍 Affects 3 files
  • temoa/extensions/economies_of_scale/components/cost_fixed_eos.py#L26-L81 (this comment)
  • temoa/extensions/economies_of_scale/components/cost_variable_eos.py#L26-L81
  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py#L66-L110
🤖 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/economies_of_scale/components/cost_fixed_eos.py` around
lines 26 - 81, Extract the shared segment collection and validation algorithm
from cost_fixed_eos.py into a reusable helper, preserving nonnegative values,
strict monotonicity, and 0.001 boundary alignment checks. Update
temoa/extensions/economies_of_scale/components/cost_fixed_eos.py:26-81 to use or
define the helper,
temoa/extensions/economies_of_scale/components/cost_variable_eos.py:26-81 to
call it with cost_variable_eos, cost_variable_eos_segments, and the “activity”
label, and
temoa/extensions/economies_of_scale/components/cost_invest_eos.py:66-110 to call
it with cost_invest_eos, cost_invest_eos_segments, and the “capacity” label;
leave the invest-specific reference-process checks at lines 112-145 unchanged.
temoa/extensions/growth_rates/components/growth_capacity.py (1)

87-111: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Compute regions/techs lazily, only when needed.

regions and techs are only used inside the if p == model.time_optimize.first(): if model.time_exist: branch (Lines 101-111), but they're computed unconditionally at Lines 87-88 for every call of this constraint rule. The sibling growth_new_capacity.py computes the equivalent values lazily inside that same branch. Move the computation inside the conditional to avoid the wasted work on every non-first-period call.

♻️ Proposed fix
-    regions = geography.gather_group_regions(model, r)
-    techs = technology.gather_group_techs(model, t)
-
     growth = model.limit_degrowth_capacity if degrowth else model.limit_growth_capacity
     rate = 1 + value(growth[r, t, op][0])
     seed = value(growth[r, t, op][1])

     cap = quicksum(
         model.v_capacity_available_by_period_and_tech[_r, p, _t]
         for _r, _t in capacity.gather_group_active_processes(model, r, p, t)
     )

     capacity_prev = 0.0

     if p == model.time_optimize.first():
         if model.time_exist:
+            regions = geography.gather_group_regions(model, r)
+            techs = technology.gather_group_techs(model, t)
             p_prev = model.time_exist.last()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    growth = model.limit_degrowth_capacity if degrowth else model.limit_growth_capacity
    rate = 1 + value(growth[r, t, op][0])
    seed = value(growth[r, t, op][1])

    cap = quicksum(
        model.v_capacity_available_by_period_and_tech[_r, p, _t]
        for _r, _t in capacity.gather_group_active_processes(model, r, p, t)
    )

    capacity_prev = 0.0

    if p == model.time_optimize.first():
        if model.time_exist:
            regions = geography.gather_group_regions(model, r)
            techs = technology.gather_group_techs(model, t)
            p_prev = model.time_exist.last()
            capacity_prev = quicksum(
                get_adjusted_existing_capacity(model, _r, _t, _v)
                * value(model.process_life_frac[_r, p_prev, _t, _v])
                for _r, _t, _v in model.existing_capacity.sparse_keys()
                if _r in regions
                and _t in techs
                and _v + value(model.lifetime_process[_r, _t, _v]) > p_prev
            )
🤖 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/growth_rates/components/growth_capacity.py` around lines 87
- 111, Move the gather_group_regions and gather_group_techs calls for regions
and techs from the top of the constraint rule into the nested if p ==
model.time_optimize.first() and model.time_exist branch, immediately before they
are used in the existing-capacity filter; leave all other calculations
unchanged.
temoa/extensions/unit_commitment/components/commitment.py (2)

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

Imports a leading-underscore ("private") symbol across modules.

_ramp_activity_increase is defined with a leading underscore in temoa/components/operations.py, signaling module-private, but is imported and used directly here. Consider dropping the underscore (or introducing a small public wrapper) now that it's a cross-module dependency, so its "private" naming doesn't mislead future maintainers of operations.py into refactoring/removing it without checking commitment.py.

Also applies to: 448-493

🤖 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/unit_commitment/components/commitment.py` at line 19, The
cross-module dependency on _ramp_activity_increase exposes a private symbol from
operations.py. Rename it to a public symbol and update its definition, imports,
and usages in commitment.py and the referenced call sites, or add a public
wrapper and route commitment.py through that API.

79-121: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Files ==\n'
git ls-files 'temoa/extensions/unit_commitment/components/commitment.py' 'temoa/components/operations.py' 'temoa/extensions/unit_commitment/model.py' 2>/dev/null || true

printf '\n== commitment.py around hours_back and constraints ==\n'
sed -n '70,150p' temoa/extensions/unit_commitment/components/commitment.py
printf '\n---\n'
sed -n '380,470p' temoa/extensions/unit_commitment/components/commitment.py

printf '\n== model.py unit commitment param definitions ==\n'
sed -n '70,110p' temoa/extensions/unit_commitment/model.py

printf '\n== operations.py relevant helper/constraint ==\n'
sed -n '1,120p' temoa/components/operations.py
printf '\n---\n'
sed -n '430,520p' temoa/components/operations.py

Repository: TemoaProject/temoa

Length of output: 7119


🌐 Web query:

Pyomo Param sparse_keys default values semantics site:pyomo.readthedocs.io sparse_keys default

💡 Result:

In Pyomo, the sparse_keys() method of a Param object is designed to return only the indices for which values have been explicitly defined, effectively ignoring any default value that might be set for the parameter [1][2]. The internal implementation of sparse_keys() achieves this by temporarily suppressing the default value of the Param component [1][2]. Specifically: 1. It saves the current default value (which could be a constant, a rule/function, or Param.NoValue) [1][2]. 2. It sets the component's default value to Param.NoValue [1][2]. 3. It calls the standard keys() method, which, without the default value, only iterates over the keys present in the _data dictionary (the explicitly defined entries) [1][2]. 4. It restores the original default value in a finally block to ensure the component's state remains unchanged [1][2]. This ensures that sparse_keys() provides a "sparse" view of the parameter, distinguishing between entries that were explicitly assigned and those that would simply inherit the default value if accessed [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import pathlib, re
p = pathlib.Path('temoa/extensions/unit_commitment/components/commitment.py')
text = p.read_text()
for pat in [
    r'uc_backslices',
    r'uc_min_up_time_hours',
    r'uc_min_down_time_hours',
    r'uc_indices_rpsdtv',
]:
    print(f'\n## {pat}')
    for m in re.finditer(pat, text):
        start = max(0, text.rfind('\n', 0, m.start()-80))
        end = text.find('\n', m.end()+160)
        print(text[start+1:end+1])
        break
PY

Repository: TemoaProject/temoa

Length of output: 1608


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== parameter and backslices declarations ==\n'
rg -n "uc_min_(up|down)_time_hours|uc_backslices|uc_indices_rpsdtv|default *= *0" temoa -g '*.py'

printf '\n== surrounding declaration context ==\n'
python3 - <<'PY'
from pathlib import Path
paths = [
    Path('temoa/extensions/unit_commitment/components/commitment.py'),
    Path('temoa/extensions/unit_commitment/model.py'),
    Path('temoa/extensions/unit_commitment/__init__.py'),
]
for path in paths:
    if path.exists():
        print(f'\n### {path} ({sum(1 for _ in path.open())} lines)')
        text = path.read_text().splitlines()
        for needle in ['uc_min_up_time_hours', 'uc_min_down_time_hours', 'uc_backslices', 'uc_indices_rpsdtv']:
            for i, line in enumerate(text, 1):
                if needle in line:
                    start = max(1, i-5)
                    end = min(len(text), i+8)
                    for j in range(start, end+1):
                        print(f'{j:4d}: {text[j-1]}')
                    print('---')
                    break
PY

Repository: TemoaProject/temoa

Length of output: 8806


Populate hours_back from the UC capacity universe, not the sparse min-time params

Param.sparse_keys() skips entries that fall back to default=0, so any UC tech that leaves uc_min_up_time_hours or uc_min_down_time_hours unset will later hit model.uc_backslices[s, d, 0] in the min-up/min-down constraints and fail model construction. Build the lookback set from model.uc_unit_capacity.sparse_keys() instead so defaulted zeros are included.

Proposed fix
-    hours_back = {
-        int(value(model.uc_min_down_time_hours[r, t]))
-        for (r, t) in model.uc_min_down_time_hours.sparse_keys()
-    }
-    hours_back |= {
-        int(value(model.uc_min_up_time_hours[r, t]))
-        for (r, t) in model.uc_min_up_time_hours.sparse_keys()
-    }
+    hours_back = {
+        int(value(model.uc_min_down_time_hours[r, t]))
+        for (r, t) in model.uc_unit_capacity.sparse_keys()
+    }
+    hours_back |= {
+        int(value(model.uc_min_up_time_hours[r, t]))
+        for (r, t) in model.uc_unit_capacity.sparse_keys()
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    # Initialise the time slice back-check lists for min up/down time constraints
    # Tells us which previous time slices' startups/shutdowns are relevant
    epsilon = 1e-6  # To buffer float/integer interactions

    # We only care about how many hours back we're looking, so why redo work?
    hours_back = {
        int(value(model.uc_min_down_time_hours[r, t]))
        for (r, t) in model.uc_unit_capacity.sparse_keys()
    }
    hours_back |= {
        int(value(model.uc_min_up_time_hours[r, t]))
        for (r, t) in model.uc_unit_capacity.sparse_keys()
    }

    # Invert the time next sequence to start...
    time_prev = {model.time_next[s, d]: (s, d) for s, d in model.time_next}
    # Then back down the chain until the next timeslice back is irrelevant
    for hb in hours_back:
        for s, d in time_prev:
            model.uc_backslices[s, d, hb] = set()
            _s, _d = s, d
            _hours = float(hb)
            i = 0  # Some loop safety
            while i + 1 < len(model.time_next):
                s_prev, d_prev = time_prev[_s, _d]
                if s_prev == _s and d_prev == _d:
                    msg = (
                        'When finding relevant past time slices for min up/downtime '
                        'constraints, looped back to the same time slice. This would '
                        'likely cause infeasibility and is not supported.  If the '
                        'min up/downtime is longer than a season, try instead '
                        'flagging the technology as production baseload "pb" and '
                        'setting min up/down times to zero to skip the constraints. '
                    )
                    logger.error(msg)
                    raise ValueError(msg)
                elapsed = tod_elapsed_hours(model, d_prev, _d)
                _hours -= elapsed
                if _hours < -epsilon:
                    break  # too far back (with some rounding buffer) so ignore
                i += 1
                model.uc_backslices[s, d, hb].add((s_prev, d_prev))
                _s, _d = s_prev, d_prev
🤖 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/unit_commitment/components/commitment.py` around lines 79 -
121, Update the hours_back construction in the min up/down back-slice
initialization to iterate over model.uc_unit_capacity.sparse_keys() rather than
the sparse keys of uc_min_down_time_hours and uc_min_up_time_hours. Include each
UC technology’s min up/down hour values, including defaulted zeros, so
uc_backslices entries for hour 0 are created for every capacity-enabled
technology; leave the backtracking logic unchanged.
temoa/extensions/unit_commitment/core/model.py (1)

89-90: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and locate the initialization routine.
ast-grep outline temoa/extensions/unit_commitment/core/model.py --view expanded || true
rg -n "uc_min_up_time_hours|uc_min_down_time_hours|initialize_unit_commitment|NonNegativeIntegers|Integers" temoa/extensions/unit_commitment/core/model.py temoa/extensions/unit_commitment/core -S

# Show the relevant slice of the model file.
sed -n '1,220p' temoa/extensions/unit_commitment/core/model.py

Repository: TemoaProject/temoa

Length of output: 9897


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the initialization logic and any parameter validation around unit commitment.
ast-grep outline temoa/extensions/unit_commitment/components/commitment.py --view expanded || true
rg -n "min_up_time_hours|min_down_time_hours|backslice|epsilon|validate|domain=NonNegativeIntegers|NonNegativeIntegers" temoa/extensions/unit_commitment/components/commitment.py temoa/extensions/unit_commitment/components -S

# Read the relevant sections of the commitment component.
sed -n '1,260p' temoa/extensions/unit_commitment/components/commitment.py

Repository: TemoaProject/temoa

Length of output: 14751


Tighten min up/down time params to non-negative integers. Negative values are accepted today and just produce empty back-slice sets in initialize_unit_commitment, which hides bad input data. Switching both params to NonNegativeIntegers would fail fast on invalid hours.

🤖 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/unit_commitment/core/model.py` around lines 89 - 90, Update
the parameter declarations for uc_min_up_time_hours and uc_min_down_time_hours
to use the NonNegativeIntegers domain instead of Integers, while preserving
their existing defaults and indexing.
temoa/extensions/unit_commitment/tables.sql (1)

31-52: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "DEFAULT ''" temoa/db_schema/temoa_schema_v4.sql

Repository: TemoaProject/temoa

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- all DEFAULT empty-string occurrences ---'
rg -n "DEFAULT ''" temoa -g '*.sql' -g '*.sqlite' -g '*.py' -g '*.ts' -g '*.js' || true

echo
echo '--- relevant table definitions ---'
sed -n '1,120p' temoa/extensions/unit_commitment/tables.sql

echo
echo '--- any references to the startup tables ---'
rg -n "unit_commitment_startup_(emissions|input)|emis_comm|input_comm" temoa -g '*.sql' -g '*.py' -g '*.ts' -g '*.js' -g '*.md' || true

Repository: TemoaProject/temoa

Length of output: 25911


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- unit commitment data manifest ---'
sed -n '1,140p' temoa/extensions/unit_commitment/data_manifest.py

echo
echo '--- table writer sections touching these tables ---'
rg -n -A4 -B4 "unit_commitment_startup_(emissions|input)|emis_comm|input_comm" temoa/_internal/table_writer.py temoa/data_io/component_manifest.py temoa/extensions/unit_commitment -g '*.py' -g '*.sql' || true

echo
echo '--- any schema docs or migration notes around startup tables ---'
rg -n "unit_commitment_startup_(emissions|input)|DEFAULT ''|emis_comm.*DEFAULT|input_comm.*DEFAULT" temoa/db_schema temoa/extensions/unit_commitment temoa -g '*.md' -g '*.sql' -g '*.py' || true

Repository: TemoaProject/temoa

Length of output: 21084


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- temoa_schema_v4.sql around unit commitment startup tables ---'
sed -n '288,323p' temoa/db_schema/temoa_schema_v4.sql

echo
echo '--- temoa_schema_v3.sql around unit commitment startup tables ---'
sed -n '585,615p' temoa/db_schema/temoa_schema_v3.sql

echo
echo '--- data manifest for the startup tables ---'
sed -n '55,90p' temoa/extensions/unit_commitment/data_manifest.py

Repository: TemoaProject/temoa

Length of output: 3058


Remove the empty-string defaults on the PK columns. emis_comm and input_comm should be required values here; otherwise an omitted field can persist as '' and create an ambiguous primary-key row.

🤖 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/unit_commitment/tables.sql` around lines 31 - 52, Remove the
DEFAULT '' clauses from the emis_comm and input_comm primary-key columns in
unit_commitment_startup_emissions and unit_commitment_startup_input, leaving
both columns as required TEXT values while preserving their NOT NULL constraints
and table keys.

@idelder
idelder force-pushed the extension/unit_commitment branch from d6cc97b to a5b0d1d Compare July 14, 2026 15:21
@idelder
idelder force-pushed the extension/unit_commitment branch from 3fe93ef to c617d2d Compare July 14, 2026 19:21

@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: 2

Caution

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

⚠️ Outside diff range comments (2)
temoa/extensions/get_comm_tech.py (1)

23-41: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Ensure the database connection is reliably closed.

If an exception occurs during the database operations, con.close() will be bypassed and the connection will leak. Based on learnings, when working with SQLite in this codebase, prefer with contextlib.closing(sqlite3.connect(...)) as con: (or a try/finally block) to ensure the connection is explicitly closed on exit.

♻️ Proposed refactor
-    con = sqlite3.connect(inp_f)
-    cur = con.cursor()  # a database cursor is a control structure that enables traversal over
-    # the records in a database
-    con.text_factory = str  # this ensures data is explored with the correct UTF-8 encoding
+    import contextlib

-    print(inp_f)
-    cur.execute('SELECT DISTINCT scenario FROM output_flow_out')
-    x = []
-    for row in cur:
-        x.append(row[0])
-    for y in x:
-        cur.execute('SELECT DISTINCT period FROM output_flow_out WHERE scenario = ?', (y,))
-        periods_list[y] = []
-        for per in cur:
-            z = per[0]
-            periods_list[y].append(z)
+    with contextlib.closing(sqlite3.connect(inp_f)) as con:
+        cur = con.cursor()  # a database cursor is a control structure that enables traversal over
+        # the records in a database
+        con.text_factory = str  # this ensures data is explored with the correct UTF-8 encoding

-    cur.close()
-    con.close()
+        print(inp_f)
+        cur.execute('SELECT DISTINCT scenario FROM output_flow_out')
+        x = []
+        for row in cur:
+            x.append(row[0])
+        for y in x:
+            cur.execute('SELECT DISTINCT period FROM output_flow_out WHERE scenario = ?', (y,))
+            periods_list[y] = []
+            for per in cur:
+                z = per[0]
+                periods_list[y].append(z)
+
+        cur.close()
🤖 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/get_comm_tech.py` around lines 23 - 41, Update the database
handling around sqlite3.connect in the surrounding function to use
contextlib.closing (or an equivalent try/finally), ensuring con.close() executes
when any query or iteration raises; keep the existing cursor cleanup and query
behavior unchanged.

Source: Learnings

temoa/extensions/unit_commitment/core/data_puller.py (1)

42-50: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Extract the numeric value from the uc_unit_capacity parameter.

model.uc_unit_capacity is a Pyomo parameter. By omitting value(), unit_cap is assigned a Pyomo component object rather than a native float. Multiplying this object by the variable values (value(model.v_uc_online...)) results in Pyomo Expression objects. When the if online > 0 or started > 0 or stopped > 0: condition subsequently attempts to evaluate the truth value of these expressions, Pyomo will raise a PyomoException ("Cannot convert non-constant Pyomo expression to bool") and crash the data puller.

Wrap model.uc_unit_capacity[r, t] with value() to ensure online, started, and stopped are evaluated safely as native floats.

🐛 Proposed fix
     for r, p, s, d, t, v in model.uc_indices_rpsdtv:
-        unit_cap = model.uc_unit_capacity[r, t]
+        unit_cap = value(model.uc_unit_capacity[r, t])

         online = unit_cap * value(model.v_uc_online[r, p, s, d, t, v])
🤖 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/unit_commitment/core/data_puller.py` around lines 42 - 50,
Update the unit_cap assignment in the loop over model.uc_indices_rpsdtv to wrap
model.uc_unit_capacity[r, t] with value(), ensuring the subsequent online,
started, stopped calculations and conditional use native numeric values.
🤖 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 `@temoa/components/limits.py`:
- Around line 801-803: Rename the generator expression’s region loop variable in
the emissions calculation within limit_emission_constraint from r to reg, and
update the corresponding uc_startup_emissions_rpe argument. Leave the function
parameter r and all other logic unchanged.

In `@temoa/extensions/economies_of_scale/components/cost_invest_eos.py`:
- Around line 400-412: Update the capacity aggregation in the segment lookup
block to use Python’s built-in sum() over the value-extracted existing_capacity
entries instead of quicksum(). In the loop over model.cost_invest_eos_segments,
replace continue after setting prev_cum_cost and in_bounds with break so
iteration stops once the matching segment is found.

---

Outside diff comments:
In `@temoa/extensions/get_comm_tech.py`:
- Around line 23-41: Update the database handling around sqlite3.connect in the
surrounding function to use contextlib.closing (or an equivalent try/finally),
ensuring con.close() executes when any query or iteration raises; keep the
existing cursor cleanup and query behavior unchanged.

In `@temoa/extensions/unit_commitment/core/data_puller.py`:
- Around line 42-50: Update the unit_cap assignment in the loop over
model.uc_indices_rpsdtv to wrap model.uc_unit_capacity[r, t] with value(),
ensuring the subsequent online, started, stopped calculations and conditional
use native numeric values.
🪄 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: e9b49697-4f9b-4f62-83dc-92bd060c0bef

📥 Commits

Reviewing files that changed from the base of the PR and between a3a78b1 and 5a33281.

📒 Files selected for processing (21)
  • docs/source/extensions/unit_commitment.rst
  • temoa/components/limits.py
  • temoa/data_io/hybrid_loader.py
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py
  • temoa/extensions/economies_of_scale/core/model.py
  • temoa/extensions/framework.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
  • temoa/extensions/unit_commitment/components/startup.py
  • temoa/extensions/unit_commitment/core/data_puller.py
  • temoa/extensions/unit_commitment/core/model.py
  • tests/test_full_runs.py
💤 Files with no reviewable changes (3)
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/discrete_capacity/core/model.py

Comment thread temoa/components/limits.py
Comment thread temoa/extensions/economies_of_scale/components/cost_invest_eos.py Outdated
@idelder
idelder force-pushed the extension/unit_commitment branch from 5a33281 to 52e847e Compare July 14, 2026 19:52

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

Caution

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

⚠️ Outside diff range comments (3)
temoa/extensions/get_comm_tech.py (1)

23-41: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Ensure the database connection is explicitly closed on exception.

If an exception occurs during the cursor execution or iteration, the connection will not be closed, leading to a resource leak. Based on learnings, sqlite3 connections in this codebase should be managed with contextlib.closing or a try/finally block to ensure they are explicitly closed on exit.

🛡️ Proposed fix
-    con = sqlite3.connect(inp_f)
-    cur = con.cursor()  # a database cursor is a control structure that enables traversal over
-    # the records in a database
-    con.text_factory = str  # this ensures data is explored with the correct UTF-8 encoding
-
-    print(inp_f)
-    cur.execute('SELECT DISTINCT scenario FROM output_flow_out')
-    x = []
-    for row in cur:
-        x.append(row[0])
-    for y in x:
-        cur.execute('SELECT DISTINCT period FROM output_flow_out WHERE scenario = ?', (y,))
-        periods_list[y] = []
-        for per in cur:
-            z = per[0]
-            periods_list[y].append(z)
-
-    cur.close()
-    con.close()
+    con = sqlite3.connect(inp_f)
+    try:
+        cur = con.cursor()  # a database cursor is a control structure that enables traversal over
+        # the records in a database
+        con.text_factory = str  # this ensures data is explored with the correct UTF-8 encoding
+
+        print(inp_f)
+        cur.execute('SELECT DISTINCT scenario FROM output_flow_out')
+        x = []
+        for row in cur:
+            x.append(row[0])
+        for y in x:
+            cur.execute('SELECT DISTINCT period FROM output_flow_out WHERE scenario = ?', (y,))
+            periods_list[y] = []
+            for per in cur:
+                z = per[0]
+                periods_list[y].append(z)
+
+        cur.close()
+    finally:
+        con.close()
🤖 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/get_comm_tech.py` around lines 23 - 41, Update the database
access flow around the connection and cursor creation in get_comm_tech to
guarantee both resources are closed when query execution or iteration raises an
exception. Use contextlib.closing or an equivalent try/finally structure, while
preserving the existing query and periods_list population behavior.

Source: Learnings

temoa/data_io/hybrid_loader.py (2)

708-710: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use member_tuples for consistency with rtv viability sets.

For consistency with self.viable_rtv.member_tuples used in _load_lifetime_process and _load_lifetime_survival_curve, prefer using member_tuples for self.viable_rt to ensure explicit handling of tuples.

♻️ Proposed refactor
         rt_getter = itemgetter(0, 1)
         if self.viable_rt:
-            valid_rt = self.viable_rt.members | self.viable_existing_rt
+            valid_rt = self.viable_rt.member_tuples | self.viable_existing_rt
             rows_to_load = [item for item in rows_to_load if rt_getter(item) in valid_rt]
🤖 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/data_io/hybrid_loader.py` around lines 708 - 710, Update the viable_rt
filtering block to use self.viable_rt.member_tuples when constructing valid_rt,
while retaining self.viable_existing_rt and the existing rt_getter-based
filtering. This aligns the logic with the member_tuples handling in
_load_lifetime_process and _load_lifetime_survival_curve.

113-115: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Initialize self.viable_existing_techs.

self.viable_existing_techs is conditionally assigned in _load_existing_capacity, but it is not initialized in __init__. If rows_to_load is empty, this attribute will remain undeclared, potentially causing an AttributeError if accessed elsewhere.

🛡️ Proposed fix
         self.data: dict[str, object] | None = None
+        self.viable_existing_techs = set()
         self.viable_existing_rt: set[tuple[Region, Technology]] = set()
         self.viable_existing_rtv: set[tuple[Region, Technology, Vintage]] = set()
🤖 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/data_io/hybrid_loader.py` around lines 113 - 115, Initialize
self.viable_existing_techs in __init__, alongside viable_existing_rt and
viable_existing_rtv, using the collection type expected by
_load_existing_capacity. Preserve the existing conditional assignment and ensure
the attribute exists even when rows_to_load is empty.
🤖 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.

Outside diff comments:
In `@temoa/data_io/hybrid_loader.py`:
- Around line 708-710: Update the viable_rt filtering block to use
self.viable_rt.member_tuples when constructing valid_rt, while retaining
self.viable_existing_rt and the existing rt_getter-based filtering. This aligns
the logic with the member_tuples handling in _load_lifetime_process and
_load_lifetime_survival_curve.
- Around line 113-115: Initialize self.viable_existing_techs in __init__,
alongside viable_existing_rt and viable_existing_rtv, using the collection type
expected by _load_existing_capacity. Preserve the existing conditional
assignment and ensure the attribute exists even when rows_to_load is empty.

In `@temoa/extensions/get_comm_tech.py`:
- Around line 23-41: Update the database access flow around the connection and
cursor creation in get_comm_tech to guarantee both resources are closed when
query execution or iteration raises an exception. Use contextlib.closing or an
equivalent try/finally structure, while preserving the existing query and
periods_list population behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b435d5c0-9ce4-4dd4-bb6c-744e731057ec

📥 Commits

Reviewing files that changed from the base of the PR and between 5a33281 and b36a6d9.

📒 Files selected for processing (21)
  • docs/source/extensions/unit_commitment.rst
  • temoa/components/limits.py
  • temoa/data_io/hybrid_loader.py
  • temoa/extensions/discrete_capacity/core/model.py
  • temoa/extensions/economies_of_scale/components/cost_invest_eos.py
  • temoa/extensions/economies_of_scale/core/model.py
  • temoa/extensions/framework.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
  • temoa/extensions/unit_commitment/components/startup.py
  • temoa/extensions/unit_commitment/core/data_puller.py
  • temoa/extensions/unit_commitment/core/model.py
  • tests/test_full_runs.py
💤 Files with no reviewable changes (3)
  • temoa/extensions/stochastics/stochastic_config.py
  • temoa/extensions/modeling_to_generate_alternatives/manager_factory.py
  • temoa/extensions/discrete_capacity/core/model.py

Signed-off-by: Davey Elder <iandavidelder@gmail.com>
@idelder
idelder force-pushed the extension/unit_commitment branch from b36a6d9 to be883c7 Compare July 16, 2026 17:25
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