1 /*
2   This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
3 
4   Author: Uwe Schulzweida
5 
6 */
7 
8 /*
9    This module contains the following operators:
10 
11         Timstat2        timcor      correlates two data files on the same grid
12 */
13 
14 #include <cdi.h>
15 
16 #include "process_int.h"
17 #include "cdo_vlist.h"
18 #include "cimdOmp.h"
19 
20 // correlation in time
21 template <typename T>
22 static void
correlation_init(bool hasMissValues,size_t gridsize,const Varray<T> & x,const Varray<T> & y,T xmv,T ymv,Varray<size_t> & nofvals,Varray<double> & work0,Varray<double> & work1,Varray<double> & work2,Varray<double> & work3,Varray<double> & work4)23 correlation_init(bool hasMissValues, size_t gridsize, const Varray<T> &x, const Varray<T> &y, T xmv, T ymv, Varray<size_t> &nofvals,
24                  Varray<double> &work0, Varray<double> &work1, Varray<double> &work2, Varray<double> &work3, Varray<double> &work4)
25 {
26   if (hasMissValues)
27     {
28 #ifdef _OPENMP
29 #pragma omp parallel for default(shared)
30 #endif
31       for (size_t i = 0; i < gridsize; ++i)
32         {
33           if ((!DBL_IS_EQUAL(x[i], xmv)) && (!DBL_IS_EQUAL(y[i], ymv)))
34             {
35               const double xx = x[i];
36               const double yy = y[i];
37               work0[i] += xx;
38               work1[i] += yy;
39               work2[i] += xx * xx;
40               work3[i] += yy * yy;
41               work4[i] += xx * yy;
42               nofvals[i]++;
43             }
44         }
45     }
46   else
47     {
48 #if _OPENMP
49 #pragma omp parallel for default(shared)
50 #endif
51       for (size_t i = 0; i < gridsize; ++i)
52         {
53           const double xx = x[i];
54           const double yy = y[i];
55           work0[i] += xx;
56           work1[i] += yy;
57           work2[i] += xx * xx;
58           work3[i] += yy * yy;
59           work4[i] += xx * yy;
60           nofvals[i]++;
61         }
62     }
63 }
64 
65 static void
correlation_init(size_t gridsize,const Field & field1,const Field & field2,Varray<size_t> & nofvals,Varray<double> & work0,Varray<double> & work1,Varray<double> & work2,Varray<double> & work3,Varray<double> & work4)66 correlation_init(size_t gridsize, const Field &field1, const Field &field2, Varray<size_t> &nofvals, Varray<double> &work0,
67                  Varray<double> &work1, Varray<double> &work2, Varray<double> &work3, Varray<double> &work4)
68 {
69   const bool hasMissValues = (field1.nmiss > 0 || field2.nmiss > 0);
70 
71   if (field1.memType == MemType::Float)
72     {
73       correlation_init(hasMissValues, gridsize, field1.vec_f, field2.vec_f, (float) field1.missval, (float) field1.missval, nofvals,
74                        work0, work1, work2, work3, work4);
75     }
76   else
77     {
78       correlation_init(hasMissValues, gridsize, field1.vec_d, field2.vec_d, field1.missval, field1.missval, nofvals, work0, work1,
79                        work2, work3, work4);
80     }
81 }
82 
83 static size_t
correlation(size_t gridsize,double missval,const Varray<size_t> & nofvals,Varray<double> & work0,const Varray<double> & work1,const Varray<double> & work2,const Varray<double> & work3,const Varray<double> & work4)84 correlation(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varray<double> &work0, const Varray<double> &work1,
85             const Varray<double> &work2, const Varray<double> &work3, const Varray<double> &work4)
86 {
87   size_t nmiss = 0;
88 
89   for (size_t i = 0; i < gridsize; ++i)
90     {
91       const auto missval1 = missval;
92       const auto missval2 = missval;
93       double cor;
94       const auto nvals = nofvals[i];
95       if (nvals > 0)
96         {
97           const auto temp0 = MULMN(work0[i], work1[i]);
98           const auto temp1 = SUBMN(work4[i], DIVMN(temp0, nvals));
99           const auto temp2 = MULMN(work0[i], work0[i]);
100           const auto temp3 = MULMN(work1[i], work1[i]);
101           const auto temp4 = SUBMN(work2[i], DIVMN(temp2, nvals));
102           const auto temp5 = SUBMN(work3[i], DIVMN(temp3, nvals));
103           const auto temp6 = MULMN(temp4, temp5);
104 
105           cor = DIVMN(temp1, SQRTMN(temp6));
106           cor = std::min(std::max(cor, -1.0), 1.0);
107 
108           if (DBL_IS_EQUAL(cor, missval)) nmiss++;
109         }
110       else
111         {
112           nmiss++;
113           cor = missval;
114         }
115 
116       work0[i] = cor;
117     }
118 
119   return nmiss;
120 }
121 
122 // covariance in time
123 template <typename T>
124 static void
covariance_init(bool hasMissValues,size_t gridsize,const Varray<T> & x,const Varray<T> & y,T xmv,T ymv,Varray<size_t> & nofvals,Varray<double> & work0,Varray<double> & work1,Varray<double> & work2)125 covariance_init(bool hasMissValues, size_t gridsize, const Varray<T> &x, const Varray<T> &y, T xmv, T ymv, Varray<size_t> &nofvals,
126                 Varray<double> &work0, Varray<double> &work1, Varray<double> &work2)
127 {
128   if (hasMissValues)
129     {
130 #ifdef _OPENMP
131 #pragma omp parallel for default(shared)
132 #endif
133       for (size_t i = 0; i < gridsize; ++i)
134         {
135           if ((!DBL_IS_EQUAL(x[i], xmv)) && (!DBL_IS_EQUAL(y[i], ymv)))
136             {
137               const double xx = x[i];
138               const double yy = y[i];
139               work0[i] += xx;
140               work1[i] += yy;
141               work2[i] += xx * yy;
142               nofvals[i]++;
143             }
144         }
145     }
146   else
147     {
148 #if _OPENMP
149 #pragma omp parallel for default(shared)
150 #endif
151       for (size_t i = 0; i < gridsize; ++i)
152         {
153           const double xx = x[i];
154           const double yy = y[i];
155           work0[i] += xx;
156           work1[i] += yy;
157           work2[i] += xx * yy;
158           nofvals[i]++;
159         }
160     }
161 }
162 
163 static void
covariance_init(size_t gridsize,const Field & field1,const Field & field2,Varray<size_t> & nofvals,Varray<double> & work0,Varray<double> & work1,Varray<double> & work2)164 covariance_init(size_t gridsize, const Field &field1, const Field &field2, Varray<size_t> &nofvals, Varray<double> &work0,
165                 Varray<double> &work1, Varray<double> &work2)
166 {
167   const bool hasMissValues = (field1.nmiss > 0 || field2.nmiss > 0);
168 
169   if (field1.memType == MemType::Float)
170     {
171       covariance_init(hasMissValues, gridsize, field1.vec_f, field2.vec_f, (float) field1.missval, (float) field1.missval, nofvals,
172                       work0, work1, work2);
173     }
174   else
175     {
176       covariance_init(hasMissValues, gridsize, field1.vec_d, field2.vec_d, field1.missval, field1.missval, nofvals, work0, work1,
177                       work2);
178     }
179 }
180 
181 static size_t
covariance(size_t gridsize,double missval,const Varray<size_t> & nofvals,Varray<double> & work0,const Varray<double> & work1,const Varray<double> & work2)182 covariance(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varray<double> &work0, const Varray<double> &work1,
183            const Varray<double> &work2)
184 {
185   size_t nmiss = 0;
186 
187   for (size_t i = 0; i < gridsize; ++i)
188     {
189       const auto missval1 = missval;
190       const auto missval2 = missval;
191       double covar;
192       const auto nvals = nofvals[i];
193       if (nvals > 0)
194         {
195           double dnvals = nvals;
196           const auto temp = DIVMN(MULMN(work0[i], work1[i]), dnvals * dnvals);
197           covar = SUBMN(DIVMN(work2[i], dnvals), temp);
198           if (DBL_IS_EQUAL(covar, missval)) nmiss++;
199         }
200       else
201         {
202           nmiss++;
203           covar = missval;
204         }
205 
206       work0[i] = covar;
207     }
208 
209   return nmiss;
210 }
211 
212 // rms in time
213 template <typename T>
214 static void
rmsd_init(size_t gridsize,const Varray<T> & x,const Varray<T> & y,T xmv,T ymv,Varray<size_t> & nofvals,Varray<double> & rmsd)215 rmsd_init(size_t gridsize, const Varray<T> &x, const Varray<T> &y, T xmv, T ymv, Varray<size_t> &nofvals, Varray<double> &rmsd)
216 {
217   for (size_t i = 0; i < gridsize; ++i)
218     {
219       if ((!DBL_IS_EQUAL(x[i], xmv)) && (!DBL_IS_EQUAL(y[i], ymv)))
220         {
221           const double xx = x[i];
222           const double yy = y[i];
223           rmsd[i] += ((xx - yy) * (xx - yy));
224           nofvals[i]++;
225         }
226     }
227 }
228 
229 static void
rmsd_init(size_t gridsize,const Field & field1,const Field & field2,Varray<size_t> & nofvals,Varray<double> & rmsd)230 rmsd_init(size_t gridsize, const Field &field1, const Field &field2, Varray<size_t> &nofvals, Varray<double> &rmsd)
231 {
232   if (field1.memType == MemType::Float)
233     {
234       rmsd_init(gridsize, field1.vec_f, field2.vec_f, (float) field1.missval, (float) field1.missval, nofvals, rmsd);
235     }
236   else
237     {
238       rmsd_init(gridsize, field1.vec_d, field2.vec_d, field1.missval, field1.missval, nofvals, rmsd);
239     }
240 }
241 
242 static size_t
rmsd_compute(size_t gridsize,double missval,const Varray<size_t> & nofvals,Varray<double> & rmsd)243 rmsd_compute(size_t gridsize, double missval, const Varray<size_t> &nofvals, Varray<double> &rmsd)
244 {
245   size_t nmiss = 0;
246 
247   for (size_t i = 0; i < gridsize; ++i)
248     {
249       if (nofvals[i] > 0)
250         {
251           rmsd[i] = std::sqrt(rmsd[i] / (double) nofvals[i]);
252         }
253       else
254         {
255           nmiss++;
256           rmsd[i] = missval;
257         }
258     }
259 
260   return nmiss;
261 }
262 
263 void *
Timstat2(void * process)264 Timstat2(void *process)
265 {
266   int64_t vdate = 0;
267   int vtime = 0;
268 
269   cdo_initialize(process);
270 
271   // clang-format off
272   cdo_operator_add("timcor",   FieldFunc_Cor,   5, nullptr);
273   cdo_operator_add("timcovar", FieldFunc_Covar, 3, nullptr);
274   cdo_operator_add("timrmsd",  FieldFunc_Rmsd,  1, nullptr);
275   // clang-format on
276 
277   const auto operatorID = cdo_operator_id();
278   const auto operfunc = cdo_operator_f1(operatorID);
279   const auto nwork = cdo_operator_f2(operatorID);
280   const auto timeIsConst = (operfunc == FieldFunc_Rmsd);
281 
282   operator_check_argc(0);
283 
284   const auto streamID1 = cdo_open_read(0);
285   const auto streamID2 = cdo_open_read(1);
286 
287   const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
288   const auto vlistID2 = cdo_stream_inq_vlist(streamID2);
289   const auto vlistID3 = vlistDuplicate(vlistID1);
290 
291   vlist_compare(vlistID1, vlistID2, CMP_ALL);
292 
293   VarList varList1, varList2;
294   varListInit(varList1, vlistID1);
295   varListInit(varList2, vlistID2);
296 
297   const auto nvars = vlistNvars(vlistID1);
298   const auto nrecs1 = vlistNrecs(vlistID1);
299   std::vector<int> recVarID(nrecs1), recLevelID(nrecs1);
300 
301   const auto taxisID1 = vlistInqTaxis(vlistID1);
302   // const auto taxisID2 = vlistInqTaxis(vlistID2);
303   const auto taxisID3 = taxisDuplicate(taxisID1);
304 
305   if (timeIsConst)
306     for (int varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_CONSTANT);
307 
308   vlistDefTaxis(vlistID3, taxisID3);
309   const auto streamID3 = cdo_open_write(2);
310   cdo_def_vlist(streamID3, vlistID3);
311 
312   Field field1, field2;
313 
314   Varray4D<double> work(nvars);
315   Varray3D<size_t> nofvals(nvars);
316 
317   for (int varID = 0; varID < nvars; varID++)
318     {
319       const auto gridsize = varList1[varID].gridsize;
320       const auto nlevels = varList1[varID].nlevels;
321 
322       work[varID].resize(nlevels);
323       nofvals[varID].resize(nlevels);
324 
325       for (int levelID = 0; levelID < nlevels; levelID++)
326         {
327           nofvals[varID][levelID].resize(gridsize, 0);
328           work[varID][levelID].resize(nwork);
329           for (int i = 0; i < nwork; i++) work[varID][levelID][i].resize(gridsize, 0.0);
330         }
331     }
332 
333   int tsID = 0;
334   while (true)
335     {
336       const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
337       if (nrecs == 0) break;
338 
339       vdate = taxisInqVdate(taxisID1);
340       vtime = taxisInqVtime(taxisID1);
341 
342       const auto nrecs2 = cdo_stream_inq_timestep(streamID2, tsID);
343       if (nrecs != nrecs2) cdo_warning("Input streams have different number of records!");
344 
345       for (int recID = 0; recID < nrecs; recID++)
346         {
347           int varID, levelID;
348           cdo_inq_record(streamID1, &varID, &levelID);
349           cdo_inq_record(streamID2, &varID, &levelID);
350 
351           field1.init(varList1[varID]);
352           field2.init(varList2[varID]);
353 
354           if (tsID == 0)
355             {
356               recVarID[recID] = varID;
357               recLevelID[recID] = levelID;
358             }
359 
360           const auto gridsize = varList1[varID].gridsize;
361 
362           cdo_read_record(streamID1, field1);
363           cdo_read_record(streamID2, field2);
364 
365           auto &rwork = work[varID][levelID];
366           auto &rnofvals = nofvals[varID][levelID];
367 
368           if (operfunc == FieldFunc_Cor)
369             {
370               correlation_init(gridsize, field1, field2, rnofvals, rwork[0], rwork[1], rwork[2], rwork[3], rwork[4]);
371             }
372           else if (operfunc == FieldFunc_Covar)
373             {
374               covariance_init(gridsize, field1, field2, rnofvals, rwork[0], rwork[1], rwork[2]);
375             }
376           else if (operfunc == FieldFunc_Rmsd)
377             {
378               rmsd_init(gridsize, field1, field2, rnofvals, rwork[0]);
379             }
380         }
381 
382       tsID++;
383     }
384 
385   tsID = 0;
386   taxisDefVdate(taxisID3, vdate);
387   taxisDefVtime(taxisID3, vtime);
388   cdo_def_timestep(streamID3, tsID);
389 
390   for (int recID = 0; recID < nrecs1; recID++)
391     {
392       const auto varID = recVarID[recID];
393       const auto levelID = recLevelID[recID];
394 
395       const auto gridsize = varList1[varID].gridsize;
396       const auto missval = varList1[varID].missval;
397 
398       auto &rwork = work[varID][levelID];
399       const auto &rnofvals = nofvals[varID][levelID];
400 
401       size_t nmiss = 0;
402       if (operfunc == FieldFunc_Cor)
403         {
404           nmiss = correlation(gridsize, missval, rnofvals, rwork[0], rwork[1], rwork[2], rwork[3], rwork[4]);
405         }
406       else if (operfunc == FieldFunc_Covar)
407         {
408           nmiss = covariance(gridsize, missval, rnofvals, rwork[0], rwork[1], rwork[2]);
409         }
410       else if (operfunc == FieldFunc_Rmsd)
411         {
412           nmiss = rmsd_compute(gridsize, missval, rnofvals, rwork[0]);
413         }
414 
415       cdo_def_record(streamID3, varID, levelID);
416       cdo_write_record(streamID3, rwork[0].data(), nmiss);
417     }
418 
419   cdo_stream_close(streamID3);
420   cdo_stream_close(streamID2);
421   cdo_stream_close(streamID1);
422 
423   cdo_finish();
424 
425   return nullptr;
426 }
427