Skip to content

feat: add build_period axis to investment once-effect matrices#98

Open
FBumann wants to merge 5 commits intomainfrom
feat/build-period-effects
Open

feat: add build_period axis to investment once-effect matrices#98
FBumann wants to merge 5 commits intomainfrom
feat/build-period-effects

Conversation

@FBumann
Copy link
Owner

@FBumann FBumann commented Mar 14, 2026

Summary

  • Add _expand_once_effect() helper implementing the scalar→1D→diagonal→2D construction rule for investment once-effects
  • Investment effects_per_size and effects_fixed now carry (invest_dim, effect, period, build_period) shape internally
  • model.py renames invest_size_at_build/invest_build period→build_period before multiplication, then sums over both flow and build_period
  • Periodic effects (effects_per_size_periodic, effects_fixed_periodic) unchanged — they don't have a build_period axis

Construction rule

Input Internal representation
Scalar 5.0 Diagonal: cost=5 lands in the build period
1D (build_period,) Diagonal: cost varies by when you build
2D (period, build_period) As-is: full control (installments, learning curves)

Test plan

  • All 383 existing tests pass (backward compat via diagonal expansion)
  • 4 new tests: 1D build_period per-size, 1D build_period fixed, 2D spread per-size, 2D spread fixed
  • ruff check, ruff format, mypy clean

Closes #96

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Support for 2D investment cost specifications to model differing costs by period and build-period.
  • Improvements

    • One-time and periodic investment handling expanded to include build-period dimension for correct aggregation.
    • Public input type system updated: TimeSeries replaced by new ArrayInput/Once/Periodic/Temporal input abstractions.
  • Documentation

    • Clarified docstrings on scalar, 1D and 2D investment input interpretation and expansion.
  • Tests

    • Added tests covering diagonal expansion, 2D passthrough, alignment, reordering, and error cases.

Investment once-effects (effects_per_size, effects_fixed) now support a
2D (period, build_period) internal representation, enabling construction
cost spread, learning curves, and installment plans.

Construction rule:
- Scalar → diagonal (constant cost lands in build period)
- 1D (build_period,) → diagonal (cost varies by when you build)
- 2D (period, build_period) → as-is (full control)

model.py renames invest_size_at_build/invest_build period→build_period
before multiplication, then sums over both flow and build_period.

Closes #96

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ca64533-32b7-46ea-af3d-5532a805633f

📥 Commits

Reviewing files that changed from the base of the PR and between 4734130 and 10a17da.

📒 Files selected for processing (6)
  • pyproject.toml
  • src/fluxopt/__init__.py
  • src/fluxopt/components.py
  • src/fluxopt/elements.py
  • src/fluxopt/model_data.py
  • src/fluxopt/types.py

📝 Walkthrough

Walkthrough

Refactors public input types and type hints (replacing TimeSeries with ArrayInput/Temporal/Periodic variants); adds build_period-aware once-effect expansion for investment effects (diagonal/2D rules), updates model aggregation to sum over build_period, and adds unit/integration tests for the new behavior.

Changes

Cohort / File(s) Summary
Core element type annotations
src/fluxopt/elements.py
Replaced TimeSeries usages with new input aliases (ArrayInput, OnceEffectInput, PeriodicInput, TemporalInput, TemporalPeriodicInput) and updated public field type signatures across Sizing, Investment, Status, Flow, Effect, Storage.
Input type definitions & normalization
src/fluxopt/types.py, src/fluxopt/__init__.py
Introduced new type aliases (ArrayInput, TemporalInput, PeriodicInput, OnceEffectInput, TemporalPeriodicInput) and updated as_dataarray signature; exported new types and removed TimeSeries from package API.
Once-effect expansion & data shapes
src/fluxopt/model_data.py
Added _expand_once_effect to expand scalar/1D/2D once-effect inputs into (period, build_period) diagonal/2D arrays; updated _InvestmentArrays.build and related DataArray shapes to include build_period for investment effects.
Model aggregation of one-time costs
src/fluxopt/model.py
Adjusted creation/aggregation of one-time investment costs to multiply and sum over flow/effect and build_period (renamed loop var) and updated comments describing the (flow, effect, period, build_period) domain.
Converter API type updates
src/fluxopt/components.py
Updated Converter method/type hints to use TemporalInput for coefficients/efficiencies (conversion_factors, _single_io, boiler, heat_pump, power2heat, chp).
Tests — expansion & multi-period behavior
tests/test_data.py, tests/math_port/test_multi_period.py
Added unit tests for _expand_once_effect (scalar, 1D, 2D, alignment, reindexing, error cases) and integration tests validating build_period diagonal/2D expansion and objective impacts.
Linting config
pyproject.toml
Added per-file Ruff ignore entry for UP040 in src/fluxopt/types.py.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I nibbled through arrays, diagonal and wide,
Scalars and matrices found the build-period tide,
Types renamed, inputs normalized with care,
Costs now span period and build_period, laid bare,
A rabbit hops — tests green, all aligned with pride.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description covers the summary, construction rule, and test plan but lacks explicit sections matching the template structure (Type of Change, Testing checklist, etc.). Add missing template sections: explicitly mark 'Type of Change' (New feature), use the Testing and Checklist sections with proper formatting to match the repository template.
Out of Scope Changes check ❓ Inconclusive The PR includes comprehensive type system refactoring (TimeSeries→ArrayInput, new input types) beyond the core build_period feature, though these changes support the main objective by providing proper typing for the new input variants. Clarify whether the type system refactoring (TimeSeries removal, new input types) should have been part of this PR or separated into a dedicated typing/refactoring PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly reflects the main change: adding a build_period axis to investment once-effect matrices, which is the core objective of this PR.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from #96: adds _expand_once_effect helper, updates effects_per_size and effects_fixed to (invest_dim, effect, period, build_period) shape, implements the construction rule (scalar→diagonal, 1D diagonal, 2D as-is), updates model.py to sum over build_period, and adds comprehensive tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/build-period-effects
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

