• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcuntz / hesseflux / 9537697060

16 Jun 2024 05:21PM UTC coverage: 3.779% (+0.03%) from 3.746%
9537697060

push

github

mcuntz
Removed all warning with new pandas and numpy versions

1 of 161 new or added lines in 4 files covered. (0.62%)

9 existing lines in 2 files now uncovered.

39 of 1032 relevant lines covered (3.78%)

0.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

4.09
/src/hesseflux/nee2gpp.py
1
#!/usr/bin/env python
2
"""
11✔
3
nee2gpp : Estimates photosynthesis (GPP) and ecosystem respiration (RECO)
4
          from Eddy covariance CO2 flux data.
5

6
This module was written by Matthias Cuntz while at Department of
7
Computational Hydrosystems, Helmholtz Centre for Environmental
8
Research - UFZ, Leipzig, Germany, and continued while at Institut
9
National de Recherche pour l'Agriculture, l'Alimentation et
10
l'Environnement (INRAE), Nancy, France.
11

12
Copyright (c) 2012-2020 Matthias Cuntz - mc (at) macu (dot) de
13
Released under the MIT License; see LICENSE file for details.
14

15
* Written 2012 by Matthias Cuntz - mc (at) macu (dot) de
16
* Set default undef to NaN, Mar 2012, Arndt Piayda
17
* Add wrapper nee2gpp for individual routines, Nov 2012, Matthias Cuntz
18
* Ported to Python 3, Feb 2013, Matthias Cuntz
19
* Use generel cost function cost_abs from functions module,
20
  May 2013, Matthias Cuntz
21
* Use fmin_tnc to allow params < 0, Aug 2014, Arndt Piayda
22
* Keyword nogppnight, Aug 2014, Arndt Piayda
23
* Add wrapper nee2gpp for individual routines, Nov 2012, Matthias Cuntz
24
* Add wrapper nee2gpp for individual routines, Nov 2012, Matthias Cuntz
25
* Input can be pandas Dataframe or numpy array(s), Apr 2020, Matthias Cuntz
26
* Using numpy docstring format, May 2020, Matthias Cuntz
27
* Removed np.float and np.int, Jun 2024, Matthias Cuntz
28

29
.. moduleauthor:: Matthias Cuntz, Arndt Piayda
30

31
The following functions are provided
32

33
.. autosummary::
34
   nee2gpp
35
"""
36
from __future__ import division, absolute_import, print_function
11✔
37
import warnings
11✔
38
import numpy as np
11✔
39
import pandas as pd
11✔
40
import scipy.optimize as opt  # curve_fit, fmin, fmin_tnc
11✔
41
from pyjams import mad
11✔
42
from pyjams.functions import cost_abs, lloyd_only_rref_p
11✔
43
from pyjams.functions import cost_lloyd_fix, lloyd_fix, lloyd_fix_p
11✔
44
from pyjams.functions import cost_lasslop, lasslop
11✔
45

46

47
__all__ = ['nee2gpp']
11✔
48

49

50
# ----------------------------------------------------------------------
51
def nee2gpp(dfin, flag=None, isday=None, date=None,
11✔
52
            timeformat='%Y-%m-%d %H:%M:%S', colhead=None, undef=-9999,
53
            method='reichstein', nogppnight=False, swthr=10.):
