Skip to content

Fix/small fixes#346

Merged
FBumann merged 9 commits into
mainfrom
fix/small-fixes
Sep 22, 2025
Merged

Fix/small fixes#346
FBumann merged 9 commits into
mainfrom
fix/small-fixes

Conversation

@FBumann
Copy link
Copy Markdown
Member

@FBumann FBumann commented Sep 22, 2025

Description

Apply multiple small fixes

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Code refactoring

Related Issues

Closes #(issue number)

Testing

  • I have tested my changes
  • Existing tests still pass

Checklist

  • My code follows the project style
  • I have updated documentation if needed
  • I have added tests for new functionality (if applicable)

Summary by CodeRabbit

  • Bug Fixes

    • Corrected COP behavior for heat pump components so conversion factors and electrical/thermal relationships are consistent.
    • Fixed total_max handling when total_min is absent.
    • Saved NetCDF datasets now honor user-specified compression levels.
    • Validation message for consecutive-hour checks now reports the correct count and tests align with the raised error.
  • Documentation

    • Changelog updated under Unreleased -> Fixed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 22, 2025

Warning

Rate limit exceeded

@FBumann has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 58 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between dcabd80 and 02bba03.

📒 Files selected for processing (3)
  • CHANGELOG.md (2 hunks)
  • flixopt/linear_converters.py (1 hunks)
  • tests/test_on_hours_computation.py (2 hunks)

Walkthrough

Updates COP getter/setter logic for HeatPump and HeatPumpWithSource, fixes ShareAllocationModel total_max initialization, corrects an error message, enables configurable NetCDF compression level, and documents three fixes in CHANGELOG. No public API signatures changed.

Changes

Cohort / File(s) Summary of changes
Changelog
CHANGELOG.md
Added three Unreleased fixes: COP getter/setter correctness for HeatPumpWithSource, custom compression levels in io.save_dataset_to_netcdf, and total_max behavior when total_min not used.
I/O: NetCDF compression
flixopt/io.py
save_dataset_to_netcdf: when apply_encoding=True, per-variable encoding now uses the provided compression value for complevel instead of a hard-coded 5.
Linear converters: COP behavior
flixopt/linear_converters.py
HeatPump and HeatPumpWithSource: COP getter now returns factor tied to P_el; COP setter enforces COP * P_el == Q_th by updating P_el and Q_th and adjusting the second conversion mapping accordingly.
Features: messaging & allocation
flixopt/features.py
compute_consecutive_hours_in_state: error message now reports the numeric nr_of_indexes_with_consecutive_ones value. ShareAllocationModel: internal _total_max defaulting now uses total_max (defaults to ∞ when None) rather than deriving from total_min.
Tests
tests/test_on_hours_computation.py
Test updated to expect ValueError instead of TypeError for an incompatible-input error from compute_consecutive_hours_in_state.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User Code
  participant IO as io.save_dataset_to_netcdf
  participant DS as xarray.Dataset
  participant FS as Filesystem

  U->>IO: save_dataset_to_netcdf(ds, path, compression=k, apply_encoding=True)
  IO->>DS: build per-variable encoding {zlib: true, complevel: k}
  note right of DS #D3E4CD: complevel uses provided k (not fixed 5)
  IO->>FS: write NetCDF with encoding
  FS-->>U: file persisted
Loading
sequenceDiagram
  autonumber
  participant U as Caller
  participant HP as HeatPump / HeatPumpWithSource
  participant C1 as Conversion 1
  participant C2 as Conversion 2

  U->>HP: set COP(value)
  HP->>C1: set P_el = COP, set Q_th = 1
  HP->>C2: set Q_ab = Q_th*(COP-1)/COP  -- with Q_th=1
  HP-->>U: internal conversions updated (COP tied to P_el)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my ears at settings fine,
