def add_solar(netCDFfile): ds = Dataset(netCDFfile, 'a') lat = ds.variables['LATITUDE'][:] lon = ds.variables['LONGITUDE'][:] print('lat ', lat, ' lon ', lon) time = ds.variables['TIME'] dt = num2date(time[:], units=time.units, calendar=time.calendar) dt_utc = [d.replace(tzinfo=pytz.UTC) for d in dt] print('time ', dt_utc[0]) altitude_deg = [get_altitude(lat, lon, d) for d in dt_utc] rad = [extraterrestrial_irrad(lat, lon, d, 1370) for d in dt_utc] print("altitude", altitude_deg[0], " rad ", rad[0]) ncVarOut = ds.createVariable( "SOLAR", "f4", ("TIME", ), fill_value=np.nan, zlib=True) # fill_value=nan otherwise defaults to max ncVarOut[:] = rad ncVarOut.units = "W/m2" ncVarOut.setncattr( 'name', 'extraterrestrial_irrad celestial incoming solar radiation') ncVarOut.long_name = 'incoming_solar_radiation' ncVarOut.comment = "using http://docs.pysolar.org/en/latest/ v0.8 extraterrestrial_irrad() with incoming = 1370 W/m^2" # update the history attribute try: hist = ds.history + "\n" except AttributeError: hist = "" ds.setncattr( 'history', hist + datetime.utcnow().strftime("%Y-%m-%d") + " : added incoming radiation") ds.close()
def test_util_extraterrestrial_irrad_no_error(self): try: util.extraterrestrial_irrad(self.lat, self.lon, self.aware) except NoTimeZoneInfoError: self.fail("""'NoTimeZoneInfoError' should not be raised \ as 'datetime' object is tz-aware.""")
def test_util_extraterrestrial_irrad_raise_error(self): with self.assertRaises(NoTimeZoneInfoError): util.extraterrestrial_irrad(self.lat, self.lon, self.unaware)
def calc_vis_spectral(self, when: datetime.datetime, temperature: float, relative_humidity: float, tau500: float): if temperature < 1: temperature = 1 solar_altitude_deg = solar.get_altitude(self.latitude, self.longitude, when, elevation=self.elevation, pressure=self.pressure, temperature=temperature) zenref = 90 - abs(solar_altitude_deg) if zenref > 90: print("Zenref greater than 90 wtf!!! : {}".format(zenref)) return np.zeros(self.n_wvls + 1, dtype=np.float64) # tricky! the way that extraterrestrial_irrad is calculated is by getting the erv. etr_total = util.extraterrestrial_irrad(self.latitude, self.longitude, when=when) if when.hour == 12 and when.minute == 0: print(when, "\t", temperature, "\t", relative_humidity, "\t", etr_total) erv = etr_total / 1367.0 # these are almost identical. DO NOT OPT FOR PYSOLAR UNTIL THEY HAVE IMPLEMENTED # Airmass Kasten, F. and Young, A. 1989. Revised optical air mass tables # amass = radiation.get_air_mass_ratio(altitude_deg=solar_altitude_deg) amass = 1.0 / np.cos(np.radians(zenref)) + 0.50572 * pow( 96.07995 - zenref, -1.6364) ampress = amass * (self.pressure / 1.013e6) O3 = self.calc_ozone(int(when.timetuple().tm_yday)) watvap = self.prec_h2o_estimate(temperature, relative_humidity) cz = np.cos(np.radians(zenref)) # Ozone mass ozone_mass = 1.003454 / np.sqrt((pow(cz, 2)) + 0.006908) if not 0 <= abs(solar_altitude_deg) <= 90: # print("NIGHTTIME : {}".format(solar_altitude_deg)) # print("Solar alt out of range!!! : {}".format(solar_altitude_deg)) # print(when, self.pressure, temperature, solar_altitude_deg, amass) return np.zeros(self.n_wvls + 1, dtype=np.float64) if not 0 <= amass < 1000: print("NIGHTTIME : {}".format(solar_altitude_deg)) print("Solar alt out of range!!! : {}".format(solar_altitude_deg)) return np.zeros(self.n_wvls + 1, dtype=np.float64) def calc(wvl): etr_spec, watvap_coeff, ozone_absorb_coeff, unif_mix_gas_ab_coeff = coeff_spline( wvl) watvap_coeff = max(watvap_coeff, 0.0) ozone_absorb_coeff = max(ozone_absorb_coeff, 0.0) unif_mix_gas_ab_coeff = max(unif_mix_gas_ab_coeff, 0.0) etr_spec = max(etr_spec, 0.0) etr = etr_spec * erv # omegl = OMEG * np.exp(-OMEGP * (np.log(wvl / 0.4) * np.log(wvl / 0.4))) c1 = tau500 * pow(wvl * 2.0, -self.alpha) # Equation 2-4 Tr = np.exp(-ampress / (pow(wvl, 4) * (115.6406 - 1.3366 / pow(wvl, 2)))) # Equation 2-9 To = np.exp(-ozone_absorb_coeff * O3 * ozone_mass) # Equation 2-8 tw_v = 1.0 + 20.07 * watvap_coeff * watvap * ampress Tw = np.exp(-0.2385 * watvap_coeff * watvap * ampress / pow(tw_v, 0.45)) # Equation 2-11 # cast to float makes this value real enough for numpy Tu = np.exp( -1.41 * unif_mix_gas_ab_coeff * ampress / pow(1.0 + 118.3 * unif_mix_gas_ab_coeff * ampress, 0.45)) # Equation 2-6, sort of Ta = np.exp(-c1 * ampress) # ........... Direct energy ............. # Temporary variable c2 = etr * To * Tw * Tu # Equation 2-1 direct = c2 * Tr * Ta return direct vectorized_calc = np.vectorize(calc) direct_spec = vectorized_calc(self.wavelengths) def integrate(wl_delta, direct, prev_direct): return wl_delta * (direct + prev_direct) vectorized_integrate = np.vectorize(integrate) integrated_d = vectorized_integrate( np.append([0], np.diff(self.wavelengths)), direct_spec, np.append([0], direct_spec[:-1])) return np.append([etr_total], integrated_d)
def calc_vis_spectral_from_array(self, when: datetime.datetime, temperature, relative_humidity, tau500): solar_altitude_deg = solar.get_altitude(self.latitude, self.longitude, when, elevation=self.elevation, pressure=self.pressure, temperature=temperature) if solar_altitude_deg < 0: # print("NIGHTTIME : {}".format(solar_altitude_deg)) return np.zeros(41, dtype=np.float64) zenref = 90 - abs(solar_altitude_deg) if zenref > 90: print("Zenref greater than 90 wtf!!! : {}".format(zenref)) return np.zeros(41, dtype=np.float64) # tricky! the way that extraterrestrial_irrad is calculated is by getting the erv. etr_total = util.extraterrestrial_irrad(self.latitude, self.longitude, when) erv = etr_total / 1367.0 # these are almost identical. opt for pysolar amass = radiation.get_air_mass_ratio(altitude_deg=solar_altitude_deg) # amass = 1.0 / np.cos(np.radians(zenref)) + 0.50572 * pow(96.07995 - zenref, -1.6364) ampress = amass * self.pressure / 1013000 if amass < 0: print(amass, solar_altitude_deg) O3 = self.calc_ozone(int(when.timetuple().tm_yday)) watvap = self.prec_h2o_estimate(temperature, relative_humidity) # spectra array spec = np.zeros((122, 5)) # this array should function as the following 5 # wavelength # delta # direct # integrated direct # etr # copy wavelengths spec[:, 0] = WAVELENGTH_MICRONS[:] # delta between wavlengths for idx, (wl1, wl2) in enumerate(zip(spec[:-1, 0], spec[1:, 0])): spec[idx + 1, 1] = 0.5 * abs(wl2 - wl1) cz = np.cos(np.radians(zenref)) # Ozone mass ozone_mass = 1.003454 / np.sqrt((cz * cz) + 0.006908) for idx, (wvl, delta, direct, direct_integrated, etr) in enumerate(spec): etr = ETR_SPECTRUM[idx] * erv spec[idx, -1] = etr watvap_coeff = WATER_VAPOR_COEFF[idx] ozone_absorb_coeff = OZONE_ABSORBTION_COEFF[idx] unif_mix_gas_ab_coeff = UNIFORMLY_MIXED_GAS_ABSORBTION_COEFF[idx] # omegl = OMEG * np.exp(-OMEGP * (np.log(wvl / 0.4) * np.log(wvl / 0.4))) c1 = tau500 * pow(wvl * 2.0, -self.alpha) # Equation 2-4 Tr = np.exp(-ampress / ((wvl * wvl * wvl * wvl) * (115.6406 - 1.3366 / (wvl * wvl)))) # Equation 2-9 To = np.exp(-ozone_absorb_coeff * O3 * ozone_mass) # Equation 2-8 tw_v = float(1.0 + 20.07 * watvap_coeff * watvap * ampress) Tw = np.exp(-0.2385 * watvap_coeff * watvap * ampress / pow(tw_v, 0.45)) # print(tw_v, Tw) # Equation 2-11 # cast to float makes this value real enough for numpy Tu = np.exp( -1.41 * unif_mix_gas_ab_coeff * ampress / pow(1.0 + 118.3 * unif_mix_gas_ab_coeff * ampress, 0.45)) # Equation 2-6, sort of Ta = np.exp(-c1 * ampress) # ........... Direct energy ............. # Temporary variable c2 = etr * To * Tw * Tu # Equation 2-1 direct = c2 * Tr * Ta spec[idx, 2] = direct # direct integration direct_integrated = delta * (direct + spec[max(idx - 1, 0), 2]) spec[idx, 3] = direct_integrated # prevdirect = direct # these probably arent needed. # totdirect = np.sum(spec[:, 3]) # totvis = np.sum(np.clip(spec[14:55, 3], 0.0, np.inf)) # visible_integration = spec[14:55, 3] # visble_wavelength_microns = WAVELENGTH_MICRONS[14:55] # trad = np.sum(np.clip(spec[:, 2], 0.0, np.inf)) # # spec[:, 2] /= (solar_irradiance * trad) # vrad = spec[14:55, 2] # etr = util.extraterrestrial_irrad(self.latitude, self.longitude, when) # only return direct and visible return np.append([etr_total] + spec[14:55, 3])
def add_solar(netCDFfiles): # add incoming radiation out_files = [] for fn in netCDFfiles: # Change the creation date in the filename to today now = datetime.utcnow() fn_new = fn if os.path.basename(fn).startswith("IMOS_"): fn_new_split = os.path.basename(fn).split('_') fn_new_split[-1] = "C-" + now.strftime("%Y%m%d") + ".nc" fn_new = os.path.join(os.path.dirname(fn), '_'.join(fn_new_split)) # If a new (different) filename has been successfully generated, make # a copy of the old file with the new name if fn_new != fn: print('copying file to ', fn_new) # copy file shutil.copy(fn, fn_new) out_files.append(fn_new) ds = Dataset(fn_new, 'a') lat = ds.variables['LATITUDE'][:] lon = ds.variables['LONGITUDE'][:] ndepth = ds.variables['NOMINAL_DEPTH'][:] print('lat ', lat, ' lon ', lon) time_var = ds.variables['TIME'] print('number of points ', len(time_var)) dt = num2date(time_var[:], units=time_var.units, calendar=time_var.calendar, only_use_cftime_datetimes=False) dt_utc = [d.replace(tzinfo=pytz.UTC) for d in dt] print('time start ', dt_utc[0]) altitude_deg = get_altitude_fast(lat, lon, dt) rad = extraterrestrial_irrad(lat, lon, dt, 1361) if ndepth > 0: #depth_var = ds.variables['PRES'] #depth = depth_var[:] depth = np.ones_like(rad) * ndepth par = rad * np.exp(-0.04 * depth) * 2.114 else: par = rad * 2.114 print("altitude", altitude_deg[0], " rad ", rad[0]) if 'ALT' in ds.variables: ncVarOut = ds.variables["ALT"] else: ncVarOut = ds.createVariable( "ALT", "f4", ("TIME", ), fill_value=np.nan, zlib=True) # fill_value=nan otherwise defaults to max ncVarOut[:] = altitude_deg ncVarOut.units = "degree" ncVarOut.long_name = 'sun_altitude' ncVarOut.coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH' ncVarOut.comment = "using http://docs.pysolar.org/en/latest/ v0.8 get_altitude" if 'SOLAR' in ds.variables: ncVarOut = ds.variables["SOLAR"] else: ncVarOut = ds.createVariable( "SOLAR", "f4", ("TIME", ), fill_value=np.nan, zlib=True) # fill_value=nan otherwise defaults to max ncVarOut[:] = rad ncVarOut.units = "W/m2" ncVarOut.long_name = 'incoming_solar_radiation' ncVarOut.coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH' ncVarOut.comment = "using http://docs.pysolar.org/en/latest/ v0.8 extraterrestrial_irrad() with incoming = 1361 W/m^2" if 'ePAR' in ds.variables: ncVarOut = ds.variables["ePAR"] else: ncVarOut = ds.createVariable( "ePAR", "f4", ("TIME", ), fill_value=np.nan, zlib=True) # fill_value=nan otherwise defaults to max ncVarOut[:] = par ncVarOut.units = "umol/m^2/s" ncVarOut.long_name = 'incoming_solar_radiation converted to PAR (x2.114) attenuated by depth' ncVarOut.coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH' ncVarOut.comment = "using http://docs.pysolar.org/en/latest/ v0.8 extraterrestrial_irrad() with incoming = 1361 W/m^2, x 2.114, kd = 0.04" # update the history attribute try: hist = ds.history + "\n" except AttributeError: hist = "" ds.setncattr( 'history', hist + datetime.utcnow().strftime("%Y-%m-%d") + " : added incoming radiation") ds.close() return out_files