54
    """
55
    Calculate photosynthesis (GPP) and ecosystem respiration (RECO)
56
    from Eddy covariance CO2 flux data.
57

58
    It uses either
59

60
      1. a fit of Reco vs. temperature to all nighttime data [1]_
61
         (`method='falge'`), or
62

63
      2. several fits over the season of Reco vs. temperature as in Reichstein
64
         et al. (2005) [2]_ (`method='reichstein'`), or
65

66
      3. the daytime method of Lasslop et al. (2010) [3]_ (`method='lasslop'`),
67

68
    in order to calculate Reco and then GPP = Reco - NEE.
69

70
    Parameters
71
    ----------
72
    dfin : pandas.Dataframe or numpy.array
73
        time series of CO2 fluxes and air temperature, and possibly
74
        incoming shortwave radiation and air vapour pressure deficit.
75

76
        `dfin` can be a pandas.Dataframe with the columns
77
        'FC' or 'NEE' (or starting with 'FC\\_' or 'NEE\\_') for observed
78
        CO2 flux [umol(CO2) m-2 s-1]
79
        'TA'    (or starting with 'TA\\_') for air temperature [K]
80

81
        `method='lasslop'` or `method='day'` needs also
82
        'SW_IN' (or starting with 'SW_IN') for incoming short-wave
83
        radiation [W m-2]
84
        'VPD'   (or starting with 'VPD') for air vapour deficit [Pa]
85
        The index is taken as date variable.
86

87
        `dfin` can also me a numpy array with the same columns. In this case
88
        `colhead`, `date`, and possibly `dateformat` must be given.
89
    flag : pandas.Dataframe or numpy.array, optional
90
        flag Dataframe or array has the same shape as `dfin`.
91
        Non-zero values in `flag` will be treated as missing values in `dfin`.
92

93
        `flag` must follow the same rules as `dfin` if pandas.Dataframe.
94

95
        If `flag` is numpy array, `df.columns.values` will be used as column
96
        heads and the index of `dfin` will be copied to `flag`.
97
    isday : array_like of bool, optional
98
        True when it is day, False when night. Must have the same length
99
        as `dfin.shape[0]`.
100

101
        If `isday` is not given, `dfin` must have a column with head 'SW_IN' or
102
        starting with 'SW_IN'. `isday` will then be `dfin['SW_IN'] > swthr`.
103
    date : array_like of string, optional
104
        1D-array_like of calendar dates in format given in `timeformat`.
105

106
        `date` must be given if `dfin` is numpy array.
107
    timeformat : str, optional
108
        Format of dates in `date`, if given (default: '%Y-%m-%d %H:%M:%S').
109
        See strftime documentation of Python's datetime module:
110
        https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior
111
    colhed : array_like of str, optional
112
        column names if `dfin` is numpy array. See `dfin` for mandatory
113
        column names.
114
    undef : float, optional
115
        values having `undef` value are treated as missing values in `dfin`
116
        (default: -9999)
117
    method : str, optional
118
        method to use for partitioning. Possible values are:
119

120
        'global' or 'falge':     fit of Reco vs. temperature to all nighttime
121
                                 data
122

123
        'local' of 'reichstein': several fits over the season of Reco vs.
124
                                 temperature as in Reichstein et al. (2005)
125
                                 (default)
126

127
        'day' or 'lasslop':      method of Lasslop et al. (2010) fitting a
128
                                 light-response curve
129
    nogppnight : float, optional
130
        GPP will be set to zero at night. RECO will then equal NEE at night
131
        (default: False)
132
    swthr : float, optional
133
        Threshold to determine daytime from incoming shortwave radiation
134
        if `isday` not given (default: 10).
135

136
    Returns
137
    -------
138
    pandas.Dataframe or numpy arrays
139
        pandas.Dataframe with two columns 'GPP' and 'RECO' with estimated
140
        photosynthesis and ecosystem respiration, or two numpy arrays
141
        [GPP, RECO].
142

143
    Notes
144
    -----
145
    Negative respiration possible at night if GPP is forced to 0 with
146
    `nogppnight=True`.
147

148
    References
149
    ----------
150
    .. [1] Falge et al. (2001)
151
       Gap filling strategies for defensible annual sums of
152
       net ecosystem exchange,
153
       Acricultural and Forest Meteorology 107, 43-69
154

155
    .. [2] Reichstein et al. (2005)
156
       On the separation of net ecosystem exchange into assimilation
157
       and ecosystem respiration: review and improved algorithm,
158
       Global Change Biology 11, 1424-1439
159

160
    .. [3] Lasslop et al. (2010)
161
       Separation of net ecosystem exchange into assimilation and respiration
162
       using a light response curve approach: critical issues and global
163
       evaluation,
164
       Global Change Biology 16, 187-208
165

166
    Examples
167
    --------
168
    >>> from fread import fread
169
    >>> from date2dec import date2dec
170
    >>> from dec2date import dec2date
171
    >>> ifile = 'test_nee2gpp.csv'
172
    >>> undef = -9999.
173
    >>> dat   = fread(ifile, skip=2, transpose=True)
174
    >>> ndat  = dat.shape[1]
175
    >>> head  = fread(ifile, skip=2, header=True)
176
    >>> head1 = head[0]
177
    >>> # date
178
    >>> jdate = date2dec(dy=dat[0,:], mo=dat[1,:], yr=dat[2,:], hr=dat[3,:], mi=dat[4,:])
179
    >>> adate = dec2date(jdate, eng=True)
180
    >>> # colhead
181
    >>> idx   = []
182
    >>> for i in head1:
183
    ...     if i in ['NEE', 'rg', 'Tair', 'VPD']: idx.append(head1.index(i))
184
    >>> colhead = ['FC', 'SW_IN', 'TA', 'VPD']
185
    >>> # data
186
    >>> dfin = dat[idx,:]
187
    >>> dfin[2,:] = np.where(dfin[2,:] == undef, undef, dfin[2,:]+273.15)
188
    >>> dfin[3,:] = np.where(dfin[3,:] == undef, undef, dfin[3,:]*100.)
189
    >>> # flag
190
    >>> flag = np.where(dfin == undef, 2, 0)
191
    >>> # partition
192
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
193
    ...                     undef=undef, method='local')
194
    >>> print(GPP[1120:1128])
195
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
196
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
197
    >>> print(Reco[1120:1128])
198
    [1.68311981 1.81012431 1.9874173  2.17108871 2.38759152 2.64372415
199
     2.90076664 3.18592735]
200

201
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
202
    ...                     undef=undef, method='local')
203
    >>> print(GPP[1120:1128])
204
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
205
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
206

207
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
208
    ...                     undef=undef, method='global')
209
    >>> print(GPP[1120:1128])
210
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.33166157e+00
211
      8.18228013e+00  1.04092252e+01  8.19395317e+00  1.08427448e+01]
212

213
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
214
    ...                     undef=undef, method='Reichstein')
215
    >>> print(GPP[1120:1128])
216
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
217
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
218

219
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
220
    ...                     undef=undef, method='reichstein')
221
    >>> print(GPP[1120:1128])
222
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
223
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
224

225
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
226
    ...                     undef=undef, method='day')
227
    >>> print(GPP[1120:1128])
228
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  2.78457540e+00
229
      6.63212545e+00  8.88902165e+00  6.74243873e+00  9.51364527e+00]
230
    >>> print(Reco[1120:1128])
231
    [0.28786696 0.34594516 0.43893276 0.5495954  0.70029545 0.90849165
232
     1.15074873 1.46137527]
233

234
    History
235
    -------
236
    Written  Matthias Cuntz, Mar 2012
237
    Modified Arndt Piayda,   Mar 2012
238
                 - undef=np.nan
239
             Matthias Cuntz, Nov 2012
240
                 - wrapper for individual routines nee2gpp_reichstein etc.
241
             Matthias Cuntz, Feb 2013
242
                 - ported to Python 3
243
             Matthias Cuntz, May 2013
244
                 - replaced cost functions by generel cost function cost_abs
245
                   if possible
246
             Arndt Piayda,   Aug 2014
247
                 - replaced fmin with fmin_tnc to permit params<0,
248
                   permit gpp<0 at any time if nogppnight=True
249
             Matthias Cuntz, Jun 2024
250
                 - removed np.int and np.float
251

252
    """
253
    # Check input
254
    # numpy or panda
255
    if isinstance(dfin, (np.ndarray, np.ma.MaskedArray)):
×
256
        isnumpy = True
×
NEW
257
        assert colhead is not None, (
×
258
            'colhead must be given if input is numpy.ndarray.')
259
        if dfin.shape[0] == len(colhead):
×
260
            df = pd.DataFrame(dfin.T, columns=colhead)
×
261
        elif dfin.shape[1] == len(colhead):
×
262
            df = pd.DataFrame(dfin, columns=colhead)
×
263
        else:
NEW
264
            raise ValueError('Length of colhead must be number of columns'
×
265
                             ' in input array. len(colhead)=' +
266
                             str(len(colhead)) + ' shape(input)=(' +
267
                             str(dfin.shape[0]) + ',' +
268
                             str(dfin.shape[1]) + ').')
269
        assert date is not None, 'Date must be given if input is numpy arrary.'
×
270
        df['Datetime'] = pd.to_datetime(date, format=timeformat)
×
271
        df.set_index('Datetime', drop=True, inplace=True)
×
272
    else:
273
        isnumpy = False
×
NEW
274
        assert isinstance(dfin, pd.core.frame.DataFrame), (
×
275
            'Input must be either numpy.ndarray or pandas.DataFrame.')
UNCOV
276
        df = dfin.copy(deep=True)
×
277

278
    # Incoming flags
279
    if flag is not None:
×
280
        if isinstance(flag, (np.ndarray, np.ma.MaskedArray)):
×
281
            if flag.shape[0] == len(df):
×
282
                ff = pd.DataFrame(flag, columns=df.columns.values)
×
283
            elif flag.shape[1] == len(df):
×
284
                ff = pd.DataFrame(flag.T, columns=df.columns.values)
×
285
            else:
NEW
286
                raise ValueError(
×
287
                    'flag must have same shape as data array.'
288
                    ' data: ({:d},{:d}); flag: ({:d},{:d})'.format(
289
                        dfin.shape[0], dfin.shape[1], flag.shape[0],
290
                        flag.shape[1]))
UNCOV
291
            ff = ff.set_index(df.index)
×
292
        else:
NEW
293
            assert isinstance(flag, pd.core.frame.DataFrame), (
×
294
                'Flag must be either numpy.ndarray or pandas.DataFrame.')
