BALTRAD PPC
Baltrad Polarimetric Processing Chain (baltrad-ppc)
Date
29 Nov 2019
Author
Anders Henja
Copyright
© 2019 by the Swedish Meteorological and Hydrological Institute (SMHI), Norrköping, Sweden
Legals
baltrad-ppc is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
baltrad-ppc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with baltrad-ppc. If not, see http://www.gnu.org/licenses/. By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions:

Introduction

The baltrad polarimetric processing chain quality algorithm is based on the work made by Gianfranco Vulpiani at SMHI in 2017. The original code was developed in Matlab and has been ported to work as a part of RAVE.

This documentation focus on the software and usage of the results so you will have to read the report for a more indepth knowledge about the original implementation and the physics behind it all.

Overview

The processing chain performs several different steps in order to produce an attenuation corrected result and is best described by the following picture.

Figure 1: The processing chain

The steps performed in the processing chain are:

  1. Clutter filtering
    • Is based on Fuzzy Logic using the reflectivity factor (Z), doppler velocity (V), the correlation coefficient ρHV, the texture of ΦDP and a statistical clutter map (cmap). The statistical clutter map has to be calculated externally and given to the process function in the _pdpprocessor module described later in this documentation.
  2. Residual clutter filtering
    • Residual, often isolated, clutter pixels can be removed by applying a median filter.
  3. Processing of differential phase shift and an estimation of the specific differential phase
    • The measured differential phase is affected by system noise, backscatter differential phase, system offset and, potentially, by aliasing-related wrapping
  4. Filtering based on melting layer height
    • When generating the attenuation mask, the bin heights must be below the melting layer height before taking RHOHV, KDP and TH into account.
  5. Attenuation correction
    • based on two different methods, linear method and ZPHI.

When the above steps have been performed a number of different products will be added to the original radar data and then returned to the user of the process.

Python functions

Some functions have been added to the public python APIs so that it's possible to run parts of the chain if needed. For example in tests or when evaluating the functions. There are 3 different modules that can be loaded.

  • _pdpprocessor
    • Actual processing
  • _ppcoptions
    • Function for loading xml configuration file
  • _ppcradaroptions
    • Object containing the configuration for one radar

The documentation for each module is obtained with the following command

>>> import module
>>> print(module.__doc__)

The documentation for respective module is displayed below..

Python PDP processor

>>> import _pdpprocessor
>>> print(_pdpprocessor.__doc__)
This is the polarimetric processing chain module itself.

The easiest way to get started is to use the process function which combines all functions into a chain but
first you will have to create an instance of the _pdpprocessor which is done by:
>>> import _pdpprocessor
>>> processor = _pdpprocessor.new()

After that you might have to modify the options that are used by the processor and these can be
accessed with:
>>> processor.options....

Next is to process the data in some way. The fastest way to get started is to use the process function
which only takes a rave polar scan as in argument.
>>> from _ppcradaroptions import P_TH_CORR,P_ZPHI_CORR,P_ATT_TH_CORR,P_ATT_DBZH_CORR,P_KDP_CORR
>>> import _raveio
>>> scan = _raveio.open(".../some.scan.h5").object
>>> processor.requestedFields = P_TH_CORR | P_ZPHI_CORR | P_ATT_TH_CORR | P_ATT_DBZH_CORR  | P_KDP_CORR
>>> processor.meltingLayerBottomHeight = 1.0 # In KM
>>> newscan = processor.process(scan)

>>> param = newscan.getParameter("KDP_CORR")

The functions are:

scan := process(scan, clutterMap)
 Performs the polarimetric processing chain.
 - indata
   scan       - a polar scan
   clutterMap - the statistical clutter map. - returns a scan of type PolarScanParam

texture := texture(field)
 Creates a texture from the provided data field.
 - indata:
   field (RaveData2DCore)      - An arbitary field
 - returns a texture of type RaveData2DCore

degree := trap(field, a, b, s, t)
 Trapezoidal function where the field can be any variable and a,b,s,t
 identifies the trapezoid coordinates along the x-axis x1 = a-s, x2=a, x3=b, x4=b+t.
 - indata:
   field (RaveData2DCore)        - An arbitary field
   a (float)                     - see above explanation of constant
   b (float)                     - see above explanation of constant
   s (float)                     - see above explanation of constant
   t (float)                     - see above explanation of constant
 - returns a RaveData2DCore field representing the membership degree

