Skip to content

9 various tidy-ups and loud failing - #430

Open
dfulu wants to merge 13 commits into
dev_feb2026_speedupsfrom
9_various
Open

9 various tidy-ups and loud failing#430
dfulu wants to merge 13 commits into
dev_feb2026_speedupsfrom
9_various

Conversation

@dfulu

@dfulu dfulu commented Jul 31, 2026

Copy link
Copy Markdown
Member

Pull Request

Description

This PR wraps up a lot of small changes mainly aimed at the code failing early and raising helpful error messages

Checklist:

  • My code follows OCF's coding style guidelines
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • I have checked my code and corrected any misspellings

@dfulu
dfulu changed the base branch from main to 8_split_generation July 31, 2026 16:06
"""

@override
def __getattr__(self, item: str) -> NWP:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

we don't use this and don't need it

return iter(self.root)

def keys(self) -> Iterator[str]:
def keys(self) -> KeysView[str]:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

the old type hints were wrong

if time_periods is not None:
mask = get_time_periods_mask(valid_t0_times, time_periods)
if not mask.any():
raise ValueError(f"`time_periods` {time_periods} excluded all valid t0 times")

@dfulu dfulu Jul 31, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We raise this here instead of letting the dataset be empty and raise later further away from the source

)

if len(valid_t0_times) == 0:
raise ValueError(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This catches a separate error where there are some time periods available but none intersect with the t0 resolution. As in we could have a time period 00:10 -> 00:20 but if we have time resolution 30mins then we get no valid t0s

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.

Was always silent / empty before right? Good one!

return valid_t0_times

@staticmethod
def find_valid_t0_and_location_ids(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just did a bit of refactoring and cleaning up in this function

Comment thread src/ocf_data_sampler/datasets/pvnet/dataset.py Outdated
valid_t0_per_location["location_id"] = location.id
valid_t0_and_location_ids.append(valid_t0_per_location)
if len(valid_t0_times) == 0:
logger.warning(f"No valid t0 times found for location {location.id}")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think this is good to warn about since the model could silently train without ever seeing this location

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This could have been an error but maybe that's too strict

Comment thread README.md
## Documentation

**ocf-data-sampler** doesn't have external documentation _yet_; you can read a bit about how our torch datasets work in the README [here](ocf_data_sampler/torch_datasets/README.md).
**ocf-data-sampler** doesn't have external documentation _yet_; you can read a bit about how our torch datasets work in the README [here](src/ocf_data_sampler/datasets/pvnet/README.md).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This was out of date

def __setstate__(self, state: dict) -> None:
"""Restore object from pickle, reloading from presaved file if possible."""
self.__dict__.update(state)
if self._pickle_path and os.path.exists(self._pickle_path):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

There was a chance here that this would fail weirdly if the pickled dataset was deleted or unavailable. So we handle this more explicitly now

max_staleness=max_staleness,
)

if len(time_periods) == 0:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The find_contiguous_t0_periods[_nwp] functions raise their own errors if no periods are found, so we never reach these

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not reaching these was a bug, but perhaps we want to reach these. But that would require us to define what find_contiguous_t0_periods[_nwp] returns if there are no time periods. Is it just an empty dataframe?

for start_time, end_time in time_periods:

start_time = times[0] if start_time is None else np.datetime64(start_time)
end_time = times[-1] if end_time is None else np.datetime64(end_time)

@dfulu dfulu Aug 4, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The new version is slightly more robust and general since it doesn't rely on times being sorted and the [-1] indexer working on whatever input type goes in. This is so marginal that I'd wonder if its worth the code churn, so I'm happy to revert if we want.

One change here that we should have had is to be exclusive of the end point. A common pattern would be

train_period=[(None, "2025-01-01 00:00")]
val_period=[("2025-01-01 00:00", None)]

This stops both train and val from containing the "2025-01-01 00:00" t0 time

"date_cos": np.cos(date_in_radians),
"time_sin": np.sin(time_in_radians),
"time_cos": np.cos(time_in_radians),
"date_sin": np.sin(date_in_radians).astype(np.float32),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These were returning float64 which is not what the function signature advertises. We also cast to float32 for the TensorBatch passed to the model

("2023-01-01 12:00", "2023-01-01 13:00"),
],
)
expected_mask = np.array([False, True, True, True, False, True, True, True])

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The end point is now exclusive

@dfulu
dfulu marked this pull request as ready for review August 4, 2026 12:17
@dfulu dfulu changed the title 9 various -TBC 9 various tidy-ups and loud failing Aug 4, 2026
@felix-e-h-p
felix-e-h-p self-requested a review August 4, 2026 15:11
@dfulu dfulu mentioned this pull request Aug 6, 2026
Base automatically changed from 8_split_generation to dev_feb2026_speedups August 7, 2026 13:16
dfulu and others added 3 commits August 7, 2026 13:25
Clarified the docstring for the presave_pickle method.
return da


def _assert_steps_uniformly_spaced(steps: NDArray[np.timedelta64], provider: str) -> None:

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.

This will cause a hard fail with uneven steps say for MO right?

mask = get_time_periods_mask(valid_t0_and_location_ids["t0"], time_periods)
valid_t0_and_location_ids = valid_t0_and_location_ids[mask]
mask = get_time_periods_mask(valid_t0_and_location_ids["t0"].values, time_periods)
if not mask.any():

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.

Nice one!


# Each init-time is unusable before init_start_timedelta and after init_end_timedelta. If they
# are the wrong way round then no t0 can ever use it, and every period below will be empty
if init_end_timedelta < init_start_timedelta:

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.

Solid one, clear flagging

Returns:
The selected DataArray-like slice.
"""
if len(locations) == 0:

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.

For any window inversion errors right?

@felix-e-h-p felix-e-h-p left a comment

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.

Thanks for this! Looking great. Think the one thing from me would be if the uniformly spaced steps assertion could reject archives, but all good asides that.

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.

2 participants