UNCOV
295
            ff = flag.copy(deep=True)
×
296
    else:
297
        # flags: 0: good; 1: input flagged; 2: output flagged
298
        ff              = df.copy(deep=True).astype(int)
×
299
        ff[:]           = 0
×
300
        ff[df == undef] = 1
×
301
        ff[df.isna()]   = 1
×
302

303
    # day or night
304
    if isday is None:
×
305
        sw_id = ''
×
306
        for cc in df.columns:
×
307
            if cc.startswith('SW_IN'):
×
308
                sw_id = cc
×
309
                break
×
NEW
310
        assert sw_id, ('Global radiation with name SW or starting with'
×
311
                       ' SW_ must be in input if isday not given.')
312
        # Papale et al. (Biogeosciences, 2006): 20; REddyProc: 10
NEW
313
        isday = df[sw_id] > swthr
×
NEW
314
    if isinstance(isday, (pd.core.series.Series, pd.core.frame.DataFrame)):
×
315
        isday = isday.to_numpy()
×
316
    isday[isday == undef] = np.nan
×
317
    ff[np.isnan(isday)]   = 1
×
318

319
    # Global relationship in Reichstein et al. (2005)
320
    if ((method.lower() == 'global') | (method.lower() == 'falge')):
×
321
        dfout = _nee2gpp_falge(df, ff, isday, undef=undef)
×
322
    # Local relationship = Reichstein et al. (2005)
323
    elif ((method.lower() == 'local') | (method.lower() == 'reichstein')):
×
NEW
324
        dfout = _nee2gpp_reichstein(df, ff, isday, undef=undef,
×
325
                                    nogppnight=nogppnight)
326
    # Lasslop et al. (2010) daytime method
327
    elif ((method.lower() == 'day') | (method.lower() == 'lasslop')):
×
NEW
328
        dfout = _nee2gpp_lasslop(df, ff, isday, undef=undef,
×
329
                                 nogppnight=nogppnight)
330
    # Include new methods here
331
    else:
332
        raise ValueError('Error nee2gpp: method not implemented yet.')
×
333

334
    if isnumpy:
×
335
        return dfout['GPP'].to_numpy(), dfout['RECO'].to_numpy()
×
336
    else:
337
        return dfout
×
338

339

340
# ----------------------------------------------------------------------
341
def _nee2gpp_falge(df, ff, isday, undef=-9999):
11✔
342
    """
343
    Calculate photosynthesis (GPP) and ecosystem respiration (RECO) from
344
    original Eddy flux data, using a fit of Reco vs. temperature to all
345
    nighttime data [1]_, in order to calculate Reco and then GPP = Reco - NEE.
346

347
    Parameters
348
    ----------
349
    df : pandas.Dataframe
350
        time series of CO2 fluxes and air temperature.
351

352
        pandas.Dataframe with the columns
353
        'FC' or 'NEE' (or starting with 'FC\\_' or 'NEE\\_') for observed
354
        CO2 flux [umol(CO2) m-2 s-1]
355
        'TA'    (or starting with 'TA\\_') for air temperature [K]
356
        The index is taken as date variable.
357
    ff : pandas.Dataframe
358
        flag Dataframe or array has the same shape as `df`. Non-zero values in
359
        `ff` will be treated as missing values in `df`.
360

361
        `ff` must follow the same rules as `df`.
362
    isday : array_like of bool
363
        True when it is day, False when night. Must have the same length
364
        as `df.shape[0].`
365
    undef : float, optional
366
        values having `undef` value are treated as missing values in `df`
367
        (default: -9999)
368

369
    Returns
370
    -------
371
    pandas.Dataframe or numpy arrays
372
        pandas.Dataframe with two columns 'GPP' and 'RECO' with estimated
373
        photosynthesis and ecosystem respiration.
374

375
    References
376
    ----------
377
    .. [1] Falge et al. (2001)
378
       Gap filling strategies for defensible annual sums of
379
       net ecosystem exchange,
380
       Acricultural and Forest Meteorology 107, 43-69
381

382
    Examples
383
    --------
384
    >>> from fread import fread
385
    >>> from date2dec import date2dec
386
    >>> from dec2date import dec2date
387
    >>> ifile = 'test_nee2gpp.csv'
388
    >>> undef = -9999.
389
    >>> dat   = fread(ifile, skip=2, transpose=True)
390
    >>> ndat  = dat.shape[1]
391
    >>> head  = fread(ifile, skip=2, header=True)
392
    >>> head1 = head[0]
393
    >>> # date
394
    >>> jdate = date2dec(dy=dat[0,:], mo=dat[1,:], yr=dat[2,:],
395
    ...                  hr=dat[3,:], mi=dat[4,:])
396
    >>> adate = dec2date(jdate, eng=True)
397
    >>> # colhead
398
    >>> idx   = []
399
    >>> for i in head1:
400
    ...     if i in ['NEE', 'rg', 'Tair', 'VPD']: idx.append(head1.index(i))
401
    >>> colhead = ['FC', 'SW_IN', 'TA', 'VPD']
402
    >>> # data
403
    >>> dfin = dat[idx,:]
404
    >>> dfin[2,:] = np.where(dfin[2,:] == undef, undef, dfin[2,:]+273.15)
405
    >>> dfin[3,:] = np.where(dfin[3,:] == undef, undef, dfin[3,:]*100.)
406
    >>> # flag
407
    >>> flag = np.where(dfin == undef, 2, 0)
408
    >>> # partition
409
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
410
    ...                     undef=undef, method='global')
411
    >>> print(GPP[1120:1128])
412
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.33166157e+00
413
      8.18228013e+00  1.04092252e+01  8.19395317e+00  1.08427448e+01]
414

415
    History
416
    -------
417
    Written  Matthias Cuntz, Mar 2012
418
    Modified Arndt Piayda,   Mar 2012 - undef=np.nan
419
             Matthias Cuntz, Nov 2012 - individual routine
420
             Matthias Cuntz, Feb 2013 - ported to Python 3
421
             Matthias Cuntz, Jun 2024 - removed np.int and np.float
422

423
    """
424
    # Variables
425
    fc_id = ''
×
426
    for cc in df.columns:
×
NEW
427
        if ( cc.startswith('FC_') or (cc == 'FC') or cc.startswith('NEE_') or
×
428
             (cc == 'NEE') ):
429
            fc_id = cc
×
430
            break
×
431
    ta_id = ''
×
432
    for cc in df.columns:
×
433
        if cc.startswith('TA_') or (cc == 'TA'):
×
434
            ta_id = cc
×
435
            break
×
NEW
436
    assert fc_id, ('Carbon net flux with name FC or NEE or starting with FC_'
×
437
                   ' or NEE_ must be in input.')
NEW
438
    assert ta_id, ('Air temperature with name TA or starting with TA_ must'
×
439
                   ' be in input.')
440