(Z, Quality, ClutterMask) := clutterCorrection(Z, VRADH, texturePHIDP, RHOHV, textureZ, ClutterMap, nodataZ, nodataVRADH, qualityThreshold)
 Clutter correction function.
 - indata:
   Z  (RaveData2DCore)           - Reflectivity
   VRADH (RaveData2DCore)        - Doppler velocity
   TexturePHIDP (RaveData2DCore) - Texture of PHIDP
   RHOHV  (RaveData2DCore)       - Correlation coefficient
   TextureZ (RaveData2DCore)     - Texture of Z
   ClutterMap (RaveData2DCore)   - Statistical clutter map
   nodataZ (float)               - Nodata for Z
   nodataVRADH (float)           - Nodata for VRADH
   qualityThreshold (float)      - Threshold for the quality as generated.
 - returns a tuple (Z, Quality, ClutterMask) of type RaveData2DCore

degree := clutterID(Z, VRADH, texturePHIDP, RHOHV, TextureZ, ClutterMap, nodataZ, nodataVRADH)
 Clutter identification function. That calculates a field containing a degree of membership of a target weather class.
 - indata:
   Z  (RaveData2DCore)         - Reflectivity
   VRADH (RaveData2DCore)      - Doppler velocity
   texturePHIDP (PyRaveData2D) - The PHIDP texture   RHOHV  (RaveData2DCore)     - Correlation coefficient
   TextureZ (RaveData2DCore)   - Texture of Z
   ClutterMap (RaveData2DCore) - Statistical clutter map
   nodataZ (float)             - Nodata for Z
   nodataVRADH (float)         - Nodata for VRADH
 - returns a RaveData2DCore representing probability (degree) of weather class

mask := medfilt(Z, threshZ, nodataZ, (filtXsize, filtYsize))
 Median filter. threshZ, nodataZ, filtXsize and filtYsize are optional. filtXsize & filtYsize are specified as a tuple.
 - indata:
   Z  (RaveData2DCore)         - Reflectivity
   threshZ (float)             - Z threshold
   nodataZ (float)             - Z nodata value
   (filtXsize, filtYsize) (2*digit), - window size
 - returns a RaveData2DCore field with the mask calculated by the filter

mask := residualClutterFilter(Z, threshZ, threshTexture, (filtXsize, filtYsize))
 Residual clutter filter. Z is a RaveData2DCore field. threshZ, threshTexture are doubles and filtXsize, filtYsize are digits. All attributes are optional except Z.
 - indata: 
   Z  (RaveData2DCore)        - Reflectivity
   threshZ (float)            - Z threshold
   threshTexture (float)      - Texture threshold
   (filtXsize, filtYsize) (2*digit), - window size
 - returns a RaveData2DCore field with the mask calculated by the filter

(PHIDP, KDP) := pdpProcessing(PDP, res, window, nrIter)
 this function applies the Iterative Finite Difference scheme for the filtering of PHIDP and estimation of KDP
 - indata: 
   PDP (RaveData2DCore)      - Input differential phase to be processed
   res (float)               - Radial resolution in km
   window (number)           - Half of moving window size expressed in bins applied to the azimuthal rays.
   nrIter (number)           - Number of iteration the procedure has to be applied to keep the excpected std dev of KDP under control
 - returns a tuple (PHIDP, KDP) of type RaveData2DCore

(PHIDP, KDP) := pdpScript(PDP, dr, rWin1, &rWin2, nrIter)
 this function applies the Iterative Finite Difference scheme for the filtering of PHIDP and estimation of KDP
 - indata: 
   PDP (RaveData2DCore)      - Input differential phase to be processed
   dr  (float)               - Radial resolution in km
   rWin1 (float)             - Half of moving window size expressed in km applied to the az-rays
                               characterized by low to moderate total phase shift
   rWin2 (float)             - Half of moving window size expressed in km applied to the az-rays
                               characterized by moderate to high total phase shift
   nrIter (number)           - Number of iteration the procedure has to be applied to keep the excpected
                               std dev of KDP under control
 - returns a tuple (PHIDP, KDP) of type RaveData2DCore

(Z, ZDR, PIA, DBZH) := attenuation(Z, ZDR, DBZH, PDP, mask, gamma_h, alpha, zundetect, dbzhundetect)
 This function applies the linear attenuation.
 - indata: 
   Z (RaveData2DCore)        - Reflectivity
   ZDR (RaveData2DCore)      - Differential reflectivity
   DBZH (RaveData2DCore)     - Reflectivity
   PDP (RaveData2DCore)      - Filtered phase shift
   mask (RaveData2DCore)     - Specifies the first and last range gates in rain to be considered for attenuation
                               correction see source code for process for more information of type RaveData2DCore
   gamma_h (float)           - Coefficient relating specific attenuation and specific differential phase.
   alpha (float)             - Is the quota AH / ADP where AH is the specific attenuation and ADP is the specific differential attenuation.
   zundectect (float)        - Z fields undetect value.
   dbzhundetect (float)      - DBZH fields undetect value.
 - returns a tuple of attenuated fields (Z, ZDR, PIA, DBZH) of type RaveData2DCore

(Zphi, AH) := zphi(Z, PDP, mask, dr, BB, gamma_h)
 This function applies the attenuation correction based on application of the analytical solution of differential equation.
 - indata: 
   Z  (RaveData2DCore)       - Reflectivity
   PDP (RaveData2DCore)      - Filtered phase shift
   mask (RaveData2DCore)     - Specifies the first and last range gates in rain to be considered for attenuation
                               correction see source code for process for more information
   dr (double)               - Range resolution expressed in km.
   BB (double)               - The exponent of the power law relating specific attenuation and Z.
 - returns a tuple of attenuated fields (Zphi, AH) of type RaveData2DCore where Zphi is attenuation
   corrected reflectivity and AH is estimated specific attenuation

Python PPC options class

>>> import _ppcoptions
>>> print(_ppcoptions.__doc__)
This is the ppc options loader. It is used to load ppc radar option configuration files written in xml-format.
There are only a few member functions available here  (getRadarOptions, exists and options) and currently there is no support for saving the configuration.

 The available functions are: 
   - radaroptions := getRadarOptions(string)
     returns a PpcRadarOptionsCore instance if found
   - boolean := exists(string)
     returns if the specified option name exists or not
   - dictionary := options()
     returns a dictionary with all available option settings

>>> import _ppcoptions
>>> options = _ppcoptions.load(".../ppc_options.xml")
>>> optionNames = options.options().keys()
>>> print(optionNames)
dict_keys(['default'])

Assuming that we are loading a polar scan from sehud and want to use the options configured for that site
one could implement the usage as follows:

>>> sehudopt = options.getRadarOptions("default")
>>> if options.exists("sehud"):
>>>   sehudopt = options.getRadarOptions("sehud")
....

Python PPC radar options class

>>> import _ppcradaroptions
>>> print(_ppcradaroptions.__doc__)
Keeps track of options used for the different radar sources when it comes to the polarimetric process chain
processing. In this documentation section all the available options are listed for the different radars and a description of the values they assume.

The following settings are available:
parametersUZ                 - parameters for TH, 5 values separated by ',' Weight,X2,X3,Delta1,Delta2
                               and the derived values will be X1=X2-Delta1, X3=X4-Delta2
parametersVEL                - parameters for VRADH, 5 values separated by ',' Weight,X2,X3,Delta1,Delta2
                               and the derived values will be X1=X2-Delta1, X3=X4-Delta2
parametersTextPHIDP          - parameters for the PHIDP texture, 5 values separated by ',' Weight,X2,X3,Delta1,Delta2
                               and the derived values will be X1=X2-Delta1, X3=X4-Delta2
parametersRHV                - parameters for RHOHV, 5 values separated by ',' Weight,X2,X3,Delta1,Delta2
                               and the derived values will be X1=X2-Delta1, X3=X4-Delta2
parametersTextUZ             - parameters for the TH texture, 5 values separated by ',' Weight,X2,X3,Delta1,Delta2
                               and the derived values will be X1=X2-Delta1, X3=X4-Delta2
parametersClutterMap         - parameters for the clutter map, 5 values separated by ',' Weight,X2,X3,Delta1,Delta2
                               and the derived values will be X1=X2-Delta1, X3=X4-Delta2
nodata                       - nodata to be used in most products
minDBZ                       - min DBZ threshold in the clutter correction
qualityThreshold             - quality threshold in the clutter correction
preprocessZThreshold         - preprocessing Z threshold before starting actual processing
residualMinZClutterThreshold - min z clutter threshold during residual clutter filtering
residualThresholdZ           - min Z threshold in the residual clutter filtering
residualThresholdTexture     - texture threshold in the residual clutter filtering
residualClutterNodata        - the nodata value to be used when creating the residual clutter image used for creating the mask
residualClutterMaskNodata    - Nodata value for the residual clutter mask
residualClutterTextureFilteringMaxZ - Max Z value when creating the residual clutter mask, anything higher will be set to min value
residualFilterBinSize        - number of bins used in the window when creating the residual mask
residualFilterRaySize        - number of rays used in the window when creating the residual mask
minZMedfilterThreshold       - min z threshold used in the median filter that is used by the residual clutter filter
processingTextureThreshold   - threshold for the texture created in the pdp processing
minWindow                    - min window size
pdpRWin1                     - pdp ray window 1
pdpRWin2                     - pdp ray window 2
pdpNrIterations              - number of iterations in pdp processing
kdpUp                        - Maximum allowed value of Kdp
kdpDown                      - Minimum allowed value of kdp
kdpStdThreshold              - Kdp STD threshold
BB                           - BB value used in the zphi part of the pdp processing
thresholdPhidp               - threshold for PHIDP in the pdp processing
minAttenuationMaskRHOHV      - min RHOHV value for marking value as 1 in the attenuation mask
minAttenuationMaskKDP        - min KDP value for marking value as 1 in the attenuation mask
minAttenuationMaskTH         - min TH value for marking value as 1 in the attenuation mask
attenuationGammaH            - gamma h value used in the attenuation
attenuationAlpha             - alpha value used in the attenuation
attenuationPIAminZ           - min PIA Z value in attenuation process
meltingLayerBottomHeight     - The melting layer bottom height
meltingLayerHourThreshold    - The number of hours before default height should be used.
invertPHIDP                  - if the PHIDP should be inverted (multiplied with -1) or not. Typically this can be needed if the RSP produces inverted values.
requestedFields              - '|' separated list of flags that defines what products should be added to the finished result.
                               If the flag begins with a P, it means that the result is added as a parameter and the name of
                               the parameter will be without the P_. If on the other hand the flag begins with a Q_ it means
                               that the result is added as a quality field and in those cases the how/task name will be
                               se.baltrad.ppc.<mask name without Q_ in lowercase>
                               Available flags are:
                                + P_TH_CORR
                                + P_ATT_TH_CORR
                                + P_DBZH_CORR
                                + P_ATT_DBZH_CORR
                                + P_KDP_CORR
                                + P_RHOHV_CORR
                                + P_PHIDP_CORR
                                + P_ZDR_CORR
                                + P_ZPHI_CORR
                                + Q_RESIDUAL_CLUTTER_MASK
                                + Q_ATTENUATION_MASK
                                + Q_ATTENUATION

Rave PGF plugin

It is probably the rave plugin that will be used the most. The actual registration of the plugin is performed in the same way as other plugins are registered in rave. Either the file .../rave_pgf_quality_registry.xml file is modified manually or else a small python script containing the following is written.

from rave_pgf_quality_registry_mgr import rave_pgf_quality_registry_mgr
a = rave_pgf_quality_registry_mgr("/etc/baltrad/rave/etc/rave_pgf_quality_registry.xml")
a.remove_plugin("ppc")
a.add_plugin("ppc", "ppc_quality_plugin", "ppc_quality_plugin")
a.save("/etc/baltrad/rave/etc/rave_pgf_quality_registry.xml")
Figure 2: Example registration

ppc_options.xml

Next step is to configure the various settings for respective radar. This file /etc/baltrad/baltrad-ppc/config/ppc_options.xml is used to configure the various options for the different radars. In ppc_options.xml you will find a brief explanation of the different parameters that are configurable and also how you can reuse configuration for different radar sources.

The information described in the xml file will be roughly the same as explained when printing the doc for _ppcradaroptions. Some things to note though is that it is possible to build hierarchies of the properties. This means that you can differentiate configurations depending on the situation. To get this configuration you will be able to override configuration settings from a parent. This is achieved by inheriting the options by defining a default attribute in the radaroptions-tag.

As an example, let us say that seang & sekkr should use default values except for attenuationAlpha. selul should use the same attenuationAlpha as sekkr but it also needs to different attenuationGammaH set to 0.09. First you define an option group with the name default (<radaroptions name="default">). After that you define a subgroup called attenuationAlpha03 inheriting default setting from the previously created group. sekkr will just inherit attenuationAlpha03s options and selul will inherit sekkr with the modified attenuationGammaH. Obviously, it's possible to setup these options in several different ways all depending on needs.

<?xml version='1.0' encoding='UTF-8'?>
<ppc-options>
  <radaroptions name="default">
    <!-- Weight | X2   |  X3  | Delta1  | Delta2   X1=X2-Delta1, X3=X4-Delta2-->
    <parametersUZ                         value="0.00,30.00,90.00,62.00,20.00" />
    <parametersVEL                        value="0.30,-0.90,0.90,0.15,0.15" />
    <parametersTextPHIDP                  value="0.80,15.00,40.00,5.00,40.00" />
    <parametersRHV                        value="0.20,0.00,0.60,0.00,0.10" />
    <parametersTextUZ                     value="0.30,20.00,60.00,5.00,10.00" />
    <parametersClutterMap                 value="0.90,5.00,70.00,20.00,60.00" />
    
    <minWindow                            value="11" />
    <nodata                               value="-999.0" />
    <minDBZ                               value="-32.0" />
    <qualityThreshold                     value="0.75" />
    <preprocessZThreshold                 value="-20.0" />
    
    <residualMinZClutterThreshold         value="-31.5" />
    <residualThresholdZ                   value="-20.0" />
    <residualThresholdTexture             value="20.0" />
    <residualClutterNodata                value="-999.0" />
    <residualClutterMaskNodata            value="-1.0" />
    <residualClutterTextureFilteringMaxZ  value="70.0" />
    <residualClutterNodata                value="-31.5" />
    <residualFilterBinSize                value="1" />
    <residualFilterRaySize                value="1" />
    
    <minZMedfilterThreshold               value="-30.0" />
    <processingTextureThreshold           value="10.0" />
    <pdpRWin1                             value="3.5" />
    <pdpRWin2                             value="1.5" />
    <pdpNrIterations                      value="2" />
    <kdpUp                                value="20.0" />
    <kdpDown                              value="-2.0" />
    <kdpStdThreshold                      value="5.0" />
    <BB                                   value="0.7987" />
    <thresholdPhidp                       value="40.0" />

    <minAttenuationMaskRHOHV              value="0.8" />
    <minAttenuationMaskKDP                value="0.001" />
    <minAttenuationMaskTH                 value="-20.0" />
    <attenuationGammaH                    value="0.08" />
    <attenuationAlpha                     value="0.2" />
    <attenuationPIAminZ                   value="-30" />
    
    <meltingLayerBottomHeight             value="2.463" />
    <meltingLayerHourThreshold            value="6" />
    <invertPHIDP                          value="0" />

    <requestedFields                      value="P_DBZH_CORR|P_ATT_DBZH_CORR|P_PHIDP_CORR|P_QUALITY_RESIDUAL_CLUTTER_MASK" />
  </radaroptions>
  
  <radaroptions name="attenuationAlpha03" default="default" > <!-- Uses default above but with some modified values-->
    <attenuationAlpha                     value="0.3" />
  </radaroptions>

  <radaroptions name="seang" default="attenuationAlpha03" > <!-- Attenuation alpha 0.3 --> 
  </radaroptions>

  <radaroptions name="sekkr" default="attenuationAlpha03" >  <!-- Attenuation alpha 0.3 -->
  </radaroptions>

  <radaroptions name="selul" default="sekkr" >  <!-- Attenuation alpha 0.3 & attenuationGammaH 0.09 -->
    <attenuationGammaH                    value="0.09" /> 
  </radaroptions>
 </ppc-options>