Skip to content

refac: rename cross-effect API and add once domain#106

Merged
FBumann merged 3 commits intofeat/component-status-piecewisefrom
refac/cross-effect-api
Mar 16, 2026
Merged

refac: rename cross-effect API and add once domain#106
FBumann merged 3 commits intofeat/component-status-piecewisefrom
refac/cross-effect-api

Conversation

@FBumann
Copy link
Owner

@FBumann FBumann commented Mar 16, 2026

Summary

  • Rename Effect.contribution_fromEffect.cross_periodic and Effect.contribution_from_per_hourEffect.cross_temporal
  • Add Effect.cross_once for one-time investment cross-effects (e.g., embodied CO2 → carbon cost)
  • Add cf_once Leontief pass in both solver (model.py) and attribution (contributions.py)
  • Update all tests and documentation

Closes #105.

BREAKING CHANGE: contribution_from and contribution_from_per_hour removed. The old contribution_from set both periodic and temporal matrices; the new API requires explicit per-domain fields.

Test plan

  • uv run pytest tests/ -q — 442 passed, 179 skipped, 11 xfailed
  • uv run mypy src/ — no issues
  • uv run ruff check . — all checks passed
  • New test: test_cross_once_prices_embodied_emissions — verifies Investment CO2 priced into cost via cross_once
  • Existing cross-effect tests updated and passing

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Restructured cross-effect API with domain-specific fields: periodic, temporal, and one-time contributions are now separated for clearer effect modeling and relationships.
  • Documentation

    • Updated comprehensive guides and mathematical notation to reflect the new cross-effect field organization and per-domain semantics.

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9b62ecec-4b99-4eb5-bc3f-b605e88b1cac

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Cross-effect API renamed from contribution_from/contribution_from_per_hour to domain-explicit cross_temporal, cross_periodic, and cross_once fields. Leontief inverse application added for once-domain cross-effects. Documentation, core library, and tests updated throughout.

Changes

Cohort / File(s) Summary
Documentation
docs/guide/effects.md, docs/math/effects.md, docs/math/notation.md
Terminology and examples updated to replace single contribution_from field with three domain-specific fields (cross_temporal, cross_periodic, cross_once). Mathematical notation and parameter tables reflect new cross-effect symbols; semantics clarified per domain (temporal per-timestep, periodic for sizing, once for one-time investments).
Core API & Data Structure
src/fluxopt/elements.py
Effect dataclass fields restructured: removed contribution_from and contribution_from_per_hour, added cross_periodic, cross_temporal, and cross_once (each dict[str, TimeSeries]).
Model Data
src/fluxopt/model_data.py
EffectsData extended with new cf_once field; build() method now constructs once-domain cross-effect matrix alongside existing periodic/temporal matrices. Self-reference and cycle checks updated to operate on new cross-effect fields.
Model & Constraint Logic
src/fluxopt/model.py, src/fluxopt/contributions.py
Once-domain effects now include cross-effect term via cf_once Leontief application; error message clarified to reference "circular cross-effect chains". Contribution computation updated to apply Leontief inverse to once contributions.
Math & Multi-Period Tests
tests/math/test_contributions.py, tests/math/test_effects.py, tests/math_port/test_effects.py, tests/math_port/test_multi_period.py
All cross-effect test construction migrated from contribution_from/contribution_from_per_hour to cross_periodic/cross_temporal. New tests added for cross_once behavior; docstrings and error messages updated to reflect cross-effect terminology.
I/O Roundtrip
tests/test_io.py
Roundtrip test class and method renamed; assertions added to verify cf_periodic and cf_temporal preservation, with updates to use new cross-effect constructor parameters.

Sequence Diagram

sequenceDiagram
    participant Build as EffectsData.build()
    participant Matrix as cf_once Matrix
    participant Model as Model._create_effects()
    participant Leontief as Leontief Inverse
    participant Constraint as effect_once Constraint

    Build->>Build: Extract cross_once from Effect fields
    Build->>Matrix: Construct once_mat from cross_once
    Build->>Build: Initialize cf_once with once_mat if non-zero

    Model->>Model: Compute once_direct from effect definition
    Model->>Leontief: Apply Leontief(cf_once) to propagate cross-effects
    Leontief->>Model: Return cross-term contribution
    Model->>Model: once_rhs = cross + once_direct
    Model->>Constraint: Add constraint: effect_once == once_rhs
    Constraint->>Constraint: Enforce once-domain with cross-effect propagation
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #94: Modifies the effect cross-contribution API and updates model_data.py contribution matrices—both PRs restructure the same Effect cross-contribution fields and their matrix representations.
  • PR #95: Changes to src/fluxopt/model_data.py array/template construction used by EffectsData.build()—directly overlaps with cf_once matrix construction in this PR.
  • PR #81: Modifies once-domain effect machinery (effect_once, Investment one-time costs)—this PR extends that work by wiring cf_once through the same once-domain pipeline.

Poem

🐰 Three domains hop through the field,
Cross-temporal, periodic, sealed—
Once-domain joins the Leontief chain,
No more shadows in the refrain!
contrib fields fade into night,
cross effects dance in the light. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely describes the main change: renaming the cross-effect API and adding the once domain, which aligns with the primary refactoring objective.
Description check ✅ Passed The description covers all required sections: summary of changes, type (refactoring), linked issues, testing results, and checklist items. It clearly explains the breaking change and provides comprehensive test coverage.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #105: renamed Effect fields (cross_temporal, cross_periodic, cross_once), added cf_once matrix construction, applied Leontief to effect_once and once contributions, updated serialization, and updated all tests.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #105 requirements. Documentation updates (guide and math docs) clarify the new API, and error message updates reflect the new cross-effect terminology, all within scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refac/cross-effect-api
📝 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.

@FBumann
Copy link
Owner Author

FBumann commented Mar 16, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 96.15385% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/fluxopt/contributions.py 66.66% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/fluxopt/elements.py 89.72% <100.00%> (+0.07%) ⬆️
src/fluxopt/model.py 95.28% <100.00%> (+0.02%) ⬆️
src/fluxopt/model_data.py 94.33% <100.00%> (+0.29%) ⬆️
src/fluxopt/contributions.py 97.43% <66.66%> (+0.02%) ⬆️
🚀 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/math/test_contributions.py (1)

143-143: Consider adding test coverage for cross_once.

This file thoroughly tests cross_periodic and cross_temporal, but the new cross_once field (one-time investment cross-effects) doesn't have dedicated tests here. Given that the PR adds cf_once Leontief application in both model.py and contributions.py, a test similar to test_sizing_cross_effect_investment but using cross_once would validate the new functionality.

Would you like me to draft a test case for cross_once covering one-time investment cross-effects (e.g., embodied emissions in CAPEX)?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/math/test_contributions.py` at line 143, Add a new unit test exercising
the new cross_once behavior: mirror the existing
test_sizing_cross_effect_investment but use Effect(..., cross_once={'co2':
<value>}) and ensure the contributions pipeline applies cf_once (the Leontief
one-time cross-effect) from model.py/contributions.py, asserting the one-time
embodied emissions are added to CAPEX/sizing contributions as expected;
reference the Effect creation with cross_once, the cf_once application path in
contributions.py, and the model's handling of cf_once to locate where to
validate the result.
🤖 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/contributions.py`:
- Around line 404-407: The module docstring claims "One-time costs bypass the
Leontief pass" but the implementation now applies the Leontief inverse to once
when data.effects.cf_once is present (see variables/functions: once,
data.effects.cf_once, _leontief, _apply_leontief); update the module docstring
(the paragraph around lines 7-9) to state that temporal, periodic and once
domains all support cross-effect propagation via the Leontief inverse (or
rephrase to reflect that one-time costs no longer bypass the Leontief pass) so
the documentation matches the code behavior.

---