COPs aligned, conversions shine.
A hop to netCDF, compressed just right—
complevel set, the bytes feel light.
Bounds now true, the logs explain—thump!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description uses the repository template but is minimal and leaves key fields incomplete: the Description is a one-line "Apply multiple small fixes", the "Related Issues" field contains a placeholder, and the Testing section provides no details or checked verification boxes. Because it does not enumerate the specific fixes, affected files, or testing/CI results, reviewers cannot easily assess the scope or test coverage. These omissions make the description insufficient for an informed review. Expand the Description to list the specific fixes and affected files, replace or remove the "Closes #(issue number)" placeholder with the actual issue reference if applicable, and update the Testing section with what was run and the CI status (or check the boxes if tests passed). Also add brief notes about any behavior changes or migration steps and reference the changelog entry or modified tests so reviewers can verify the fixes.
Title Check ❓ Inconclusive The PR title "Fix/small fixes" is vague and does not convey the primary change(s) in the diff. The changeset includes distinct fixes (HeatPump/HeatPumpWithSource COP conversion behavior, save_dataset_to_netcdf compression level, compute_consecutive_hours_in_state message/test update, and a changelog entry) but the title gives no indication which is the main focus and reads like a branch name. Because it uses non-descriptive language and a slash, the title is inconclusive for reviewers scanning history. Please replace the title with a concise, single-sentence summary that highlights the dominant fix (avoid slashes and generic phrases); for example "Fix: HeatPump COP conversions, netCDF compression level, and consecutive-hours error handling" or "Fix: HeatPump COP handling and save_dataset_to_netcdf compression."
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

Copy link
Copy Markdown
Contributor

@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

Caution

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

⚠️ Outside diff range comments (2)
flixopt/io.py (1)

219-238: Bug: path may be str; .suffix will crash.

save_dataset_to_netcdf accepts Union[str, Path] but uses path.suffix directly. Coerce to Path first.

 def save_dataset_to_netcdf(
     ds: xr.Dataset,
     path: Union[str, pathlib.Path],
     compression: int = 0,
 ) -> None:
@@
-    if path.suffix not in ['.nc', '.nc4']:
+    # Normalize to Path
+    path = pathlib.Path(path) if not isinstance(path, pathlib.Path) else path
+    if path.suffix not in ['.nc', '.nc4']:
         raise ValueError(f'Invalid file extension for path {path}. Only .nc and .nc4 are supported')
@@
-        else {data_var: {'zlib': True, 'complevel': compression} for data_var in ds.data_vars},
+        else {data_var: {'zlib': True, 'complevel': int(compression)} for data_var in ds.data_vars},
flixopt/linear_converters.py (1)

547-568: Validate COP before building conversion_factors in init.

The constructor computes COP/(COP-1) before any checks; if COP==1 (or contains 1), this will explode. Validate first.

     def __init__(
         self,
         label: str,
         COP: NumericDataTS,
         P_el: Flow,
         Q_ab: Flow,
         Q_th: Flow,
         on_off_parameters: OnOffParameters = None,
         meta_data: Optional[Dict] = None,
     ):
-        # super:
+        # Validate COP strictly > 1 to avoid division by zero
+        check_bounds(COP, 'COP', label, 1, 20)
+        _arr = COP.data if isinstance(COP, TimeSeriesData) else COP
+        if np.any(np.asarray(_arr) <= 1):
+            raise ValueError(f'HeatPumpWithSource({label}).COP must be strictly > 1.')
+        # Build equations
         electricity = {P_el.label: COP, Q_th.label: 1}
         heat_source = {Q_ab.label: COP / (COP - 1), Q_th.label: 1}
🧹 Nitpick comments (3)
CHANGELOG.md (1)

48-50: Polish wording and formatting for consistency.

Add backticks and periods; clarify the last bullet.

- - Fix custom compression levels in io.save_dataset_to_netcdf
- - Fix `total_max` did not work when total min was not used.
+- Fix custom compression levels in `io.save_dataset_to_netcdf`.
+- Fix `total_max` not applied when `total_min` was not used.
flixopt/io.py (1)

222-231: Validate compression range (0–9).

Prevent invalid values early instead of relying on downstream errors.

