source: flex_extract.git/source/python/mods/disaggregation.py @ 708c667

ctbtodev
Last change on this file since 708c667 was 708c667, checked in by Anne Philipp <anne.philipp@…>, 6 years ago

generated first sphinx instance and adapted code doctsrings for automated api generation for disaggregation module as a first test

  • Property mode set to 100644
File size: 15.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#*******************************************************************************
4# @Author: Anne Philipp (University of Vienna)
5#
6# @Date: March 2018
7#
8# @Change History:
9#
10#    November 2015 - Leopold Haimberger (University of Vienna):
11#        - migration of the methods dapoly and darain from Fortran
12#          (flex_extract_v6 and earlier) to Python
13#
14#    April 2018 - Anne Philipp (University of Vienna):
15#        - applied PEP8 style guide
16#        - added structured documentation
17#        - outsourced the disaggregation functions dapoly and darain
18#          to a new module named disaggregation
19#
20# @License:
21#    (C) Copyright 2015-2018.
22#
23#    This software is licensed under the terms of the Apache Licence Version 2.0
24#    which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
25#
26# @Module Description:
27#    disaggregation of deaccumulated flux data from an ECMWF model FG field.
28#    Initially the flux data to be concerned are:
29#    - large-scale precipitation
30#    - convective precipitation
31#    - surface sensible heat flux
32#    - surface solar radiation
33#    - u stress
34#    - v stress
35#    Different versions of disaggregation is provided for rainfall
36#    data (darain, modified linear) and the surface fluxes and
37#    stress data (dapoly, cubic polynomial).
38#
39# @Module Content:
40#    - dapoly
41#    - darain
42#    - IA3
43#
44#*******************************************************************************
45
46# ------------------------------------------------------------------------------
47# MODULES
48# ------------------------------------------------------------------------------
49
50# ------------------------------------------------------------------------------
51# FUNCTIONS
52# ------------------------------------------------------------------------------
53def dapoly(alist):
54    """Cubic polynomial interpolation of deaccumulated fluxes.
55
56    Interpolation of deaccumulated fluxes of an ECMWF model FG field
57    using a cubic polynomial solution which conserves the integrals
58    of the fluxes within each timespan.
59    Disaggregation is done for 4 accumluated timespans which
60    generates a new, disaggregated value which is output at the
61    central point of the 4 accumulation timespans.
62    This new point is used for linear interpolation of the complete
63    timeseries afterwards.
64
65    Parameters
66    ----------
67    alist : :obj:`list` of :obj:`array` of :obj:`float`
68        List of 4 timespans as 2-dimensional, horizontal fields.
69        E.g. [[array_t1], [array_t2], [array_t3], [array_t4]]
70
71    Return
72    ------
73    nfield : :obj:`array` of :obj:`float`
74        Interpolated flux at central point of accumulation timespan.
75
76    Note
77    ----
78    March 2000    : P. JAMES
79        Original author
80
81    June 2003     : A. BECK
82        Adaptations
83
84    November 2015 : Leopold Haimberger (University of Vienna)
85        Migration from Fortran to Python
86
87    """
88
89    pya = (alist[3] - alist[0] + 3. * (alist[1] - alist[2])) / 6.
90    pyb = (alist[2] + alist[0]) / 2. - alist[1] - 9. * pya / 2.
91    pyc = alist[1] - alist[0] - 7. * pya / 2. - 2. * pyb
92    pyd = alist[0] - pya / 4. - pyb / 3. - pyc / 2.
93    nfield = 8. * pya + 4. * pyb + 2. * pyc + pyd
94
95    return nfield
96
97
98def darain(alist):
99    """Linear interpolation of deaccumulated fluxes.
100
101    Interpolation of deaccumulated fluxes of an ECMWF model FG rainfall
102    field using a modified linear solution which conserves the integrals
103    of the fluxes within each timespan.
104    Disaggregation is done for 4 accumluated timespans which generates
105    a new, disaggregated value which is output at the central point
106    of the 4 accumulation timespans. This new point is used for linear
107    interpolation of the complete timeseries afterwards.
108
109    Parameters
110    ----------
111    alist : :obj:`list` of :obj:`array` of :obj:`float`
112        List of 4 timespans as 2-dimensional, horizontal fields.
113        E.g. [[array_t1], [array_t2], [array_t3], [array_t4]]
114
115    Return
116    ------
117    nfield : :obj:`array` of :obj:`float`
118        Interpolated flux at central point of accumulation timespan.
119
120    Note
121    ----
122    March 2000    : P. JAMES
123        Original author
124
125    June 2003     : A. BECK
126        Adaptations
127
128    November 2015 : Leopold Haimberger (University of Vienna)
129        Migration from Fortran to Python
130    """
131
132    xa = alist[0]
133    xb = alist[1]
134    xc = alist[2]
135    xd = alist[3]
136    xa[xa < 0.] = 0.
137    xb[xb < 0.] = 0.
138    xc[xc < 0.] = 0.
139    xd[xd < 0.] = 0.
140
141    xac = 0.5 * xb
142    mask = xa + xc > 0.
143    xac[mask] = xb[mask] * xc[mask] / (xa[mask] + xc[mask])
144    xbd = 0.5 * xc
145    mask = xb + xd > 0.
146    xbd[mask] = xb[mask] * xc[mask] / (xb[mask] + xd[mask])
147    nfield = xac + xbd
148
149    return nfield
150
151def IA3(g):
152    """ Interpolation with a non-negative geometric mean based algorithm.
153
154    The original grid is reconstructed by adding two sampling points in each
155    data series interval. This subgrid is used to keep all information during
156    the interpolation within the associated interval. Additionally, an advanced
157    monotonicity filter is applied to improve the monotonicity properties of
158    the series.
159
160    Note
161    ----
162    Copyright 2017
163    Sabine Hittmeir, Anne Philipp, Petra Seibert
164
165    This work is licensed under the Creative Commons Attribution 4.0
166    International License. To view a copy of this license, visit
167    http://creativecommons.org/licenses/by/4.0/ or send a letter to
168    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
169
170    Parameters
171    ----------
172    g : :obj:`list` of :obj:`float`
173        Complete data series that will be interpolated having
174        the dimension of the original raw series.
175
176    Return
177    ------
178    f : :obj:`list` of :obj:`float`
179        The interpolated data series with additional subgrid points.
180        Its dimension is equal to the length of the input data series
181        times three.
182
183
184    References
185    ----------
186    For more information see article:
187    Hittmeir, S.; Philipp, A.; Seibert, P. (2017): A conservative
188    interpolation scheme for extensive quantities with application to the
189    Lagrangian particle dispersion model FLEXPART.,
190    Geoscientific Model Development
191    """
192
193    #######################  variable description #############################
194    #                                                                         #
195    # i      - index variable for looping over the data series                #
196    # g      - input data series                                              #
197    # f      - interpolated and filtered data series with additional          #
198    #          grid points                                                    #
199    # fi     - function value at position i, f_i                              #
200    # fi1    - first  sub-grid function value f_i^1                           #
201    # fi2    - second sub-grid function value f_i^2                           #
202    # fip1   - next function value at position i+1, f_(i+1)                   #
203    # dt     - time step                                                      #
204    # fmon   - monotonicity filter                                            #
205    #                                                                         #
206    ###########################################################################
207
208
209    import numpy as np
210
211    # time step
212    dt=1.0
213
214    ############### Non-negative Geometric Mean Based Algorithm ###############
215
216    # for the left boundary the following boundary condition is valid:
217    # the value at t=0 of the interpolation algorithm coincides with the
218    # first data value according to the persistence hypothesis
219    f=[g[0]]
220
221    # compute two first sub-grid intervals without monotonicity check
222    # go through the data series and extend each interval by two sub-grid
223    # points and interpolate the corresponding data values
224    # except for the last interval due to boundary conditions
225    for i in range(0,2):
226
227        # as a requirement:
228        # if there is a zero data value such that g[i]=0, then the whole
229        # interval in f has to be zero to such that f[i+1]=f[i+2]=f[i+3]=0
230        # according to Eq. (6)
231        if g[i]==0.:
232            f.extend([0.,0.,0.])
233
234        # otherwise the sub-grid values are calculated and added to the list
235        else:
236            # temporal save of last value in interpolated list
237            # since it is the left boundary and hence the new (fi) value
238            fi = f[-1]
239
240            # the value at the end of the interval (fip1) is prescribed by the
241            # geometric mean, restricted such that non-negativity is guaranteed
242            # according to Eq. (25)
243            fip1=min( 3.*g[i] , 3.*g[i+1] , np.sqrt(g[i+1]*g[i]) )
244
245            # the function value at the first sub-grid point (fi1) is determined
246            # according to the equal area condition with Eq. (19)
247            fi1=3./2.*g[i]-5./12.*fip1-1./12.*fi
248
249            # the function value at the second sub-grid point (fi2) is determined
250            # according Eq. (18)
251            fi2=fi1+1./3.*(fip1-fi)
252
253            # add next interval of interpolated (sub-)grid values
254            f.append(fi1)
255            f.append(fi2)
256            f.append(fip1)
257
258    # compute rest of the data series intervals
259    # go through the data series and extend each interval by two sub-grid
260    # points and interpolate the corresponding data values
261    # except for the last interval due to boundary conditions
262    for i in range(2,len(g)-1):
263
264        # as a requirement:
265        # if there is a zero data value such that g[i]=0, then the whole
266        # interval in f has to be zero to such that f[i+1]=f[i+2]=f[i+3]=0
267        # according to Eq. (6)
268        if g[i]==0.:
269            # apply monotonicity filter for interval before
270            # check if there is "M" or "W" shape
271            if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5])==-1 \
272               and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4])==-1 \
273               and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3])==-1:
274
275                # the monotonicity filter corrects the value at (fim1) by
276                # substituting (fim1) with (fmon), see Eq. (27), (28) and (29)
277                fmon = min(3.*g[i-2], \
278                           3.*g[i-1], \
279                           np.sqrt(max(0,(18./13.*g[i-2] - 5./13.*f[-7]) *
280                                         (18./13.*g[i-1] - 5./13.*f[-1]))))
281
282                # recomputation of the sub-grid interval values while the
283                # interval boundaries (fi) and (fip2) remains unchanged
284                # see Eq. (18) and (19)
285                f[-4]=fmon
286                f[-6]=3./2.*g[i-2]-5./12.*fmon-1./12.*f[-7]
287                f[-5]=f[-6]+(fmon-f[-7])/3.
288                f[-3]=3./2.*g[i-1]-5./12.*f[-1]-1./12.*fmon
289                f[-2]=f[-3]+(f[-1]-fmon)/3.
290
291            f.extend([0.,0.,0.])
292
293        # otherwise the sub-grid values are calculated and added to the list
294        else:
295            # temporal save of last value in interpolated list
296            # since it is the left boundary and hence the new (fi) value
297            fi = f[-1]
298
299            # the value at the end of the interval (fip1) is prescribed by the
300            # geometric mean, restricted such that non-negativity is guaranteed
301            # according to Eq. (25)
302            fip1=min( 3.*g[i] , 3.*g[i+1] , np.sqrt(g[i+1]*g[i]) )
303
304            # the function value at the first sub-grid point (fi1) is determined
305            # according to the equal area condition with Eq. (19)
306            fi1=3./2.*g[i]-5./12.*fip1-1./12.*fi
307
308            # the function value at the second sub-grid point (fi2) is determined
309            # according Eq. (18)
310            fi2=fi1+1./3.*(fip1-fi)
311
312            # apply monotonicity filter for interval before
313            # check if there is "M" or "W" shape
314            if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5])==-1 \
315               and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4])==-1 \
316               and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3])==-1:
317
318                # the monotonicity filter corrects the value at (fim1) by
319                # substituting (fim1) with fmon, see Eq. (27), (28) and (29)
320                fmon = min(3.*g[i-2], \
321                           3.*g[i-1], \
322                           np.sqrt(max(0,(18./13.*g[i-2] - 5./13.*f[-7]) *
323                                         (18./13.*g[i-1] - 5./13.*f[-1]))))
324
325                # recomputation of the sub-grid interval values while the
326                # interval boundaries (fi) and (fip2) remains unchanged
327                # see Eq. (18) and (19)
328                f[-4]=fmon
329                f[-6]=3./2.*g[i-2]-5./12.*fmon-1./12.*f[-7]
330                f[-5]=f[-6]+(fmon-f[-7])/3.
331                f[-3]=3./2.*g[i-1]-5./12.*f[-1]-1./12.*fmon
332                f[-2]=f[-3]+(f[-1]-fmon)/3.
333
334            # add next interval of interpolated (sub-)grid values
335            f.append(fi1)
336            f.append(fi2)
337            f.append(fip1)
338
339    # separate treatment of the final interval
340
341    # as a requirement:
342    # if there is a zero data value such that g[i]=0, then the whole
343    # interval in f has to be zero to such that f[i+1]=f[i+2]=f[i+3]=0
344    # according to Eq. (6)
345    if g[-1]==0.:
346        # apply monotonicity filter for interval before
347        # check if there is "M" or "W" shape
348        if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5])==-1 \
349           and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4])==-1 \
350           and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3])==-1:
351
352            # the monotonicity filter corrects the value at (fim1) by
353            # substituting (fim1) with (fmon), see Eq. (27), (28) and (29)
354            fmon = min(3.*g[-3], \
355                       3.*g[-2], \
356                       np.sqrt(max(0,(18./13.*g[-3] - 5./13.*f[-7]) *
357                                     (18./13.*g[-2] - 5./13.*f[-1]))))
358
359            # recomputation of the sub-grid interval values while the
360            # interval boundaries (fi) and (fip2) remains unchanged
361            # see Eq. (18) and (19)
362            f[-4]=fmon
363            f[-6]=3./2.*g[-3]-5./12.*fmon-1./12.*f[-7]
364            f[-5]=f[-6]+(fmon-f[-7])/3.
365            f[-3]=3./2.*g[-2]-5./12.*f[-1]-1./12.*fmon
366            f[-2]=f[-3]+(f[-1]-fmon)/3.
367
368        f.extend([0.,0.,0.])
369
370    # otherwise the sub-grid values are calculated and added to the list
371    # using the persistence hypothesis as boundary condition
372    else:
373        # temporal save of last value in interpolated list
374        # since it is the left boundary and hence the new (fi) value
375        fi = f[-1]
376        # since last interval in series, last value is also fip1
377        fip1 = g[-1]
378        # the function value at the first sub-grid point (fi1) is determined
379        # according to the equal area condition with Eq. (19)
380        fi1 = 3./2.*g[-1]-5./12.*fip1-1./12.*fi
381        # the function value at the second sub-grid point (fi2) is determined
382        # according Eq. (18)
383        fi2 = fi1+dt/3.*(fip1-fi)
384
385        # apply monotonicity filter for interval before
386        # check if there is "M" or "W" shape
387        if     np.sign(f[-5]-f[-6]) * np.sign(f[-4]-f[-5])==-1 \
388           and np.sign(f[-4]-f[-5]) * np.sign(f[-3]-f[-4])==-1 \
389           and np.sign(f[-3]-f[-4]) * np.sign(f[-2]-f[-3])==-1:
390
391            # the monotonicity filter corrects the value at (fim1) by
392            # substituting (fim1) with (fmon), see Eq. (27), (28) and (29)
393            fmon = min(3.*g[-3], \
394                       3.*g[-2], \
395                       np.sqrt(max(0,(18./13.*g[-3] - 5./13.*f[-7]) *
396                                     (18./13.*g[-2] - 5./13.*f[-1]))))
397
398            # recomputation of the sub-grid interval values while the
399            # interval boundaries (fi) and (fip2) remains unchanged
400            # see Eq. (18) and (19)
401            f[-4]=fmon
402            f[-6]=3./2.*g[-3]-5./12.*fmon-1./12.*f[-7]
403            f[-5]=f[-6]+(fmon-f[-7])/3.
404            f[-3]=3./2.*g[-2]-5./12.*f[-1]-1./12.*fmon
405            f[-2]=f[-3]+(f[-1]-fmon)/3.
406
407        # add next interval of interpolated (sub-)grid values
408        f.append(fi1)
409        f.append(fi2)
410        f.append(fip1)
411
412    return f
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG