def swin1D(pob, sob, tob, stat, dates, index): # many arrays transposed """ toposcale surface pressure using hypsometric equation - move to own class index: index of station array (numeric) """ g = 9.81 R = 287.05 # Gas constant for dry air. tz = 0 # ERA5 is always utc0, used to compute sunvector ztemp = pob.z[:, index, :].T Ttemp = pob.t[:, index, :].T statz = stat.ele[index] * g dz = ztemp.T - statz # transpose not needed but now consistent with CGC surface pressure equations psf = [] # loop through timesteps #for i in range(starti,endi): for i in range(0, dates.size): # # find overlying layer thisp = dz[:, i] == np.min(dz[:, i][dz[:, i] > 0]) # booleen indexing T1 = Ttemp[i, thisp] z1 = ztemp[i, thisp] p1 = pob.levels[thisp] * 1e2 #Convert to Pa. Tbar = np.mean([T1, tob.t[i, index]], axis=0) """ Hypsometric equation.""" psf.append(p1 * np.exp((z1 - statz) * (g / (Tbar * R)))) psf = np.array(psf).squeeze() ## Specific humidity routine. # mrvf=0.622.*vpf./(psf-vpf); #Mixing ratio for water vapor at subgrid. # qf=mrvf./(1+mrvf); # Specific humidity at subgrid [kg/kg]. # fout.q(:,:,n)=qf; """ Maybe follow Dubayah's approach (as in Rittger and Girotto) instead for the shortwave downscaling, other than terrain effects. """ """ Height of the "grid" (coarse scale)""" Zc = sob.z[:, index] # should this be a single value or vector? """ toa """ SWtoa = sob.tisr[:, index] """ Downwelling shortwave flux of the "grid" using nearest neighbor.""" SWc = sob.ssrd[:, index] """Calculate the clearness index.""" kt = SWc / SWtoa #kt[is.na(kt)==T]<-0 # make sure 0/0 =0 #kt[is.infinite(kt)==T]<-0 # make sure 0/0 =0 kt[kt < 0] = 0 kt[kt > 1] = 0.8 #upper limit of kt kt = kt """ Calculate the diffuse fraction following the regression of Ruiz-Arias 2010 """ kd = 0.952 - 1.041 * np.exp(-1 * np.exp(2.3 - 4.702 * kt)) kd = kd """ Use this to calculate the downwelling diffuse and direct shortwave radiation at grid. """ SWcdiff = kd * SWc SWcdir = (1 - kd) * SWc SWcdiff = SWcdiff SWcdir = SWcdir """ Use the above with the sky-view fraction to calculate the downwelling diffuse shortwave radiation at subgrid. """ SWfdiff = stat.svf[index] * SWcdiff SWfdiff = np.nan_to_num(SWfdiff) # convert nans (night) to 0 """ Direct shortwave routine, modified from Joel. Get surface pressure at "grid" (coarse scale). Can remove this part once surface pressure field is downloaded, or just check for existance. """ ztemp = pob.z[:, index, :].T Ttemp = pob.t[:, index, :].T dz = ztemp.transpose() - sob.z[:, index] psc = [] for i in range(0, dz.shape[1]): #thisp.append(np.argmin(dz[:,i][dz[:,i]>0])) thisp = dz[:, i] == np.min(dz[:, i][dz[:, i] > 0]) z0 = sob.z[i, index] T0 = sob.t2m[i, index] T1 = Ttemp[i, thisp] z1 = ztemp[i, thisp] p1 = pob.levels[thisp] * 1e2 #Convert to Pa. Tbar = np.mean([T0, T1], axis=0) """ Hypsometric equation.""" psc.append(p1 * np.exp((z1 - z0) * (g / (Tbar * R)))) psc = np.array(psc).squeeze() #T1=Ttemp(thisp) #z1=ztemp(thisp) #p1=pob.levels(thisp)*1e2 #Convert to Pa. #Tbar=mean([T0 T1]) """compute julian dates""" jd = sg.to_jd(dates) """ Calculates a unit vector in the direction of the sun from the observer position. """ sunv = sg.sunvector(jd=jd, latitude=stat.lat[index], longitude=stat.lon[index], timezone=tz) """ Computes azimuth , zenith and sun elevation for each timestamp """ sp = sg.sunpos(sunv) sp = sp # Cosine of the zenith angle. sp.zen = sp.zen #sp.zen=sp.zen*(sp.zen>0) # Sun might be below the horizon. muz = np.cos(sp.zen) muz = muz # NB! psc must be in Pa (NOT hPA!). #if np.max(psc<1.5e3): # Obviously not in Pa #psc=psc*1e2 # Calculate the "broadband" absorption coefficient. Elevation correction # from Kris ka = (g * muz / (psc)) * np.log(SWtoa / SWcdir) #ka.set_fill_value(0) #ka = ka.filled() # set inf (from SWtoa/SWcdir at nigh, zero division) to 0 (night) ka[ka == -np.inf] = 0 ka[ka == np.inf] = 0 # Note this equation is obtained by inverting Beer's law, i.e. use #I_0=I_inf x exp[(-ka/mu) int_z0**inf rho dz] # Along with hydrostatic equation to convert to pressure coordinates then # solve for ka using p(z=inf)=0. # Now you can (finally) find the direct component at subgrid. SWfdir = SWtoa * np.exp(-ka * psf / (g * muz)) """ Then perform the terrain correction. [Corripio 2003 / rpackage insol port].""" """compute mean horizon elevation - why negative hor.el possible??? """ horel = (((np.arccos(np.sqrt(stat.svf[index])) * 180) / np.pi) * 2) - stat.slp[index] if horel < 0: horel = 0 meanhorel = horel """ normal vector - Calculates a unit vector normal to a surface defined by slope inclination and slope orientation. """ nv = sg.normalvector(slope=stat.slp[index], aspect=stat.asp[index]) """ Method 1: Computes the intensity according to the position of the sun (sunv) and dotproduct normal vector to slope. From corripio r package """ dotprod = np.dot(sunv, np.transpose(nv)) dprod = dotprod.squeeze() dprod[dprod < 0] = 0 #negative indicates selfshading dprod = dprod """Method 2: Illumination angles. Dozier""" saz = sp.azi cosis = muz * np.cos(stat.slp[index]) + np.sin( sp.zen) * np.sin(stat.slp[index]) * np.cos( sp.azi - stat.asp[index] ) # cosine of illumination angle at subgrid. cosic = muz # cosine of illumination angle at grid (slope=0). """ SUN ELEVATION below hor.el set to 0 - binary mask """ selMask = sp.sel selMask[selMask < horel] = 0 selMask[selMask > 0] = 1 selMask = selMask """ derive incident radiation on slope accounting for self shading and cast shadow and solar geometry BOTH formulations seem to be broken """ #SWfdirCor=selMask*(cosis/cosic)*SWfdir SWfdirCor = selMask * dprod * SWfdir SWfglob = SWfdiff + SWfdirCor return SWfglob """
def swin(self, pob, sob, tob, stat, dates): """ toposcale surface pressure using hypsometric equation - move to own class EDITS Feb 20 2020: -See https://www.evernote.com/Home.action?login=true#n=78e92684-4d64-4802-ab6c-2cb90b277e44&s=s500&ses=1&sh=5&sds=5&x=& - EDITS Jul 11 2019: - removed elevation scaling as this degrade results - at least in WFJ test- Kris? - reimplemnt original additative ele method - better than beers, or at least less damaging - removed mask, why is this causing problems? - dotprod method (corripio) does not seem to work - I dont think it ever did as we used SWTopo ==FALSE - use Dozier cos corrction method (as in paper right) - So ... we have ele, illumination angle, self shading and svf correction. We do not have horizon correction (partly in self shading of course) """ ## Specific humidity routine. # mrvf=0.622.*vpf./(psf-vpf); #Mixing ratio for water vapor at subgrid. # qf=mrvf./(1+mrvf); # Specific humidity at subgrid [kg/kg]. # fout.q(:,:,n)=qf; """ Maybe follow Dubayah's approach (as in Rittger and Girotto) instead for the shortwave downscaling, other than terrain effects. """ """ Height of the "grid" (coarse scale)""" Zc = sob.z """ toa """ SWtoa = sob.tisr """ Downwelling shortwave flux of the "grid" using nearest neighbor.""" SWc = sob.ssrd """Calculate the clearness index.""" kt = SWc / SWtoa #kt[is.na(kt)==T]<-0 # make sure 0/0 =0 #kt[is.infinite(kt)==T]<-0 # make sure 0/0 =0 kt[kt < 0] = 0 kt[kt > 1] = 0.8 #upper limit of kt self.kt = kt """ Calculate the diffuse fraction following the regression of Ruiz-Arias 2010 """ kd = 0.952 - 1.041 * np.exp(-1 * np.exp(2.3 - 4.702 * kt)) self.kd = kd """ Use this to calculate the downwelling diffuse and direct shortwave radiation at grid. """ SWcdiff = kd * SWc SWcdir = (1 - kd) * SWc self.SWcdiff = SWcdiff self.SWcdir = SWcdir """ Use the above with the sky-view fraction to calculate the downwelling diffuse shortwave radiation at subgrid. """ self.SWfdiff = SWcdiff #* stat.svf self.SWfdiff.set_fill_value(0) self.SWfdiff = self.SWfdiff.filled() """ Direct shortwave routine, modified from Joel. Get surface pressure at "grid" (coarse scale). Can remove this part once surface pressure field is downloaded, or just check for existance. """ #T1=Ttemp(thisp) #z1=ztemp(thisp) #p1=pob.levels(thisp)*1e2 #Convert to Pa. #Tbar=mean([T0 T1]) """compute julian dates""" jd = sg.to_jd(dates) """ Calculates a unit vector in the direction of the sun from the observer position. """ sunv = sg.sunvector(jd=jd, latitude=stat.lat, longitude=stat.lon, timezone=stat.tz) """ Computes azimuth , zenith and sun elevation for each timestamp """ sp = sg.sunpos(sunv) self.sp = sp # Cosine of the zenith angle. sp.zen = sp.zen #sp.zen=sp.zen*(sp.zen>0) # Sun might be below the horizon. muz = np.cos(sp.zen) self.muz = muz # NB! psc must be in Pa (NOT hPA!). #if np.max(psc<1.5e3): # Obviously not in Pa #psc=psc*1e2 # Compute air pressure at fine grid ztemp = pob.z Ttemp = pob.t statz = stat.ele * self.g dz = ztemp.transpose( ) - statz # transpose not needed but now consistent with CGC surface pressure equations # compute air pressure at fine grid (could remove if download air pressure variable) self.psf = [] # loop through timesteps for i in range(0, dates.size): # # find overlying layer thisp = dz[:, i] == np.min(dz[:, i][dz[:, i] > 0]) # booleen indexing T1 = Ttemp[i, thisp] z1 = ztemp[i, thisp] p1 = pob.levels[thisp] * 1e2 #Convert to Pa. Tbar = np.mean([T1, tob.t[i]], axis=0) """ Hypsometric equation.""" self.psf.append(p1 * np.exp( (z1 - statz) * (self.g / (Tbar * self.R)))) self.psf = np.array(self.psf).squeeze() # Method 1 BEERS LAW if (BEERS == 'TRUE'): # compute air pressure at coarse grid dz = ztemp.transpose() - sob.z self.psc = [] for i in range(0, dz.shape[1]): #thisp.append(np.argmin(dz[:,i][dz[:,i]>0])) thisp = dz[:, i] == np.min(dz[:, i][dz[:, i] > 0]) z0 = sob.z[i] T0 = sob.t2m[i] T1 = Ttemp[i, thisp] z1 = ztemp[i, thisp] p1 = pob.levels[thisp] * 1e2 #Convert to Pa. Tbar = np.mean([T0, T1], axis=0) """ Hypsometric equation.""" self.psc.append(p1 * np.exp( (z1 - z0) * (self.g / (Tbar * self.R)))) self.psc = np.array(self.psc).squeeze() # Calculate the "broadband" absorption coefficient. Elevation correction # from Kris ka = (self.g * muz / (self.psc)) * np.log(SWtoa / SWcdir) #ka.set_fill_value(0) #ka = ka.filled() # set inf (from SWtoa/SWcdir at nigh, zero division) to 0 (night) ka[ka == -np.inf] = 0 ka[ka == np.inf] = 0 # Note this equation is obtained by inverting Beer's law, i.e. use #I_0=I_inf x exp[(-ka/mu) int_z0**inf rho dz] # Along with hydrostatic equation to convert to pressure coordinates then # solve for ka using p(z=inf)=0. # Method 1 Beers law # Now you can (finally) find the direct component at subgrid. self.SWfdir = SWtoa * np.exp(-ka * self.psf / (self.g * muz)) #self.SWfdir=SWcdir # Method 2 ORIGINAL ELEVATION Correction # https://hal.archives-ouvertes.fr/hal-00470155/document """ele diff in km""" coarseZ = Zc[0] / 9.9 dz = (stat.ele - coarseZ) / 1000 # fine - coase in knm s = SWtoa b = SWcdir zen = sp.zen thetaz = (np.pi / 180) * zen #radians m = 1 / np.cos(thetaz) k = -np.log(b / s) / m k.set_fill_value(0) k = k.filled() #k[is.na(k)==T]<-0 #correct b=0/s=0 problem t = np.exp(1)**(-k * dz * np.cos(thetaz)) t[t > 1] < -1.1 #to constrain to reasonable values t[t < 0.8] < -0.9 #to constrain to reasonable values db = (1 - t) * SWcdir self.SWfdir = SWcdir + db #additative correction #====================================== """ Then perform the terrain correction. [Corripio 2003 / rpackage insol port].""" """compute mean horizon elevation - why negative hor.el possible??? Have tested seesm OK """ # In [485]: (((np.arccos(np.sqrt(0.9))*180.)/np.pi)*2)-20 # Out[485]: 16.869897645844034 # In [486]: (((np.arccos(np.sqrt(1))*180.)/np.pi)*2)-0 # Out[486]: 0.0 # In [487]: (((np.arccos(np.sqrt(0.9))*180.)/np.pi)*2)-0 # Out[487]: 36.869897645844034 # In [488]: (((np.arccos(np.sqrt(0.9))*180.)/np.pi)*2)-10 # Out[488]: 26.869897645844034 # In [489]: (((np.arccos(np.sqrt(0.95))*180.)/np.pi)*2)-10 # Out[489]: 15.841932763167158 horel = (((np.arccos(np.sqrt(stat.svf)) * 180) / np.pi) * 2) - stat.slp if horel < 0: horel = 0 self.meanhorel = horel """ normal vector - Calculates a unit vector normal to a surface defined by slope inclination and slope orientation. """ nv = sg.normalvector(slope=stat.slp, aspect=stat.asp) """ Method 1: Computes the intensity according to the position of the sun (sunv) and dotproduct normal vector to slope. From corripio r package THIS IS GOOD # consider october 31 29018 at 46/9 UTC=0 dt= datetime.datetime(2018, 10, 31, 12, 0) In [471]: sg.to_jd(dt) Out[471]: 2458423.0 sunv=sg.sunvector(jd=2458423 , latitude=46, longitude=9, timezone=0) sp=sg.sunpos(sunv) - sun is in the south: In [456]: sp.azi Out[456]: array([192.82694139]) - at quite low ele In [455]: sp.sel Out[455]: array([19.94907576]) # FLAT CASE In [449]: nv = sg.normalvector(slope=0, aspect=0) In [450]: np.dot(sunv ,np.transpose(nv)) Out[450]: array([[0.34118481]]) # SOOUTH SLOPE CASE = enhanced rad wrt. flat case nv = sg.normalvector(slope=30, aspect=180) In [446]: np.dot(sunv ,np.transpose(nv)) Out[446]: array([[0.75374406]]) # NORTH SLOPE CASE = self shaded In [447]: nv = sg.normalvector(slope=30, aspect=0) In [448]: np.dot(sunv ,np.transpose(nv)) Out[448]: array([[-0.16279463]]) """ dotprod = np.dot(sunv, np.transpose(nv)) dprod = dotprod.squeeze() dprod[dprod < 0] = 0 #negative indicates selfshading self.dprod = dprod """Method 2: Illumination angles. Dozier and self shading""" # THIS IS WRONG # eg consider south facting 30 degree slope on 31 oct 2018 at midday at 46,9 utc=0 # In [420]: sp.azi # Out[420]: array([192.82694139]) # In [421]: stat.slp # Out[421]: 30 # In [422]: stat.asp # Out[422]: 180 # In [423]: sp.azi # Out[423]: array([192.82694139]) # In [424]: sp.sel # Out[424]: array([19.94907576]) # In [425]: ^I^I saz=sp.azi # ...: ^I^I cosis=muz*np.cos(stat.slp)+np.sin(sp.zen)*np.sin(stat.slp)*np.cos(sp.azi-stat.asp)# cosine of illumination angle at subgrid. # ...: ^I^I cosic=muz # cosine of illumination angle at grid (slope=0). # ...: ^I^I cosi = (cosis/cosic) # ...: # In [427]: cosi # Out[427]: array([-1.14169945]) # negative cosi shows sun below horizon! # saz=sp.azi # cosis=muz*np.cos(stat.slp)+np.sin(sp.zen)*np.sin(stat.slp)*np.cos(sp.azi-stat.asp)# cosine of illumination angle at subgrid. # cosic=muz # cosine of illumination angle at grid (slope=0). # cosi = (cosis/cosic) # #If ratio of illumination angle subgrid/ illum angle grid is negative the point is selfshaded # cosi[cosi<0]=0 #WRONG!!! """ CAST SHADOWS: SUN ELEVATION below hor.el set to 0 - binary mask """ selMask = sp.sel selMask[selMask < horel] = 0 selMask[selMask > 0] = 1 self.selMask = selMask """ derive incident radiation on slope accounting for self shading and cast shadow and solar geometry BOTH formulations seem to be broken """ #self.SWfdirCor=selMask*(cosis/cosic)*self.SWfdir # this is really wrong! #self.SWfdirCor=(cosis/cosic)*self.SWfdir self.SWfdirCor = dprod * self.SWfdir * selMask # this is bad WHYYY? #self.SWfdirCor=dprod*self.SWfdir self.SWfglob = self.SWfdiff + self.SWfdirCor #self.SWfglob = self.SWfdiff+ self.SWfdir """
def swin_joel(sob): # vertical profiles https://hal.archives-ouvertes.fr/hal-00470155/document """PARTITION""" """ Height of the "grid" (coarse scale)""" Zc = sob.z """ Height of the "station" (fine scale)""" statz = stat.ele * self.g """ toa """ SWtoa = sob.tisr """ Downwelling shortwave flux of the "grid" using nearest neighbor.""" SWc = sob.ssrd """Calculate the clearness index.""" kt = SWc / SWtoa #kt[is.na(kt)==T]<-0 # make sure 0/0 =0 #kt[is.infinite(kt)==T]<-0 # make sure 0/0 =0 kt[kt < 0] = 0 kt[kt > 1] = 0.8 #upper limit of kt self.kt = kt """ Calculate the diffuse fraction following the regression of Ruiz-Arias 2010 """ kd = 0.952 - 1.041 * np.exp(-1 * np.exp(2.3 - 4.702 * kt)) self.kd = kd """ Use this to calculate the downwelling diffuse and direct shortwave radiation at grid. """ SWcdiff = kd * SWc SWcdir = (1 - kd) * SWc self.SWcdiff = SWcdiff self.SWcdir = SWcdir """ Use the above with the sky-view fraction to calculate the downwelling diffuse shortwave radiation at subgrid. """ self.SWfdiff = stat.svf * SWcdiff self.SWfdiff.set_fill_value(0) self.SWfdiff = self.SWfdiff.filled() """ELEVATION SCALING""" """ Computes azimuth , zenith and sun elevation for each timestamp """ sp = sg.sunpos(sunv) self.sp = sp """ele diff in km""" dz = (stat.z - Zc) / 1000 # fine - coase s = SWtoa b = SWcdir zen = sp.zen #thetaz=(pi/180)*zen #radians #m=1/np.cos(thetaz) #k= -np.log(b/s)/m #k[is.na(k)==T]<-0 #correct b=0/s=0 problem #t=exp(1)^(-k*dz*cos(thetaz)) #t[t>1]<-1.1 #to constrain to reasonable values #t[t<0.8]<-0.9 #to constrain to reasonable values #db=(1-t)*SWcdir SWcdir = SWcdir + db #additative correction """REDUCE DIFFUSE BY SVF""" """ Use the above with the sky-view fraction to calculate the downwelling diffuse shortwave radiation at subgrid. """ self.SWfdiff = stat.svf * SWcdiff self.SWfdiff.set_fill_value(0) self.SWfdiff = self.SWfdiff.filled()
def swin(self, pob, sob, tob, stat, dates): """ toposcale surface pressure using hypsometric equation - move to own class EDITS Jul 11 2019: - removed elevation scaling as this degrade results - at least in WFJ test- Kris? - reimplemnt original additative ele method - better than beers, or at least less damaging - removed mask, why is this causing problems? - dotprod method (corripio) does not seem to work - I dont think it ever did as we used SWTopo ==FALSE - use Dozier cos corrction method (as in paper right) - So ... we have ele, illumination angle, self shading and svf correction. We do not have horizon correction (partly in self shading of course) """ ztemp = pob.z Ttemp = pob.t statz = stat.ele * self.g dz1 = ztemp - statz # transpose not needed but now consistent with CGC surface pressure equations dz = dz1.transpose() self.psf = [] # loop through timesteps for i in range(0, dates.size): # # find overlying layer thisp = dz[:, i] == np.min(dz[:, i][dz[:, i] > 0]) # booleen indexing T1 = Ttemp[i, thisp] z1 = ztemp[i, thisp] p1 = pob.levels[thisp] * 1e2 #Convert to Pa. Tbar = np.mean([T1, tob.t[i]], axis=0) """ Hypsometric equation.""" self.psf.append(p1 * np.exp( (z1 - statz) * (self.g / (Tbar * self.R)))) self.psf = np.array(self.psf).squeeze() ## Specific humidity routine. # mrvf=0.622.*vpf./(psf-vpf); #Mixing ratio for water vapor at subgrid. # qf=mrvf./(1+mrvf); # Specific humidity at subgrid [kg/kg]. # fout.q(:,:,n)=qf; """ Maybe follow Dubayah's approach (as in Rittger and Girotto) instead for the shortwave downscaling, other than terrain effects. """ """ Height of the "grid" (coarse scale)""" Zc = sob.z """ toa """ SWtoa = sob.tisr """ Downwelling shortwave flux of the "grid" using nearest neighbor.""" SWc = sob.ssrd """Calculate the clearness index.""" kt = SWc / SWtoa #kt[is.na(kt)==T]<-0 # make sure 0/0 =0 #kt[is.infinite(kt)==T]<-0 # make sure 0/0 =0 kt[kt < 0] = 0 kt[kt > 1] = 0.8 #upper limit of kt self.kt = kt """ Calculate the diffuse fraction following the regression of Ruiz-Arias 2010 """ kd = 0.952 - 1.041 * np.exp(-1 * np.exp(2.3 - 4.702 * kt)) self.kd = kd """ Use this to calculate the downwelling diffuse and direct shortwave radiation at grid. """ SWcdiff = kd * SWc SWcdir = (1 - kd) * SWc self.SWcdiff = SWcdiff self.SWcdir = SWcdir """ Use the above with the sky-view fraction to calculate the downwelling diffuse shortwave radiation at subgrid. """ self.SWfdiff = stat.svf * SWcdiff self.SWfdiff.set_fill_value(0) self.SWfdiff = self.SWfdiff.filled() """ Direct shortwave routine, modified from Joel. Get surface pressure at "grid" (coarse scale). Can remove this part once surface pressure field is downloaded, or just check for existance. """ ztemp = pob.z Ttemp = pob.t dz = ztemp.transpose() - sob.z self.psc = [] for i in range(0, dz.shape[1]): #thisp.append(np.argmin(dz[:,i][dz[:,i]>0])) thisp = dz[:, i] == np.min(dz[:, i][dz[:, i] > 0]) z0 = sob.z[i] T0 = sob.t2m[i] T1 = Ttemp[i, thisp] z1 = ztemp[i, thisp] p1 = pob.levels[thisp] * 1e2 #Convert to Pa. Tbar = np.mean([T0, T1], axis=0) """ Hypsometric equation.""" self.psc.append(p1 * np.exp( (z1 - z0) * (self.g / (Tbar * self.R)))) self.psc = np.array(self.psc).squeeze() #T1=Ttemp(thisp) #z1=ztemp(thisp) #p1=pob.levels(thisp)*1e2 #Convert to Pa. #Tbar=mean([T0 T1]) """compute julian dates""" jd = sg.to_jd(dates) """ Calculates a unit vector in the direction of the sun from the observer position. """ sunv = sg.sunvector(jd=jd, latitude=stat.lat, longitude=stat.lon, timezone=stat.tz) """ Computes azimuth , zenith and sun elevation for each timestamp """ sp = sg.sunpos(sunv) self.sp = sp # Cosine of the zenith angle. sp.zen = sp.zen #sp.zen=sp.zen*(sp.zen>0) # Sun might be below the horizon. muz = np.cos(sp.zen) self.muz = muz # NB! psc must be in Pa (NOT hPA!). #if np.max(psc<1.5e3): # Obviously not in Pa #psc=psc*1e2 # Calculate the "broadband" absorption coefficient. Elevation correction # from Kris ka = (self.g * muz / (self.psc)) * np.log(SWtoa / SWcdir) #ka.set_fill_value(0) #ka = ka.filled() # set inf (from SWtoa/SWcdir at nigh, zero division) to 0 (night) ka[ka == -np.inf] = 0 ka[ka == np.inf] = 0 # Note this equation is obtained by inverting Beer's law, i.e. use #I_0=I_inf x exp[(-ka/mu) int_z0**inf rho dz] # Along with hydrostatic equation to convert to pressure coordinates then # solve for ka using p(z=inf)=0. # Method 1 Beers law # Now you can (finally) find the direct component at subgrid. self.SWfdir = SWtoa * np.exp(-ka * self.psf / (self.g * muz)) #self.SWfdir=SWcdir # Method 2 ORIGINAL ELEVATION Correction """ele diff in km""" coarseZ = Zc[0] / 9.9 dz = (stat.ele - coarseZ) / 1000 # fine - coase in knm s = SWtoa b = SWcdir zen = sp.zen thetaz = (np.pi / 180) * zen #radians m = 1 / np.cos(thetaz) k = -np.log(b / s) / m k.set_fill_value(0) k = k.filled() #k[is.na(k)==T]<-0 #correct b=0/s=0 problem t = np.exp(1)**(-k * dz * np.cos(thetaz)) t[t > 1] < -1.1 #to constrain to reasonable values t[t < 0.8] < -0.9 #to constrain to reasonable values db = (1 - t) * SWcdir #self.SWfdir=SWcdir+db #additative correction #====================================== """ Then perform the terrain correction. [Corripio 2003 / rpackage insol port].""" """compute mean horizon elevation - why negative hor.el possible??? """ horel = (((np.arccos(np.sqrt(stat.svf)) * 180) / np.pi) * 2) - stat.slp if horel < 0: horel = 0 self.meanhorel = horel """ normal vector - Calculates a unit vector normal to a surface defined by slope inclination and slope orientation. """ nv = sg.normalvector(slope=stat.slp, aspect=stat.asp) """ Method 1: Computes the intensity according to the position of the sun (sunv) and dotproduct normal vector to slope. From corripio r package REMOVE THIS """ dotprod = np.dot(sunv, np.transpose(nv)) dprod = dotprod.squeeze() dprod[dprod < 0] = 0 #negative indicates selfshading self.dprod = dprod """Method 2: Illumination angles. Dozier and self shading""" saz = sp.azi cosis = muz * np.cos( stat.slp) + np.sin(sp.zen) * np.sin(stat.slp) * np.cos( sp.azi - stat.asp) # cosine of illumination angle at subgrid. cosic = muz # cosine of illumination angle at grid (slope=0). cosi = (cosis / cosic) #If ratio of illumination angle subgrid/ illum angle grid is negative the point is selfshaded cosi[cosi < 0] = 0 """ CAST SHADOWS: SUN ELEVATION below hor.el set to 0 - binary mask """ selMask = sp.sel selMask[selMask < horel] = 0 selMask[selMask > 0] = 1 self.selMask = selMask """ derive incident radiation on slope accounting for self shading and cast shadow and solar geometry BOTH formulations seem to be broken """ #self.SWfdirCor=selMask*(cosis/cosic)*self.SWfdir # this is really wrong! self.SWfdirCor = (cosis / cosic) * self.SWfdir self.SWfdirCor = selMask * dprod * self.SWfdir # this is bad #self.SWfdirCor=dprod*self.SWfdir self.SWfglob = self.SWfdiff + self.SWfdirCor #self.SWfglob = self.SWfdiff+ self.SWfdir """