def main(self, tair, par, vpd, wind, pressure, Ca, doy, hod, lat, lon, lai, rnet=None): """ Parameters: ---------- tair : float air temperature (deg C) par : float Photosynthetically active radiation (umol m-2 s-1) vpd : float Vapour pressure deficit (kPa, needs to be in Pa, see conversion below) wind : float wind speed (m s-1) pressure : float air pressure (using constant) (Pa) Ca : float ambient CO2 concentration doy : float day of day hod : float hour of day lat : float latitude lon : float longitude lai : floar leaf area index Returns: -------- An : float net leaf assimilation (umol m-2 s-1) gs : float stomatal conductance (mol m-2 s-1) et : float transpiration (mol H2O m-2 s-1) """ F = FarquharC3(theta_J=0.85, peaked_Jmax=True, peaked_Vcmax=True, model_Q10=True, gs_model=self.gs_model, gamma=self.gamma, g0=self.g0, g1=self.g1, D0=self.D0, alpha=self.alpha) P = PenmanMonteith(self.leaf_width, self.SW_abs) An = np.zeros(2) # sunlit, shaded gsc = np.zeros(2) # sunlit, shaded et = np.zeros(2) # sunlit, shaded Tcan = np.zeros(2) # sunlit, shaded lai_leaf = np.zeros(2) #cos_zenith = calculate_solar_geometry(doy, hod, lat, lon) cos_zenith = sinbet(doy, lat, hod) #print(doy, lat, hod, cos_zenith) zenith_angle = np.rad2deg(np.arccos(cos_zenith)) elevation = 90.0 - zenith_angle sw_rad = par * c.PAR_2_SW # W m-2 # this matches CABLE's logic ... which means they are halving the # SW_down that is used to compute the direct/diffuse terms and presumbly # all other calcs sw_rad_half = par / 4.6 # W m-2 # get diffuse/beam frac (diffuse_frac, direct_frac) = spitters(doy, sw_rad_half, cos_zenith) (apar, lai_leaf, kb) = calculate_absorbed_radiation(par, cos_zenith, lai, direct_frac, diffuse_frac, doy, sw_rad_half) # Calculate scaling term to go from a single leaf to canopy, # see Wang & Leuning 1998 appendix C scalex = calc_leaf_to_canopy_scalar(lai, kb) if lai_leaf[0] < 1.e-3: # to match line 336 of CABLE radiation scalex[0] = 0. # Is the sun up? if elevation > 0.0 and par > 50.: # sunlit / shaded loop for ileaf in range(2): # initialise values of Tleaf, Cs, dleaf at the leaf surface dleaf = vpd Cs = Ca Tleaf = tair Tleaf_K = Tleaf + c.DEG_2_KELVIN iter = 0 while True: if scalex[ileaf] > 0.: (An[ileaf], gsc[ileaf]) = F.photosynthesis(Cs=Cs, Tleaf=Tleaf_K, Par=apar[ileaf], Jmax25=self.Jmax25, Vcmax25=self.Vcmax25, Q10=self.Q10, Eaj=self.Eaj, Eav=self.Eav, deltaSj=self.deltaSj, deltaSv=self.deltaSv, Rd25=self.Rd25, Hdv=self.Hdv, Hdj=self.Hdj, vpd=dleaf, scalex=scalex[ileaf]) else: An[ileaf], gsc[ileaf] = 0., 0. # Calculate new Tleaf, dleaf, Cs (new_tleaf, et[ileaf], le_et, gbH, gw) = self.calc_leaf_temp(P, Tleaf, tair, gsc[ileaf], apar[ileaf], vpd, pressure, wind, rnet=rnet, lai=lai_leaf[ileaf]) gbc = gbH * c.GBH_2_GBC if gbc > 0.0 and An[ileaf] > 0.0: Cs = Ca - An[ileaf] / gbc # boundary layer of leaf else: Cs = Ca if np.isclose(et[ileaf], 0.0) or np.isclose(gw, 0.0): dleaf = vpd else: dleaf = (et[ileaf] * pressure / gw) * c.PA_2_KPA # kPa # Check for convergence...? if math.fabs(Tleaf - new_tleaf) < 0.02: break if iter > self.iter_max: #raise Exception('No convergence: %d' % (iter)) An[ileaf] = 0.0 gsc[ileaf] = 0.0 et[ileaf] = 0.0 break # Update temperature & do another iteration Tleaf = new_tleaf Tleaf_K = Tleaf + c.DEG_2_KELVIN Tcan[ileaf] = Tleaf iter += 1 # scale to canopy: sum contributions from beam and diffuse leaves an_canopy = np.sum(An) an_cansun = An[c.SUNLIT] an_cansha = An[c.SHADED] par_sun = apar[c.SUNLIT] par_sha = apar[c.SHADED] gsw_canopy = np.sum(gsc) * c.GSC_2_GSW et_canopy = np.sum(et) sun_frac = lai_leaf[c.SUNLIT] / np.sum(lai_leaf) sha_frac = lai_leaf[c.SHADED] / np.sum(lai_leaf) tcanopy = (Tcan[c.SUNLIT] * sun_frac) + (Tcan[c.SHADED] * sha_frac) lai_sun = lai_leaf[c.SUNLIT] lai_sha = lai_leaf[c.SHADED] else: an_canopy = 0.0 an_cansun = 0.0 an_cansha = 0.0 gsw_canopy = 0.0 et_canopy = 0.0 par_sun = 0.0 par_sha = 0.0 tcanopy = tair lai_sun = 0.0 lai_sha = lai return (an_canopy, gsw_canopy, et_canopy, tcanopy, an_cansun, an_cansha, par_sun, par_sha, lai_sun, lai_sha)
def main(self, tair, par, vpd, wind, pressure, Ca, doy, hod, lai, rnet=None, tsoil=None): """ Parameters: ---------- tair : float air temperature (deg C) par : float Photosynthetically active radiation (umol m-2 s-1) vpd : float Vapour pressure deficit (kPa, needs to be in Pa, see conversion below) wind : float wind speed (m s-1) pressure : float air pressure (using constant) (Pa) Ca : float ambient CO2 concentration doy : float day of day hod : float hour of day lai : floar leaf area index Returns: -------- An : float net leaf assimilation (umol m-2 s-1) gs : float stomatal conductance (mol m-2 s-1) et : float transpiration (mol H2O m-2 s-1) """ F = FarquharC3(peaked_Jmax=self.peaked_Jmax, peaked_Vcmax=self.peaked_Vcmax, model_Q10=self.model_Q10, gs_model=self.gs_model) PM = PenmanMonteith() An = np.zeros(2) # sunlit, shaded Anc = np.zeros(2) # sunlit, shaded Anj = np.zeros(2) # sunlit, shaded gsc = np.zeros(2) # sunlit, shaded et = np.zeros(2) # sunlit, shaded Tcan = np.zeros(2) # sunlit, shaded lai_leaf = np.zeros(2) sw_rad = np.zeros(2) # VIS, NIR XX = np.zeros(2) (cos_zenith, elevation) = calculate_cos_zenith(doy, p.lat, hod) sw_rad[c.VIS] = 0.5 * (par * c.PAR_2_SW) # W m-2 sw_rad[c.NIR] = 0.5 * (par * c.PAR_2_SW) # W m-2 # get diffuse/beam frac, just use VIS as the answer is the same for NIR (diffuse_frac, direct_frac) = spitters(doy, sw_rad[0], cos_zenith) (qcan, apar, lai_leaf, kb, gradis) = calculate_absorbed_radiation(self.p, par, cos_zenith, lai, direct_frac, diffuse_frac, doy, sw_rad, tair, tsoil) # Calculate scaling term to go from a single leaf to canopy, # see Wang & Leuning 1998 appendix C scalex = calc_leaf_to_canopy_scalar(lai, kb=kb, kn=p.kn) if lai_leaf[0] < 1.e-3: # to match line 336 of CABLE radiation scalex[0] = 0. if np.sum(sw_rad) < c.RAD_THRESH: scalex[0] = 0.0 #scalex[1] = 0.0 # Is the sun up? #print(doy, hod, elevation, par) yes = True if yes: #if elevation > 0.0 and par > 50.: # sunlit / shaded loop for ileaf in range(2): # initialise values of Tleaf, Cs, dleaf at the leaf surface dleaf = vpd Cs = Ca Tleaf = tair Tleaf_K = Tleaf + c.DEG_2_KELVIN iter = 0 while True: if scalex[ileaf] > 0.: (An[ileaf], gsc[ileaf]) = F.photosynthesis(p, Cs=Cs, Tleaf=Tleaf_K, Par=apar[ileaf], vpd=dleaf, scalex=scalex[ileaf]) else: An[ileaf] = 0.0 gsc[ileaf] = 0.0 # Calculate new Tleaf, dleaf, Cs (new_tleaf, et[ileaf], le_et, gbH, gw) = self.calc_leaf_temp(p, PM, Tleaf, tair, gsc[ileaf], None, vpd, pressure, wind, rnet=qcan[ileaf], lai=lai_leaf[ileaf], gradis=gradis[ileaf]) gbc = gbH * c.GBH_2_GBC if gbc > 0.0 and An[ileaf] > 0.0: Cs = Ca - An[ileaf] / gbc # boundary layer of leaf else: Cs = Ca if np.isclose(et[ileaf], 0.0) or np.isclose(gw, 0.0): dleaf = vpd else: dleaf = (et[ileaf] * pressure / gw) * c.PA_2_KPA # kPa if dleaf < 0.05: dleaf = 0.05 # Check for convergence...? if math.fabs(Tleaf - new_tleaf) < 0.02: Tcan[ileaf] = Tleaf break if iter > self.iter_max: #raise Exception('No convergence: %d' % (iter)) An[ileaf] = 0.0 gsc[ileaf] = 0.0 et[ileaf] = 0.0 break # Update temperature & do another iteration Tleaf = new_tleaf Tleaf_K = Tleaf + c.DEG_2_KELVIN Tcan[ileaf] = Tleaf """ # Update leaf surface vapour pressure deficit: tetena = 6.106 tetenb = 17.27 tetenc = 237.3 TFRZ = 273.15 Tair_K = tair + c.DEG_2_KELVIN # d(es)/dT (Pa/K) a1 = tetena * tetenb * tetenc a2 = ((Tair_K - TFRZ) + tetenc)**2 a3 = np.exp(tetenb * \ (Tair_K-TFRZ) / ((Tair_K-TFRZ) + tetenc)) dsatdk = 100.0 * a1 / a2 * a3 * c.PA_TO_KPA dleafx = vpd + dsatdk * (Tleaf_K - Tair_K) """ iter += 1 return (An, et, Tcan, apar, lai_leaf)