def calculate_dsh_from_dsdh_sdn(obj, dsdh='down_short_diffuse_hemisp', sdn='short_direct_normal', lat='lat', lon='lon'): """ Function to derive the downwelling shortwave hemispheric irradiance from the downwelling shortwave diffuse hemispheric irradiance (dsdh) and the shortwave direct normal irradiance (sdn) at a given location (lat,lon) Parameters ---------- obj : Xarray dataset Object where variables for these calculations are stored dsdh : str Name of the downwelling shortwave diffuse hemispheric irradiance field to use. Defaults to downwelling_sw_diffuse_hemisp_irradiance. sdn : str Name of shortwave direct normal irradiance field to use. Defaults to shortwave_direct_normal_irradiance. lat : str Name of latitude field in dataset to use. Defaults to 'lat'. lon : str Name of longitued field in dataset to use. Defaults to 'lon'. Returns ------- obj: Xarray dataset ACT Xarray dataset oject with calculations included as new variables. """ # Calculating Derived Down Short Hemisp tt = datetime64_to_datetime(obj['time'].values) elevation, _, _ = get_solar_azimuth_elevation(obj[lat].values, obj[lon].values, tt) solar_zenith = np.cos(np.radians(90. - elevation)) dsh = (obj[dsdh].values + (solar_zenith * obj[sdn].values)) # Add data back to object atts = {'long_name': 'Derived Downwelling Shortwave Hemispheric Irradiance', 'units': 'W/m^2'} da = xr.DataArray(dsh, coords={'time': obj['time'].values}, dims=['time'], attrs=atts) obj['derived_down_short_hemisp'] = da return obj
def is_sun_visible(latitude=None, longitude=None, date_time=None, dawn_dusk=False): """ Determine if sun is above horizon at for a list of times. Parameters ---------- latitude : int, float Latitude in degrees north positive. Must be a scalar. longitude : int, float Longitude in degrees east positive. Must be a scalar. date_time : datetime.datetime, numpy.array.datetime64, list of datetime.datetime Datetime with timezone, datetime with no timezone in UTC, or numpy.datetime64 format in UTC. Can be a single datetime object or list of datetime objects. dawn_dusk : boolean If set to True, will use skyfields dark_twilight_day function to calculate sun up Returns a list of int's instead of boolean. 0 - Dark of Night 1 - Astronomical Twilight 2 - Nautical Twilight 3 - Civil Twilight 4 - Sun Is Up Returns ------- result : list List matching size of date_time containing True/False if sun is above horizon. """ sf_dates = None # Check if datetime object is scalar and if has no timezone. if isinstance(date_time, datetime): if date_time.tzinfo is None: sf_dates = [date_time.replace(tzinfo=pytz.UTC)] else: sf_dates = [date_time] # Check if datetime objects in list have timezone. If not add. if isinstance(date_time, (list, tuple)) and isinstance(date_time[0], datetime): if isinstance(date_time[0], datetime) and date_time[0].tzinfo is not None: sf_dates = date_time else: sf_dates = [ii.replace(tzinfo=pytz.UTC) for ii in date_time] # Convert datetime64 to datetime with timezone. if type(date_time).__module__ == np.__name__ and np.issubdtype(date_time.dtype, np.datetime64): sf_dates = datetime64_to_datetime(date_time) sf_dates = [ii.replace(tzinfo=pytz.UTC) for ii in sf_dates] if sf_dates is None: raise ValueError('The date_time values entered into is_sun_visible() ' 'do not match input types.') ts = load.timescale() eph = load_file(skyfield_bsp_file) t0 = ts.from_datetimes(sf_dates) location = wgs84.latlon(latitude, longitude) if dawn_dusk: f = almanac.dark_twilight_day(eph, location) else: f = almanac.sunrise_sunset(eph, location) sun_up = f(t0) eph.close() return sun_up
def get_sunrise_sunset_noon(latitude=None, longitude=None, date=None, library='skyfield', timezone=False): """ Calculate sunrise, sunset and local solar noon times. Parameters ---------- latitude : int, float Latitude in degrees north positive. Must be a scalar. longitude : int, float Longitude in degrees east positive. Must be a scalar. date : (datetime.datetime, numpy.datetime64, list of datetime.datetime, numpy.array of numpy.datetime64, string, list of string) Date(s) to return sunrise, sunset and noon times spaning the first date to last date if more than one provided. May be a scalar or vector. If entered as a string must follow YYYYMMDD format. library : str Library to use for making calculations. Options include ['skyfield'] timezone : boolean Have timezone with datetime. Returns ------- result : tuple of three numpy.array Tuple of three values sunrise, sunset, noon. Values will be a list. If no values can be calculated will return empty list. If the date is within polar night will return empty lists. If spans the transition to polar day will return previous sunrise or next sunset outside of date range provided. """ sunrise, sunset, noon = np.array([]), np.array([]), np.array([]) if library == 'skyfield': ts = load.timescale() eph = load_file(skyfield_bsp_file) sf_dates = [] # Parse datetime object if isinstance(date, datetime): if date.tzinfo is None: sf_dates = [date.replace(tzinfo=pytz.UTC)] else: sf_dates = [date] if isinstance(date, (list, tuple)) and isinstance(date[0], datetime): if date[0].tzinfo is not None: sf_dates = date else: sf_dates = [ii.replace(tzinfo=pytz.UTC) for ii in date] # Parse string date if isinstance(date, str): sf_dates = [datetime.strptime(date, '%Y%m%d').replace(tzinfo=pytz.UTC)] # Parse list of string dates if isinstance(date, (list, tuple)) and isinstance(date[0], str): sf_dates = [datetime.strptime(dt, '%Y%m%d').replace(tzinfo=pytz.UTC) for dt in date] # Convert datetime64 to datetime if type(date).__module__ == np.__name__ and np.issubdtype(date.dtype, np.datetime64): sf_dates = datetime64_to_datetime(date) sf_dates = [ii.replace(tzinfo=pytz.UTC) for ii in sf_dates] # Function for calculating solar noon # Convert location into skyfield location object location = wgs84.latlon(latitude, longitude) # Set up function to indicate calculating locatin of Sun from Earth f = almanac.meridian_transits(eph, eph['Sun'], location) # Set up dates to be start of day and end of day so have a range t0 = sf_dates[0] t0 = t0.replace(hour=0, minute=0, second=0) t1 = sf_dates[-1] t1 = t1.replace(hour=23, minute=59, second=59) # Convert times from datetime to skyfild times t0 = ts.from_datetime(t0) t1 = ts.from_datetime(t1) # Calculate Meridian Transits. n contains times and x contains 1 and 0's # indicating when transit time is above or below location. n, x = almanac.find_discrete(t0, t1, f) # Determine if time is during daylight f = almanac.sunrise_sunset(eph, location) sun_up = f(n) # Filter out times when sun is below location or in polar night n = n[(x == 1) & sun_up] noon = n.utc_datetime() if noon.size == 0: return sunrise, sunset, noon # Calcuate sunrise and sunset times. Calcuate over range 12 less than minimum # noon time and 12 hours greater than maximum noon time. t0 = min(noon) - timedelta(hours=12) t1 = max(noon) + timedelta(hours=12) t0 = ts.from_datetime(t0) t1 = ts.from_datetime(t1) f = almanac.sunrise_sunset(eph, location) t, y = almanac.find_discrete(t0, t1, f) times = t.utc_datetime() sunrise = times[y == 1] sunset = times[y == 0] # Fill in sunrise and sunset if asked to during polar day if len(noon) > 0 and (y.size == 0 or len(sunrise) != len(sunset)): t0 = min(noon) - timedelta(days=90) t1 = max(noon) + timedelta(days=90) t0 = ts.from_datetime(t0) t1 = ts.from_datetime(t1) t, yy = almanac.find_discrete(t0, t1, f) times = t.utc_datetime() # If first time is sunset and/or last time is sunrise filter # from times if yy[0] == 0: yy = yy[1:] times = times[1:] if yy[-1] == 1: yy = yy[:-1] times = times[:-1] # Extract sunrise times temp_sunrise = times[yy == 1] # Extract sunset times temp_sunset = times[yy == 0] # Look for index closest to first noon time to get the time of last sunrise # since we are in polar day. diff = temp_sunrise - min(noon) sunrise_index = np.max(np.where(diff < timedelta(seconds=1))) # Look for index closest to last noon time to get the time of first sunset # since we are in polar day. diff = max(noon) - temp_sunset sunset_index = np.min(np.where(diff < timedelta(seconds=1))) + 1 sunrise = temp_sunrise[sunrise_index: sunset_index] sunset = temp_sunset[sunrise_index: sunset_index] eph.close() if timezone is False: for ii in range(0, sunset.size): sunrise[ii] = sunrise[ii].replace(tzinfo=None) sunset[ii] = sunset[ii].replace(tzinfo=None) for ii in range(0, noon.size): noon[ii] = noon[ii].replace(tzinfo=None) return sunrise, sunset, noon