def dVmaxoAdXi(p, trans, Ci, inf_gb=False): try: # is Tleaf one of the input fields? Tleaf = p.Tleaf except (IndexError, AttributeError, ValueError): # calc. Tleaf Tleaf, __ = leaf_temperature(p, trans, inf_gb=inf_gb) # gamstar, Vmax, Kc and Ko are known at Tref, get their T dependency Tref = p.Tref + conv.C_2_K # degk, Tref set to 25 degC # CO2 compensation point gamstar = arrhen(p.gamstar25, p.Egamstar, Tref, Tleaf) # Michaelis-Menten constants #Kc = arrhen(p.Kc25, p.Ec, Tref, Tleaf) # cst for carboxylation, Pa #Ko = arrhen(p.Ko25, p.Eo, Tref, Tleaf) # cst for oxygenation, kPa #Ko = np.maximum(cst.zero, Ko) # we don't want zeros in Km div # Michaelis-Menten constant for O2/CO2 #Km = Kc * (1. + p.O2 / Ko) #dVmaxoA = -(p.CO2 * (Km + gamstar)) / ((Ci - gamstar) ** 2.) Vmax = arrhen(p.Vmax25, p.Ev, Tref, Tleaf, deltaS=p.deltaSv, Hd=p.Hdv) Vmax = adjust_low_T(Vmax, Tleaf) return Vmax #dVmaxoA
def profit_psi(p, photo='Farquhar', res='low', inf_gb=False, deriv=False): """ Finds the instateneous profit maximization, following the optmization criterion for which, at each instant in time, the stomata regulate canopy gas exchange and pressure to achieve the maximum profit, which is the maximum difference between the normalized photosynthetic gain (gain) and the hydraulic cost function (cost). That is when d(gain)/dP = d(cost)/dP. Arguments: ---------- p: recarray object or pandas series or class containing the data time step's met data & params photo: string either the Farquhar model for photosynthesis, or the Collatz model res: string either 'low' (default), 'med', or 'high' to run the optimising solver onopt: boolean if True, the optimisation is performed. If Fall, fall back on previously performed optimisation for the value of the maximum profit. inf_gb: bool if True, gb is prescrived and very large Returns: -------- E_can: float transpiration [mmol m-2 s-1] at maximum profit across leaves gs_can: float stomatal conductance [mol m-2 s-1] at maximum profit across leaves An_can: float net photosynthetic assimilation rate [umol m-2 s-1] at maximum profit across leaves Ci_can: float intercellular CO2 concentration [Pa] at maximum profit across leaves rublim_can: string 'True' if the C assimilation is rubisco limited, 'False' otherwise """ # hydraulics P, trans = hydraulics(p, res=res) cost, __ = hydraulic_cost(p, P) # look for the most net profit gain, Ci, mask = photo_gain(p, trans, photo, res, inf_gb=inf_gb) expr = gain - cost[mask] if deriv: expr = np.abs(np.gradient(expr, P[mask])) # deal with edge cases by rebounding the solution gc, gs, gb, __ = leaf_energy_balance(p, trans[mask], inf_gb=inf_gb) try: if inf_gb: # check on valid range check = expr[gc > cst.zero] else: # further constrain the realm of possible gs check = expr[np.logical_and(gc > cst.zero, gs < 1.5 * gb)] idx = np.isclose(expr, max(check)) if deriv: idx = np.isclose(expr, min(check)) idx = [list(idx).index(e) for e in idx if e] if inf_gb: # check for algo. "overshooting" due to inf. gb while Ci[idx[0]] < 2. * p.gamstar25: idx[0] -= 1 if idx[0] < 3: break # optimized where Ci for both photo models are close Ci = Ci[idx[0]] trans = trans[mask][idx[0]] # mol.m-2.s-1 gs = gs[idx[0]] Pleaf = P[mask][idx[0]] # rubisco- or electron transport-limitation? An, Aj, Ac = calc_photosynthesis(p, trans, Ci, photo, inf_gb=inf_gb) rublim = rubisco_limit(Aj, Ac) # leaf temperature? Tleaf, __ = leaf_temperature(p, trans, inf_gb=inf_gb) if (np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero) and (An > 0.)) or (idx[0] == len(P) - 1) or any( np.isnan([An, Ci, trans, gs, Tleaf, Pleaf])): An, Ci, trans, gs, gb, Tleaf, Pleaf = (9999., ) * 7 elif not np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero): trans *= conv.MILI # mmol.m-2.s-1 return An, Ci, rublim, trans, gs, gb, Tleaf, Pleaf except ValueError: # no opt return (9999., ) * 8
# calculate gs, mol m-2 s-1 if dAdCi <= 0.: gs = gscol * conv.FROM_MILI else: gs = 0.5 * dAdCi * conv.FROM_MILI * (( (1. + 4. * Xi / dAdCi)**0.5) - 1.) else: # retrieve the Ci stream of possible Ci values Cis = Ci_stream(p, Cs, Tleaf, res) # rate of photosynthesis, μmol m-2 s-1 A, __, __ = calc_photosynthesis(p, 0., Cis, photo, Tleaf=Tleaf) # gb? __, gb = leaf_temperature(p, 0., Tleaf=Tleaf, inf_gb=inf_gb) if inf_gb or (iter < 1): # gas-exchange trans, mmol m-2 s-1 E = A * conv.GwvGc * Dleaf / (p.CO2 - Cis) else: E = (A * (gb * conv.GwvGc + gs * conv.GbvGbc) / (gs + gb) * Dleaf / (p.CO2 - Cis)) # kcost, Pleaf mask = np.logical_and(Pleaf_pd - E / p.ksc_prev <= Pleaf_pd, Pleaf_pd - E / p.ksc_prev >= P[-1]) P = (Pleaf_pd - E / p.ksc_prev)[mask] cost = kcost(p, P, Pleaf_pd) # optimal point
def Cmax_gs(p, photo='Farquhar', res='low', inf_gb=False): """ Finds the instantaneous optimal C gain for a given C cost. First, the C gain equation is derived for gs, beta, Ci unknown. Then, the derived form of the equation is solved for Ci over a range of possible betas, gs, all of which are directly or indirectly leaf water potential P dependent. A check (check_solve) is performed to verify that the optimization satisfies the zero equality criteria and, finally, results are bound via a range of physically possible Ci values. N.B.: there can be several possible optimizations Arguments: ---------- p: recarray object or pandas series or class containing the data time step's met data & params photo: string either the Farquhar model for photosynthesis, or the Collatz model inf_gb: bool if True, gb is prescrived and very large Returns: -------- gsOPT: float stomatal conductance [mol.m-2.s-1] for which the A(gs) is maximized AnOPT: float maximum C assimilation rate [μmol.m-2.s-1] given by the diffusive supply of CO2 transOPT: float transpiration rate [mmol.m-2.s-1] for which the A(gs) is maximized CiOPT: float intercellular CO2 concentration [Pa] for which the A(gs) is maximized """ # energy balance P, trans = hydraulics(p, res=res, kmax=p.kmaxCM) # expression of optimization Ci, mask = Ci_sup_dem(p, trans, photo=photo, res=res, inf_gb=inf_gb) gc, gs, gb, __ = leaf_energy_balance(p, trans[mask], inf_gb=inf_gb) expr = np.abs( np.gradient(A_trans(p, trans[mask], Ci, inf_gb=inf_gb), P[mask]) - dcost_dpsi(p, P[mask], gs)) try: if inf_gb: # check on valid range check = expr[gc > cst.zero] else: # further constrain the realm of possible gs check = expr[np.logical_and(gc > cst.zero, gs < 1.5 * gb)] idx = np.isclose(expr, min(check)) idx = [list(idx).index(e) for e in idx if e] if inf_gb: # check for algo. "overshooting" due to inf. gb while Ci[idx[0]] < 2. * p.gamstar25: idx[0] -= 1 if idx[0] < 3: break # optimized where Ci for both photo models are close Ci = Ci[idx[0]] trans = trans[mask][idx[0]] # mol.m-2.s-1 gs = gs[idx[0]] Pleaf = P[mask][idx[0]] # rubisco limitation or electron transport-limitation? An, Aj, Ac = calc_photosynthesis(p, trans, Ci, photo=photo, inf_gb=inf_gb) rublim = rubisco_limit(Aj, Ac) # leaf temperature? Tleaf, __ = leaf_temperature(p, trans, inf_gb=inf_gb) if (np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero) and (An > 0.)) or (idx[0] == len(P) - 1) or any( np.isnan([An, Ci, trans, gs, Tleaf, Pleaf])): An, Ci, trans, gs, gb, Tleaf, Pleaf = (9999., ) * 7 elif not np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero): trans *= conv.MILI # mmol.m-2.s-1 return An, Ci, rublim, trans, gs, gb, Tleaf, Pleaf except ValueError: # no opt return (9999., ) * 8
def gas_exchange(p, fw, photo='Farquhar', res='low', dynamic=True, inf_gb=False, iter_max=40, threshold_conv=0.1): # initial state Cs = p.CO2 # Pa Tleaf = p.Tair # deg C # hydraulics P, E = hydraulics(p, res=res, kmax=p.kmaxT) # initialise gs over A g0 = 1.e-9 # g0 ~ 0, removing it entirely introduces errors Cs_umol_mol = Cs * conv.MILI / p.Patm # umol mol-1 gsoA = g0 + p.g1T * fw / Cs_umol_mol # iter on the solution until it is stable enough iter = 0 while True: An, Aj, Ac, Ci = calc_photosynthesis(p, 0., Cs, photo, Tleaf=Tleaf, gs_over_A=gsoA) # stomatal conductance, with fwsoil effect gs = np.maximum(cst.zero, conv.GwvGc * gsoA * An) # calculate new trans, gw, gb, etc. trans, real_zero, gw, gb, __ = calc_trans(p, Tleaf, gs, inf_gb=inf_gb) new_Tleaf, __ = leaf_temperature(p, trans, Tleaf=Tleaf, inf_gb=inf_gb) Pleaf = P[bn.nanargmin(np.abs(E - trans))] # update Cs (Pa) boundary_CO2 = p.Patm * conv.FROM_MILI * An / (gb * conv.GbcvGb) Cs = np.maximum(cst.zero, np.minimum(p.CO2, p.CO2 - boundary_CO2)) Cs_umol_mol = Cs * conv.MILI / p.Patm # update gs over A gsoA = g0 + p.g1T * fw / Cs_umol_mol # force stop when atm. conditions yield E < 0. (non-physical) if (iter < 1) and (not real_zero): real_zero = None # check for convergence if ((real_zero is None) or (iter >= iter_max) or ((iter >= 2) and real_zero and (abs(Tleaf - new_Tleaf) <= threshold_conv) and not np.isclose(gs, cst.zero, rtol=cst.zero, atol=cst.zero))): break # no convergence, iterate on leaf temperature Tleaf = new_Tleaf iter += 1 if ((np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero) and (An > 0.)) or np.isclose(Ci, 0., rtol=cst.zero, atol=cst.zero) or (Ci < 0.) or np.isclose(Ci, p.CO2, rtol=cst.zero, atol=cst.zero) or (Ci > p.CO2) or (real_zero is None) or (not real_zero) or any(np.isnan([An, Ci, trans, gs, Tleaf, Pleaf]))): An, Ci, trans, gs, gb, Tleaf, Pleaf = (9999.,) * 7 return An, Aj, Ac, Ci, trans, gs, gb, new_Tleaf, Pleaf
def floop(p, model, photo='Farquhar', inf_gb=True): # initialize the system Dleaf = p.VPD # kPa Cs = p.CO2 # Pa if model == 'Tuzet': iter_min = 2 # needed to update the fw with the LWP else: iter_min = 1 # hydraulics P, trans = hydraulics(p, kmax=p.kmaxT) try: # is Tleaf one of the input fields? Tleaf = p.Tleaf iter_max = 0 if model == 'Tuzet': iter_max = 2 # needed to update the fw with the LWP except (IndexError, AttributeError, ValueError): # calc. Tleaf Tleaf = p.Tair # deg C iter_max = 40 if model != 'Tuzet': # energy balance requirements Pleaf_pd = p.Ps_pd - p.height * cst.rho * cst.g0 * conv.MEGA if model == 'Medlyn': Dleaf = np.maximum(0.05, Dleaf) # gs model not valid < 0.05 if p.height > 0 and sw >= p.fc: fw = 1. # no moisture stress else: fw = fwWP(p, p.Ps) # moisture stress function else: # Tuzet model fw = fLWP(p, p.LWP_ini) # stress factor if (model == 'Medlyn') or (model == 'Tuzet'): # init. gs over A g0 = 1.e-9 # g0 ~ 0, removing it entirely introduces errors Cs_umol_mol = Cs * conv.MILI / p.Patm # umol mol-1 if model == 'Medlyn': gsoA = g0 + (1. + p.g1 * fw / (Dleaf**0.5)) / Cs_umol_mol else: # Tuzet gsoA = g0 + p.g1T * fw * Cs_umol_mol # iter on the solution until it is stable enough iter = 0 while True: if model == 'Eller': Cicol = calc_colim_Ci(p, Cs, Tleaf, photo) dCi = Cs - Cicol # Pa # calculate dA, μmol m-2 s-1 As, __, __ = calc_photosynthesis(p, 0., Cs, photo, Tleaf=Tleaf, gsc=0.) Acol, __, __ = calc_photosynthesis(p, 0., Cicol, photo, Tleaf=Tleaf, gsc=0.) dA = As - Acol # ambient - colimitation # dAdCi (in mol H2O) is needed to calculate gs, mmol m-2 s-1 dAdCi = dA * conv.GwvGc * p.Patm / dCi # kcost, unitless cost_pd = kcost(p, Pleaf_pd, Pleaf_pd) cost_mid = kcost(p, -p.P50, Pleaf_pd) dkcost = cost_pd - cost_mid # dP is needed to calculate gs dP = 0.5 * (Pleaf_pd + p.P50) # MPa, /!\ sign of P50 # xi, the loss of xylem cost of stomatal opening, mmol m-2 s-1 dq = Dleaf / p.Patm # mol mol-1, equivalent to D / Patm Xi = 2. * p.kmaxS1 * (cost_pd**2.) * dP / (dq * dkcost) # calculate gs at the co-limitation point, mmol m-2 s-1 gscol = Acol * conv.GwvGc * p.Patm / dCi # calculate gs, mol m-2 s-1 if dAdCi <= 0.: # cp from SOX code, ??? it should never happen! gs = gscol * conv.FROM_MILI else: gs = 0.5 * dAdCi * conv.FROM_MILI * (( (1. + 4. * Xi / dAdCi)**0.5) - 1.) elif model == 'SOX-OPT': # retrieve all potential Ci values Cis = Ci_stream(p, Cs, Tleaf, 'low') # rate of photosynthesis, μmol m-2 s-1 A, __, __ = calc_photosynthesis(p, 0., Cis, photo, Tleaf=Tleaf) # gb? __, gb = leaf_temperature(p, 0., Tleaf=Tleaf, inf_gb=inf_gb) if inf_gb or (iter < 1): # gas-exchange trans, mmol m-2 s-1 E = A * conv.GwvGc * Dleaf / (p.CO2 - Cis) else: E = (A * (gb * conv.GwvGc + gs * conv.GbvGbc) / (gs + gb) * Dleaf / (p.CO2 - Cis)) # cost, Pleaf mask = np.logical_and(Pleaf_pd - E / p.ksc_prev <= Pleaf_pd, Pleaf_pd - E / p.ksc_prev >= P[-1]) P = (Pleaf_pd - E / p.ksc_prev)[mask] cost = kcost(p, P, Pleaf_pd) try: # optimal point iopt = np.argmax(cost * A[mask]) Ci = Cis[mask][iopt] except Exception: return 9999. * 1000., p.ksc_prev # get net rate of photosynthesis at optimum, μmol m-2 s-1 An, __, __ = calc_photosynthesis(p, 0., Ci, photo, Tleaf=Tleaf) # get associated gc and gs gc = p.Patm * conv.FROM_MILI * An / (p.CO2 - Ci) if inf_gb: gs = gc * conv.GwvGc else: gs = np.maximum(cst.zero, gc * gb * conv.GwvGc / (gb - conv.GbvGbc * gc)) else: An, __, __, __ = calc_photosynthesis(p, 0., Cs, photo, Tleaf=Tleaf, gs_over_A=gsoA) gs = np.maximum(cst.zero, conv.GwvGc * gsoA * An) # calculate new trans, gw, gb, Tleaf E, real_zero, gw, gb, Dleaf = calc_trans(p, Tleaf, gs, inf_gb=inf_gb) new_Tleaf, __ = leaf_temperature(p, E, Tleaf=Tleaf, inf_gb=inf_gb) if model == 'Eller': # calculate An An, __, __ = calc_photosynthesis(p, 0., Cs, photo, Tleaf=Tleaf, gsc=conv.U * conv.GcvGw * gs) # update Cs (Pa) boundary_CO2 = p.Patm * conv.FROM_MILI * An / (gb * conv.GbcvGb) Cs = np.maximum(cst.zero, np.minimum(p.CO2, p.CO2 - boundary_CO2)) Cs_umol_mol = Cs * conv.MILI / p.Patm if model == 'Tuzet': # update Pleaf and fw Pleaf = P[np.nanargmin(np.abs(trans - E))] # Tuzet model if np.abs(fw - fLWP(p, Pleaf)) < 0.5: # is fw stable? fw = fLWP(p, Pleaf) # update # update gsoA gsoA = g0 + p.g1T * fw / Cs_umol_mol else: # update the leaf-to-air VPD if (np.isclose(E, cst.zero, rtol=cst.zero, atol=cst.zero) or np.isclose(gw, cst.zero, rtol=cst.zero, atol=cst.zero) or np.isclose(gs, cst.zero, rtol=cst.zero, atol=cst.zero)): Dleaf = p.VPD # kPa if model == 'Medlyn': Dleaf = np.maximum(0.05, Dleaf) # gs model not valid < 0.05 # update gs over A gsoA = g0 + (1. + p.g1 * fw / (Dleaf**0.5)) / Cs_umol_mol # force stop when atm. conditions yield E < 0. (non-physical) if (iter < 1) and (not real_zero): real_zero = None # check for convergence if ((real_zero is None) or (iter >= iter_max) or ((iter > iter_min) and real_zero and (abs(Tleaf - new_Tleaf) <= 0.1) and not np.isclose(gs, cst.zero, rtol=cst.zero, atol=cst.zero))): break # no convergence, iterate Tleaf = new_Tleaf iter += 1 if iter_max < 5: # no "real" iteration if Tleaf is prescribed Cs = p.CO2 Tleaf = p.Tleaf if model == 'Tuzet': return gs * 1000., Pleaf elif model == 'SOX-OPT': return gs * 1000., p.kmaxS2 * cost[iopt] else: return gs * 1000. # mmol/m2/s
def solve_std(p, sw, photo='Farquhar', res='low', iter_max=40, threshold_conv=0.1, inf_gb=False): """ Checks the energy balance by looking for convergence of the new leaf temperature with the leaf temperature predicted by the previous iteration. Then returns the corresponding An, E, Ci, etc. Arguments: ---------- p: recarray object or pandas series or class containing the data time step's met data & params sw: float mean volumetric soil moisture content [m3 m-3] photo: string either the Farquhar model for photosynthesis, or the Collatz model threshold_conv: float convergence threshold for the new leaf temperature to be in energy balance iter_max: int maximum number of iterations allowed on the leaf temperature before reaching the conclusion that the system is not energy balanced inf_gb: bool if True, gb is prescrived and very large Returns: -------- trans_can: float transpiration rate of canopy [mmol m-2 s-1] across leaves gs_can: float stomatal conductance of canopy [mol m-2 s-1] across leaves An_can: float C assimilation rate of canopy [umol m-2 s-1] across leaves Ci_can: float average intercellular CO2 concentration of canopy [Pa] across leaves rublim_can: string 'True' if the C assimilation is rubisco limited, 'False' otherwise. """ # initial state Cs = p.CO2 # Pa Tleaf = p.Tair # deg C Dleaf = np.maximum(0.05, p.VPD) # gs model not valid < 0.05 # hydraulics P, E = hydraulics(p, res=res) if sw >= p.fc: g1 = p.g1 else: g1 = p.g1 * fwWP(p, p.Ps) # initialise gs over A g0 = 1.e-9 # g0 ~ 0, removing it entirely introduces errors Cs_umol_mol = Cs * conv.MILI / p.Patm # umol mol-1 gsoA = g0 + (1. + g1 / (Dleaf ** 0.5)) / Cs_umol_mol # iter on the solution until it is stable enough iter = 0 while True: An, Aj, Ac, Ci = calc_photosynthesis(p, 0., Cs, photo, Tleaf=Tleaf, gs_over_A=gsoA) # stomatal conductance, with moisture stress effect gs = np.maximum(cst.zero, conv.GwvGc * gsoA * An) # calculate new trans, gw, gb, mol.m-2.s-1 trans, real_zero, gw, gb, Dleaf = calc_trans(p, Tleaf, gs, inf_gb=inf_gb) new_Tleaf, __ = leaf_temperature(p, trans, Tleaf=Tleaf, inf_gb=inf_gb) # update Cs (Pa) boundary_CO2 = p.Patm * conv.FROM_MILI * An / (gb * conv.GbcvGb) Cs = np.maximum(cst.zero, np.minimum(p.CO2, p.CO2 - boundary_CO2)) Cs_umol_mol = Cs * conv.MILI / p.Patm # new leaf-air vpd, kPa if (np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero) or np.isclose(gw, cst.zero, rtol=cst.zero, atol=cst.zero) or np.isclose(gs, cst.zero, rtol=cst.zero, atol=cst.zero)): Dleaf = np.maximum(0.05, p.VPD) # kPa # update gs over A gsoA = g0 + (1. + g1 / (Dleaf ** 0.5)) / Cs_umol_mol # force stop when atm. conditions yield E < 0. (non-physical) if (iter < 1) and (not real_zero): real_zero = None # check for convergence if ((real_zero is None) or (iter >= iter_max) or ((iter >= 1) and real_zero and (abs(Tleaf - new_Tleaf) <= threshold_conv) and not np.isclose(gs, cst.zero, rtol=cst.zero, atol=cst.zero))): break # no convergence, iterate on leaf temperature Tleaf = new_Tleaf iter += 1 Pleaf = P[bn.nanargmin(np.abs(trans - E))] rublim = rubisco_limit(Aj, Ac) # lim? if ((np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero) and (An > 0.)) or np.isclose(Ci, 0., rtol=cst.zero, atol=cst.zero) or (Ci < 0.) or np.isclose(Ci, p.CO2, rtol=cst.zero, atol=cst.zero) or (Ci > p.CO2) or (real_zero is None) or (not real_zero) or any(np.isnan([An, Ci, trans, gs, new_Tleaf, Pleaf]))): An, Ci, trans, gs, gb, new_Tleaf, Pleaf = (9999.,) * 7 elif not np.isclose(trans, cst.zero, rtol=cst.zero, atol=cst.zero): trans *= conv.MILI # mmol.m-2.s-1 return An, Ci, rublim, trans, gs, gb, new_Tleaf, Pleaf
def Ci_sup_dem(p, trans, photo='Farquhar', res='low', Vmax25=None, phi=None, inf_gb=False): # ref. photosynthesis A_ref, __, __ = calc_photosynthesis(p, trans, p.CO2, photo, Vmax25=Vmax25, inf_gb=inf_gb) # Cs < Ca, used to ensure physical solutions __, __, gb, __ = leaf_energy_balance(p, trans, inf_gb=inf_gb) boundary_CO2 = p.Patm * conv.FROM_MILI * A_ref / (gb * conv.GbcvGb) Cs = np.maximum(cst.zero, np.minimum(p.CO2, p.CO2 - boundary_CO2)) # Pa # potential Ci values over the full range of transpirations if res == 'low': NCis = 500 if res == 'med': NCis = 2000 if res == 'high': NCis = 8000 # retrieve the appropriate Cis from the supply-demand Cis = np.asarray( [np.linspace(0.1, Cs[e], NCis) for e in range(len(trans))]) if (Vmax25 is None) and (phi is not None): # account for gm Tref = p.Tref + conv.C_2_K # degk, Tref set to 25 degC try: # is Tleaf one of the input fields? Tleaf = p.Tleaf except (IndexError, AttributeError, ValueError): # calc. Tleaf Tleaf, __ = leaf_temperature(p, trans, inf_gb=inf_gb) # CO2 compensation point gamstar = arrhen(p.gamstar25, p.Egamstar, Tref, Tleaf) try: # now getting the Cc Ccs = np.asarray([ phi[e] * (Cis[e] - gamstar[e]) + gamstar[e] for e in range(len(trans)) ]) except IndexError: # only one Tleaf Ccs = np.asarray([ phi[e] * (Cis[e] - gamstar) + gamstar for e in range(len(trans)) ]) if phi is None: Ccs = None Ci = mtx_minimise(p, trans, Cis, photo, Vmax25=Vmax25, all_Ccs=Ccs, inf_gb=inf_gb) mask = ~Ci.mask try: if len(mask) > 0: pass except TypeError: mask = ([False] + [ True, ] * len(Ci))[:-1] return Ci[mask], mask