PVNet 3.0.26__tar.gz → 3.0.27__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 (52) hide show
  1. {pvnet-3.0.26 → pvnet-3.0.27}/PKG-INFO +1 -1
  2. {pvnet-3.0.26 → pvnet-3.0.27}/PVNet.egg-info/PKG-INFO +1 -1
  3. pvnet-3.0.27/pvnet/__init__.py +2 -0
  4. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/data/wind_datamodule.py +2 -2
  5. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/site_encoders/encoders.py +36 -14
  6. {pvnet-3.0.26 → pvnet-3.0.27}/tests/conftest.py +31 -0
  7. pvnet-3.0.26/pvnet/__init__.py +0 -2
  8. {pvnet-3.0.26 → pvnet-3.0.27}/LICENSE +0 -0
  9. {pvnet-3.0.26 → pvnet-3.0.27}/MANIFEST.in +0 -0
  10. {pvnet-3.0.26 → pvnet-3.0.27}/PVNet.egg-info/SOURCES.txt +0 -0
  11. {pvnet-3.0.26 → pvnet-3.0.27}/PVNet.egg-info/dependency_links.txt +0 -0
  12. {pvnet-3.0.26 → pvnet-3.0.27}/PVNet.egg-info/requires.txt +0 -0
  13. {pvnet-3.0.26 → pvnet-3.0.27}/PVNet.egg-info/top_level.txt +0 -0
  14. {pvnet-3.0.26 → pvnet-3.0.27}/README.md +0 -0
  15. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/callbacks.py +0 -0
  16. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/data/__init__.py +0 -0
  17. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/data/base.py +0 -0
  18. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/data/datamodule.py +0 -0
  19. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/data/pv_site_datamodule.py +0 -0
  20. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/data/utils.py +0 -0
  21. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/__init__.py +0 -0
  22. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/base_model.py +0 -0
  23. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/baseline/__init__.py +0 -0
  24. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/baseline/last_value.py +0 -0
  25. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/baseline/single_value.py +0 -0
  26. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/ensemble.py +0 -0
  27. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/__init__.py +0 -0
  28. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/basic_blocks.py +0 -0
  29. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/__init__.py +0 -0
  30. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/basic_blocks.py +0 -0
  31. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/encoders2d.py +0 -0
  32. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/encoders3d.py +0 -0
  33. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/encodersRNN.py +0 -0
  34. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/linear_networks/__init__.py +0 -0
  35. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/linear_networks/basic_blocks.py +0 -0
  36. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/linear_networks/networks.py +0 -0
  37. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/multimodal.py +0 -0
  38. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/multimodal_base.py +0 -0
  39. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/site_encoders/__init__.py +0 -0
  40. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/site_encoders/basic_blocks.py +0 -0
  41. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/multimodal/unimodal_teacher.py +0 -0
  42. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/models/utils.py +0 -0
  43. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/optimizers.py +0 -0
  44. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/training.py +0 -0
  45. {pvnet-3.0.26 → pvnet-3.0.27}/pvnet/utils.py +0 -0
  46. {pvnet-3.0.26 → pvnet-3.0.27}/pyproject.toml +0 -0
  47. {pvnet-3.0.26 → pvnet-3.0.27}/requirements-dev.txt +0 -0
  48. {pvnet-3.0.26 → pvnet-3.0.27}/requirements.txt +0 -0
  49. {pvnet-3.0.26 → pvnet-3.0.27}/setup.cfg +0 -0
  50. {pvnet-3.0.26 → pvnet-3.0.27}/setup.py +0 -0
  51. {pvnet-3.0.26 → pvnet-3.0.27}/tests/__init__.py +0 -0
  52. {pvnet-3.0.26 → pvnet-3.0.27}/tests/test_end2end.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PVNet
3
- Version: 3.0.26
3
+ Version: 3.0.27
4
4
  Summary: PVNet
5
5
  Author: Peter Dudfield
6
6
  Author-email: info@openclimatefix.org
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PVNet
3
- Version: 3.0.26
3
+ Version: 3.0.27
4
4
  Summary: PVNet
5
5
  Author: Peter Dudfield
6
6
  Author-email: info@openclimatefix.org
@@ -0,0 +1,2 @@
1
+ """PVNet"""
2
+ __version__ = "3.0.27"
@@ -13,7 +13,7 @@ class WindDataModule(BaseDataModule):
13
13
  def _get_datapipe(self, start_time, end_time):
14
14
  data_pipeline = windnet_netcdf_datapipe(
15
15
  self.configuration,
16
- keys=["wind", "nwp"],
16
+ keys=["wind", "nwp", "sensor"],
17
17
  )
18
18
 
19
19
  data_pipeline = (
@@ -26,7 +26,7 @@ class WindDataModule(BaseDataModule):
26
26
  def _get_premade_batches_datapipe(self, subdir, shuffle=False):
27
27
  filenames = list(glob.glob(f"{self.batch_dir}/{subdir}/*.nc"))
28
28
  data_pipeline = windnet_netcdf_datapipe(
29
- keys=["wind", "nwp"],
29
+ keys=["wind", "nwp", "sensor"],
30
30
  filenames=filenames,
31
31
  )
32
32
  data_pipeline = (
@@ -2,6 +2,7 @@
2
2
 
3
3
  """
4
4
 
5
+ import einops
5
6
  import torch
6
7
  from ocf_datapipes.batch import BatchKey
7
8
  from torch import nn
@@ -128,6 +129,8 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
128
129
  target_id_dim: int = 318,
129
130
  target_key_to_use: str = "gsp",
130
131
  input_key_to_use: str = "pv",
132
+ num_channels: int = 1,
133
+ num_sites_in_inference: int = 1,
131
134
  ):
132
135
  """A simple attention-based model with a single multihead attention layer
133
136
 
@@ -148,6 +151,13 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
148
151
  target_id_dim: The number of unique IDs.
149
152
  target_key_to_use: The key to use for the target in the attention layer.
150
153
  input_key_to_use: The key to use for the input in the attention layer.
154
+ num_channels: Number of channels in the input data. For single site generation,
155
+ this will be 1, as there is not channel dimension, for Sensors,
156
+ this will probably be higher than that
157
+ num_sites_in_inference: Number of sites to use in inference.
158
+ This is used to determine the number of sites to use in the
159
+ attention layer, for a single site, 1 works, while for multiple sites
160
+ (such as multiple sensors), this would be higher than that
151
161
 
152
162
  """
153
163
  super().__init__(sequence_length, num_sites, out_features)
@@ -158,15 +168,18 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
158
168
  self.use_id_in_value = use_id_in_value
159
169
  self.target_key_to_use = target_key_to_use
160
170
  self.input_key_to_use = input_key_to_use
171
+ self.num_channels = num_channels
172
+ self.num_sites_in_inference = num_sites_in_inference
161
173
 
162
174
  if use_id_in_value:
163
175
  self.value_id_embedding = nn.Embedding(num_sites, id_embed_dim)
164
176
 
165
177
  self._value_encoder = nn.Sequential(
166
178
  ResFCNet2(
167
- in_features=sequence_length + int(use_id_in_value) * id_embed_dim,
179
+ in_features=sequence_length * self.num_channels
180
+ + int(use_id_in_value) * id_embed_dim,
168
181
  out_features=out_features,
169
- fc_hidden_features=sequence_length,
182
+ fc_hidden_features=sequence_length * self.num_channels,
170
183
  n_res_blocks=n_kv_res_blocks,
171
184
  res_block_layers=kv_res_block_layers,
172
185
  dropout_frac=0,
@@ -175,9 +188,9 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
175
188
 
176
189
  self._key_encoder = nn.Sequential(
177
190
  ResFCNet2(
178
- in_features=sequence_length + id_embed_dim,
191
+ in_features=id_embed_dim + sequence_length * self.num_channels,
179
192
  out_features=kdim,
180
- fc_hidden_features=id_embed_dim + sequence_length,
193
+ fc_hidden_features=id_embed_dim + sequence_length * self.num_channels,
181
194
  n_res_blocks=n_kv_res_blocks,
182
195
  res_block_layers=kv_res_block_layers,
183
196
  dropout_frac=0,
@@ -192,6 +205,20 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
192
205
  batch_first=True,
193
206
  )
194
207
 
208
+ def _encode_inputs(self, x):
209
+ # Shape: [batch size, sequence length, PV site] -> [8, 197, 1]
210
+ # Shape: [batch size, station_id, sequence length, channels] -> [8, 197, 26, 23]
211
+ input_data = x[BatchKey[f"{self.input_key_to_use}"]]
212
+ if len(input_data.shape) == 4: # Has multiple channels
213
+ input_data = input_data[:, :, : self.sequence_length]
214
+ input_data = einops.rearrange(input_data, "b id s c -> b (s c) id")
215
+ else:
216
+ input_data = input_data[:, : self.sequence_length]
217
+ site_seqs = input_data.float()
218
+ batch_size = site_seqs.shape[0]
219
+ site_seqs = site_seqs.swapaxes(1, 2) # [batch size, Site ID, sequence length]
220
+ return site_seqs, batch_size
221
+
195
222
  def _encode_query(self, x):
196
223
  # Select the first one
197
224
  if self.target_key_to_use == "gsp":
@@ -206,14 +233,12 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
206
233
  return query
207
234
 
208
235
  def _encode_key(self, x):
209
- # Shape: [batch size, sequence length, PV site]
210
- site_seqs = x[BatchKey[f"{self.input_key_to_use}"]][:, : self.sequence_length].float()
211
- batch_size = site_seqs.shape[0]
236
+ site_seqs, batch_size = self._encode_inputs(x)
212
237
 
213
238
  # wind ID embeddings are the same for each sample
214
239
  site_id_embed = torch.tile(self.site_id_embedding(self._ids), (batch_size, 1, 1))
215
240
  # Each concated (wind sequence, wind ID embedding) is processed with encoder
216
- x_seq_in = torch.cat((site_seqs.swapaxes(1, 2), site_id_embed), dim=2).flatten(0, 1)
241
+ x_seq_in = torch.cat((site_seqs, site_id_embed), dim=2).flatten(0, 1)
217
242
  key = self._key_encoder(x_seq_in)
218
243
 
219
244
  # Reshape to [batch size, PV site, kdim]
@@ -221,19 +246,16 @@ class SingleAttentionNetwork(AbstractPVSitesEncoder):
221
246
  return key
222
247
 
223
248
  def _encode_value(self, x):
224
- # Shape: [batch size, sequence length, PV site]
225
- site_seqs = x[BatchKey[f"{self.input_key_to_use}"]][:, : self.sequence_length].float()
226
- batch_size = site_seqs.shape[0]
249
+ site_seqs, batch_size = self._encode_inputs(x)
227
250
 
228
251
  if self.use_id_in_value:
229
252
  # wind ID embeddings are the same for each sample
230
253
  site_id_embed = torch.tile(self.value_id_embedding(self._ids), (batch_size, 1, 1))
231
254
  # Each concated (wind sequence, wind ID embedding) is processed with encoder
232
- x_seq_in = torch.cat((site_seqs.swapaxes(1, 2), site_id_embed), dim=2).flatten(0, 1)
255
+ x_seq_in = torch.cat((site_seqs, site_id_embed), dim=2).flatten(0, 1)
233
256
  else:
234
257
  # Encode each PV sequence independently
235
- x_seq_in = site_seqs.swapaxes(1, 2).flatten(0, 1)
236
-
258
+ x_seq_in = site_seqs.flatten(0, 1)
237
259
  value = self._value_encoder(x_seq_in)
238
260
 
239
261
  # Reshape to [batch size, PV site, vdim]
@@ -14,6 +14,7 @@ from datetime import timedelta
14
14
 
15
15
  import pvnet
16
16
  from pvnet.data.datamodule import DataModule
17
+ from pvnet.data.wind_datamodule import WindDataModule
17
18
 
18
19
  import pvnet.models.multimodal.encoders.encoders3d
19
20
  import pvnet.models.multimodal.linear_networks.networks
@@ -161,6 +162,22 @@ def sample_pv_batch(sample_batch):
161
162
  return pv_data
162
163
 
163
164
 
165
+ @pytest.fixture()
166
+ def sample_wind_batch():
167
+ dm = WindDataModule(
168
+ configuration=None,
169
+ batch_size=2,
170
+ num_workers=0,
171
+ prefetch_factor=None,
172
+ train_period=[None, None],
173
+ val_period=[None, None],
174
+ test_period=[None, None],
175
+ batch_dir="tests/test_data/sample_wind_batches",
176
+ )
177
+ batch = next(iter(dm.train_dataloader()))
178
+ return batch
179
+
180
+
164
181
  @pytest.fixture()
165
182
  def model_minutes_kwargs():
166
183
  kwargs = dict(
@@ -193,6 +210,20 @@ def site_encoder_model_kwargs():
193
210
  return kwargs
194
211
 
195
212
 
213
+ @pytest.fixture()
214
+ def site_encoder_sensor_model_kwargs():
215
+ # Used to test site encoder model on PV data
216
+ kwargs = dict(
217
+ sequence_length=180 // 5 + 1,
218
+ num_sites=26,
219
+ out_features=128,
220
+ num_channels=23,
221
+ target_key_to_use="wind",
222
+ input_key_to_use="sensor",
223
+ )
224
+ return kwargs
225
+
226
+
196
227
  @pytest.fixture()
197
228
  def raw_multimodal_model_kwargs(model_minutes_kwargs):
198
229
  kwargs = dict(
@@ -1,2 +0,0 @@
1
- """PVNet"""
2
- __version__ = "3.0.26"
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes