File: griddedData2GriddedDataStatistics.cpp

package info (click to toggle)
groops 0%2Bgit20250907%2Bds-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 11,140 kB
  • sloc: cpp: 135,607; fortran: 1,603; makefile: 20
file content (212 lines) | stat: -rw-r--r-- 9,955 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/***********************************************/
/**
* @file griddedData2GriddedDataStatistics.cpp
*
* @brief Assign gridded data to grid points.
*
* @author Torsten Mayer-Guerr
* @date 2018-07-03
*/
/***********************************************/

// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
This program assigns values \configFile{inputfileGriddedData}{griddedData} to the nearest points
of a new \configClass{grid}{gridType}. If some of the new points are not filled in with data
\config{emptyValue} is used instead. If multiple points of the input fall on the same node
the result can be selected with \config{statistics} (e.g. mean, root mean square, min, max, \ldots).
It also is possible to simply count the number of data points that were assigned to each point.

Be aware in case borders are given within \configClass{grid}{gridType}, the \configFile{outputfileGriddedData}{griddedData} will have points excluded before the assignement of old points to the new points.
The data from \configFile{inputfileGriddedData}{griddedData} will not be limited by the given borders! See \reference{GriddedDataConcatenate}{GriddedDataConcatenate} to limit the
\configFile{inputfileGriddedData}{griddedData} to given borders.

\fig{!hb}{0.8}{griddedData2GriddedDataStatistics}{fig:griddedData2GriddedDataStatistics}{Assignement of irregular distributed data to grid.}
)";

/***********************************************/

#include "programs/program.h"
#include "files/fileGriddedData.h"
#include "classes/grid/grid.h"
#include "misc/miscGriddedData.h"

/***** CLASS ***********************************/

/** @brief Assign gridded data to grid points.
* @ingroup programsGroup */
class GriddedData2GriddedDataStatistics
{
public:
  void run(Config &config, Parallel::CommunicatorPtr comm);
};

GROOPS_REGISTER_PROGRAM(GriddedData2GriddedDataStatistics, SINGLEPROCESS, "Assign gridded data to grid points", Grid)

/***********************************************/

void GriddedData2GriddedDataStatistics::run(Config &config, Parallel::CommunicatorPtr /*comm*/)
{
  try
  {
    enum Type {MEAN, WMEAN, RMS, WRMS, STD, WSTD, SUM, MIN, MAX, COUNT, FIRST, LAST};
    Type        type = MEAN;
    FileName    fileNameOutGrid, fileNameInGrid;
    GridPtr     gridPtr;
    Double      emptyValue = NAN_EXPR;
    Double      a, f;
    std::string choice;

    readConfig(config, "outputfileGriddedData", fileNameOutGrid, Config::MUSTSET,  "",    "");
    readConfig(config, "inputfileGriddedData",  fileNameInGrid,  Config::MUSTSET,  "",    "");
    readConfig(config, "grid",                  gridPtr,         Config::MUSTSET,  "",    "");
    if(readConfigChoice(config, "statistic", choice, Config::MUSTSET, "", "statistic used if multiple values fall on the same cell"))
    {
      if(readConfigChoiceElement(config, "mean",  choice, "mean"))                             type = MEAN;
      if(readConfigChoiceElement(config, "wmean", choice, "area weighted mean"))               type = WMEAN;
      if(readConfigChoiceElement(config, "rms",   choice, "root mean square"))                 type = RMS;
      if(readConfigChoiceElement(config, "wrms",  choice, "area weighted root mean square"))   type = WRMS;
      if(readConfigChoiceElement(config, "std",   choice, "standard deviation"))               type = STD;
      if(readConfigChoiceElement(config, "wstd",  choice, "area weighted standard deviation")) type = WSTD;
      if(readConfigChoiceElement(config, "sum",   choice, "sum"))                              type = SUM;
      if(readConfigChoiceElement(config, "min",   choice, "minimum value"))                    type = MIN;
      if(readConfigChoiceElement(config, "max",   choice, "maximum value"))                    type = MAX;
      if(readConfigChoiceElement(config, "count", choice, "number of values"))                 type = COUNT;
      if(readConfigChoiceElement(config, "first", choice, "first value"))                      type = FIRST;
      if(readConfigChoiceElement(config, "last",  choice, "last value"))                       type = LAST;
    }
    endChoice(config);
    readConfig(config, "emptyValue",        emptyValue, Config::DEFAULT, "nan()", "value for nodes without data");
    readConfig(config, "R",                 a,          Config::DEFAULT, STRING_DEFAULT_GRS80_a, "reference radius for ellipsoidal coordinates");
    readConfig(config, "inverseFlattening", f,          Config::DEFAULT, STRING_DEFAULT_GRS80_f, "reference flattening for ellipsoidal coordinates");
    if(isCreateSchema(config)) return;

    // read grid
    // ---------
    logStatus<<"read grid from file <"<<fileNameInGrid<<">"<<Log::endl;
    GriddedData grid;
    readFileGriddedData(fileNameInGrid, grid);
    MiscGriddedData::printStatistics(grid);
    if((grid.areas.size() == 0) && ((type == WMEAN) || (type == WRMS) || (type == WSTD)))
      throw(Exception("no area elements"));

    // Create grid
    // -----------
    logStatus<<"create new grid"<<Log::endl;
    GriddedData gridNew(Ellipsoid(a, f), gridPtr->points(), gridPtr->areas(), {});
    Double initialValue = 0;
    if(type == MIN) initialValue =  1e99;
    if(type == MAX) initialValue = -1e99;
    gridNew.values.resize(grid.values.size(), std::vector<Double>(gridNew.points.size(), initialValue));

    std::vector<Angle>  lambda, phi;
    std::vector<Double> radius;
    const Bool isRectangle = gridNew.isRectangle(lambda, phi, radius);

    // additional variables
    std::vector<std::vector<Double>> count, wmean, weight;
    count.resize(gridNew.values.size(), std::vector<Double>(gridNew.points.size(), 0));
    if((type == STD) || (type == WSTD))
      wmean.resize(gridNew.values.size(), std::vector<Double>(gridNew.points.size(), 0));
    if((type == WMEAN) || (type == WRMS) || (type == WSTD))
      weight.resize(gridNew.values.size(), std::vector<Double>(gridNew.points.size(), 0));

    // Assign grid
    // -----------
    logStatus<<"assign grid"<<Log::endl;
    Single::forEach(grid.points.size(), [&](UInt i)
    {
      // find nearest neighbor
      UInt idx = 0;
      if(isRectangle)
      {
        const Angle lon = grid.points.at(i).lambda();
        const Angle lat = grid.points.at(i).phi();
        const UInt  col = std::distance(lambda.begin(), std::min_element(lambda.begin(), lambda.end(),
                                        [&](Angle lon1, Angle lon2) {return std::fabs(std::remainder(lon-lon1, 2*PI)) < std::fabs(std::remainder(lon-lon2, 2*PI));}));
        const UInt  row = std::distance(phi.begin(), std::min_element(phi.begin(), phi.end(),
                                        [&](Angle lat1, Angle lat2) {return std::fabs(lat-lat1) < std::fabs(lat-lat2);}));
        idx = row * lambda.size() + col;
      }
      else
      {
        Double minDistance = std::numeric_limits<Double>::max();
        for(UInt k=0; k<gridNew.points.size(); k++)
        {
          const Double distance = (gridNew.points[k] - grid.points[i]).r();
          if(distance < minDistance)
          {
            minDistance = distance;
            idx = k;
          }
        }
      }
      Double w = 1;
      if((type == WMEAN) || (type == WRMS) || (type == WSTD))
        w = grid.areas.at(i);

      for(UInt k=0; k<grid.values.size(); k++)
      {
        const Double v = grid.values.at(k).at(i);
        if(std::isnan(v))
          continue;

        if(count.size())  count.at(k).at(idx)++;
        if(weight.size()) weight.at(k).at(idx) += w;
        if(wmean.size())  wmean.at(k).at(idx)  += w*v;

        switch(type)
        {
          case MEAN:
          case WMEAN:
          case SUM:   gridNew.values.at(k).at(idx) += w*v;   break;
          case RMS:
          case WRMS:
          case STD:
          case WSTD:  gridNew.values.at(k).at(idx) += w*v*v; break;
          case MIN:   gridNew.values.at(k).at(idx)  = std::min(v, gridNew.values.at(k).at(idx)); break;
          case MAX:   gridNew.values.at(k).at(idx)  = std::max(v, gridNew.values.at(k).at(idx)); break;
          case LAST:  gridNew.values.at(k).at(idx)  = v  ; break;
          case FIRST: if(count.at(k).at(idx) == 1) gridNew.values.at(k).at(idx) = v; break;
          default: ;
        }
      }
    });

    // post computation
    // ----------------
    for(UInt k=0; k<gridNew.values.size(); k++)
      for(UInt idx=0; idx<gridNew.values.at(k).size(); idx++)
        if(count.at(k).at(idx))
        {
          switch(type)
          {
            case MEAN:  gridNew.values.at(k).at(idx) /= count.at(k).at(idx);  break;
            case WMEAN: gridNew.values.at(k).at(idx) /= weight.at(k).at(idx); break;
            case RMS:   gridNew.values.at(k).at(idx) = std::sqrt(gridNew.values.at(k).at(idx)/count.at(k).at(idx));  break;
            case WRMS:  gridNew.values.at(k).at(idx) = std::sqrt(gridNew.values.at(k).at(idx)/weight.at(k).at(idx)); break;
            case STD:   wmean.at(k).at(idx) /= count.at(k).at(idx);
                        gridNew.values.at(k).at(idx) = std::sqrt(count.at(k).at(idx)/(count.at(k).at(idx)-1.)*(gridNew.values.at(k).at(idx)/count.at(k).at(idx)-std::pow(wmean.at(k).at(idx), 2))); break;
            case WSTD:  wmean.at(k).at(idx) /= weight.at(k).at(idx);
                        gridNew.values.at(k).at(idx) = std::sqrt(count.at(k).at(idx)/(count.at(k).at(idx)-1.)*(gridNew.values.at(k).at(idx)/weight.at(k).at(idx)-std::pow(wmean.at(k).at(idx), 2))); break;
            case COUNT: gridNew.values.at(k).at(idx) = count.at(k).at(idx); break;
            default: ;
          }
        }
        else
          gridNew.values.at(k).at(idx) = emptyValue;

    // Output
    // ------
    logStatus<<"write grid to file <"<<fileNameOutGrid<<">"<<Log::endl;
    writeFileGriddedData(fileNameOutGrid, gridNew);
    MiscGriddedData::printStatistics(gridNew);
  }
  catch(std::exception &e)
  {
    GROOPS_RETHROW(e)
  }
}

/***********************************************/