You can customize the high-level summary generated by CodeRabbit.

Configure the reviews.high_level_summary_instructions setting to provide custom instructions for generating the high-level summary.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/fluxopt/model_data.py`:
- Around line 83-94: The code paths handling once-effect DataArrays (in the
block using vdims, value, dims, coords, and n) must align/validate the DataArray
coordinates for 'period' and 'build_period' before using them; update the branch
where vdims == {'period','build_period'} and the branch that diagonalizes 1D
inputs so that you reindex or sel the incoming value to the expected
period/build_period coordinate ordering (using the target coords derived from
coords or dims), check the reindexed length matches n, and then construct the 2D
DataArray using those aligned values (instead of raw .values) so costs map to
the correct period/build_period labels; also include the same coordinate
alignment/validation before raising the ValueError that references dim_name and
lengths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 95827c70-678d-4c89-9597-cb0d2b284796

📥 Commits

Reviewing files that changed from the base of the PR and between e0f8e66 and c8fbb74.

📒 Files selected for processing (4)
  • src/fluxopt/elements.py
  • src/fluxopt/model.py
  • src/fluxopt/model_data.py
  • tests/math_port/test_multi_period.py

Reindex 1D and 2D DataArray inputs to the target period/build_period
coords so values map to the correct labels regardless of input ordering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Mar 14, 2026

Codecov Report

❌ Patch coverage is 94.73684% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/fluxopt/model_data.py 90.00% 5 Missing ⚠️
Files with missing lines Coverage Δ
src/fluxopt/__init__.py 100.00% <ø> (ø)
src/fluxopt/components.py 97.67% <100.00%> (ø)
src/fluxopt/elements.py 97.95% <100.00%> (ø)
src/fluxopt/model.py 97.29% <100.00%> (+<0.01%) ⬆️
src/fluxopt/types.py 87.50% <100.00%> (+0.97%) ⬆️
src/fluxopt/model_data.py 92.60% <90.00%> (-0.24%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/fluxopt/model_data.py (1)

83-84: ⚠️ Potential issue | 🟡 Minor

Add null check after reindexing 2D DataArray.

The 1D path (lines 87-92) validates for nulls after reindex, but the 2D path returns directly without checking. If the input value has period/build_period coordinates that don't fully cover the model's period index, reindex fills gaps with NaN, silently corrupting the cost matrix.

🐛 Proposed fix
         if vdims == {'period', 'build_period'}:
-            return value.reindex(period=period, build_period=period)
+            aligned = value.reindex(period=period, build_period=period)
+            if aligned.isnull().any():
+                raise ValueError(
+                    f'Once-effect DataArray (period, build_period) has coords that do not '
+                    f'fully cover the model periods {list(period)}'
+                )
+            return aligned
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fluxopt/model_data.py` around lines 83 - 84, The 2D branch that handles
vdims == {'period', 'build_period'} currently returns
value.reindex(period=period, build_period=period) without validating for NaNs;
change it to perform the reindex into a local variable (e.g., reindexed =
value.reindex(period=period, build_period=period)), then mirror the 1D-path null
check (e.g., if reindexed.isnull().any(): raise ValueError(...) or handle
consistently) before returning reindexed so missing period/build_period
coordinates don't silently produce NaNs in the cost matrix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/fluxopt/model_data.py`:
- Around line 83-84: The 2D branch that handles vdims == {'period',
'build_period'} currently returns value.reindex(period=period,
build_period=period) without validating for NaNs; change it to perform the
reindex into a local variable (e.g., reindexed = value.reindex(period=period,
build_period=period)), then mirror the 1D-path null check (e.g., if
reindexed.isnull().any(): raise ValueError(...) or handle consistently) before
returning reindexed so missing period/build_period coordinates don't silently
produce NaNs in the cost matrix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a947efa-3db1-4075-afa9-a7197cb43ee7

📥 Commits

Reviewing files that changed from the base of the PR and between c8fbb74 and 4daf890.

📒 Files selected for processing (1)
  • src/fluxopt/model_data.py

FBumann and others added 3 commits March 14, 2026 20:32
Cover all branches: scalar, list, 1D build_period, 1D period,
reordered coords alignment, 2D passthrough, 2D reordered, mismatched
coords error, and foreign dim error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the misleading TimeSeries type alias with ArrayInput — it accepts
scalars, sequences, and DataArrays, not just time-indexed data.

Add dimension-annotated variants via Annotated for IDE hover hints:
- TemporalInput (dims ⊆ {time})
- TemporalPeriodicInput (dims ⊆ {time, period})
- PeriodicInput (dims ⊆ {period})
- OnceEffectInput (dims ⊆ {period, build_period})

Uses TypeAlias assignments (not PEP 695 type statements) so that
mkdocstrings/griffe renders them as linkable, documented members.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

feat: add build_period axis to investment effect matrices

1 participant