-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hourly FFMC #4153
Merged
Merged
Hourly FFMC #4153
Changes from 5 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
7628b12
Hourly FFMC
dgboss 5239ae2
parameter order
dgboss 7454cbf
minor fixes
dgboss bcd5ec0
Fix test
dgboss c1b74d7
docstrings
dgboss e2b9ad1
Fix rh over 100
dgboss 0e7bda3
Fix comments
dgboss bdf469a
Merge branch 'main' into task/hffmc/3517
dgboss 51059b1
Consistency
dgboss c348767
name hffmc with UTC based time
dgboss c0d3524
Update docs to reflect UTC based naming of calculated hFFMC
dgboss dda121e
Fix test
dgboss 972be14
Fix one more test
dgboss File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import logging | ||
import os | ||
import tempfile | ||
from datetime import datetime, timedelta | ||
from osgeo import gdal | ||
from typing import List, cast | ||
|
||
from app.weather_models.rdps_filename_marshaller import model_run_for_hour | ||
|
||
from app.geospatial.wps_dataset import WPSDataset | ||
from app.jobs.rdps_sfms import MAX_MODEL_RUN_HOUR | ||
from app.sfms.daily_fwi_processor import MultiDatasetContext | ||
from app.sfms.fwi_processor import calculate_ffmc | ||
from app.sfms.raster_addresser import RasterKeyAddresser | ||
from app.utils.geospatial import GDALResamplingMethod | ||
from app.utils.s3 import set_s3_gdal_config | ||
from app.utils.s3_client import S3Client | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class HourlyFFMCProcessor: | ||
""" | ||
Class for calculating/generating forecasted hourly FFMC rasters. | ||
""" | ||
|
||
def __init__(self, start_datetime: datetime, addresser: RasterKeyAddresser): | ||
self.start_datetime = start_datetime | ||
self.addresser = addresser | ||
|
||
async def process(self, s3_client: S3Client, input_dataset_context: MultiDatasetContext, hours_to_process: int = MAX_MODEL_RUN_HOUR): | ||
set_s3_gdal_config() | ||
|
||
# 1 - Determine starting hFFMC (4am or 4pm) from SFMS and get key, confirm exists, if not, exit | ||
# 2 - Determine what would be last key of run and check if exists, if exists, exit | ||
# 3 - Get all weather variable keys and check if last one exists, if not, exit | ||
# 4 - Use seed hFFMC plus: | ||
# - rh, temp and wind speed from RDPS model run hour n = 000 | ||
# - computed precip at n = 0Z or 12Z | ||
# 5 - Use newly calculated hFFMC to calculate next hFFMC using: | ||
# - rh, temp and wind speed from RDPS model run hour n + 1 | ||
# - computed precip at n + 1 | ||
# hFFMC files from SFMS use PST datetimes | ||
dgboss marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Determine most recent RDPS model run | ||
rdps_model_run_hour = model_run_for_hour(self.start_datetime.hour) | ||
rdps_model_run_start = datetime( | ||
year=self.start_datetime.year, month=self.start_datetime.month, day=self.start_datetime.day, hour=rdps_model_run_hour, tzinfo=self.start_datetime.tzinfo | ||
) | ||
|
||
# Determine key to the initial/seed hFFMC from SFMS and check if it exists. Initial hffmc will be a 04 or 16 hour hffmc from SFMS. | ||
hffmc_key = self.addresser.get_uploaded_hffmc_key(rdps_model_run_start) | ||
hffmc_key_exists = await s3_client.all_objects_exist(hffmc_key) | ||
if not hffmc_key_exists: | ||
logger.warning(f"Missing initial hFFMC raster from SFMS for date {self.start_datetime}. Missing key is {hffmc_key}.") | ||
return | ||
|
||
for hour in range(0, hours_to_process): | ||
with tempfile.TemporaryDirectory() as temp_dir: | ||
# Get and check existence of weather s3 keys | ||
temp_key, rh_key, wind_speed_key, precip_key = self.addresser.get_weather_data_keys_hffmc(rdps_model_run_start, hour) | ||
weather_keys_exist = await s3_client.all_objects_exist(temp_key, rh_key, wind_speed_key, precip_key) | ||
if not weather_keys_exist: | ||
logging.warning(f"Missing weather keys for model run: {rdps_model_run_start}") | ||
break | ||
|
||
# Prefix our S3 keys for access via gdal | ||
temp_key, rh_key, wind_speed_key, precip_key, hffmc_key = self.addresser.gdal_prefix_keys(temp_key, rh_key, wind_speed_key, precip_key, hffmc_key) | ||
with input_dataset_context([temp_key, rh_key, wind_speed_key, precip_key, hffmc_key]) as input_datasets: | ||
input_datasets = cast(List[WPSDataset], input_datasets) # Ensure correct type inference | ||
temp_ds, rh_ds, wind_speed_ds, precip_ds, hffmc_ds = input_datasets | ||
# Warp weather datasets to match hffmc | ||
warped_temp_ds = temp_ds.warp_to_match(hffmc_ds, f"{temp_dir}/{os.path.basename(temp_key)}", GDALResamplingMethod.BILINEAR) | ||
warped_rh_ds = rh_ds.warp_to_match(hffmc_ds, f"{temp_dir}/{os.path.basename(rh_key)}", GDALResamplingMethod.BILINEAR) | ||
warped_wind_speed_ds = wind_speed_ds.warp_to_match(hffmc_ds, f"{temp_dir}/{os.path.basename(wind_speed_key)}", GDALResamplingMethod.BILINEAR) | ||
warped_precip_ds = precip_ds.warp_to_match(hffmc_ds, f"{temp_dir}/{os.path.basename(precip_key)}", GDALResamplingMethod.BILINEAR) | ||
|
||
# Create and store new hFFMC dataset | ||
hffmc_values, hffmc_no_data_value = calculate_ffmc(hffmc_ds, warped_temp_ds, warped_rh_ds, warped_precip_ds, warped_wind_speed_ds) | ||
new_hffmc_datetime = rdps_model_run_start + timedelta(hours=hour) | ||
hffmc_key = self.addresser.get_calculated_hffmc_index_key(new_hffmc_datetime) | ||
geotransform = hffmc_ds.as_gdal_ds().GetGeoTransform() | ||
projection = hffmc_ds.as_gdal_ds().GetProjection() | ||
hffmc_ds.close() | ||
await s3_client.persist_raster_data( | ||
temp_dir, | ||
hffmc_key, | ||
geotransform, | ||
projection, | ||
hffmc_values, | ||
hffmc_no_data_value, | ||
) | ||
# Clear gdal virtual file system cache of S3 metadata in order to allow newly uploaded hffmc rasters to be opened immediately. | ||
dgboss marked this conversation as resolved.
Show resolved
Hide resolved
|
||
gdal.VSICurlClearCache() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ouch, good find, I wonder if there's a test we could add to catch this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add some sort of name or id property to
WPSDataset
objects and use this to assert that we're getting parameters in the right order?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think that'd be worth it since we're passing so many
WPSDataset
's around