def partition_from_qc_averages(qcdata, wue): """Partition H2O and CO2 fluxes using interval average q and c data. All arguments are passed directly to the findroot function Parameters ---------- qcdata : QCData namedtuple or equivalent namespace wue : float, kg CO2 / kg H2O Leaf-level water use efficiency, `wue` < 0 Returns ------- namedtuples :class:`~fluxpart.containers.RootSoln`, :class:`~fluxpart.containers.FluxComponents` """ rsoln = findroot(qcdata, wue) if rsoln.validroot: fluxes = flux_components(rsoln.var_cp, rsoln.corr_cp_cr, qcdata, wue, rsoln.co2soln_id) else: fluxes = FluxComponents(*np.full(6, np.nan)) return rsoln, fluxes
def adjust_partitioned_fluxes(fc, wue, wq_tot, wc_tot): """Adjust partitioned fluxes so they match measured totals. If filtering has been applied to the series data, covariances in the filtered data may differ from those in the original data. Consequently, partitioned flux totals may not match exactly the total fluxes indicated by the original data. Here, partitioned fluxes are adjusted proportionally so that they match the totals in the original data. Parameters ---------- fc : :class:`~fluxpart.containers.FluxComponents` or equivalent Container holding partitioned flux components, kg/m^2/s. wue : float Leaf-level water use efficiency (`wue` < 0), kg CO2 / kg H2O wq_tot, wc_tot : float Desired net total H2O (`wq_tot`) and CO2 (`wc_tot`) fluxes, kg/m^2/s. Returns ------- namedtuple :class:`~fluxpart.containers.FluxComponents` """ wq_diff = wq_tot - (fc.wqe + fc.wqt) wqe = fc.wqe + wq_diff * (fc.wqe / (fc.wqt + fc.wqe)) wqt = wq_tot - wqe wcp = wue * wqt wcr = wc_tot - wcp return FluxComponents(wq=wq_tot, wqt=wqt, wqe=wqe, wc=wc_tot, wcp=wcp, wcr=wcr)
def partition_from_qc_averages(qcdat, wue, init=None): """Partition H2O and CO2 fluxes using interval average q and c data. All arguments are passed directly to the findroot function Parameters ---------- qcdat : QCData namedtuple or equivalent namespace wue : float, kg CO2 / kg H2O Leaf-level water use efficiency, `wue` < 0 init : (float, float), optional 2-Tuple is initial value for (corr_cp_cr, var_cp). If `init` = None (default), an initial estimate is calculated internally. Note when specifying initial values: -1 < `corr_cp_cr` < 0, and `var_cp` has units of (kg/m^3)^2. Returns ------- namedtuples :class:`~fluxpart.containers.NumerSoln`, :class:`~fluxpart.containers.FluxComponents` """ nsoln = findroot(qcdat, wue, init) if nsoln.success and nsoln.validroot: fluxes = flux_components(nsoln.var_cp, nsoln.corr_cp_cr, qcdat, wue, nsoln.co2soln_id) else: fluxes = FluxComponents(*np.full(6, np.nan)) return nsoln, fluxes
def flux_components(var_cp, corr_cp_cr, qcdata, wue, co2soln_id): """Calculate flux components for given (var_cp, corr_cp_cr) pair.""" wcr_ov_wcp = flux_ratio(var_cp, corr_cp_cr, qcdata, 'co2', co2soln_id) # TODO: handle wcr_ov_wcp ~ -1 wcp = qcdata.wc / (wcr_ov_wcp + 1) wcr = qcdata.wc - wcp wqt = wcp / wue wqe = qcdata.wq - wqt return FluxComponents(wq=qcdata.wq, wqt=wqt, wqe=wqe, wc=qcdata.wc, wcp=wcp, wcr=wcr)
def partition_from_wqc_series(w, q, c, wue, adjust_fluxes=True): """Partition H2O and CO2 fluxes using series data for w, q, and c. If a valid partitioning solution is not found for the passed series data, low-frequency (large-scale) components are progressively removed from the data until either a valid solution is found or the series decomposition is exhausted. Parameters ---------- w,q,c : array_like 1D time series for vertical wind speed `w` (m/s), water vapor concentration `q` (kg/m^3), and CO2 concentration `c` (kg/m^3). wue : float leaf-level water use efficiency, `wue` < 0, kg CO2 / kg H2O. adjust_fluxes : bool, optional Indicates whether the obtained partitioned fluxes should be adjusted so that the totals match the original data. Default is `adjust_fluxes` = True. Returns ------- dict {'valid_partition': bool, 'partmssg': str, 'fluxcomps': :class:`~fluxpart.containers.FluxComponents`, 'rootsoln': :class:`~fluxpart.containers.RootSoln`, 'qcdata': :class:`~fluxpart.containers.QCData`} Notes ----- If a valid partitioning is not found, the returned `numersoln` and `qcdata` correspond to the final iteration attempted. """ max_decomp_lvl = int(np.log2(w.size)) wq_tot = np.cov((w, q))[0, 1] wc_tot = np.cov((w, c))[0, 1] # The loop progressively filters the data until a physically valid # partitioning is found or the loop/filter is exhausted. The first # iteration of progressive_lowcut removes only the mean value # (q'=q-<q>, etc.), so the first iteration uses the unfiltered # deviations. for cnt, lowcut_wqc in enumerate(progressive_lowcut(w, q, c)): cov = np.cov(lowcut_wqc) qcdata = QCData( wq=cov[0, 1], wc=cov[0, 2], var_q=cov[1, 1], var_c=cov[2, 2], corr_qc=cov[1, 2] / math.sqrt(cov[1, 1] * cov[2, 2]), wave_lvl=(max_decomp_lvl - cnt, max_decomp_lvl)) rsoln, fcomp = partition_from_qc_averages(qcdata, wue) if rsoln.validroot: if adjust_fluxes: fcomp = adjust_partitioned_fluxes(fcomp, wue, wq_tot, wc_tot) valid_partition, partition_mssg = isvalid_partition(fcomp) if valid_partition: break if not rsoln.validroot: valid_partition = False partition_mssg = rsoln.validmssg if not valid_partition: fcomp = FluxComponents(*np.full(6, np.nan)) return {'valid_partition': valid_partition, 'partmssg': partition_mssg, 'fluxcomps': fcomp, 'rootsoln': rsoln, 'qcdata': qcdata}