/* This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data. Copyright (C) 2003-2020 Uwe Schulzweida, See COPYING file for copying and redistribution conditions. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. */ #include #include // qsort #include #include #include "percentiles.h" #include "array.h" #include "field.h" #include "functs.h" #include "cdo_output.h" Field::Field() : fpeRaised(0), nwpv(1), memType(MemType::Native), grid(-1), gridsize(0), size(0), nsamp(0), nmiss(0), missval(0) { m_count = 0; } Field3D::Field3D() : nlevels(0) {} void Field::init(const CdoVar &var) { grid = var.gridID; gridsize = var.gridsize; missval = var.missval; memType = var.memType; size = var.gridsize * var.nwpv; if (memType == MemType::Float) vec_f.resize(size); else vec_d.resize(size); } void Field::resize(size_t count) { m_count = count; vec_d.resize(m_count); if (!size) size = m_count; } void Field::resize(size_t count, double value) { m_count = count; vec_d.resize(m_count, value); if (!size) size = m_count; } void Field::resizef(size_t count) { m_count = count; vec_f.resize(m_count); if (!size) size = m_count; } void Field::resizef(size_t count, float value) { m_count = count; vec_f.resize(m_count, value); if (!size) size = m_count; } bool Field::empty() const { return m_count == 0; } void Field::check_gridsize() const { if (size == 0) fprintf(stderr, "Internal problem, size of field not set!"); if (size > m_count) fprintf(stderr, "Internal problem, size of field is greater than allocated size of field!"); } void Field3D::init(const CdoVar &var) { nlevels = var.nlevels; grid = var.gridID; gridsize = var.gridsize; missval = var.missval; memType = var.memType; size = var.nlevels * var.gridsize * var.nwpv; if (memType == MemType::Float) vec_f.resize(size); else vec_d.resize(size); } void fieldFill(Field &field, double value) { field.check_gridsize(); if (field.memType == MemType::Float) std::fill(field.vec_f.begin(), field.vec_f.begin() + field.size, value); else std::fill(field.vec_d.begin(), field.vec_d.begin() + field.size, value); } void fieldCopy(const Field &field_src, Field &field_tgt) { if (field_src.memType == MemType::Float) field_tgt.vec_f = field_src.vec_f; else field_tgt.vec_d = field_src.vec_d; } void fieldCopy(const Field3D &field_src, int levelID, Field &field_tgt) { const auto size = field_src.gridsize * field_src.nwpv; const auto offset = levelID * size; if (field_src.memType == MemType::Float) std::copy(field_src.vec_f.begin() + offset, field_src.vec_f.begin() + offset + size, field_tgt.vec_f.begin()); else std::copy(field_src.vec_d.begin() + offset, field_src.vec_d.begin() + offset + size, field_tgt.vec_d.begin()); } void fieldAdd(Field &field1, const Field3D &field2, int levelID) { const auto size = field1.gridsize * field1.nwpv; const auto offset = levelID * size; if (field1.memType == MemType::Float) for (size_t i = 0; i < size; i++) field1.vec_f[i] += field2.vec_f[offset + i]; else for (size_t i = 0; i < size; i++) field1.vec_d[i] += field2.vec_d[offset + i]; } // functor that returns true if value is equal to the value of the constructor parameter provided class valueDblIsEqual { double _missval; public: valueDblIsEqual(double missval) : _missval(missval) {} bool operator()(double value) const { return DBL_IS_EQUAL(value, _missval); } }; // functor that returns true if value is equal to the value of the constructor parameter provided class valueIsEqual { double _missval; public: valueIsEqual(double missval) : _missval(missval) {} bool operator()(double value) const { return IS_EQUAL(value, _missval); } }; size_t fieldNumMiss(const Field &field) { field.check_gridsize(); if (std::isnan(field.missval)) { return std::count_if(field.vec_d.begin(), field.vec_d.begin() + field.size, valueDblIsEqual(field.missval)); } else { return std::count_if(field.vec_d.begin(), field.vec_d.begin() + field.size, valueIsEqual(field.missval)); } } size_t fieldNumMV(Field &field) { if (field.memType == MemType::Float) field.nmiss = varrayNumMV(field.size, field.vec_f, (float)field.missval); else field.nmiss = varrayNumMV(field.size, field.vec_d, field.missval); return field.nmiss; } MinMaxVal fieldMinMax(Field &field) { if (field.memType == MemType::Float) return varrayMinMax(field.vec_f); else return varrayMinMax(field.vec_d); } static double vfldrange(const Field &field) { return field.nmiss ? varrayRangeMV(field.size, field.vec_d, field.missval) : varrayRange(field.size, field.vec_d); } double vfldmin(const Field &field) { return field.nmiss ? varrayMinMV(field.size, field.vec_d, field.missval) : varrayMin(field.size, field.vec_d); } double vfldmax(const Field &field) { return field.nmiss ? varrayMaxMV(field.size, field.vec_d, field.missval) : varrayMax(field.size, field.vec_d); } double vfldsum(const Field &field) { return field.nmiss ? varraySumMV(field.size, field.vec_d, field.missval) : varraySum(field.size, field.vec_d); } double vfldmean(const Field &field) { return field.nmiss ? varrayMeanMV(field.size, field.vec_d, field.missval) : varrayMean(field.size, field.vec_d); } double vfldmeanw(const Field &field) { return field.nmiss ? varrayWeightedMeanMV(field.size, field.vec_d, field.weightv, field.missval) : varrayWeightedMean(field.size, field.vec_d, field.weightv, field.missval); } double vfldavg(const Field &field) { return field.nmiss ? varrayAvgMV(field.size, field.vec_d, field.missval) : varrayMean(field.size, field.vec_d); } double vfldavgw(const Field &field) { return field.nmiss ? varrayWeightedAvgMV(field.size, field.vec_d, field.weightv, field.missval) : varrayWeightedMean(field.size, field.vec_d, field.weightv, field.missval); } double vfldvar(const Field &field) { return varrayVar(field.size, field.vec_d, field.nmiss, field.missval); } double vfldvar1(const Field &field) { return varrayVar1(field.size, field.vec_d, field.nmiss, field.missval); } double vfldkurt(const Field &field) { return varrayKurt(field.size, field.vec_d, field.nmiss, field.missval); } double vfldskew(const Field &field) { return varraySkew(field.size, field.vec_d, field.nmiss, field.missval); } double vfldvarw(const Field &field) { return varrayWeightedVar(field.size, field.vec_d, field.weightv, field.nmiss, field.missval); } double vfldvar1w(const Field &field) { return varrayWeightedVar1(field.size, field.vec_d, field.weightv, field.nmiss, field.missval); } double varToStd(double rvar, double missval) { if (DBL_IS_EQUAL(rvar, missval) || rvar < 0) { return missval; } else { return IS_NOT_EQUAL(rvar, 0) ? std::sqrt(rvar) : 0; } } double vfldstd(const Field &field) { return varToStd(vfldvar(field), field.missval); } double vfldstd1(const Field &field) { return varToStd(vfldvar1(field), field.missval); } double vfldstdw(const Field &field) { return varToStd(vfldvarw(field), field.missval); } double vfldstd1w(const Field &field) { return varToStd(vfldvar1w(field), field.missval); } void vfldrms(const Field &field, const Field &field2, Field &field3) { size_t i; size_t rnmiss = 0; auto grid1 = field.grid; // size_t nmiss1 = field.nmiss; const auto array1 = field.vec_d.data(); auto grid2 = field2.grid; // size_t nmiss2 = field2.nmiss; const auto array2 = field2.vec_d.data(); const auto missval1 = field.missval; const auto missval2 = field2.missval; const auto &w = field.weightv; double rsum = 0, rsumw = 0; const auto len = gridInqSize(grid1); if (len != gridInqSize(grid2)) cdoAbort("fields have different size!"); /* if ( nmiss1 ) */ { for (i = 0; i < len; i++) if (!DBL_IS_EQUAL(w[i], missval1)) { rsum = ADDMN(rsum, MULMN(w[i], MULMN(SUBMN(array2[i], array1[i]), SUBMN(array2[i], array1[i])))); rsumw = ADDMN(rsumw, w[i]); } } /* else { for ( i = 0; i < len; i++ ) { rsum += w[i] * array1[i]; rsumw += w[i]; } } */ const double ravg = SQRTMN(DIVMN(rsum, rsumw)); if (DBL_IS_EQUAL(ravg, missval1)) rnmiss++; field3.vec_d[0] = ravg; field3.nmiss = rnmiss; } double vfldpctl(Field &field, const double pn) { auto pctl = field.missval; if ((field.size - field.nmiss) > 0) { if (field.nmiss) { Varray v(field.size - field.nmiss); size_t j = 0; for (size_t i = 0; i < field.size; i++) if (!DBL_IS_EQUAL(field.vec_d[i], field.missval)) v[j++] = field.vec_d[i]; pctl = percentile(v.data(), j, pn); } else { pctl = percentile(field.vec_d.data(), field.size, pn); } } return pctl; } static int compareDouble(const void *a, const void *b) { const double *x = (const double *) a; const double *y = (const double *) b; return ((*x > *y) - (*x < *y)) * 2 + (*x > *y) - (*x < *y); } double vfldrank(Field &field) { double res = 0; // Using first value as reference (observation) double *array = &field.vec_d[1]; const auto val = array[-1]; const auto len = field.size - 1; if (field.nmiss) return field.missval; std::qsort(array, len, sizeof(double), compareDouble); if (val > array[len - 1]) res = (double) len; else for (size_t j = 0; j < len; j++) if (array[j] >= val) { res = (double) j; break; } return res; } double fldbrs(Field &field) // Not used! { const auto nmiss = field.nmiss; const auto len = field.size; const auto array = field.vec_d.data(); const auto missval = field.missval; double brs = 0; size_t i, count = 0; // Using first value as reference if (nmiss == 0) { for (i = 1; i < len; i++) brs += (array[i] - array[0]) * (array[i] - array[0]); count = i - 1; } else { if (DBL_IS_EQUAL(array[0], missval)) return missval; for (i = 1; i < len; i++) if (!DBL_IS_EQUAL(array[i], missval)) { brs += (array[i] - array[0]) * (array[i] - array[0]); count++; } } return brs / count; } double vfldfun(const Field &field, int function) { double rval = 0; // clang-format off switch (function) { case func_range: rval = vfldrange(field); break; case func_min: rval = vfldmin(field); break; case func_max: rval = vfldmax(field); break; case func_sum: rval = vfldsum(field); break; case func_mean: rval = vfldmean(field); break; case func_avg: rval = vfldavg(field); break; case func_std: rval = vfldstd(field); break; case func_std1: rval = vfldstd1(field); break; case func_var: rval = vfldvar(field); break; case func_var1: rval = vfldvar1(field); break; case func_meanw: rval = vfldmeanw(field); break; case func_avgw: rval = vfldavgw(field); break; case func_stdw: rval = vfldstdw(field); break; case func_std1w: rval = vfldstd1w(field); break; case func_varw: rval = vfldvarw(field); break; case func_var1w: rval = vfldvar1w(field); break; case func_skew: rval = vfldskew(field); break; case func_kurt: rval = vfldkurt(field); break; default: cdoAbort("%s: function %d not implemented!", __func__, function); } // clang-format on return rval; }