Nitpick comments:
In `@tests/math/test_contributions.py`:
- Line 143: Add a new unit test exercising the new cross_once behavior: mirror
the existing test_sizing_cross_effect_investment but use Effect(...,
cross_once={'co2': <value>}) and ensure the contributions pipeline applies
cf_once (the Leontief one-time cross-effect) from model.py/contributions.py,
asserting the one-time embodied emissions are added to CAPEX/sizing
contributions as expected; reference the Effect creation with cross_once, the
cf_once application path in contributions.py, and the model's handling of
cf_once to locate where to validate the result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5c46a7e-ec63-417d-8b03-b3ca822364aa

📥 Commits

Reviewing files that changed from the base of the PR and between 30c5395 and 4b1667e.

📒 Files selected for processing (12)
  • docs/guide/effects.md
  • docs/math/effects.md
  • docs/math/notation.md
  • src/fluxopt/contributions.py
  • src/fluxopt/elements.py
  • src/fluxopt/model.py
  • src/fluxopt/model_data.py
  • tests/math/test_contributions.py
  • tests/math/test_effects.py
  • tests/math_port/test_effects.py
  • tests/math_port/test_multi_period.py
  • tests/test_io.py

Comment on lines +404 to +407
# Cross-effects on once via Leontief inverse
if data.effects.cf_once is not None:
once = _apply_leontief(_leontief(data.effects.cf_once), once)

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Module docstring is now inconsistent with the implementation.

The module docstring (lines 7-9) states: "One-time costs bypass the Leontief pass". However, this code now applies the Leontief inverse to once when cf_once is present. The docstring should be updated to reflect that all three domains (temporal, periodic, once) now support cross-effect propagation via Leontief inverse.

📝 Suggested docstring fix (lines 7-9)
-Cross-effects use the Leontief inverse: total = (I - C)^-1 * direct,
-where C is the cross-effect coefficient matrix. One-time costs bypass
-the Leontief pass (matching the solver's ``effect_once`` variable).
+Cross-effects use the Leontief inverse: total = (I - C)^-1 * direct,
+where C is the cross-effect coefficient matrix. All three domains
+(temporal, periodic, once) apply Leontief when cross-effects are defined.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fluxopt/contributions.py` around lines 404 - 407, The module docstring
claims "One-time costs bypass the Leontief pass" but the implementation now
applies the Leontief inverse to once when data.effects.cf_once is present (see
variables/functions: once, data.effects.cf_once, _leontief, _apply_leontief);
update the module docstring (the paragraph around lines 7-9) to state that
temporal, periodic and once domains all support cross-effect propagation via the
Leontief inverse (or rephrase to reflect that one-time costs no longer bypass
the Leontief pass) so the documentation matches the code behavior.

@FBumann FBumann changed the base branch from fix/pr99-review-comments to feat/component-status-piecewise March 16, 2026 21:06
FBumann and others added 3 commits March 16, 2026 22:07
Replace `contribution_from` / `contribution_from_per_hour` with explicit
per-domain fields on Effect:
- `cross_periodic` — sizing + recurring investment (Leontief on effect_periodic)
- `cross_temporal` — per-timestep operational (Leontief on effect_temporal)
- `cross_once` — one-time investment costs (Leontief on effect_once)

This enables pricing embodied emissions from investment through cross-effects
(e.g., construction CO2 → carbon cost), which was previously impossible.

BREAKING CHANGE: `Effect.contribution_from` and `Effect.contribution_from_per_hour`
are removed. The old `contribution_from` set both periodic and temporal matrices;
the new API requires setting each domain explicitly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix module docstring: all three domains now support Leontief, not just
  temporal and periodic
- Add TestCrossOnce with test verifying embodied CO2 priced into cost
  via cross_once Leontief pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove per-field unknown-effect checks in model_data.py (dead code —
the early cycle-detection check catches unknown effects first).
Add test_cross_effect_unknown_effect_raises to cover the early check.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@FBumann FBumann force-pushed the refac/cross-effect-api branch from 73e30c0 to 0272c27 Compare March 16, 2026 21:08
@FBumann FBumann merged commit 48f3375 into feat/component-status-piecewise Mar 16, 2026
10 checks passed
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