Beispiel #1
0
        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
            """ 
Beispiel #2
0
	def swin2D(pob,sob,tob, stat, dates): 
		'''
		main edit over standard function for points:
		- 3d tob,sob reduce to 2D (reshape)
		'''
		
		timesize=len(dates)
		statsize=len(stat.ele)
		""" toposcale surface pressure using hypsometric equation - move to own 
		class """
		g=9.81
		R=287.05  # Gas constant for dry air.
		#ztemp = pob.z # geopotential height b = np.transpose(a, (2, 0, 1))
		ztemp = np.transpose(pob.z, (2, 0, 1)) # pob is originally ordered levels,stations/cells,time
		#Ttemp = pob.t
		Ttemp = np.transpose(pob.t, (2, 0, 1))# pob is originally ordered levels,stations/cells,time
		statz = np.array(stat.ele)*g
		#dz=ztemp.transpose()-statz[None,:,None] # transpose not needed but now consistent with CGC surface pressure equations
		dz=ztemp-statz # dimensions of dz : time, levels, stations
		
		# set all levels below surface to very big number so they canot be found by min
		newdz=dz
		newdz[newdz<0]=999999
		
		psf =np.zeros( (dates.size, statz.shape[0]) )

		# reshape tob.t here
		tob.tr=tob.t.reshape(tob.t.shape[0]*tob.t.shape[1], tob.t.shape[2], order='F')
		tob.trT=tob.tr.T # transpose to get right order


		# loop through timesteps
		for i in range(0,dates.size):
			
			# find overlying layer
			thisp = dz[i,:,:]==np.min(newdz[i,:,:],axis=0) # thisp is a booleen matrix of levels x stations with true indicating overlying plevel over station surface ele

			# flatten to 1 dimension order='Fortran' or row major
			thispVec =thisp.reshape(thisp.size,order='F')
			TtempVec = Ttemp.reshape(Ttemp.shape[0], Ttemp.shape[1]*Ttemp.shape[2], order='F')
			ztempVec = ztemp.reshape(ztemp.shape[0], ztemp.shape[1]*ztemp.shape[2], order='F')
			
			# booleen indexing to find temp and geopotential that correspond to lowesest overlying layer
			T1=TtempVec[i,thispVec]
			z1=ztempVec[i,thispVec]


			p1=np.tile(pob.levels[::-1],statz.shape[0])[thispVec]*1e2 #Convert to Pa. Reverse levels to ensure low ele (hig pressure) to high elel (low pressure)
			Tbar=np.mean([T1, tob.trT[i, :]],axis=0) # temperature midway between surface (toposcale T) and loweset overlying level (T1)
			""" Hypsometric equation.""" #P1 is above surface is this correct? Yes!
			psf[i,:]=(p1*np.exp((z1-statz)*(g/(Tbar*R)))) # exponent is positive ie increases pressure as surface is lower than pressure level


		""" 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.reshape(sob.z.shape[0]*sob.z.shape[1], sob.z.shape[2]).T # reshape and transpose to remove dimension and make broadcastable

		""" toa """
		SWtoa = sob.tisr.reshape(sob.tisr.shape[0]*sob.tisr.shape[1], sob.tisr.shape[2]).T  

		""" Downwelling shortwave flux of the "grid" using nearest neighbor."""
		SWc=sob.ssrd.reshape(sob.ssrd.shape[0]*sob.ssrd.shape[1], sob.ssrd.shape[2]).T 

		"""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=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 = np.transpose(pob.z, (0, 2, 1))
		Ttemp = np.transpose(pob.t, (0, 2, 1))
		dz=ztemp-Zc # dimensions of dz : levels, time, stations

		# set all levels below surface to very big number so they canot be found by min
		newdz=dz
		newdz[newdz<0]=999999

		psc =np.zeros( (dates.size, statz.shape[0]) )
		for i in range(0,dates.size):
		
			#thisp.append(np.argmin(dz[:,i][dz[:,i]>0]))
			# find overlying layer
			thisp = dz[:,i,:]==np.min(newdz[:,i,:],axis=0) # thisp is a booleen matrix of levels x stations with true indicating overlying plevel over station surface ele !! time index in middle this time!!!
			z0 = Zc[i,:]
			T0 = sob.t2m.reshape(sob.t2m.shape[0]*sob.t2m.shape[1], sob.t2m.shape[2]).T[i,:]

			# flatten to 1 dimension order='Fortran' or row major
			thispVec =thisp.reshape(thisp.size,order='F')
			TtempVec = Ttemp.reshape(Ttemp.shape[1], Ttemp.shape[0]*Ttemp.shape[2], order='F') # !! order of permutations is different from pressure at finegrid routine (time is middle dimension)
			ztempVec = ztemp.reshape(ztemp.shape[1], ztemp.shape[0]*ztemp.shape[2], order='F')# !! order of permutations is different from pressure at finegrid routine (time is middle dimension)
			
			# booleen indexing to find temp and geopotential that correspond to lowesest overlying layer
			T1=TtempVec[i,thispVec]
			z1=ztempVec[i,thispVec]

			p1=np.tile(pob.levels[::-1],statz.shape[0])[thispVec]*1e2 #Convert to Pa.
			Tbar=np.mean([T0, T1],axis=0)
			""" Hypsometric equation."""
			psc[i,:] = (p1*np.exp((z1-z0)*(g/(Tbar*R))))


		
		"""compute julian dates"""
		jd= sg.to_jd(dates)

		"""
		Calculates a unit vector in the direction of the sun from the observer 
		position.
		"""
		svx,svy,svz =	sg.sunvectorMD(jd, stat.lat, stat.lon, stat.tz,statsize,timesize)
		sp=sg.sunposMD(svx,svy,svz)


		# Cosine of the zenith angle.
		#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!).
	  
		# Calculate the "broadband" absorption coefficient. Elevation correction
		ka=(g*muz/(psc))*np.log(SWtoa/SWcdir)	
		ka = np.nan_to_num(ka) 

		# Now you can (finally) find the direct component at subgrid. 
		SWfdir=SWtoa*np.exp(-ka*psf/(g*muz))
		SWfdirCor=SWfdir #*dprod
	  
		SWfglob =  SWfdiff+ SWfdirCor
		print(" %f minutes for VECTORISED interpolation %s" % (round((time.time()/60 - start_time/60),2),"swin") )
		return SWfglob
Beispiel #3
0
def main(coords, eraDir, outDir, startDT, endDT, startIndex):
    print(startDT)
    print(endDT)
    g = 9.81  # geopotential constant
    tz = 0  # timezone always utc0 for era5 data
    myyear = startDT.split('-')[0]

    zp_file = eraDir + "/PLEV_geopotential_" + myyear + ".nc"

    plevDict = {
        eraDir + "/PLEV_temperature_" + myyear + ".nc": "t",
        eraDir + "/PLEV_u_component_of_wind_" + myyear + ".nc": "u",
        eraDir + "/PLEV_v_component_of_wind_" + myyear + ".nc": "v",
        eraDir + "/PLEV_relative_humidity_" + myyear + ".nc": "r"
    }

    surfDict = {
        eraDir + "/SURF_2m_temperature_" + myyear + ".nc": "t2m",
        eraDir + "/SURF_2m_dewpoint_temperature_" + myyear + ".nc": "d2m",
        eraDir + "/SURF_geopotential_" + myyear + ".nc": "z",
        eraDir + "/SURF_surface_solar_radiation_downwards_" + myyear + ".nc":
        "ssrd",
        eraDir + "/SURF_surface_thermal_radiation_downwards_" + myyear + ".nc":
        "strd",
        eraDir + "/SURF_Total precipitation_" + myyear + ".nc":
        "tp",  # removed _
        eraDir + "/SURF_TOA incident solar radiation_" + myyear + ".nc": "tisr"
    }

    # read in lispoints
    lpin = pd.read_csv(coords, header=None)

    # make out path for results
    out = outDir
    if not os.path.exists(out):
        os.makedirs(out)

    # time stuff
    f = nc.Dataset(zp_file)
    nctime = f.variables['time']
    dtime = pd.to_datetime(
        nc.num2date(nctime[:],
                    nctime.units,
                    calendar="standard",
                    only_use_cftime_datetimes=False,
                    only_use_python_datetimes=True))

    if (np.array(np.where(dtime == startDT)).size == 0):
        sys.exit("SYSTEMEXIT:Start date not in netcdf, end of timeseries")

    starti = np.where(
        dtime == startDT)[0].item()  # so can run on a single timestep
    print(endDT)
    if (np.array(np.where(dtime == endDT)).size == 0):
        sys.exit("SYSTEMEXIT: End date not in netcdf, end of timeseries")

    endi = np.where(dtime == endDT)[0].item()
    year = dtime.year[0]

    # compute timestep before we cut timeseries
    a = dtime[2] - dtime[1]
    step = a.seconds
    stephr = step / (60 * 60)
    # extract timestep
    dtime = dtime[starti:endi, ]

    print(("Running timestep " + str(startDT)))

    #===============================================================================
    # tscale3d - 3D interpolation of pressure level fields
    #===============================================================================
    ele = lpin.iloc[:, 2]  #[:,3]
    lats = lpin.iloc[:, 0]  #[:,2] #[s['lat'] for s in stations]
    lons = lpin.iloc[:,
                     1] + 180  #[:,1] #[s['lon'] for s in stations] convert -180-180 to 0-360

    lp = hp.Bunch(ele=ele, lat=lats, lon=lons)

    out_xyz_dem = np.asarray([lats, lons, ele * g], order="F").transpose()

    # init gtob object
    gtob = hp.Bunch(time=dtime)

    for plev in plevDict:

        t = nc.Dataset(plev)  # key is filename
        varname = plevDict[plev]  # value of key is par shortname
        z = nc.Dataset(zp_file)

        # init grid stack
        xdim = out_xyz_dem.shape[0]
        sa_vec = np.zeros(xdim)
        #names=1:n

        for timestep in range(starti, endi):
            """
	        Return original grid temperatures and geopotential of differnet
	        pressure levels. The function are called by inLevelInterp() to
	        get the input ERA5 values.
	        
	        Args: 
	            variable: Given interpolated climate variable
	            timestep: Time need to be interpolated. Time is in interger (e.g.
	            0, 1, 2)
	            
	        Returns:
	            gridT: Grid temperatures of different pressure levels. Retruned 
	            temperature are formated in [level, lat, lon]
	            gridZ: Grid geopotential of different pressure levels. Retruned 
	            temperature are formated in [level, lat, lon]
	            gridLon: Grid longitude of pressure level variables
	            gridLat: Grid latitude of pressure level variables
	        
	        Example:
	            gridT,gridZ,gridLat,gridLon=downscaling.gridValue('Temperature',0)
	            
			"""

            gridT = t.variables[varname][timestep, :, :, :]
            gridZ = z.variables['z'][timestep, :, :, :]
            gridLat = t[
                'latitude'][:]  # coords of grid centre https://confluence.ecmwf.int/display/CKB/ERA5%3A+What+is+the+spatial+reference
            gridLat = gridLat[::-1]  # reverse to deal with ERA5 order
            gridLon = t[
                'longitude'][:]  # coords of grid centre https://confluence.ecmwf.int/display/CKB/ERA5%3A+What+is+the+spatial+reference
            gridLev = t.variables['level'][::-1]
            #return gridT,gridZ,gridLat,gridLon
            """
			This is a 2D interpolatation, and returns interpolated temperatures
			of different pressure levels.

			Interpolated domain is smaller than original domain - original (ERA5) domain
			should be one cell larger than expected point or grid domain.

			Args:
			    gridT: Grid temperatures of different pressure levels. Retruned 
			        temperature are formated in [level, lat, lon]
			    gridZ: Grid geopotential of different pressure levels. Retruned 
			        temperature are formated in [level, lat, lon]
			    gridLat: Grid longitude of pressure level variables
			    gridLon: Grid latitude of pressure level variables
			    out_xyz: Given sites, which will be interpolated.
			    
			Returns:
			    t_interp: Interpolated temperatre of different pressure levels. 
			        The returned values are fomrated in [level, lat, lon]
			    z_interp: Interpolated geopotential of different pressure levels. 
			        The returned values are fomrated in [level, lat, lon]

			Examples:
			    downscaling = downscaling(dem, geop, sa, pl)

			    out_xyz_dem, lats, lons, shape = downscaling.demGrid()
			    out_xyz_sur = downscaling.surGrid(lats, lons, None)

			    #interpolate 2-meter temperature
			    surTa = downscaling.surTa(0, out_xyz_sur)
			    #original ERA-I values
			    gridT,gridZ,gridLat,gridLon = downscaling.gridValue(variable,0)
			    #interpolate temperatures and geopotential of different 
			    pressure levels.

			    t_interp, z_interp = downscaling.inLevelInterp(gridT,gridZ,
			                                                   gridLat,gridLon,
			                                                   out_xyz_dem)
			"""

            shape = gridT.shape
            #create array to hold interpolation resultes
            t_interp = np.zeros([shape[0], len(out_xyz_dem)])
            z_interp = np.zeros([
                shape[0], len(out_xyz_dem)
            ])  # HOW MANY TIMES DO WE REALLY NEED TO COMPUTE THIS?

            #temperatue and elevation interpolation 2d
            for i in range(shape[0]):
                ft = RegularGridInterpolator((gridLat, gridLon),
                                             gridT[i, ::-1, :],
                                             'linear',
                                             bounds_error=False)
                fz = RegularGridInterpolator((gridLat, gridLon),
                                             gridZ[i, ::-1, :],
                                             'linear',
                                             bounds_error=False)
                t_interp[i, :] = ft(out_xyz_dem[:, :2])  #temperature

                z_interp[i, :] = fz(out_xyz_dem[:, :2])  #elevation

                # invert pressure levels
                #t_interp = t_interp[::-1,:]
                #z_interp = z_interp[::-1,:]
                """This is a 1D interpoation. The function return interpolated 
				upper air temperature at the given sites by 
				interpolation between different pressure levels.
				"""
            ele = out_xyz_dem[:, 2]
            size = np.arange(out_xyz_dem.shape[0])
            n = [bisect_left(z_interp[:, i], ele[i]) for i in size]
            n = [x + 1 if x == 0 else x for x in n]

            lowN = [l - 1 for l in n]

            upperT = t_interp[n, size]
            upperZ = z_interp[n, size]
            dG = upperT - t_interp[lowN, size]  #<0
            dG /= upperZ - z_interp[lowN, size]  #<0
            dG *= out_xyz_dem[:, 2] - upperZ  #>0
            dG += upperT

            pl_obs = dG

            sa_vec = np.column_stack((sa_vec, pl_obs))

        # drop init row
        sa_vec = sa_vec[:, 1:]
        # rename to variable
        setattr(gtob, varname, sa_vec)
    print("t,r,u,v done")
    #===============================================================================
    # tscale2d - Generates 2D interpolations from coarse (ERA5) to fine (1km) grid
    #===============================================================================
    gsob = hp.Bunch(dtime=dtime)
    # init grid stack
    xdim = shape[0]
    sa_vec = np.zeros((xdim))

    for surf in surfDict:

        t = nc.Dataset(surf)  # key is filename
        varname = surfDict[surf]  # value of key is par shortname
        z = nc.Dataset(zp_file)  # could be outside loop

        # init grid stack
        xdim = out_xyz_dem.shape[0]
        sa_vec = np.zeros(xdim)

        #names=1:n
        for timestep in range(starti, endi):
            """
			2D interpolated of surface firelds.
				Args:
					timestep: Timestep of interpolation as an interger (index)
					stations: pandas dataframe of input station csv file (id,lon,lat,ele)
					var: surface variarble eg "ssrd"
				Returns:
					t_sp: 2D interpolation of ERA surface field to stations points

				Example:
					surTa = ds.surTaPoint(0, mystations, 't2m')
			"""
            # read in data from variable 'varname'
            in_v = t[varname][timestep, :, :]  #geopotential
            in_v = in_v[::
                        -1, :]  # reverse latitude dimension to agree with ascending 'lat'
            lat = t.variables['latitude'][:]
            lat = lat[::-1]  # must be ascending for RegularGridInterpolator
            lon = t.variables['longitude'][:]

            # 2d interpolation
            f_sa = RegularGridInterpolator((lat, lon),
                                           in_v,
                                           'linear',
                                           bounds_error=False)
            out_xy = np.asarray([lats, lons]).T
            sa_t = f_sa(out_xy)

            # stack timepoint to existing
            sa_vec = np.column_stack((sa_vec, sa_t))

        # drop init row
        sa_vec = sa_vec[:, 1:]

        # Add to gsob
        setattr(gsob, varname, sa_vec)

    print("Made a sob")
    #===============================================================================
    # Conversions
    #===============================================================================
    """ convert tp from m/timestep (total accumulation over timestep) to rate in mm/h 

				Args:
					step: timstep in seconds (era5=3600, ensemble=10800)

				Note: both EDA (ensemble 3h) and HRES (1h) are accumulated over the timestep
				and therefore treated here the same.
			https://confluence.ecmwf.int/display/CKB/ERA5+data+documentation
	"""
    tphrm = gsob.tp  #/step*60*60 # convert metres per timestep (in secs) -> m/hour
    gsob.pmmhr = tphrm * 1000  # m/hour-> mm/hour
    """ Convert SWin from accumulated quantities in J/m2 to 
	instantaneous W/m2 see: 
	https://confluence.ecmwf.int/pages/viewpage.action?pageId=104241513

	Args:
		step: timstep in seconds (era5=3600, ensemble=10800)

	Note: both EDA (ensemble 3h) and HRES (1h) are accumulated over the timestep
	and therefore treated here the same ie step=3600s (1h)
	https://confluence.ecmwf.int/display/CKB/ERA5+data+documentation
	"""
    gsob.strd = gsob.strd / 3600
    gsob.ssrd = gsob.ssrd / 3600
    gsob.tisr = gsob.tisr / 3600

    gsob.gridEle = gsob.z[:, 0] / g
    gtob.prate = gsob.pmmhr  #*pcf # mm/hour
    gtob.psum = gsob.tp * 1000 * stephr  #pcf mm/timestep
    print("conversions done")
    #===============================================================================
    # TopoSCALE
    #=============================================================================

    #===============================================================================
    # Precip
    #===============================================================================
    '''
	Args:
	fineEle:ele vector from station dataframe
	sob: contains gridEle, tp and dtime
	'''
    # convert TP to mm/hr

    # lookups = {
    # 	   1:0.35,
    # 	   2:0.35,
    # 	   3:0.35,
    # 	   4:0.3,
    # 	   5:0.25,
    # 	   6:0.2,
    # 	   7:0.2,
    # 	   8:0.2,
    # 	   9:0.2,
    # 	   10:0.25,
    # 	   11:0.3,
    # 	   12:0.35
    # }

    # # Precipitation lapse rate, varies by month (Liston and Elder, 2006).
    # pfis = gsob.dtime.month.map(lookups)
    # pfis = np.repeat(pfis.values[:,None], lp.ele.size, axis=1)

    # dz=(lp.ele-gsob.gridEle)/1e3  # Elevation difference in kilometers between the fine and coarse surface.

    # pcf=(1+pfis.T*dz[:,None])/(1-pfis.T*dz[:,None])# Precipitation correction factor.
    #Pf=sob.pmmhr.T*lp

    #===============================================================================
    # Longwave
    #===============================================================================
    """Convert to RH (should be in a function). Following MG Lawrence 
	DOI 10.1175/BAMS-86-2-225 """
    A1 = 7.625
    B1 = 243.04
    C1 = 610.94
    tc = gsob.t2m - 273.15
    tdc = gsob.d2m - 273.15
    tf = gtob.t - 273.15  # fout.T
    c = (A1 * tc) / (B1 + tc)
    RHc = 100 * np.exp((tdc * A1 - tdc * c - B1 * c) /
                       (B1 + tdc))  # Inverting eq. 8 in Lawrence.
    """ Calculate saturation vapor pressure at grid and "subgrid" [also
	through function] using the Magnus formula."""

    svpf = C1 * np.exp(A1 * tf / (B1 + tf))
    svpc = C1 * np.exp(A1 * tc / (B1 + tc))
    """
	Calculate the vapor pressure at grid (c) and subgrid (f).
	"""
    vpf = gtob.r * svpf / 1e2  # RHf
    vpc = RHc * svpc / 1e2
    """
	Use the vapor pressure and temperature to calculate clear sky
	# emssivity at grid and subgrid. [also function]
	Konzelmann et al. 1994
	Ta in kelvin
	"""
    x1 = 0.43
    x2 = 5.7
    cef = 0.23 + x1 * (vpf / gtob.t)**(
        1 / x2)  #Pretty sure the use of Kelvin is correct.
    cec = 0.23 + x1 * (vpc / gsob.t2m)**(1 / x2)
    """Diagnose the all sky emissivity at grid."""
    sbc = 5.67e-8
    aec = gsob.strd / (sbc * gsob.t2m**4)
    # need to constrain to 1 as original code?
    """ 
	Calculate the "cloud" emissivity at grid, assume this is the same at
	subgrid.
	"""
    deltae = aec - cec
    """ 
	Use the former cloud emissivity to compute the all sky emissivity at 
	subgrid. 
	"""
    aef = cef + deltae
    gtob.lwin = aef * sbc * gtob.t**4

    print("Lwin done")
    #===============================================================================
    # make a pob - required as input to swin routine. This object is data on each
    # pressure level interpolated to the fine grid ie has dimensions xy (finegrid) x plev
    #===============================================================================

    f = nc.Dataset(zp_file)
    lev = f.variables['level'][:]
    var = 't'
    #	ds = rc.t3d( pl=plevfile, dem =demfile)
    #	out_xyz_dem, lats, lons, shape= ds.demGrid()
    xdim = lev.shape[0]
    ydim = out_xyz_dem.shape[0]
    t_interp_out = np.zeros((xdim, ydim))
    z_interp_out = np.zeros((xdim, ydim))

    # for timestep in range(starti,endi):
    # 	gridT,gridZ,gridLat,gridLon=ds.gridValue(var,timestep)
    # 	t_interp, z_interp = ds.inLevelInterp(gridT,gridZ, gridLat,gridLon,out_xyz_dem)
    # 	t_interp_out = np.dstack((t_interp_out, t_interp))
    # 	z_interp_out = np.dstack((z_interp_out, z_interp))

    #======================= all this can be done in first instance need to gen t/z_interp_out additionally
    tfile = list(plevDict.keys())[list(plevDict.values()).index('t')]
    t = nc.Dataset(tfile)  # key is filename
    z = nc.Dataset(zp_file)  # could be outside loop

    for timestep in range(starti, endi):

        gridT = t.variables['t'][timestep, :, :, :]
        gridZ = z.variables['z'][timestep, :, :, :]
        gridLat = t['latitude'][:]
        gridLat = gridLat[::-1]  # reverse to deal with ERA5 order
        gridLon = t['longitude'][:]
        gridLev = t.variables['level'][::-1]
        #return gridT,gridZ,gridLat,gridLon

        shape = gridT.shape
        #create array to hold interpolation resultes
        t_interp = np.zeros([shape[0], len(out_xyz_dem)])
        z_interp = np.zeros([
            shape[0], len(out_xyz_dem)
        ])  # HOW MANY TIMES DO WE REALLY NEED TO COMPUTE THIS?

        #temperatue and elevation interpolation 2d
        for i in range(shape[0]):
            ft = RegularGridInterpolator((gridLat, gridLon),
                                         gridT[i, ::-1, :],
                                         'linear',
                                         bounds_error=False)
            fz = RegularGridInterpolator((gridLat, gridLon),
                                         gridZ[i, ::-1, :],
                                         'linear',
                                         bounds_error=False)
            t_interp[i, :] = ft(out_xyz_dem[:, :2])  #temperature
            z_interp[i, :] = fz(out_xyz_dem[:, :2])  #elevation

        t_interp_out = np.dstack((t_interp_out, t_interp))
        z_interp_out = np.dstack((z_interp_out, z_interp))

        # invert pressure levels
        #t_interp = t_interp[::-1,:]
        #z_interp = z_interp[::-1,:]

    # drop init blank layer
    tinterp = t_interp_out[:, :, 1:]
    zinterp = z_interp_out[:, :, 1:]

    gpob = hp.Bunch(t=tinterp, z=zinterp, levels=lev)

    # dem  = nc.Dataset(demfile)
    # lon = dem.variables['longitude'][:]
    # lat = dem.variables['latitude'][:]
    # demv = dem.variables['ele'][:]
    # demlon1 = dem.variables['longitude'][:]
    # demlat1 = dem.variables['latitude'][:]
    # demlon = np.tile(demlon1,demlat1.size)
    # demlat = np.repeat(demlat1,demlon1.size)
    # demv=np.reshape(demv,demv.size)

    # # why are these masked values generated?
    # demv =np.ma.filled(demv, fill_value=1)
    # tz = np.repeat(tz,demv.size)

    # stat = pd.DataFrame({	"ele":demv,
    # 				"lon":demlon,
    # 				"lat":demlat,
    # 				"tz":tz
    # 				})
    #===============================================================================
    # Compute Shortwave
    #===============================================================================

    #def swin2D(pob,sob,tob, stat, dates):
    '''
	main edit over standard function for points:
	- 3d tob,sob reduce to 2D (reshape)
	'''
    """ toposcale surface pressure using hypsometric equation - move to own 
	class """
    g = 9.81
    R = 287.05  # Gas constant for dry air.
    #ztemp = pob.z # geopotential height b = np.transpose(a, (2, 0, 1))
    ztemp = np.transpose(
        gpob.z,
        (2, 0, 1))  # pob is originally ordered levels,stations/cells,time
    #Ttemp = pob.t
    Ttemp = np.transpose(
        gpob.t,
        (2, 0, 1))  # pob is originally ordered levels,stations/cells,time
    statz = np.array(lp.ele) * g
    #dz=ztemp.transpose()-statz[None,:,None] # transpose not needed but now consistent with CGC surface pressure equations
    dz = ztemp - statz  # dimensions of dz : time, levels, stations

    # set all levels below surface to very big number so they canot be found by min
    newdz = dz
    newdz[newdz < 0] = 999999

    psf = np.zeros((gsob.dtime.size, statz.shape[0]))

    # reshape tob.t here
    #gtob.tr=gtob.t.reshape(gtob.t.shape[0]*gtob.t.shape[1], gtob.t.shape[2], order='F')
    #tob.trT=tob.tr.T # transpose to get right order

    # loop through timesteps
    for i in range(0, gsob.dtime.size):

        # find overlying layer
        thisp = dz[i, :, :] == np.min(
            newdz[i, :, :], axis=0
        )  # thisp is a booleen matrix of levels x stations with true indicating overlying plevel over station surface ele

        # flatten to 1 dimension order='Fortran' or row major
        thispVec = thisp.reshape(thisp.size, order='F')
        TtempVec = Ttemp.reshape(Ttemp.shape[0],
                                 Ttemp.shape[1] * Ttemp.shape[2],
                                 order='F')
        ztempVec = ztemp.reshape(ztemp.shape[0],
                                 ztemp.shape[1] * ztemp.shape[2],
                                 order='F')

        # booleen indexing to find temp and geopotential that correspond to lowesest overlying layer
        T1 = TtempVec[i, thispVec]
        z1 = ztempVec[i, thispVec]

        p1 = np.tile(
            gpob.levels[::-1], statz.shape[0]
        )[thispVec] * 1e2  #Convert to Pa. Reverse levels to ensure low ele (hig pressure) to high elel (low pressure)
        Tbar = np.mean(
            [T1, gtob.t[:, i]], axis=0
        )  # temperature midway between surface (toposcale T) and loweset overlying level (T1)
        """ Hypsometric equation."""  #P1 is above surface is this correct? Yes!
        psf[i, :] = p1 * np.exp(
            ((z1 / g) - (statz / g)) * (g / (Tbar * R))
        )  # exponent is positive ie increases pressure as surface is lower than pressure level
    """ 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 = gsob.z.T  #.reshape(gsob.z.shape[0]*gsob.z.shape[1], gsob.z.shape[2]).T # reshape and transpose to remove dimension and make broadcastable
    """ toa """
    SWtoa = gsob.tisr  #.reshape(gsob.tisr.shape[0]*gsob.tisr.shape[1], gsob.tisr.shape[2]).T
    """ Downwelling shortwave flux of the "grid" using nearest neighbor."""
    SWc = gsob.ssrd  #.reshape(sob.ssrd.shape[0]*sob.ssrd.shape[1], sob.ssrd.shape[2]).T
    """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))
    """ 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 = 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 = np.transpose(gpob.z, (0, 2, 1))
    Ttemp = np.transpose(gpob.t, (0, 2, 1))
    dz = ztemp - Zc  # dimensions of dz : levels, time, stations

    # set all levels below surface to very big number so they canot be found by min
    newdz = dz
    newdz[newdz < 0] = 999999

    psc = np.zeros((gsob.dtime.size, statz.shape[0]))
    for i in range(0, gsob.dtime.size):

        #thisp.append(np.argmin(dz[:,i][dz[:,i]>0]))
        # find overlying layer
        thisp = dz[:, i, :] == np.min(
            newdz[:, i, :], axis=0
        )  # thisp is a booleen matrix of levels x stations with true indicating overlying plevel over station surface ele !! time index in middle this time!!!
        z0 = Zc[i, :]
        T0 = gsob.t2m.T[i, :]

        # flatten to 1 dimension order='Fortran' or row major
        thispVec = thisp.reshape(thisp.size, order='F')
        TtempVec = Ttemp.reshape(
            Ttemp.shape[1], Ttemp.shape[0] * Ttemp.shape[2], order='F'
        )  # !! order of permutations is different from pressure at finegrid routine (time is middle dimension)
        ztempVec = ztemp.reshape(
            ztemp.shape[1], ztemp.shape[0] * ztemp.shape[2], order='F'
        )  # !! order of permutations is different from pressure at finegrid routine (time is middle dimension)

        # booleen indexing to find temp and geopotential that correspond to lowesest overlying layer
        T1 = TtempVec[i, thispVec]
        z1 = ztempVec[i, thispVec]

        p1 = np.tile(gpob.levels[::-1],
                     statz.shape[0])[thispVec] * 1e2  #Convert to Pa.
        Tbar = np.mean([T0, T1], axis=0)
        """ Hypsometric equation."""
        psc[i, :] = p1 * np.exp(((z1 / g) - (z0 / g)) * (g / (Tbar * R)))
    """compute julian dates"""
    jd = sg.to_jd(gsob.dtime)
    """
	Calculates a unit vector in the direction of the sun from the observer 
	position.
	"""
    svx, svy, svz = sg.sunvectorMD(jd, lp.lat, lp.lon, tz, lp.ele.size,
                                   gsob.dtime.size)
    sp = sg.sunposMD(svx, svy, svz)

    # Cosine of the zenith angle.
    #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!).

    # Calculate the "broadband" absorption coefficient. Elevation correction
    ka = (g * muz / (psc)) * np.log(SWtoa.T / SWcdir.T)
    ka = np.nan_to_num(ka)

    # Now you can (finally) find the direct component at subgrid.
    SWfdir = SWtoa.T * np.exp(-ka * psf / (g * muz))
    SWfdirCor = SWfdir  #*dprod

    gtob.swin = SWfdiff + SWfdirCor.T
    gtob.psf = psf
    print("Swin done")

    #gtob.swin = swin2D(gpob,gsob,gtob, stat, dtime)
    #gtob.swin =gtob.swin.reshape(gtob.swin.shape[0], gsob.ssrd.shape[0], gsob.ssrd.shape[1])

    ntime = len(gsob.dtime)
    stephr = a.seconds / 60 / 60
    rtime = np.array(list(range(len(gsob.dtime)))) * stephr

    #===============================================================================
    # write results
    #===============================================================================
    ## conversuions
    T = np.single(gtob.t - 273.15)
    gtob.r[gtob.r > 100] = 100
    RH = np.single(gtob.r)
    ws = np.single(np.sqrt(gtob.u**2 + gtob.v**2))
    prate = np.single(gtob.prate)

    #===========================================================================
    # Calculate absolute humidity kg/kg
    #===========================================================================
    # Tk=273.15+20
    # RH=80.
    # ah = 13.82g/m3
    pws = calc_Pws(gtob.t)
    pw = calc_Pw(pws, RH)
    ah_gm3 = calc_AH(pw, gtob.t)  # ah in g/m3
    #AH_kgkg = ah_gm3_To_ah_kgkg(ah_gm3,gtob.psf,gtob.t )
    SH = rh2sh(pw, gtob.psf)

    # Dictionary to loop over
    varDict = {
        "t": T,
        "ws": ws,
        "shum": SH.transpose(),
        "swin": gtob.swin,
        "lwin": gtob.lwin,
        "prate": prate,
        "P": gtob.psf.transpose()
    }

    for var in varDict:

        #open
        f = nc.Dataset(outDir + "/" + var + "_" + str(startIndex + 1) + "_" +
                       str(year) + ".nc",
                       'w',
                       format='NETCDF4')

        # Implement cf H.2.1 http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/build/cf-conventions.html#idp9763584

        # #make dimensions
        # f.createDimension('time', ntime)
        # f.createDimension('lon', len(lp.lon))
        # f.createDimension('lat', len(lp.lat))

        # #make dimension variables
        # mytime = f.createVariable('time', 'i', ('time',))
        # longitude = f.createVariable('lon',    'f4',('lon',))
        # latitude  = f.createVariable('lat',    'f4',('lat',))

        # myvar = f.createVariable(var,    'f4',('time','lon'))

        # #assign dimensions
        # mytime[:] = rtime
        # longitude = lp.lon
        # latitude  = lp.lat

        # myvar[:] = varDict[var].T

        # #metadata
        # from time import ctime
        # mytime=ctime()
        # f.history = 'Created by toposcale on '+mytime
        # mytime.units = 'hours since '+str(gsob.dtime[0])
        # f.close()

        #make dimensions
        f.createDimension('time', ntime)
        f.createDimension('station', len(lp.ele))

        #make dimension variables
        mytime = f.createVariable('time', 'i', ('time', ))
        station = f.createVariable('station', 'i', ('station', ))

        #make variables
        myvar = f.createVariable(var, 'f4', ('time', 'station'))
        longitude = f.createVariable('longitude', 'f4', ('station'))
        latitude = f.createVariable('latitude', 'f4', ('station'))

        #assign dimensions
        mytime[:] = rtime
        longitude[:] = np.array(lp.lon)
        latitude[:] = np.array(lp.lat)
        station[:] = range(len(lp.ele))
        myvar[:] = varDict[var].T

        #metadata
        from time import ctime
        mycomptime = ctime()
        f.history = 'Created by tscale-cci on ' + mycomptime
        mytime.units = 'hours since ' + str(gsob.dtime[0])
        f.close()

    print("Toposcale complete!")