441
    nee    = np.ma.array(df[fc_id], mask=(ff[fc_id] > 0))
×
442
    t      = np.ma.array(df[ta_id], mask=(ff[ta_id] > 0))
×
NEW
443
    misday = np.ma.array(isday, mask=((~np.isfinite(isday)) |
×
444
                                      (isday == undef)))
445

446
    # Partition - Global relationship as in Falge et al. (2001)
447

448
    # Select valid nighttime
449
    ndata = nee.size
×
450
    mask = misday | nee.mask | t.mask | misday.mask
×
451
    ii   = np.where(~mask)[0]
×
452
    tt   = np.ma.compressed(t[ii])
×
453
    net  = np.ma.compressed(nee[ii])
×
454
    p        = opt.fmin(cost_abs, [2., 200.],
×
455
                        args=(lloyd_fix_p, tt, net), disp=False)
456

NEW
457
    Reco     = np.ones(ndata) * undef
×
458
    ii       = np.where(~t.mask)[0]
×
459
    Reco[ii] = lloyd_fix(t[ii], p[0], p[1])
×
460

461
    # GPP
NEW
462
    GPP     = np.ones(ndata) * undef
×
463
    ii      = np.where(~(t.mask | nee.mask))[0]
×
464
    GPP[ii] = Reco[ii] - nee[ii]
×
465

NEW
466
    dfout = pd.DataFrame({'GPP': GPP, 'RECO': Reco}, index=df.index)
×
467

468
    return dfout
×
469

470

471
# ----------------------------------------------------------------------
472
def _nee2gpp_reichstein(df, ff, isday, undef=-9999, nogppnight=False):
11✔
473
    """
474
    Calculate photosynthesis (GPP) and ecosystem respiration (RECO) from
475
    original Eddy flux data, using several fits of Reco vs. temperature of
476
    nighttime data over the season, as in Reichstein et al. (2005) [2]_, in
477
    order to calculate Reco and then GPP = Reco - NEE.
478

479
    Parameters
480
    ----------
481
    df : pandas.Dataframe
482
        time series of CO2 fluxes and air temperature.
483

484
        pandas.Dataframe with the columns
485
        'FC' or 'NEE' (or starting with 'FC\\_' or 'NEE\\_') for
486
        observed CO2 flux [umol(CO2) m-2 s-1]
487
        'TA'    (or starting with 'TA\\_') for air temperature [K]
488
        The index is taken as date variable.
489
    ff : pandas.Dataframe
490
        flag Dataframe or array has the same shape as `df`. Non-zero values in
491
        `ff` will be treated as missing values in `df`.
492

493
        `ff` must follow the same rules as `df`.
494
    isday : array_like of bool
495
        True when it is day, False when night. Must have the same length
496
        as `df.shape[0].`
497
    undef : float, optional
498
        values having `undef` value are treated as missing values in `df`
499
        (default: -9999)
500
    nogppnight : float, optional
501
        GPP will be set to zero at night. RECO will then equal NEE at night
502
        (default: False)
503

504
    Returns
505
    -------
506
    pandas.Dataframe
507
        pandas.Dataframe with two columns 'GPP' and 'RECO' with estimated
508
        photosynthesis and ecosystem respiration.
509

510
    References
511
    ----------
512
    .. [2] Reichstein et al. (2005)
513
       On the separation of net ecosystem exchange into assimilation and
514
       ecosystem respiration: review and improved algorithm,
515
       Global Change Biology 11, 1424-1439
516

517
    Examples
518
    --------
519
    >>> from fread import fread
520
    >>> from date2dec import date2dec
521
    >>> from dec2date import dec2date
522
    >>> ifile = 'test_nee2gpp.csv'
523
    >>> undef = -9999.
524
    >>> dat   = fread(ifile, skip=2, transpose=True)
525
    >>> ndat  = dat.shape[1]
526
    >>> head  = fread(ifile, skip=2, header=True)
527
    >>> head1 = head[0]
528
    >>> # date
529
    >>> jdate = date2dec(dy=dat[0,:], mo=dat[1,:], yr=dat[2,:], hr=dat[3,:],
530
    ...                  mi=dat[4,:])
531
    >>> adate = dec2date(jdate, eng=True)
532
    >>> # colhead
533
    >>> idx   = []
534
    >>> for i in head1:
535
    ...     if i in ['NEE', 'rg', 'Tair', 'VPD']: idx.append(head1.index(i))
536
    >>> colhead = ['FC', 'SW_IN', 'TA', 'VPD']
537
    >>> # data
538
    >>> dfin = dat[idx,:]
539
    >>> dfin[2,:] = np.where(dfin[2,:] == undef, undef, dfin[2,:]+273.15)
540
    >>> dfin[3,:] = np.where(dfin[3,:] == undef, undef, dfin[3,:]*100.)
541
    >>> # flag
542
    >>> flag = np.where(dfin == undef, 2, 0)
543
    >>> # partition
544
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
545
    ...                     undef=undef, method='local')
546
    >>> print(GPP[1120:1128])
547
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
548
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
549
    >>> print(Reco[1120:1128])
550
    [1.68311981 1.81012431 1.9874173  2.17108871 2.38759152 2.64372415
551
     2.90076664 3.18592735]
552

553
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
554
    ...                     undef=undef, method='local')
555
    >>> print(GPP[1120:1128])
556
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
557
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
558

559
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
560
    ...                     undef=undef, method='Reichstein')
561
    >>> print(GPP[1120:1128])
562
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
563
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
564

565
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
566
    ...                     undef=undef, method='reichstein')
567
    >>> print(GPP[1120:1128])
568
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
569
      8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
570

571
    History
572
    -------
573
    Written  Matthias Cuntz, Mar 2012
574
    Modified Arndt Piayda,   Mar 2012 - undef=np.nan
575
             Matthias Cuntz, Nov 2012 - individual routine
576
             Matthias Cuntz, Feb 2013 - ported to Python 3
577
             Matthias Cuntz, Jun 2024 - removed np.int and np.float
578

579
    """
580
    # Variables
581
    fc_id = ''
×
582
    for cc in df.columns:
×
NEW
583
        if ( cc.startswith('FC_') or (cc == 'FC') or cc.startswith('NEE_') or
×
584
             (cc == 'NEE') ):
585
            fc_id = cc
×
586
            break
×
587
    ta_id = ''
×
588
    for cc in df.columns:
×
589
        if cc.startswith('TA_') or (cc == 'TA'):
×
590
            ta_id = cc
×
591
            break
×
NEW
592
    assert fc_id, ('Carbon net flux with name FC or NEE or starting with FC_'
×
593
                   ' or NEE_ must be in input.')
NEW
594
    assert ta_id, ('Air temperature with name TA or starting with TA_ must'
×
595
                   ' be in input.')
596

597
    nee    = np.ma.array(df[fc_id], mask=(ff[fc_id] > 0))
×
598
    t      = np.ma.array(df[ta_id], mask=(ff[ta_id] > 0))
×
NEW
599
    misday = np.ma.array(isday, mask=((~np.isfinite(isday)) |
×
600
                                      (isday == undef)))
UNCOV
601
    dates  = df.index.to_julian_date()
×
602

603
    # Partition - Local relationship = Reichstein et al. (2005)
604

605
    ndata = nee.size
×
NEW
606
    GPP   = np.ones(ndata) * undef
×
NEW
607
    Reco  = np.ones(ndata) * undef
×
NEW
608
    dfout = pd.DataFrame({'GPP': GPP, 'RECO': Reco}, index=df.index)
×
609

610
    # Select valid nighttime
611
    mask  = misday | nee.mask | t.mask | misday.mask
×
612
    ii    = np.where(~mask)[0]
×
NEW
613
    if (ii.size == 0):
×
614
        # raise ValueError('Error _nee2gpp_reichstein:'
615
        #                  ' no valid nighttime data.')
616
        print('Warning _nee2gpp_reichstein: no valid nighttime data.')
×
617
        return dfout
×
618
    jul  = dates[ii]
×
619
    tt   = np.ma.compressed(t[ii])
×
620
    net  = np.ma.compressed(nee[ii])
×
621
    # 1. each 5 days, in 15 day period, fit if range of T > 5
NEW
622
    locp = []  # local param
×
NEW
623
    locs = []  # local err
×
624
    # be aware that julian days starts at noon, i.e. 1.0 is 12h
625
    # so the search will be from noon to noon and thus includes all nights
NEW
626
    dmin = np.floor(np.amin(jul)).astype(int)
×
NEW
627
    dmax = np.ceil(np.amax(jul)).astype(int)
×
NEW
628
    for i in range(dmin, dmax, 5):
×
NEW
629
        iii  = np.where((jul >= i) & (jul < (i + 14)))[0]
×
630
        niii = iii.size
×
631
        if niii > 6:
×
632
            tt1  = tt[iii]
×
633
            net1 = net[iii]
×
634
            # make fit more robust by removing outliers
NEW
635
            mm   = ~mad(net1, z=4.5)
×
636
            if (np.ptp(tt[iii]) >= 5.) & (np.sum(mm) > 6):
×
637
                p, temp1, temp2 = opt.fmin_tnc(cost_lloyd_fix, [2., 200.],
×
638
                                               bounds=[[0., None], [0., None]],
639
                                               args=(tt1[mm], net1[mm]),
640
                                               approx_grad=True, disp=False)
641
                try:
×
642
                    # params, covariance
NEW
643
                    p1, c = opt.curve_fit(lloyd_fix, tt1[mm], net1[mm],
×
644
                                          p0=p, maxfev=10000)
645
                    # possible return of curvefit: c=inf
NEW
646
                    if np.all(np.isfinite(c)):
×
UNCOV
647
                        s = np.sqrt(np.diag(c))
×
648
                    else:
NEW
649
                        s = 10. * np.abs(p)
×
650
                except:
×
NEW
651
                    s = 10. * np.abs(p)
×
652
                locp += [p]
×
653
                locs += [s]
×
654
                # if ((s[1]/p[1])<0.5) & (p[1] > 0.): pdb.set_trace()
655
    if len(locp) == 0:
×
656
        # raise ValueError('Error _nee2gpp_reichstein:'
657
        #                  ' No local relationship found.')
658
        print('Warning _nee2gpp_reichstein: No local relationship found.')
×
659
        return dfout
×
NEW
660
    locp   = np.squeeze(np.array(locp).astype(float))
×
NEW
661
    locs   = np.squeeze(np.array(locs).astype(float))
×
662
    # 2. E0 = avg of best 3
663
    # Reichstein et al. (2005), p. 1430, 1st paragraph.
664
    with warnings.catch_warnings():
×
665
        warnings.simplefilter("ignore")
×
NEW
666
        iii  = np.where((locp[:, 1] > 0.) & (locp[:, 1] < 450.) &
×
667
                        (np.abs(locs[:, 1] / locp[:, 1]) < 0.5))[0]
668
    niii = iii.size
×
NEW
669
    if niii == 0:
×
670
        # raise ValueError('Error _nee2gpp_reichstein:'
671
        #                  ' No good local relationship found.')
672
        # loosen the criteria: take the best three estimates anyway
NEW
673
        iii   = np.where((locp[:, 1] > 0.))[0]
×
674
        niii = iii.size
×
NEW
675
        if niii < 1:
×
676
            # raise ValueError('Error _nee2gpp_reichstein: No E0>0 found.')
677
            print('Warning _nee2gpp_reichstein: No E0>0 found.')
×
678
            return dfout
×
NEW
679
        lp    = locp[iii, :]
×
NEW
680
        ls    = locs[iii, :]
×
NEW
681
        iis   = np.argsort(ls[:, 1])
×
NEW
682
        bestp = np.mean(lp[iis[0:np.minimum(3, niii)], :], axis=0)
×
NEW
683
        bests = np.mean(ls[iis[0:np.minimum(3, niii)], :], axis=0)
×
NEW
684
    elif niii == 1:
×
NEW
685
        bestp = np.squeeze(locp[iii, :])
×
NEW
686
        bests = np.squeeze(locs[iii, :])
×
NEW
687
    elif niii == 2:
×
NEW
688
        bestp = np.mean(locp[iii, :], axis=0)
×
NEW
689
        bests = np.mean(locs[iii, :], axis=0)
×
690
        # ls    = locs[iii,:]
691
        # iis   = np.argsort(ls[:,1])
692
    else:
NEW
693
        lp    = locp[iii, :]
×
NEW
694
        ls    = locs[iii, :]
×
NEW
695
        iis   = np.argsort(ls[:, 1])
×
NEW
696
        bestp = np.mean(lp[iis[0:3], :], axis=0)
×
NEW
697
        bests = np.mean(ls[iis[0:3], :], axis=0)
×
698

699
    # 3. Refit Rref with fixed E0, each 4 days
NEW
700
    refp  = []  # Rref param
×
NEW
701
    refii = []  # mean index of data points
×
702
    E0    = bestp[1]
×
703
    et    = lloyd_fix(tt, 1., E0)
×
NEW
704
    for i in range(dmin, dmax, 4):
×
NEW
705
        iii  = np.where((jul >= i) & (jul < (i + 4)))[0]
×
706
        niii = iii.size
×
707
        if niii > 3:
×
708
            # Calc directly minisation of (nee-p*et)**2
709
            p, temp1, temp2 = opt.fmin_tnc(cost_abs, [2.],
×
710
                                           bounds=[[0., None]],
711
                                           args=(lloyd_only_rref_p, et[iii],
712
                                                 net[iii]),
713
                                           approx_grad=True, disp=False)
714
            refp  += [p]
×
NEW
715
            refii += [int((iii[0] + iii[-1]) // 2)]
×
716
    if len(refp) == 0:
×
717
        # raise ValueError('Error _nee2gpp_reichstein:'
718
        #                  ' No ref relationship found.')
719
        print('Warning _nee2gpp_reichstein: No ref relationship found.')
×
720
        return dfout
×
721
    refp  = np.squeeze(np.array(refp))
×
722
    refii = np.squeeze(np.array(refii))
×
723

724
    # 4. Interpol Rref
725
    Rref = np.interp(dates, jul[refii], refp)
×
726

727
    # 5. Calc Reco
NEW
728
    Reco     = np.ones(ndata) * undef
×
729
    ii       = np.where(~t.mask)[0]
×
730
    Reco[ii] = lloyd_fix(t[ii], Rref[ii], E0)
×
731

732
    # 6. Calc GPP
NEW
733
    GPP     = np.ones(ndata) * undef
×
734
    ii      = np.where(~(t.mask | nee.mask))[0]
×
735
    GPP[ii] = Reco[ii] - nee[ii]
×
736

737
    # 7. Set GPP=0 at night, if wanted
738
    if nogppnight:
×
NEW
739
        mask = misday | nee.mask | t.mask | misday.mask  # night
×
740
        ii   = np.where(~mask)[0]
×
741
        Reco[ii] = nee[ii]
×
742
        GPP[ii]  = 0.
×
743
        # and prohibit negative gpp at any time
NEW
744
        mask = nee.mask | t.mask | (GPP > 0.)
×
745
        ii   = np.where(~mask)[0]
×
746
        Reco[ii] -= GPP[ii]
×
747
        GPP[ii]  = 0.
×
748

NEW
749
    dfout = pd.DataFrame({'GPP': GPP, 'RECO': Reco}, index=df.index)
×
750

751
    return dfout
×
752

753

754
# ----------------------------------------------------------------------
755
def _nee2gpp_lasslop(df, ff, isday, undef=-9999, nogppnight=False):
11✔
756
    """
757
    Calculate photosynthesis (GPP) and ecosystem respiration (RECO) from
758
    original Eddy flux data, using the daytime method of Lasslop et al. (2010)
759
    [3]_, in order to calculate Reco and then GPP = Reco - NEE.
760

761
    Parameters
762
    ----------
763
    df : pandas.Dataframe or numpy.array
764
        time series of CO2 fluxes, air temperature,
765
        incoming shortwave radiation, and air vapour pressure deficit.
766

767
        `df` can be a pandas.Dataframe with the columns
768
        'FC' or 'NEE' (or starting with 'FC\\_' or 'NEE\\_') for observed
769
        CO2 flux [umol(CO2) m-2 s-1]
770
        'TA'    (or starting with 'TA\\_') for air temperature [K]
771
        'SW_IN' (or starting with 'SW_IN') for incoming short-wave
772
        radiation [W m-2]
773
        'VPD'   (or starting with 'VPD') for air vapour deficit [Pa]
774
        The index is taken as date variable.
775
    ff : pandas.Dataframe or numpy.array, optional
776
        flag Dataframe or array has the same shape as `df`. Non-zero values in
777
        `ff` will be treated as missing values in `df`.
778

779
        `ff` must follow the same rules as `df` if pandas.Dataframe.
780
    isday : array_like of bool, optional
781
        True when it is day, False when night. Must have the same length
782
        as `df.shape[0]`.
783
    undef : float, optional
784
        values having `undef` value are treated as missing values in `df`
785
        (default: -9999)
786
    nogppnight : float, optional
787
        GPP will be set to zero at night. RECO will then equal NEE at night
788
        (default: False)
789

790
    Returns
791
    -------
792
    pandas.Dataframe
793
        pandas.Dataframe with two columns 'GPP' and 'RECO' with estimated
794
        photosynthesis and ecosystem respiration.
795

796
    References
797
    ----------
798
    .. [3] Lasslop et al. (2010)
799
       Separation of net ecosystem exchange into assimilation and respiration
800
       using a light response curve approach: critical issues and global
801
       evaluation,
802
       Global Change Biology 16, 187-208
803

804
    Examples
805
    --------
806
    >>> from fread import fread
807
    >>> from date2dec import date2dec
808
    >>> from dec2date import dec2date
809
    >>> ifile = 'test_nee2gpp.csv'
810
    >>> undef = -9999.
811
    >>> dat   = fread(ifile, skip=2, transpose=True)
812
    >>> ndat  = dat.shape[1]
813
    >>> head  = fread(ifile, skip=2, header=True)
814
    >>> head1 = head[0]
815
    >>> # date
816
    >>> jdate = date2dec(dy=dat[0,:], mo=dat[1,:], yr=dat[2,:], hr=dat[3,:],
817
                         mi=dat[4,:])
818
    >>> adate = dec2date(jdate, eng=True)
819
    >>> # colhead
820
    >>> idx   = []
821
    >>> for i in head1:
822
    ...     if i in ['NEE', 'rg', 'Tair', 'VPD']: idx.append(head1.index(i))
823
    >>> colhead = ['FC', 'SW_IN', 'TA', 'VPD']
824
    >>> # data
825
    >>> dfin = dat[idx,:]
826
    >>> dfin[2,:] = np.where(dfin[2,:] == undef, undef, dfin[2,:]+273.15)
827
    >>> dfin[3,:] = np.where(dfin[3,:] == undef, undef, dfin[3,:]*100.)
828
    >>> # flag
829
    >>> flag = np.where(dfin == undef, 2, 0)
830
    >>> # partition
831
    >>> GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead,
832
                            undef=undef, method='day')
833
    >>> print(GPP[1120:1128])
834
    [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  2.78457540e+00
835
      6.63212545e+00  8.88902165e+00  6.74243873e+00  9.51364527e+00]
836
    >>> print(Reco[1120:1128])
837
    [0.28786696 0.34594516 0.43893276 0.5495954  0.70029545 0.90849165
838
     1.15074873 1.46137527]
839

840
    History
841
    -------
842
    Written  Matthias Cuntz, Mar 2012
843
    Modified Arndt Piayda,   Mar 2012 - undef=np.nan
844
             Matthias Cuntz, Nov 2012 - individual routine
845
             Matthias Cuntz, Feb 2013 - ported to Python 3
846
             Matthias Cuntz, Jun 2024 - removed np.int and np.float
847

848
    """
849
    # Variables
850
    fc_id = ''
×
851
    for cc in df.columns:
×
NEW
852
        if ( cc.startswith('FC_') or (cc == 'FC') or cc.startswith('NEE_') or
×
853
             (cc == 'NEE') ):
854
            fc_id = cc
×
855
            break
×
856
    ta_id = ''
×
857
    for cc in df.columns:
×
858
        if cc.startswith('TA_') or (cc == 'TA'):
×
859
            ta_id = cc
×
860
            break
×
861
    sw_id = ''
×
862
    for cc in df.columns:
×
863
        if cc.startswith('SW_IN_') or (cc == 'SW_IN'):
×
864
            sw_id = cc
×
865
            break
×
866
    vpd_id = ''
×
867
    for cc in df.columns:
×
868
        if cc.startswith('VPD_') or (cc == 'VPD'):
×
869
            vpd_id = cc
×
870
            break
×
NEW
871
    assert fc_id, ('Carbon net flux with name FC or NEE or starting with FC_'
×
872
                   ' or NEE_ must be in input.')
NEW
873
    assert ta_id, ('Air temperature with name TA or starting with TA_ must'
×
874
                   ' be in input.')
NEW
875
    assert sw_id, ('Global radiation with name SW or starting with SW_ must'
×
876
                   ' be in input.')
NEW
877
    assert vpd_id, ('Vapour pressure deficit with name VPD or starting with'
×
878
                    ' VPD_ must be in input.')
879

NEW
880
    nee    = np.ma.array(df[fc_id], mask=(ff[fc_id] > 0))
×
NEW
881
    t      = np.ma.array(df[ta_id], mask=(ff[ta_id] > 0))
×
NEW
882
    sw     = np.ma.array(df[sw_id], mask=(ff[sw_id] > 0))
×
883
    vpd    = np.ma.array(df[vpd_id], mask=(ff[vpd_id] > 0))
×
NEW
884
    misday = np.ma.array(isday, mask=((~np.isfinite(isday)) |
×
885
                                      (isday == undef)))
UNCOV
886
    dates  = df.index.to_julian_date()
×
887

888
    # Partition - Lasslop et al. (2010) method
889

890
    ndata = nee.size
×
NEW
891
    GPP   = np.ones(ndata) * undef
×
NEW
892
    Reco  = np.ones(ndata) * undef
×
NEW
893
    dfout = pd.DataFrame({'GPP': GPP, 'RECO': Reco}, index=df.index)
×
894

895
    do_lgpp = False
×
896
    mask  = nee.mask | t.mask | misday.mask | sw.mask | vpd.mask
×
897
    # night
898
    nmask = misday | mask
×
899
    nii   = np.ma.where(~nmask)[0]
×
900
    njul  = dates[nii]
×
901
    ntt   = np.ma.compressed(t[nii])
×
902
    nnet  = np.ma.compressed(nee[nii])
×
903
    aRref = np.mean(nnet)
×
904
    # day
905
    dmask = (~misday) | mask
×
906
    dii   = np.ma.where(~dmask)[0]
×
907
    djul  = dates[dii]
×
908
    dtt   = np.ma.compressed(t[dii])
×
909
    dnet  = np.ma.compressed(nee[dii])
×
910
    dsw   = np.ma.compressed(sw[dii])
×
911
    dvpd  = np.ma.compressed(vpd[dii])
×
912
    # starting values for optim
913
    aalpha = 0.01
×
914
    qnet   = np.sort(dnet)
×
915
    nqnet  = qnet.size
×
NEW
916
    abeta0 = np.abs(qnet[np.floor(0.97 * nqnet).astype(int)] -
×
917
                    qnet[np.ceil(0.03 * nqnet).astype(int)])
UNCOV
918
    ak     = 0.
×
919
    # out
920
    lE0    = []
×
921
    lalpha = []
×
922
    if do_lgpp:
×
923
        lbeta0 = []
×
924
        lk     = []
×
925
    lRref  = []
×
926
    lii    = []
×
NEW
927
    dmin = np.floor(np.amin(dates)).astype(int)
×
NEW
928
    dmax = np.ceil(np.amax(dates)).astype(int)
×
929
    zaehl = -1
×
NEW
930
    for i in range(dmin, dmax, 2):
×
931
        good = True
×
932
        # 1. Estimate E0 from nighttime data
NEW
933
        iii  = np.squeeze(np.where((njul >= i) & (njul < (i + 12))))
×
934
        niii = iii.size
×
935
        if niii > 3:
×
936
            p, temp1, temp2 = opt.fmin_tnc(cost_abs, [aRref, 100.],
×
937
                                           bounds=[[0., None], [0., None]],
938
                                           args=(lloyd_fix_p, ntt[iii],
939
                                                 nnet[iii]),
940
                                           approx_grad=True, disp=False)
941
            E0 = np.maximum(p[1], 50.)
×
942
        else:
943
            if zaehl >= 0:
×
944
                E0 = lE0[zaehl]
×
945
            else:
946
                # large gap at beginning of data set, i.e. skip the period
947
                good = False
×
948
                continue
×
949
        # 2. Estimate alpha, k, beta0, Rref from daytime data
NEW
950
        iii  = np.squeeze(np.where((djul >= i) & (djul < (i + 4))))
×
951
        niii = iii.size
×
952
        if niii > 3:
×
953
            et     = lloyd_fix(dtt[iii], 1., E0)
×
954
            again  = True
×
955
            ialpha = aalpha
×
956
            ibeta0 = abeta0
×
957
            ik     = ak
×
958
            iRref  = aRref
×
NEW
959
            bounds = [[None, None], [None, None], [None, None], [None, None]]
×
960
            while again:
×
961
                again = False
×
NEW
962
                p, nfeval, rc  = opt.fmin_tnc(cost_lasslop,
×
963
                                              [ialpha, ibeta0, ik, iRref],
964
                                              bounds=bounds,
965
                                              args=(dsw[iii], et, dvpd[iii],
966
                                                    dnet[iii]),
967
                                              approx_grad=True, disp=False)
968
                # if parameters beyond some bounds, set params and redo
969
                # the optim or skip
NEW
970
                if ((p[0] < 0.) | (p[0] > 0.22)):  # alpha
×
971
                    again = True
×
972
                    if zaehl >= 0:
×
NEW
973
                        bounds[0] = [lalpha[zaehl], lalpha[zaehl]]
×
974
                        ialpha    = lalpha[zaehl]
×
975
                    else:
NEW
976
                        bounds[0] = [0., 0.]
×
977
                        ialpha    = 0.
×
NEW
978
                if p[1] < 0.:                      # beta0
×
NEW
979
                    bounds[1] = [0., 0.]
×
980
                    ibeta0    = 0.
×
981
                    again = True
×
982
                if p[1] > 250.:
×
983
                    good = False
×
984
                    continue
×
NEW
985
                if p[2] < 0.:                      # k
×
NEW
986
                    bounds[2] = [0., 0.]
×
987
                    ik        = 0.
×
988
                    again = True
×
NEW
989
                if p[3] < 0:                       # Rref
×
990
                    good = False
×
991
                    continue
×
992
            if good:
×
993
                lalpha = lalpha + [p[0]]
×
994
                if do_lgpp:
×
995
                    lbeta0 = lbeta0 + [p[1]]
×
996
                    lk     = lk     + [p[2]]
×
997
                lRref  = lRref  + [p[3]]
×
NEW
998
                lii    = lii    + [int((iii[0] + iii[-1]) / 2)]
×
999
            else:
1000
                continue
×
1001
        else:
1002
            continue
×
1003
        lE0    = lE0 + [E0]
×
1004
        zaehl += 1
×
1005
    if len(lE0) == 0:
×
1006
        # raise ValueError('Error _nee2gpp_lasslop:'
1007
        #                  ' No day relationship found.')
1008
        print('Warning _nee2gpp_lasslop: No day relationship found.')
×
1009
        return dfout
×
1010
    lE0 = np.squeeze(np.array(lE0))
×
1011
    if do_lgpp:
×
1012
        lalpha = np.squeeze(np.array(lalpha))
×
1013
        lbeta0 = np.squeeze(np.array(lbeta0))
×
1014
        lk     = np.squeeze(np.array(lk))
×
1015
    lRref  = np.squeeze(np.array(lRref))
×
1016
    lii    = np.squeeze(np.array(lii))
×
1017

1018
    # 3. Interpol E0 and Rref
1019
    E0   = np.interp(dates, djul[lii], lE0)
×
1020
    Rref = np.interp(dates, djul[lii], lRref)
×
1021

1022
    # 4. Calc Reco
NEW
1023
    Reco     = np.ones(ndata) * undef
×
1024
    ii       = np.squeeze(np.where(~t.mask))
×
1025
    Reco[ii] = lloyd_fix(t[ii], Rref[ii], E0[ii])
×
1026

1027
    # 5. Calc GPP from light response for check
1028
    if do_lgpp:
×
1029
        alpha    = np.interp(dates, djul[lii], lE0)
×
1030
        beta0    = np.interp(dates, djul[lii], lbeta0)
×
1031
        k        = np.interp(dates, djul[lii], lk)
×
1032
        et       = lloyd_fix(t, 1., E0)
×
1033
        lmask    = t.mask | misday.mask | sw.mask | vpd.mask
×
1034
        ii       = np.squeeze(np.where(~lmask))
×
1035
        lgpp     = np.zeros(ndata)
×
NEW
1036
        lgpp[ii] = lasslop(sw[ii], et[ii], vpd[ii], alpha[ii],
×
1037
                           beta0[ii], k[ii], Rref[ii]) - Reco[ii]
1038

1039
    # 6. GPP
NEW
1040
    GPP     = np.ones(ndata) * undef
×
1041
    ii      = np.squeeze(np.where(~(t.mask | nee.mask)))
×
1042
    GPP[ii] = Reco[ii] - nee[ii]
×
1043

1044
    # 7. Set GPP=0 at night, if wanted
1045
    if nogppnight:
×
NEW
1046
        mask = misday | nee.mask | t.mask | misday.mask  # night
×
1047
        ii   = np.where(~mask)[0]
×
1048
        Reco[ii] = nee[ii]
×
1049
        GPP[ii]  = 0.
×
1050
        # and prohibit negative gpp at any time
NEW
1051
        mask = nee.mask | t.mask | (GPP > 0.)
×
1052
        ii   = np.where(~mask)[0]
×
1053
        Reco[ii] -= GPP[ii]
×
1054
        GPP[ii]   = 0.
×
1055

NEW
1056
    dfout = pd.DataFrame({'GPP': GPP, 'RECO': Reco}, index=df.index)
×
1057

1058
    return dfout
×
1059

1060

1061
# -------------------------------------------------------------
1062
if __name__ == '__main__':
1063
    import doctest
1064
    doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)
1065

1066
    # from fread import fread
1067
    # from date2dec import date2dec
1068
    # from dec2date import dec2date
1069
    # ifile = 'test_nee2gpp.csv'
1070
    # undef = -9999.
1071
    # # Day,Month,Year,Hour,Minute,NEE,rg,Tair,VPD,GPP_f,Reco
1072
    # # -,-,-,-,-,umolm-2s-1,Wm-2,degC,hPa,umol_m-2_s-1,umol_m-2_s-1
1073
    # # 1,1,2006,0,15,-9999,0,5.235,0.14192,-0.42485,2.89716
1074
    # dat   = fread(ifile, skip=2, transpose=True)
1075
    # ndat  = dat.shape[1]
1076
    # head  = fread(ifile, skip=2, header=True)
1077
    # head1 = head[0]
1078
    # # date
1079
    # jdate = date2dec(dy=dat[0,:], mo=dat[1,:], yr=dat[2,:], hr=dat[3,:], mi=dat[4,:])
1080
    # adate = dec2date(jdate, eng=True)
1081
    # # colhead
1082
    # idx   = []
1083
    # for i in head1:
1084
    #     if i in ['NEE', 'rg', 'Tair', 'VPD']: idx.append(head1.index(i))
1085
    # colhead = ['FC', 'SW_IN', 'TA', 'VPD']
1086
    # # data
1087
    # dfin = dat[idx,:]
1088
    # dfin[2,:] = np.where(dfin[2,:] == undef, undef, dfin[2,:]+273.15)
1089
    # dfin[3,:] = np.where(dfin[3,:] == undef, undef, dfin[3,:]*100.)
1090
    # # flag
1091
    # flag = np.where(dfin == undef, 2, 0)
1092
    # # isday = np.where(dfin[1,:] > 10., True, False)
1093
    # GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead, undef=undef, method='local')
1094
    # print(GPP[1120:1128])
1095
    # # [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
1096
    # #   8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
1097
    # print(Reco[1120:1128])
1098
    # # [1.68311981 1.81012431 1.9874173  2.17108871 2.38759152 2.64372415
1099
    # #  2.90076664 3.18592735]
1100
    # GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead, undef=undef, method='local')
1101
    # print(GPP[1120:1128])
1102
    # # [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
1103
    # #   8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
1104
    # GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead, undef=undef, method='global')
1105
    # print(GPP[1120:1128])
1106
    # # [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.33166157e+00
1107
    # #   8.18228013e+00  1.04092252e+01  8.19395317e+00  1.08427448e+01]
1108
    # GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead, undef=undef, method='Reichstein')
1109
    # print(GPP[1120:1128])
1110
    # # [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.406068706013192
1111
    # #   8.319421516040766 10.624254150217764  8.492456637225963 11.238197347837367]
1112
    # GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead, undef=undef, method='reichstein')
1113
    # print(GPP[1120:1128])
1114
    # # [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  4.40606871e+00
1115
    # #   8.31942152e+00  1.06242542e+01  8.49245664e+00  1.12381973e+01]
1116
    # GPP, Reco = nee2gpp(dfin, flag=flag, date=adate, colhead=colhead, undef=undef, method='day')
1117
    # print(GPP[1120:1128])
1118
    # # [-9.99900000e+03 -9.99900000e+03 -9.99900000e+03  2.78457540e+00
1119
    # #   6.63212545e+00  8.88902165e+00  6.74243873e+00  9.51364527e+00]
1120
    # print(Reco[1120:1128])
1121
    # # [0.28786696 0.34594516 0.43893276 0.5495954  0.70029545 0.90849165
1122
    # #  1.15074873 1.46137527]
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc