import numpy as np
import logging
import pandas as pd
import datetime
import pytz
from smrf.distribute import image_data
from smrf.envphys import radiation
from smrf.utils import utils
[docs]class albedo(image_data.image_data):
"""
The :mod:`~smrf.distribute.albedo.albedo` class allows for variable
specific distributions that go beyond the base class.
The visible (280-700nm) and infrared (700-2800nm) albedo follows the
relationships described in Marks et al. (1992) :cite:`Marks&al:1992`. The
albedo is a function of the time since last storm, the solar zenith angle,
and grain size. The time since last storm is tracked on a pixel by pixel
basis and is based on where there is significant accumulated distributed
precipitation. This allows for storms to only affect a small part of the
basin and have the albedo decay at different rates for each pixel.
Args:
albedoConfig: The [albedo] section of the configuration file
Attributes:
albedo_vis: numpy array of the visible albedo
albedo_ir: numpy array of the infrared albedo
config: configuration from [albedo] section
min: minimum value of albedo is 0
max: maximum value of albedo is 1
stations: stations to be used in alphabetical order
output_variables: Dictionary of the variables held within class
:mod:`!smrf.distribute.albedo.albedo` that specifies the ``units``
and ``long_name`` for creating the NetCDF output file.
variable: 'albedo'
"""
variable = 'albedo'
# these are variables that can be output
output_variables = {'albedo_vis': {
'units': 'None',
'standard_name': 'visible_albedo',
'long_name': 'Visible wavelength albedo'
},
'albedo_ir': {
'units': 'None',
'standard_name': 'infrared_albedo',
'long_name': 'Infrared wavelength albedo'
}
}
# these are variables that are operate at the end only and do not need to
# be written during main distribute loop
post_process_variables = {}
def __init__(self, albedoConfig):
"""
Initialize albedo()
Args:
albedoConfig: configuration from [albedo] section
"""
# extend the base class
image_data.image_data.__init__(self, self.variable)
self._logger = logging.getLogger(__name__)
#Get the veg values for the decay methods. Date method uses self.veg
#Hardy2000 uses self.litter
for d in ['veg','litter']:
v = {}
matching = [s for s in albedoConfig.keys() if "{0}_".format(d) in s]
for m in matching:
ms = m.split('_')
v[ms[1]] = albedoConfig[m]
#create self.litter,self.veg
setattr(self,d,v)
#Add time zone
self.config = albedoConfig
self.min = self.config['min']
self.max = self.config['max']
self._logger.debug('Created distribute.albedo')
[docs] def initialize(self, topo, data):
"""
Initialize the distribution, calls image_data.image_data._initialize()
Args:
topo: smrf.data.loadTopo.topo instance contain topo data/info
data: data dataframe containing the station data
"""
# self._initialize(topo, metadata)
self._logger.debug('Initializing distribute.albedo')
self.veg_type = topo.veg_type
[docs] def distribute(self, current_time_step, cosz, storm_day):
"""
Distribute air temperature given a Panda's dataframe for a single time
step. Calls :mod:`smrf.distribute.image_data.image_data._distribute`.
Args:
current_time_step: Current time step in datetime object
cosz: numpy array of the illumination angle for the current time
step
storm_day: numpy array of the decimal days since it last
snowed at a grid cell
"""
self._logger.debug('%s Distributing albedo' % current_time_step)
# only need to calculate albedo if the sun is up
if cosz is not None:
alb_v, alb_ir = radiation.albedo(storm_day, cosz,
self.config['grain_size'],
self.config['max_grain'],
self.config['dirt'])
# Perform litter decay
if self.config['decay_method'] == 'date_method':
if (self.config['start_decay'] is not None and
self.config['end_decay'] is not None and
self.config['end_decay'] > self.config['start_decay']):
alb_v_d, alb_ir_d = radiation.decay_alb_power(self.veg,
self.veg_type,
self.config['start_decay'],
self.config['end_decay'],
current_time_step,
self.config['decay_power'], alb_v, alb_ir)
alb_v = alb_v_d
alb_ir = alb_ir_d
else:
self._logger.error('Need correct inputs for decay method: {0}. Check your dates!'.format(self.config['decay_method']))
raise ValueError('Albedo decay dates are not correct!')
elif self.config['decay_method'] == 'hardy2000':
alb_v_d, alb_ir_d = radiation.decay_alb_hardy(self.litter,
self.veg_type,
storm_day,
alb_v,
alb_ir)
alb_v = alb_v_d
alb_ir = alb_ir_d
self.albedo_vis = utils.set_min_max(alb_v,self.min,self.max)
self.albedo_ir = utils.set_min_max(alb_ir,self.min,self.max)
else:
self.albedo_vis = np.zeros(storm_day.shape)
self.albedo_ir = np.zeros(storm_day.shape)
[docs] def distribute_thread(self, queue, date):
"""
Distribute the data using threading and queue
Args:
queue: queue dict for all variables
date: dates to loop over
Output:
Changes the queue albedo_vis, albedo_ir
for the given date
"""
for t in date:
illum_ang = queue['illum_ang'].get(t)
storm_day = queue['storm_days'].get(t)
self.distribute(t, illum_ang, storm_day)
self._logger.debug('Putting %s -- %s' % (t, 'albedo_vis'))
queue['albedo_vis'].put([t, self.albedo_vis])
self._logger.debug('Putting %s -- %s' % (t, 'albedo_ir'))
queue['albedo_ir'].put([t, self.albedo_ir])