Beispiel #4
0
def swin2D(
    pob, sob, tob, lp, dtime
):  # does not work yet (booleen indexing of 2d arraya fauiles as np.min returns single value when we need 161

    timesize = len(dtime)
    statsize = len(lp.ele)
    """ toposcale surface pressure using hypsometric equation - move to own 
	class """
    g = 9.81
    R = 287.05  # Gas constant for dry air.
    #ztemp = pob.z # geopotential height b = np.transpose(a, (2, 0, 1))
    ztemp = np.transpose(pob.z, (2, 0, 1))
    #Ttemp = pob.t
    Ttemp = np.transpose(pob.t, (2, 0, 1))
    statz = np.array(lp.ele) * g
    #dz=ztemp.transpose()-statz[None,:,None] # transpose not needed but now consistent with CGC surface pressure equations
    dz = ztemp - statz  # dimensions of dz : time, levels, stations

    # set all levels below surface to very big number so they canot be found by min
    newdz = dz
    newdz[newdz < 0] = 999999

    psf = np.zeros((dtime.size, statz.shape[0]))

    # loop through timesteps
    for i in range(0, dtime.size):

        # find overlying layer
        thisp = dz[i, :, :] == np.min(
            newdz[i, :, :], axis=0
        )  # thisp is a booleen matrix of levels x stations with true indicating overlying plevel over station surface ele

        # flatten to 1 dimension order='Fortran' or row major
        thispVec = thisp.reshape(thisp.size, order='F')
        TtempVec = Ttemp.reshape(Ttemp.shape[0],
                                 Ttemp.shape[1] * Ttemp.shape[2],
                                 order='F')
        ztempVec = ztemp.reshape(ztemp.shape[0],
                                 ztemp.shape[1] * ztemp.shape[2],
                                 order='F')

        # booleen indexing to find temp and geopotential that correspond to lowesest overlying layer
        T1 = TtempVec[i, thispVec]
        z1 = ztempVec[i, thispVec]

        p1 = np.tile(
            pob.levels[::-1], statz.shape[0]
        )[thispVec] * 1e2  #Convert to Pa. Reverse levels to ensure low ele (hig pressure) to high elel (low pressure)
        Tbar = np.mean(
            [T1, tob.t[i, :]], axis=0
        )  # temperature midway between surface (toposcale T) and loweset overlying level (T1)
        """ Hypsometric equation."""  #P1 is above surface is this correct? Yes!
        psf[i, :] = (
            p1 * np.exp((z1 - statz) * (g / (Tbar * R)))
        )  # exponent is positive ie increases pressure as surface is lower than pressure level

    #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
    """ 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
    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 = np.array(lp.svf) * 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
    #Ttemp = pob.t
    #dz=ztemp.transpose()-sob.z

    ztemp = np.transpose(pob.z, (0, 2, 1))
    Ttemp = np.transpose(pob.t, (0, 2, 1))
    dz = ztemp - sob.z  # dimensions of dz : levels, time, stations

    # set all levels below surface to very big number so they canot be found by min
    newdz = dz
    newdz[newdz < 0] = 999999

    psc = np.zeros((dtime.size, statz.shape[0]))
    for i in range(0, dtime.size):

        #thisp.append(np.argmin(dz[:,i][dz[:,i]>0]))
        # find overlying layer
        thisp = dz[:, i, :] == np.min(
            newdz[:, i, :], axis=0
        )  # thisp is a booleen matrix of levels x stations with true indicating overlying plevel over station surface ele !! time index in middle this time!!!
        z0 = sob.z[i, :]
        T0 = sob.t2m[i, :]

        # flatten to 1 dimension order='Fortran' or row major
        thispVec = thisp.reshape(thisp.size, order='F')
        TtempVec = Ttemp.reshape(
            Ttemp.shape[1], Ttemp.shape[0] * Ttemp.shape[2], order='F'
        )  # !! order of permutations is different from pressure at finegrid routine (time is middle dimension)
        ztempVec = ztemp.reshape(
            ztemp.shape[1], ztemp.shape[0] * ztemp.shape[2], order='F'
        )  # !! order of permutations is different from pressure at finegrid routine (time is middle dimension)

        # booleen indexing to find temp and geopotential that correspond to lowesest overlying layer
        T1 = TtempVec[i, thispVec]
        z1 = ztempVec[i, thispVec]

        p1 = np.tile(pob.levels[::-1],
                     statz.shape[0])[thispVec] * 1e2  #Convert to Pa.
        Tbar = np.mean([T0, T1], axis=0)
        """ Hypsometric equation."""
        psc[i, :] = (p1 * np.exp((z1 - z0) * (g / (Tbar * R))))
    """compute julian dtime"""
    jd = sg.to_jd(dtime)
    """
	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)
    svx, svy, svz = sg.sunvectorMD(jd, lp.lat, lp.lon, lp.tz, statsize,
                                   timesize)
    """
	Computes azimuth , zenith  and sun elevation 
	for each timestamp !!! NOW THIS NEEDS ACCEPT SEPARATE VECTORS!!
	"""
    sp = sg.sunposMD(svx, svy, svz)

    # Cosine of the zenith angle.
    #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()
    ka = np.nan_to_num(ka)

    # 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(lp.svf)) * 180) / np.pi) * 2) - lp.slp
    horel[horel < 0] = 0
    """
	normal vector - Calculates a unit vector normal to a surface defined by 
	slope inclination and slope orientation.
	"""
    nv = sg.normalvector(slope=lp.slp, aspect=lp.asp)
    """
	Method 1: Computes the intensity according to the position of the sun (sunv) and 
	dotproduct normal vector to slope.
	From corripio r package
	"""
    """ need to reconstruct sunv matrix for multipoint case here"""
    sunv = np.array((svx, svy, svz))

    #dotprod=np.dot(sunv ,np.transpose(nv))\
    dotprod = np.tensordot(sunv, nv, axes=([0], [1]))

    dprod = dotprod[:, :, 0]
    dprod[dprod < 0] = 0  #negative indicates selfshading
    #dprod = dprod
    """Method 2: Illumination angles. Dozier"""  #SEE tscale.py
    #saz=sp.azi

    # a=np.array(stat.asp)
    # b =np.tile(a,timesize)
    # asp=b.reshape(timesize,statsize)
    # a=np.array(stat.slp)
    # b =np.tile(a,timesize)
    # slp=b.reshape(timesize,statsize)
    # cosis=muz*np.cos(slp)+np.sin(sp.zen)*np.sin(slp)*np.cos(sp.azi-asp)# 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
	"""
    a = np.array(horel)
    b = np.tile(a, timesize)
    horel2 = b.reshape(timesize, statsize)

    selMask = sp.sel
    selMask[selMask < horel2] = 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, psf
    """ 
Beispiel #5
0
    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
        """ 
Beispiel #6
0
    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
        """