PVNet 5.3.23__tar.gz → 5.3.25__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {pvnet-5.3.23 → pvnet-5.3.25}/PKG-INFO +1 -1
  2. {pvnet-5.3.23 → pvnet-5.3.25}/PVNet.egg-info/PKG-INFO +1 -1
  3. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/training/lightning_module.py +26 -1
  4. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/training/plots.py +23 -5
  5. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/training/train.py +23 -25
  6. {pvnet-5.3.23 → pvnet-5.3.25}/LICENSE +0 -0
  7. {pvnet-5.3.23 → pvnet-5.3.25}/PVNet.egg-info/SOURCES.txt +0 -0
  8. {pvnet-5.3.23 → pvnet-5.3.25}/PVNet.egg-info/dependency_links.txt +0 -0
  9. {pvnet-5.3.23 → pvnet-5.3.25}/PVNet.egg-info/requires.txt +0 -0
  10. {pvnet-5.3.23 → pvnet-5.3.25}/PVNet.egg-info/top_level.txt +0 -0
  11. {pvnet-5.3.23 → pvnet-5.3.25}/README.md +0 -0
  12. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/__init__.py +0 -0
  13. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/datamodule.py +0 -0
  14. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/load_model.py +0 -0
  15. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/__init__.py +0 -0
  16. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/base_model.py +0 -0
  17. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/ensemble.py +0 -0
  18. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/__init__.py +0 -0
  19. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/basic_blocks.py +0 -0
  20. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/encoders/__init__.py +0 -0
  21. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/encoders/basic_blocks.py +0 -0
  22. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/encoders/encoders3d.py +0 -0
  23. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/late_fusion.py +0 -0
  24. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/linear_networks/__init__.py +0 -0
  25. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/linear_networks/basic_blocks.py +0 -0
  26. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/linear_networks/networks.py +0 -0
  27. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/site_encoders/__init__.py +0 -0
  28. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/site_encoders/basic_blocks.py +0 -0
  29. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/models/late_fusion/site_encoders/encoders.py +0 -0
  30. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/optimizers.py +0 -0
  31. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/training/__init__.py +0 -0
  32. {pvnet-5.3.23 → pvnet-5.3.25}/pvnet/utils.py +0 -0
  33. {pvnet-5.3.23 → pvnet-5.3.25}/pyproject.toml +0 -0
  34. {pvnet-5.3.23 → pvnet-5.3.25}/setup.cfg +0 -0
  35. {pvnet-5.3.23 → pvnet-5.3.25}/tests/test_datamodule.py +0 -0
  36. {pvnet-5.3.23 → pvnet-5.3.25}/tests/test_end2end.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PVNet
3
- Version: 5.3.23
3
+ Version: 5.3.25
4
4
  Summary: PVNet
5
5
  Author-email: Peter Dudfield <info@openclimatefix.org>
6
6
  Requires-Python: <3.14,>=3.11
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PVNet
3
- Version: 5.3.23
3
+ Version: 5.3.25
4
4
  Summary: PVNet
5
5
  Author-email: Peter Dudfield <info@openclimatefix.org>
6
6
  Requires-Python: <3.14,>=3.11
@@ -129,11 +129,15 @@ class PVNetLightningModule(pl.LightningModule):
129
129
  if self.model.use_quantile_regression:
130
130
  metric_name = "val_fraction_below/fraction_below_{:.2f}_quantile"
131
131
  # Add fraction below each quantile for calibration
132
+ val_quantiles = np.zeros(len(self.model.output_quantiles))
132
133
  for i, quantile in enumerate(self.model.output_quantiles):
133
134
  below_quant = y <= y_hat[..., i]
134
135
  # Mask values small values, which are dominated by night
135
136
  mask = y >= 0.01
136
- losses[metric_name.format(quantile)] = below_quant[mask].float().mean()
137
+ below_quant_masked_mean = below_quant[mask].float().mean()
138
+ losses[metric_name.format(quantile)] = below_quant_masked_mean
139
+ val_quantiles[i] = below_quant_masked_mean
140
+ self._val_quantiles.append(val_quantiles)
137
141
 
138
142
  return losses
139
143
 
@@ -185,6 +189,8 @@ class PVNetLightningModule(pl.LightningModule):
185
189
  # Set up stores which we will fill during validation
186
190
  self.all_val_results: list[xr.Dataset] = []
187
191
  self._val_horizon_maes: list[np.array] = []
192
+ if self.model.use_quantile_regression:
193
+ self._val_quantiles: list[np.array] = []
188
194
  if self.current_epoch == 0:
189
195
  self._val_persistence_horizon_maes: list[np.array] = []
190
196
 
@@ -355,6 +361,25 @@ class PVNetLightningModule(pl.LightningModule):
355
361
  step=self.trainer.global_step,
356
362
  )
357
363
 
