PVNet 3.0.25__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.
- {pvnet-3.0.25 → pvnet-3.0.27}/PKG-INFO +1 -1
- {pvnet-3.0.25 → pvnet-3.0.27}/PVNet.egg-info/PKG-INFO +1 -1
- pvnet-3.0.27/pvnet/__init__.py +2 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/data/wind_datamodule.py +2 -2
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/multimodal.py +7 -1
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/site_encoders/encoders.py +36 -14
- {pvnet-3.0.25 → pvnet-3.0.27}/tests/conftest.py +31 -0
- pvnet-3.0.25/pvnet/__init__.py +0 -2
- {pvnet-3.0.25 → pvnet-3.0.27}/LICENSE +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/MANIFEST.in +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/PVNet.egg-info/SOURCES.txt +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/PVNet.egg-info/dependency_links.txt +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/PVNet.egg-info/requires.txt +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/PVNet.egg-info/top_level.txt +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/README.md +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/callbacks.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/data/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/data/base.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/data/datamodule.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/data/pv_site_datamodule.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/data/utils.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/base_model.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/baseline/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/baseline/last_value.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/baseline/single_value.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/ensemble.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/basic_blocks.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/basic_blocks.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/encoders2d.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/encoders3d.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/encoders/encodersRNN.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/linear_networks/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/linear_networks/basic_blocks.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/linear_networks/networks.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/multimodal_base.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/site_encoders/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/site_encoders/basic_blocks.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/multimodal/unimodal_teacher.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/models/utils.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/optimizers.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/training.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pvnet/utils.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/pyproject.toml +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/requirements-dev.txt +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/requirements.txt +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/setup.cfg +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/setup.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/tests/__init__.py +0 -0
- {pvnet-3.0.25 → pvnet-3.0.27}/tests/test_end2end.py +0 -0
|
@@ -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 = (
|
|
@@ -57,6 +57,7 @@ class Model(MultimodalBaseModel):
|
|
|
57
57
|
pv_history_minutes: Optional[int] = None,
|
|
58
58
|
wind_history_minutes: Optional[int] = None,
|
|
59
59
|
sensor_history_minutes: Optional[int] = None,
|
|
60
|
+
sensor_forecast_minutes: Optional[int] = None,
|
|
60
61
|
optimizer: AbstractOptimizer = pvnet.optimizers.Adam(),
|
|
61
62
|
target_key: str = "gsp",
|
|
62
63
|
interval_minutes: int = 30,
|
|
@@ -121,6 +122,7 @@ class Model(MultimodalBaseModel):
|
|
|
121
122
|
addition to the full forecast
|
|
122
123
|
sensor_encoder: Encoder for sensor data
|
|
123
124
|
sensor_history_minutes: Length of recent sensor data used as input.
|
|
125
|
+
sensor_forecast_minutes: Length of forecast sensor data used as input.
|
|
124
126
|
adapt_batches: If set to true, we attempt to slice the batches to the expected shape for
|
|
125
127
|
the model to use. This allows us to overprepare batches and slice from them for the
|
|
126
128
|
data we need for a model run.
|
|
@@ -240,9 +242,13 @@ class Model(MultimodalBaseModel):
|
|
|
240
242
|
if self.include_sensor:
|
|
241
243
|
if sensor_history_minutes is None:
|
|
242
244
|
sensor_history_minutes = history_minutes
|
|
245
|
+
if sensor_forecast_minutes is None:
|
|
246
|
+
sensor_forecast_minutes = forecast_minutes
|
|
243
247
|
|
|
244
248
|
self.sensor_encoder = sensor_encoder(
|
|
245
|
-
sequence_length=sensor_history_minutes // sensor_interval_minutes
|
|
249
|
+
sequence_length=sensor_history_minutes // sensor_interval_minutes
|
|
250
|
+
+ sensor_forecast_minutes // sensor_interval_minutes
|
|
251
|
+
+ 1,
|
|
246
252
|
target_key_to_use=self._target_key_name,
|
|
247
253
|
input_key_to_use="sensor",
|
|
248
254
|
)
|
|
@@ -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
|
|
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=
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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(
|
pvnet-3.0.25/pvnet/__init__.py
DELETED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|