source: flex_extract.git/Source/Python/Mods/disaggregation.py @ 0f89116

ctbtodev
Last change on this file since 0f89116 was 0f89116, checked in by Anne Philipp <anne.philipp@…>, 4 years ago

diverse changes due to PEP8 style guide and eliminating grib2flexpart; removed unused parameter

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