Skip to content

Commit

Permalink
Merge pull request #209 from USDA-ARS-NWRC/wind_hrrr
Browse files Browse the repository at this point in the history
Wind Ninja - Skip loading wind data from HRRR
  • Loading branch information
jomey authored May 6, 2021
2 parents 4d43666 + 75cb866 commit 0cb0f13
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 54 deletions.
2 changes: 1 addition & 1 deletion smrf/data/gridded_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, start_date, end_date, bbox, topo, config):
self.topo = topo
self.bbox = bbox

self.config = config
self.config = config[GriddedInput.TYPE]

self._logger = logging.getLogger(self.__class__.__name__)

Expand Down
71 changes: 56 additions & 15 deletions smrf/data/hrrr_grib.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import numpy as np
import pandas as pd
import weather_forecast_retrieval.data.hrrr as hrrr_data
import weather_forecast_retrieval as wfr

from smrf.data.gridded_input import GriddedInput
from smrf.distribute.wind import Wind
from smrf.distribute.wind.wind_ninja import WindNinjaModel
from smrf.envphys.solar.cloud import get_hrrr_cloud
from smrf.envphys.vapor_pressure import rh2vp

Expand All @@ -15,9 +17,11 @@ class InputGribHRRR(GriddedInput):
'air_temp',
'vapor_pressure',
'precip',
'cloud_factor'
]
WIND_VARIABLES = [
'wind_speed',
'wind_direction',
'cloud_factor'
]

TIME_STEP = pd.to_timedelta(20, 'minutes')
Expand All @@ -30,6 +34,17 @@ def __init__(self, *args, **kwargs):
self.timestep_dates()
self.cf_memory = None

self._load_wind = not Wind.config_model_type(
kwargs['config'], WindNinjaModel.MODEL_TYPE
)

@property
def variables(self):
if self._load_wind:
return np.union1d(self.VARIABLES, self.WIND_VARIABLES)
else:
return self.VARIABLES

def timestep_dates(self):
self.end_date = self.start_date + self.TIME_STEP

Expand All @@ -55,14 +70,19 @@ def load(self):
self.config['hrrr_directory']
))

metadata, data = hrrr_data.FileLoader(
var_keys = wfr.data.hrrr.GribFile.VARIABLES
if not self._load_wind:
var_keys = [v for v in var_keys if v not in ['wind_u', 'wind_v']]

metadata, data = wfr.data.hrrr.FileLoader(
file_dir=self.config['hrrr_directory'],
external_logger=self._logger
).get_saved_data(
self.start_date,
self.end_date,
self.bbox,
force_zone_number=self.topo.zone_number
force_zone_number=self.topo.zone_number,
var_keys=var_keys
)

self.parse_data(metadata, data)
Expand Down Expand Up @@ -91,7 +111,7 @@ def load_timestep_thread(self, date_times, data_queue):
for date_time in date_times[1:]:
self.load_timestep(date_time)

for variable in self.VARIABLES:
for variable in self.variables:
data_queue[variable].put(
[date_time, getattr(self, variable).iloc[0]])

Expand Down Expand Up @@ -126,16 +146,7 @@ def parse_data(self, metadata, data):
data['relative_humidity'].values)
self.vapor_pressure = pd.DataFrame(vp, index=idx, columns=cols)

# calculate the wind speed and wind direction
self._logger.debug('Loading wind_speed and wind_direction')

s = np.sqrt(data['wind_u']**2 + data['wind_v']**2)

d = np.degrees(np.arctan2(data['wind_v'], data['wind_u']))
ind = d < 0
d[ind] = d[ind] + 360
self.wind_speed = pd.DataFrame(s, index=idx, columns=cols)
self.wind_direction = pd.DataFrame(d, index=idx, columns=cols)
self.calculate_wind(data)

# precip
self._logger.debug('Loading precip')
Expand All @@ -149,6 +160,36 @@ def parse_data(self, metadata, data):
solar, self.metadata,
self.topo.basin_lat, self.topo.basin_long)

def calculate_wind(self, data):
"""
Calculate the wind speed and wind direction.
Args:
data: Loaded data from weather_forecast_retrieval
"""
dataframe_options = dict(
index=data['air_temp'].index,
columns=data['air_temp'].columns
)
wind_speed = np.empty_like(data['air_temp'].values)
wind_speed[:] = np.nan
wind_direction = np.empty_like(data['air_temp'].values)
wind_direction[:] = np.nan

if self._load_wind:
self._logger.debug('Loading wind_speed and wind_direction')

wind_speed = np.sqrt(data['wind_u']**2 + data['wind_v']**2)

wind_direction = np.degrees(
np.arctan2(data['wind_v'], data['wind_u'])
)
ind = wind_direction < 0
wind_direction[ind] = wind_direction[ind] + 360

self.wind_speed = pd.DataFrame(wind_speed, **dataframe_options)
self.wind_direction = pd.DataFrame(wind_direction, **dataframe_options)

def check_cloud_factor(self):
"""Check the cloud factor when in the timestep mode.
This will fill NaN values as they happen by linearly
Expand Down
2 changes: 1 addition & 1 deletion smrf/data/input_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def __determine_data_type(self, smrf_config):
self.data_type = smrf_config[GriddedInput.TYPE]['data_type']
data_inputs = dict(
bbox=self.bbox,
config=smrf_config[GriddedInput.TYPE],
config=smrf_config,
topo=self.topo,
)
if self.data_type == InputGribHRRR.DATA_TYPE:
Expand Down
40 changes: 26 additions & 14 deletions smrf/distribute/wind/wind.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import logging

import numpy as np
from smrf.utils import utils

from smrf.distribute import image_data
from smrf.distribute.wind.wind_ninja import WindNinjaModel
from smrf.distribute.wind.winstral import WinstralWindModel
from smrf.utils import utils


class Wind(image_data.image_data):
Expand Down Expand Up @@ -39,6 +39,7 @@ class Wind(image_data.image_data):
"""

INTERP = 'interp'
VARIABLE = 'wind'

# these are variables that can be output
Expand Down Expand Up @@ -69,41 +70,52 @@ class Wind(image_data.image_data):
])

def __init__(self, config):

# extend the base class
image_data.image_data.__init__(self, self.VARIABLE)
self._logger = logging.getLogger(__name__)

# check and assign the configuration
self.smrf_config = config
self.getConfig(config['wind'])

if self.check_wind_model_type('interp'):
if self.model_type(self.INTERP):
# Straight interpolation of the wind
self.wind_model = self
self.wind_model.flatwind = None
self.wind_model.cellmaxus = None
self.wind_model.dir_round_cell = None

elif self.check_wind_model_type('wind_ninja'):
elif self.model_type(WindNinjaModel.MODEL_TYPE):
self.wind_model = WindNinjaModel(self.smrf_config)

elif self.check_wind_model_type('winstral'):
elif self.model_type(WinstralWindModel.MODEL_TYPE):
self.wind_model = WinstralWindModel(self.smrf_config)

self._logger.debug('Created distribute.wind')

def check_wind_model_type(self, wind_model):
"""Check if the wind model is of a given type
def model_type(self, wind_model):
"""Check if given model is set on config
Args:
wind_model (str): name of the wind model
Returns:
bool: True/False
"""
return Wind.config_model_type(self.smrf_config, wind_model)

@staticmethod
def config_model_type(config, wind_model):
"""Check if the wind model is of a given type for given config
Args:
config (dict): run configuration for SMRF
wind_model (str): name of the wind model
Returns:
bool: True/False if the wind_model matches the config
bool: True/False if the wind_model is set in the config
"""

return self.config['wind_model'] == wind_model
return config.get('wind', {}).get('wind_model', None) == wind_model

def initialize(self, topo, data, date_time=None):
"""
Expand All @@ -123,10 +135,10 @@ def initialize(self, topo, data, date_time=None):
self.date_time = date_time
self.wind_model._initialize(topo, data.metadata)

if self.check_wind_model_type('winstral'):
if self.model_type(WinstralWindModel.MODEL_TYPE):
self.add_thread_variables(self.wind_model.thread_variables)

if not self.check_wind_model_type('interp'):
if not self.model_type(self.INTERP):
self.wind_model.initialize(topo, data)

def distribute(self, data_speed, data_direction, t):
Expand All @@ -146,7 +158,7 @@ def distribute(self, data_speed, data_direction, t):
self._logger.debug('{} Distributing wind_direction and wind_speed'
.format(data_speed.name))

if self.check_wind_model_type('interp'):
if self.model_type(self.INTERP):

self._distribute(data_speed, other_attribute='wind_speed')

Expand Down Expand Up @@ -202,7 +214,7 @@ def distribute_thread(self, smrf_queue, data_queue):
smrf_queue['wind_direction'].put(
[date_time, self.wind_model.wind_direction])

if self.check_wind_model_type('winstral'):
if self.model_type(WinstralWindModel.MODEL_TYPE):
smrf_queue['flatwind'].put(
[date_time, self.wind_model.flatwind])
smrf_queue['cellmaxus'].put(
Expand Down
1 change: 1 addition & 0 deletions smrf/distribute/wind/wind_ninja.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class WindNinjaModel(image_data.image_data):
files and interpolates to the model domain.
"""

MODEL_TYPE = 'wind_ninja'
VARIABLE = 'wind'
WN_DATE_FORMAT = '%m-%d-%Y_%H%M'
DATE_FORMAT = '%Y%m%d'
Expand Down
1 change: 1 addition & 0 deletions smrf/distribute/wind/winstral.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class WinstralWindModel(image_data.image_data):
distributed flat wind speed and each cell's maxus value.
"""

MODEL_TYPE = 'winstral'
VARIABLE = 'wind'

BASE_THREAD_VARIABLES = frozenset([
Expand Down
10 changes: 5 additions & 5 deletions smrf/framework/model_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@
from inicheck.config import UserConfig
from inicheck.output import generate_config, print_config_report
from inicheck.tools import check_config, get_user_config
from smrf.envphys import sunang
from smrf.utils import queue
from topocalc.shade import shade

from smrf import distribute
from smrf.data import InputData, Topo
from smrf.envphys import sunang
from smrf.envphys.solar import model
from smrf.framework import art, logger
from smrf.output import output_hru, output_netcdf
from smrf.utils import queue
from smrf.utils.utils import backup_input, date_range, getqotw


Expand Down Expand Up @@ -234,14 +234,14 @@ def loadTopo(self):

def create_distribution(self):
"""
This initializes the distirbution classes based on the configFile
This initializes the distribution classes based on the configFile
sections for each variable.
:func:`~smrf.framework.model_framework.SMRF.create_distribution`
will initialize the variables within the :func:`smrf.distribute`
package and insert into a dictionary 'distribute' with variable names
as the keys.
Variables that are intialized are:
Variables that are initialized are:
* :func:`Air temperature <smrf.distribute.air_temp.ta>`
* :func:`Vapor pressure <smrf.distribute.vapor_pressure.vp>`
* :func:`Wind speed and direction <smrf.distribute.wind.wind>`
Expand Down Expand Up @@ -571,7 +571,7 @@ def create_distributed_threads(self, other_queue=None):
Designed for smrf runs in memory
Returns
t: list of threads for distirbution
t: list of threads for distribution
q: queue
"""

Expand Down
Loading

0 comments on commit 0cb0f13

Please sign in to comment.