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

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

ECMWF server output directory renamed; reformulate dapoly formula; changed template filenames; make use of config jobscript names

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