Skip to content

Commit

Permalink
Start changes for WindNet
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobbieker committed Nov 27, 2023
1 parent 2fdb5d9 commit c3a4719
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 3 deletions.
13 changes: 13 additions & 0 deletions configs/model/multimodal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ pv_encoder:
kdim: 40
pv_id_embed_dim: 20

#--------------------------------------------
# Sensor encoder settings
#--------------------------------------------

sensor_encoder:
_target_: pvnet.models.multimodal.site_encoders.encoders.SingleAttentionNetwork
_partial_: True
num_sites: 123
out_features: 40
num_heads: 4
kdim: 40
pv_id_embed_dim: 20

#--------------------------------------------
# Tabular network settings
#--------------------------------------------
Expand Down
14 changes: 11 additions & 3 deletions pvnet/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def make_clean_data_config(input_path, output_path, placeholder="PLACEHOLDER"):
d["pv_filename"] = f"{placeholder}.netcdf"
d["pv_metadata_filename"] = f"{placeholder}.csv"

if "sensor" in config["input_data"]:
# If not empty - i.e. if used
if config["input_data"][source][f"{source}_filename"] != "":
config["input_data"][source][f"{source}_filename"] = f"{placeholder}.nc"

with open(output_path, "w") as outfile:
yaml.dump(config, outfile, default_flow_style=False)

Expand Down Expand Up @@ -237,6 +242,7 @@ def __init__(
forecast_minutes: int,
optimizer: AbstractOptimizer,
output_quantiles: Optional[list[float]] = None,
target_key: BatchKey = BatchKey.gsp,
):
"""Abtstract base class for PVNet submodels.
Expand All @@ -246,10 +252,12 @@ def __init__(
optimizer (AbstractOptimizer): Optimizer
output_quantiles: A list of float (0.0, 1.0) quantiles to predict values for. If set to
None the output is a single value.
target_key: BatchKey of the target variable
"""
super().__init__()

self._optimizer = optimizer
self._target_key = target_key

# Model must have lr to allow tuning
# This setting is only used when lr is tuned with callback
Expand Down Expand Up @@ -424,7 +432,7 @@ def _training_accumulate_log(self, batch, batch_idx, losses, y_hat):
def training_step(self, batch, batch_idx):
"""Run training step"""
y_hat = self(batch)
y = batch[BatchKey.gsp][:, -self.forecast_len_30 :, 0]
y = batch[self._target_key][:, -self.forecast_len_30 :, 0]

losses = self._calculate_common_losses(y, y_hat)
losses = {f"{k}/train": v for k, v in losses.items()}
Expand All @@ -440,7 +448,7 @@ def training_step(self, batch, batch_idx):
def validation_step(self, batch: dict, batch_idx):
"""Run validation step"""
y_hat = self(batch)
y = batch[BatchKey.gsp][:, -self.forecast_len_30 :, 0]
y = batch[self._target_key][:, -self.forecast_len_30 :, 0]

losses = self._calculate_common_losses(y, y_hat)
losses.update(self._calculate_val_losses(y, y_hat))
Expand Down Expand Up @@ -484,7 +492,7 @@ def validation_step(self, batch: dict, batch_idx):
def test_step(self, batch, batch_idx):
"""Run test step"""
y_hat = self(batch)
y = batch[BatchKey.gsp][:, -self.forecast_len_30 :, 0]
y = batch[self._target_key][:, -self.forecast_len_30 :, 0]

losses = self._calculate_common_losses(y, y_hat)
losses.update(self._calculate_val_losses(y, y_hat))
Expand Down
22 changes: 22 additions & 0 deletions pvnet/models/multimodal/multimodal.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ def __init__(
nwp_encoder: Optional[AbstractNWPSatelliteEncoder] = None,
sat_encoder: Optional[AbstractNWPSatelliteEncoder] = None,
pv_encoder: Optional[AbstractPVSitesEncoder] = None,
sensor_encoder: Optional[AbstractPVSitesEncoder] = None, # TODO Change to SensorEncoder
add_image_embedding_channel: bool = False,
include_gsp_yield_history: bool = True,
include_sun: bool = True,
include_gsp: bool = True,
embedding_dim: Optional[int] = 16,
forecast_minutes: int = 30,
history_minutes: int = 60,
Expand All @@ -53,6 +55,7 @@ def __init__(
nwp_forecast_minutes: Optional[int] = None,
nwp_history_minutes: Optional[int] = None,
pv_history_minutes: Optional[int] = None,
sensor_history_minutes: Optional[int] = None,
optimizer: AbstractOptimizer = pvnet.optimizers.Adam(),
):
"""Neural network which combines information from different sources.
Expand Down Expand Up @@ -100,6 +103,8 @@ def __init__(
self.include_nwp = nwp_encoder is not None
self.include_pv = pv_encoder is not None
self.include_sun = include_sun
self.include_gsp = include_gsp
self.include_sensor = sensor_encoder is not None
self.embedding_dim = embedding_dim
self.add_image_embedding_channel = add_image_embedding_channel

Expand Down Expand Up @@ -159,6 +164,18 @@ def __init__(
# Update num features
fusion_input_features += self.pv_encoder.out_features

if self.include_sensor:
if sensor_history_minutes is None:
sensor_history_minutes = history_minutes

self.sensor_encoder = sensor_encoder(
sequence_length=sensor_history_minutes // 30 + 1,
# Sensors are currently resampled to 30min
)

# Update num features
fusion_input_features += self.sensor_encoder.out_features

if self.embedding_dim:
self.embed = nn.Embedding(num_embeddings=318, embedding_dim=embedding_dim)

Expand Down Expand Up @@ -227,6 +244,11 @@ def forward(self, x):
id_embedding = self.embed(id)
modes["id"] = id_embedding

# *********************** Sensor Data ************************************
# add sensor yield history
if self.include_sensor:
modes["sensor"] = self.sensor_encoder(x)

if self.include_sun:
sun = torch.cat(
(x[BatchKey.gsp_solar_azimuth], x[BatchKey.gsp_solar_elevation]), dim=1
Expand Down
3 changes: 3 additions & 0 deletions pvnet/models/multimodal/site_encoders/encoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ def __init__(
n_kv_res_blocks: int = 2,
kv_res_block_layers: int = 2,
use_pv_id_in_value: bool = False,
use_gsp_embedding: bool = True,
sensor_embed_dim: int = 123,
):
"""A simple attention-based model with a single multihead attention layer
Expand All @@ -142,6 +144,7 @@ def __init__(
the key and value encoders.
use_pv_id_in_value: Whether to use a PV ID embedding in network used to produce the
value for the attention layer.
use_gsp_embedding: Whether to use embedding of the GSP ID
"""
super().__init__(sequence_length, num_sites, out_features)
Expand Down

0 comments on commit c3a4719

Please sign in to comment.