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