-    apply_encoding = False
+    if not (0 <= int(compression) <= 9):
+        raise ValueError(f'Invalid compression={compression}. Must be an integer in [0, 9].')
+    apply_encoding = False
flixopt/features.py (1)

593-597: Docstring mismatch: raises ValueError, not TypeError.

Align the “Raises” section with the implementation.

-        Raises
-        ------
-        TypeError
-            If the length of binary_values and dt_in_hours is not equal, but None is a scalar.
+        Raises
+        ------
+        ValueError
+            If the last consecutive duration length exceeds the provided hours_per_timestep.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3069f23 and a139c24.

📒 Files selected for processing (4)
  • CHANGELOG.md (1 hunks)
  • flixopt/features.py (2 hunks)
  • flixopt/io.py (1 hunks)
  • flixopt/linear_converters.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
flixopt/linear_converters.py (2)
flixopt/structure.py (3)
  • label (381-382)
  • label_full (278-279)
  • label_full (385-391)
flixopt/elements.py (1)
  • label_full (471-472)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
🔇 Additional comments (3)
flixopt/features.py (2)

616-621: LGTM: clearer error detail.

Message now reports the actual consecutive-on length; good improvement.


946-952: Defaulting to ±∞ is fine; confirm solver tolerance.

Switching None→±∞ means bounds are always applied (often as ±∞). Confirm linopy/solver handles infinite bounds without numeric side effects in this context.

Would you run a small scenario ensuring total_per_timestep lower/upper accept ±∞ and don’t emit solver warnings?

flixopt/linear_converters.py (1)

575-576: LGTM: COP getter now tied to P_el.

Getter correctly reflects the new mapping.

Comment on lines 573 to 586
@property
def COP(self): # noqa: N802
return self.conversion_factors[0][self.Q_th.label]
return self.conversion_factors[0][self.P_el.label]

@COP.setter
def COP(self, value): # noqa: N802
check_bounds(value, 'COP', self.label_full, 1, 20)
self.conversion_factors[0][self.Q_th.label] = value
self.conversion_factors[1][self.Q_th.label] = value / (value - 1)
# electricity equation: COP * P_el == 1 * Q_th
self.conversion_factors[0][self.P_el.label] = value
self.conversion_factors[0][self.Q_th.label] = 1
# heat source equation: (COP/(COP-1)) * Q_ab == 1 * Q_th -> Q_ab = Q_th*(COP-1)/COP
self.conversion_factors[1][self.Q_ab.label] = value / (value - 1)
self.conversion_factors[1][self.Q_th.label] = 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against COP ≤ 1 to avoid division by zero/infinity.

Setter computes value/(value-1). With COP==1 this blows up; check_bounds only warns. Enforce strictly >1.

 @COP.setter
 def COP(self, value):  # noqa: N802
-    check_bounds(value, 'COP', self.label_full, 1, 20)
-    # electricity equation: COP * P_el == 1 * Q_th
-    self.conversion_factors[0][self.P_el.label] = value
-    self.conversion_factors[0][self.Q_th.label] = 1
-    # heat source equation: (COP/(COP-1)) * Q_ab == 1 * Q_th  -> Q_ab = Q_th*(COP-1)/COP
-    self.conversion_factors[1][self.Q_ab.label] = value / (value - 1)
-    self.conversion_factors[1][self.Q_th.label] = 1
+    check_bounds(value, 'COP', self.label_full, 1, 20)
+    # Strict validation: avoid singularity at COP <= 1
+    arr = value.data if isinstance(value, TimeSeriesData) else value
+    if np.any(np.asarray(arr) <= 1):
+        raise ValueError(f'{self.label_full}.COP must be strictly > 1 for HeatPumpWithSource.')
+    # electricity equation: COP * P_el == 1 * Q_th
+    self.conversion_factors[0][self.P_el.label] = value
+    self.conversion_factors[0][self.Q_th.label] = 1
+    # heat source equation: (COP/(COP-1)) * Q_ab == 1 * Q_th  -> Q_ab = Q_th*(COP-1)/COP
+    self.conversion_factors[1][self.Q_ab.label] = value / (value - 1)
+    self.conversion_factors[1][self.Q_th.label] = 1
📝 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.

Suggested change
@property
def COP(self): # noqa: N802
return self.conversion_factors[0][self.Q_th.label]
return self.conversion_factors[0][self.P_el.label]
@COP.setter
def COP(self, value): # noqa: N802
check_bounds(value, 'COP', self.label_full, 1, 20)
self.conversion_factors[0][self.Q_th.label] = value
self.conversion_factors[1][self.Q_th.label] = value / (value - 1)
# electricity equation: COP * P_el == 1 * Q_th
self.conversion_factors[0][self.P_el.label] = value
self.conversion_factors[0][self.Q_th.label] = 1
# heat source equation: (COP/(COP-1)) * Q_ab == 1 * Q_th -> Q_ab = Q_th*(COP-1)/COP
self.conversion_factors[1][self.Q_ab.label] = value / (value - 1)
self.conversion_factors[1][self.Q_th.label] = 1
@property
def COP(self): # noqa: N802
return self.conversion_factors[0][self.P_el.label]
@COP.setter
def COP(self, value): # noqa: N802
check_bounds(value, 'COP', self.label_full, 1, 20)
# Strict validation: avoid singularity at COP <= 1
arr = value.data if isinstance(value, TimeSeriesData) else value
if np.any(np.asarray(arr) <= 1):
raise ValueError(f'{self.label_full}.COP must be strictly > 1 for HeatPumpWithSource.')
# electricity equation: COP * P_el == 1 * Q_th
self.conversion_factors[0][self.P_el.label] = value
self.conversion_factors[0][self.Q_th.label] = 1
# heat source equation: (COP/(COP-1)) * Q_ab == 1 * Q_th -> Q_ab = Q_th*(COP-1)/COP
self.conversion_factors[1][self.Q_ab.label] = value / (value - 1)
self.conversion_factors[1][self.Q_th.label] = 1
🤖 Prompt for AI Agents
In flixopt/linear_converters.py around lines 573-586, the COP setter can perform
value/(value-1) which will divide by zero or produce infinite values when COP ==
1; enforce COP > 1 by adding an explicit guard before doing the division: if
value <= 1 raise a ValueError (or use the project's preferred exception) with a
clear message (e.g. "COP must be > 1"), then proceed to set conversion_factors
as before; do not rely solely on check_bounds which currently allows 1.

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (3)
tests/test_on_hours_computation.py (3)

7-9: Rename test class/docstring to reflect the actual API name.

They target compute_consecutive_hours_in_state, not “consecutive duration”.

-class TestComputeConsecutiveDuration:
-    """Tests for the compute_consecutive_duration static method."""
+class TestComputeConsecutiveHoursInState:
+    """Tests for compute_consecutive_hours_in_state."""

45-46: Optionally assert error message content for stronger guarantees.

If the implementation now includes details (e.g., lengths), add a regex match.

-        with pytest.raises(ValueError):
+        with pytest.raises(ValueError, match=r"(length|shape|incompatible)"):
             ConsecutiveStateModel.compute_consecutive_hours_in_state(binary_values, hours_per_timestep)

70-73: Fix misleading comment (“2D arrays” are not used).

Minor clarity tweak.

-            # Case 6: Mix of None, 1D and 2D arrays
+            # Case 6: Mix of None and 1D arrays
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a139c24 and dcabd80.

📒 Files selected for processing (1)
  • tests/test_on_hours_computation.py (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
🔇 Additional comments (1)
tests/test_on_hours_computation.py (1)

45-46: Approve: ValueError change is correct.

compute_consecutive_hours_in_state (flixopt/features.py) raises ValueError (length check); tests/test_on_hours_computation.py now expects ValueError and I found no tests still asserting TypeError for this API.

@FBumann FBumann merged commit 23b23d1 into main Sep 22, 2025
7 checks passed
@FBumann FBumann deleted the fix/small-fixes branch September 22, 2025 10:02
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