364
+ # Create a quantile-quantile plot
365
+ if self.model.use_quantile_regression:
366
+ val_quantiles = np.mean(self._val_quantiles, axis=0)
367
+ self._val_quantiles = []
368
+
369
+ qq_plot = wandb_line_plot(
370
+ x=self.model.output_quantiles,
371
+ y=val_quantiles,
372
+ xlabel="Target quantile",
373
+ ylabel="Fraction below quantile",
374
+ title="Quantile-quantile plot",
375
+ add_identity_line=True
376
+ )
377
+
378
+ wandb.log(
379
+ {"quantile_quantile": qq_plot},
380
+ step=self.trainer.global_step,
381
+ )
382
+
358
383
  # Create persistence horizon accuracy curve but only on first epoch
359
384
  if self.current_epoch == 0:
360
385
  persist_horizon_mae_plot = wandb_line_plot(
@@ -14,13 +14,31 @@ def wandb_line_plot(
14
14
  y: Sequence[float],
15
15
  xlabel: str,
16
16
  ylabel: str,
17
- title: str | None = None
17
+ title: str | None = None,
18
+ add_identity_line: bool = False,
18
19
  ) -> wandb.plot.CustomChart:
19
20
  """Make a wandb line plot"""
20
- data = [[xi, yi] for (xi, yi) in zip(x, y)]
21
- table = wandb.Table(data=data, columns=[xlabel, ylabel])
22
- return wandb.plot.line(table, xlabel, ylabel, title=title)
23
-
21
+ # Main series data
22
+ data = [[xi, yi, "model"] for xi, yi in zip(x, y)]
23
+
24
+ # Add identity line endpoints if requested
25
+ if add_identity_line:
26
+ min_val, max_val = min(x), max(x)
27
+ data.append([min_val, min_val, "y=x"])
28
+ data.append([max_val, max_val, "y=x"])
29
+
30
+ table = wandb.Table(data=data, columns=[xlabel, ylabel, "key"])
31
+
32
+ # stroke=None creates a clean single line; stroke="key" creates multi-line legend
33
+ stroke_col = "key" if add_identity_line else None
34
+
35
+ return wandb.plot.line(
36
+ table=table,
37
+ x=xlabel,
38
+ y=ylabel,
39
+ stroke=stroke_col,
40
+ title=title
41
+ )
24
42
 
25
43
  def plot_sample_forecasts(
26
44
  batch: TensorBatch,
@@ -85,31 +85,29 @@ def train(config: DictConfig) -> None:
85
85
 
86
86
  # Set the output directory based in the wandb-id of the run
87
87
  if use_wandb_logger:
88
- for callback in callbacks:
89
- if isinstance(callback, ModelCheckpoint):
90
- # Calling the .experiment property instantiates a wandb run
91
- wandb_id = wandb_logger.experiment.id
92
-
93
- # Save the run results to the expected parent folder but with the folder name
94
- # set by the wandb ID
95
- save_dir = "/".join(callback.dirpath.split("/")[:-1] + [wandb_id])
96
-
97
- callback.dirpath = save_dir
98
-
99
- # Save the model config
100
- os.makedirs(save_dir, exist_ok=True)
101
- OmegaConf.save(config.model, f"{save_dir}/{MODEL_CONFIG_NAME}")
102
-
103
- # Save the data config to the output directory and to wandb
104
- data_config = config.datamodule.configuration
105
- shutil.copyfile(data_config, f"{save_dir}/{DATA_CONFIG_NAME}")
106
- wandb_logger.experiment.save(f"{save_dir}/{DATA_CONFIG_NAME}", base_path=save_dir)
107
-
108
- # Save the full hydra config to the output directory and to wandb
109
- OmegaConf.save(config, f"{save_dir}/{FULL_CONFIG_NAME}")
110
- wandb_logger.experiment.save(f"{save_dir}/{FULL_CONFIG_NAME}", base_path=save_dir)
111
-
112
- break
88
+ ckpt_callbacks = [cb for cb in callbacks if isinstance(cb, ModelCheckpoint)]
89
+ if ckpt_callbacks:
90
+ wandb_id = wandb_logger.experiment.id
91
+
92
+ # Save run results to the expected folder but with folder name set by ID
93
+ save_dir = "/".join(ckpt_callbacks[0].dirpath.split("/")[:-1] + [wandb_id])
94
+
95
+ # Point every ckpt callback (best + last) at same folder
96
+ for cb in ckpt_callbacks:
97
+ cb.dirpath = save_dir
98
+
99
+ # Save model config
100
+ os.makedirs(save_dir, exist_ok=True)
101
+ OmegaConf.save(config.model, f"{save_dir}/{MODEL_CONFIG_NAME}")
102
+
103
+ # Save data config to output directory and to wandb
104
+ data_config = config.datamodule.configuration
105
+ shutil.copyfile(data_config, f"{save_dir}/{DATA_CONFIG_NAME}")
106
+ wandb_logger.experiment.save(f"{save_dir}/{DATA_CONFIG_NAME}", base_path=save_dir)
107
+
108
+ # Save full hydra config to output directory and to wandb
109
+ OmegaConf.save(config, f"{save_dir}/{FULL_CONFIG_NAME}")
110
+ wandb_logger.experiment.save(f"{save_dir}/{FULL_CONFIG_NAME}", base_path=save_dir)
113
111
 
114
112
  trainer: Trainer = hydra.utils.instantiate(
115
113
  config.trainer,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes