Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 9
Feature/v3/several improvements#372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
e663d35a6741947d20b94d4d0dda12f120385d499ac2ecc284248f81487cce497c258d37ff43ab6b8ac3e69c50436f4986c4b57cfFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -53,49 +53,67 @@ def to_binary( | ||||||||||||||||||||||||||||||
| @staticmethod | ||||||||||||||||||||||||||||||
| def count_consecutive_states( | ||||||||||||||||||||||||||||||
| binary_values: xr.DataArray, | ||||||||||||||||||||||||||||||
| binary_values: xr.DataArray | np.ndarray | list[int, float], | ||||||||||||||||||||||||||||||
| dim: str = 'time', | ||||||||||||||||||||||||||||||
| epsilon: float = None, | ||||||||||||||||||||||||||||||
| epsilon: float | None = None, | ||||||||||||||||||||||||||||||
| ) -> float: | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| Counts the number of consecutive states in a binary time series. | ||||||||||||||||||||||||||||||
| """Count consecutive steps in the final active state of a binary time series. | ||||||||||||||||||||||||||||||
| This function counts how many consecutive time steps the series remains "on" | ||||||||||||||||||||||||||||||
| (non-zero) at the end of the time series. If the final state is "off", returns 0. | ||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||
| binary_values: Binary DataArray | ||||||||||||||||||||||||||||||
| dim: Dimension to count consecutive states over | ||||||||||||||||||||||||||||||
| epsilon: Tolerance for zero detection (uses CONFIG.modeling.EPSILON if None) | ||||||||||||||||||||||||||||||
| binary_values: Binary DataArray with values close to 0 (off) or 1 (on). | ||||||||||||||||||||||||||||||
| dim: Dimension along which to count consecutive states. | ||||||||||||||||||||||||||||||
| epsilon: Tolerance for zero detection. Uses CONFIG.modeling.EPSILON if None. | ||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||
| The consecutive number of steps spent in the final state of the timeseries | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| if epsilon is None: | ||||||||||||||||||||||||||||||
| epsilon = CONFIG.modeling.EPSILON | ||||||||||||||||||||||||||||||
| Sum of values in the final consecutive "on" period. Returns 0.0 if the | ||||||||||||||||||||||||||||||
| final state is "off". | ||||||||||||||||||||||||||||||
| Examples: | ||||||||||||||||||||||||||||||
| >>> arr = xr.DataArray([0, 0, 1, 1, 1, 0, 1, 1], dims=['time']) | ||||||||||||||||||||||||||||||
| >>> ModelingUtilitiesAbstract.count_consecutive_states(arr) | ||||||||||||||||||||||||||||||
| 2.0 | ||||||||||||||||||||||||||||||
| binary_values = binary_values.any(dim=[d for d in binary_values.dims if d != dim]) | ||||||||||||||||||||||||||||||
| >>> arr = [0, 0, 1, 0, 1, 1, 1, 1] | ||||||||||||||||||||||||||||||
| >>> ModelingUtilitiesAbstract.count_consecutive_states(arr) | ||||||||||||||||||||||||||||||
| 4.0 | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| epsilon = epsilon or CONFIG.modeling.EPSILON | ||||||||||||||||||||||||||||||
| if isinstance(binary_values, xr.DataArray): | ||||||||||||||||||||||||||||||
| # xarray path | ||||||||||||||||||||||||||||||
| other_dims = [d for d in binary_values.dims if d != dim] | ||||||||||||||||||||||||||||||
| if other_dims: | ||||||||||||||||||||||||||||||
| binary_values = binary_values.any(dim=other_dims) | ||||||||||||||||||||||||||||||
| arr = binary_values.values | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| # numpy/array-like path | ||||||||||||||||||||||||||||||
| arr = np.asarray(binary_values) | ||||||||||||||||||||||||||||||
| # Handle scalar case | ||||||||||||||||||||||||||||||
| if binary_values.ndim == 0: | ||||||||||||||||||||||||||||||
| return float(binary_values.item()) | ||||||||||||||||||||||||||||||
| # Flatten to 1D if needed | ||||||||||||||||||||||||||||||
| arr = arr.ravel() if arr.ndim > 1 else arr | ||||||||||||||||||||||||||||||
| # Check if final state is off | ||||||||||||||||||||||||||||||
| if np.isclose(binary_values.isel({dim: -1}), 0, atol=epsilon).all(): | ||||||||||||||||||||||||||||||
| # Handle edge cases | ||||||||||||||||||||||||||||||
| if arr.size == 0: | ||||||||||||||||||||||||||||||
| return 0.0 | ||||||||||||||||||||||||||||||
| if arr.size == 1: | ||||||||||||||||||||||||||||||
| return float(arr[0]) if not np.isclose(arr[0], 0, atol=epsilon) else 0.0 | ||||||||||||||||||||||||||||||
| # Find consecutive 'on' period from the end | ||||||||||||||||||||||||||||||
| is_zero = np.isclose(binary_values, 0, atol=epsilon) | ||||||||||||||||||||||||||||||
| # Return 0 if final state is off | ||||||||||||||||||||||||||||||
| if np.isclose(arr[-1], 0, atol=epsilon): | ||||||||||||||||||||||||||||||
| return 0.0 | ||||||||||||||||||||||||||||||
| # Find the last zero, then sum everything after it | ||||||||||||||||||||||||||||||
| # Find the last zero position (treat NaNs as off) | ||||||||||||||||||||||||||||||
| arr = np.nan_to_num(arr, nan=0.0) | ||||||||||||||||||||||||||||||
| is_zero = np.isclose(arr, 0, atol=epsilon) | ||||||||||||||||||||||||||||||
| zero_indices = np.where(is_zero)[0] | ||||||||||||||||||||||||||||||
| if len(zero_indices) == 0: | ||||||||||||||||||||||||||||||
| # All 'on' - sum everything | ||||||||||||||||||||||||||||||
| start_idx = 0 | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| # Start after last zero | ||||||||||||||||||||||||||||||
| start_idx = zero_indices[-1] + 1 | ||||||||||||||||||||||||||||||
| consecutive_values = binary_values.isel({dim: slice(start_idx, None)}) | ||||||||||||||||||||||||||||||
| # Calculate sum from last zero to end | ||||||||||||||||||||||||||||||
| start_idx = zero_indices[-1] + 1 if zero_indices.size > 0 else 0 | ||||||||||||||||||||||||||||||
| return float(consecutive_values.sum().item()) # TODO: Som only over one dim? | ||||||||||||||||||||||||||||||
| return float(np.sum(arr[start_idx:])) | ||||||||||||||||||||||||||||||
| class ModelingUtilities: | ||||||||||||||||||||||||||||||
| @@ -308,7 +326,13 @@ def consecutive_duration_tracking( | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| # Handle initial condition for minimum duration | ||||||||||||||||||||||||||||||
| if previous_duration > 0 and previous_duration < minimum_duration.isel({duration_dim: 0}).max(): | ||||||||||||||||||||||||||||||
| prev = ( | ||||||||||||||||||||||||||||||
| float(previous_duration) | ||||||||||||||||||||||||||||||
| if not isinstance(previous_duration, xr.DataArray) | ||||||||||||||||||||||||||||||
| else float(previous_duration.max().item()) | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| min0 = float(minimum_duration.isel({duration_dim: 0}).max().item()) | ||||||||||||||||||||||||||||||
| if prev > 0 and prev < min0: | ||||||||||||||||||||||||||||||
Comment on lines
+329
to
+335
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use duration_dim instead of hard-coded 'time'. Hard-coding time ignores the duration_dim parameter and breaks for other coordinate names. - prev = (- float(previous_duration)- if not isinstance(previous_duration, xr.DataArray)- else float(previous_duration.max().item())- )- min0 = float(minimum_duration.isel(time=0).max().item())+ prev = (+ float(previous_duration)+ if not isinstance(previous_duration, xr.DataArray)+ else float(previous_duration.max().item())+ )+ min0 = float(minimum_duration.isel({duration_dim: 0}).max().item())📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents | ||||||||||||||||||||||||||||||
| constraints['initial_lb'] = model.add_constraints( | ||||||||||||||||||||||||||||||
| state_variable.isel({duration_dim: 0}) == 1, name=f'{duration.name}|initial_lb' | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| @@ -435,7 +459,7 @@ def bounds_with_state( | ||||||||||||||||||||||||||||||
| lower_bound, upper_bound = bounds | ||||||||||||||||||||||||||||||
| name = name or f'{variable.name}' | ||||||||||||||||||||||||||||||
| if np.all(lower_bound - upper_bound) < 1e-10: | ||||||||||||||||||||||||||||||
| if np.allclose(lower_bound, upper_bound, atol=1e-10, equal_nan=True): | ||||||||||||||||||||||||||||||
| fix_constraint = model.add_constraints(variable == variable_state * upper_bound, name=f'{name}|fix') | ||||||||||||||||||||||||||||||
| return [fix_constraint] | ||||||||||||||||||||||||||||||
| @@ -481,7 +505,7 @@ def scaled_bounds( | ||||||||||||||||||||||||||||||
| rel_lower, rel_upper = relative_bounds | ||||||||||||||||||||||||||||||
| name = name or f'{variable.name}' | ||||||||||||||||||||||||||||||
| if np.abs(rel_lower - rel_upper).all() < 10e-10: | ||||||||||||||||||||||||||||||
| if np.allclose(rel_lower, rel_upper, atol=1e-10, equal_nan=True): | ||||||||||||||||||||||||||||||
| return [model.add_constraints(variable == scaling_variable * rel_lower, name=f'{name}|fixed')] | ||||||||||||||||||||||||||||||
| upper_constraint = model.add_constraints(variable <= scaling_variable * rel_upper, name=f'{name}|ub') | ||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: ambiguous truth value with xarray and improve NaN handling for “constant” detection.
(da.max(dim) == da.min(dim)).all()returns a 0‑D DataArray; using it directly inifcan raise “truth value is ambiguous.” It may also fail to treat “all‑NaN” slices as constant. Replace with a robust check that handles non‑numeric dtypes and NaNs.Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents