/* dim_op.c : engine of operations along a dimension (e.g. running mean) */ #include #include #include "ruby.h" #include "narray.h" #include #ifndef RARRAY_PTR # define RARRAY_PTR(ary) (RARRAY(ary)->ptr) #endif #ifndef RARRAY_LEN # define RARRAY_LEN(ary) (RARRAY(ary)->len) #endif /* for compatibility for NArray and NArray with big memory patch */ #ifndef NARRAY_BIGMEM typedef int na_shape_t; #endif enum bc_type { BC_SIMPLE=10, BC_CYCLIC=11, BC_TRIM=12 }; #define ID3(i,j,k) ((i) + (j)*n0 + (k)*n0*n1) #define ID3T(i,j,k) ((i) + (j)*n0 + (k)*n0*(n1-wlen+1)) #define ID3o(i,j,k) ((i) + (j)*n0 + (k)*n0*n1o) #define ID3z(i,j,k) ((i) + (j)*n0 + (k)*n0*nz) #define ID3c(i,j,k) ((i) + (j)*n0 + (k)*n0*nc) #define ID2(i,j) ((i) + (j)*n0) #define ID5(i,j,k,l,m) ((i) + (j)*ni + (k)*ni*nd1 + (l)*ni*nd1*no1 + (m)*ni*nd1*no1*nd2) #define NMax(i,j) ( (i) >= (j) ? (i) : (j) ) #define NMin(i,j) ( (i) <= (j) ? (i) : (j) ) static int convol_result_length(na_shape_t n1, na_shape_t wlen, int ibc) { int n1o; switch (ibc) { case BC_SIMPLE: case BC_CYCLIC: n1o = n1; break; case BC_TRIM: n1o = n1-wlen+1; break; } return n1o; } static void running_mean_nomiss(zi,n0,n1,n2, w,wlen, ibc, zo) // IN double *zi; na_shape_t n0; na_shape_t n1; na_shape_t n2; double *w; na_shape_t wlen; int ibc; // OUT double *zo; // must have been set to 0 everywhere { na_shape_t i,j,k,m, jj; na_shape_t wl2s, wl2; double wsum; double *wc; // scaled w (sum wc == 1) double *wcsum1,*wcsum2; // for BC_SIMPLE; for shorter summation at edges double wcm; // modified wcm for shorter summation wl2s = (wlen - 1)/2; // e.g. 7->3, 6->2 wl2 = wlen/2; // e.g. 7->3, 6->3; wl2s + wl2 == wlen - 1 // < scale w to make its sum == 1 > for (m=0,wsum=0.0; m switch (ibc) { case BC_SIMPLE: wcsum1 = ALLOCA_N(double, wl2s); wcsum1[0] = 1.0 - wc[0]; // sum of wc[1...wlen] for (m=1; mwlen-1-wl2; m--) { wcsum2[wlen-1-m] = wcsum2[wlen-2-m] - wc[m]; // sum of wc[0...m] } // 1..wl2-1 with m for (k=0; k3, 6->2 wl2 = wlen/2; // e.g. 7->3, 6->3; wl2s + wl2 == wlen - 1 // < weighted running mean > switch (ibc) { case BC_SIMPLE: for (k=0; k= nminvalid) { zo[ID3(i,j,k)] /= wsum; } else { zo[ID3(i,j,k)] = zmiss; } } } // where grids are enough on both sides for (j=wl2s; j= nminvalid) { zo[ID3(i,j,k)] /= wsum; } else { zo[ID3(i,j,k)] = zmiss; } } } // where j is close to the "right" end for (j=n1-wl2; j= nminvalid) { zo[ID3(i,j,k)] /= wsum; } else { zo[ID3(i,j,k)] = zmiss; } } } } break; case BC_CYCLIC: for (k=0; k= nminvalid) { zo[ID3(i,j,k)] /= wsum; } else { zo[ID3(i,j,k)] = zmiss; } } } } break; case BC_TRIM: // trim where extra grids are not enough for (k=0; k= nminvalid) { zo[ID3T(i,j,k)] /= wsum; } else { zo[ID3T(i,j,k)] = zmiss; } } } } break; default: rb_raise(rb_eArgError,"Undefined boundary condision (%d)", ibc); break; } } // 注:convolution も引数は同じなのでドライバーは兼ねられる // (入り口はわけないとならない)が今のところやってない. // static VALUE running_mean(int argc, VALUE *argv, VALUE self) { VALUE vi; // mandatory 1st arg; input NArray VALUE dim; // mandatory 2nd arg; Integer VALUE wgt; // mandatory 3rd arg; weight 1D NArray VALUE bc; // mandatory 4th arg; boundary condition (Integer class const) VALUE missv=Qnil; // optional 5th arg; if present(Float) vi may have missing int nminvalid=1; // optional 6th arg; miminum count of non-missing vales int with_miss; struct NARRAY *na; na_shape_t *shi, wlen; na_shape_t n0, n1, n2; double *zi, *w, zmiss; int d, i, rank, ibc; VALUE vo; na_shape_t *sho, n1o; double *zo; // < process arguments > if ( argc<4 || argc>6 ) { rb_raise(rb_eArgError,"Need 4 to 6 arguments"); } vi = argv[0]; dim = argv[1]; wgt = argv[2]; bc = argv[3]; with_miss = (argc > 4) && (!NIL_P(argv[4])); if ( with_miss ) { missv = argv[4]; } if ( argc == 6) { nminvalid = NUM2INT( argv[5] ); } // 1st arg if (!IsNArray(vi)) { rb_raise(rb_eArgError,"1st arg must be a NArray"); } vi = na_cast_object(vi, NA_DFLOAT); rank = NA_RANK(vi); zi = NA_PTR_TYPE(vi, double *); GetNArray(vi, na); shi = na->shape; // 2nd arg d = NUM2INT( dim ); // 3rd arg if (!IsNArray(wgt)) {rb_raise(rb_eArgError,"3rd arg must be a 1D NArray");} if (NA_RANK(wgt)!=1) {rb_raise(rb_eArgError,"3rd arg must be a 1D NArray");} wgt = na_cast_object(wgt, NA_DFLOAT); wlen = NA_TOTAL(wgt); w = NA_PTR_TYPE(wgt, double *); // 4th arg ibc = NUM2INT( bc ); // 5th arg if ( with_miss ) { zmiss = NUM2DBL(missv); } // 6th arg if (nminvalid > wlen) {rb_raise(rb_eArgError,"nminvalid > filtering length");} // < shape as 3D > n1 = shi[d]; // length of the dim to filter if (wlen >= n1) {rb_raise(rb_eArgError,"filter len >= len of the dim");} n0 = n2 = 1; for (i=0; i n1o = convol_result_length(n1, wlen, ibc); sho = ALLOCA_N(int, rank); for(i=0; i if ( with_miss ) { running_mean_miss(zi,n0,n1,n2, w,wlen, ibc, zmiss, nminvalid, zo); } else { running_mean_nomiss(zi,n0,n1,n2, w,wlen, ibc, zo); } return vo; } /* Running tile (2D) mean for data with missing. (if no missing, you can just run the 1D running mean twice.) */ static void running_mean_2D_miss(zi, ni,nd1,no1,nd2,no2, with_w1, w1, wlen1, ibc1, with_w2, w2, wlen2, ibc2, zmiss, nminvalid, zo) // IN double *zi; na_shape_t ni,nd1,no1,nd2,no2; /* treated as 5D array (inner => outer) */ int with_w1, with_w2; double *w1, *w2; na_shape_t wlen1, wlen2; int ibc1, ibc2; double zmiss; int nminvalid; // OUT double *zo; // must have been set to 0 everywhere { na_shape_t i,j1,k1,j2,k2,m1,m2, jj1, jj2; na_shape_t mstr1, mend1, mstr2, mend2; na_shape_t wlhs1, wlhs2; double wsum, wv; int nm; wlhs1 = (wlen1 - 1)/2; // e.g. 7->3, 6->2 wlhs2 = (wlen2 - 1)/2; // e.g. 7->3, 6->2 // < weighted running mean > if ( !( ibc1 == BC_SIMPLE || ibc1 == BC_CYCLIC ) ) { rb_raise(rb_eArgError,"Undefined boundary condision(1st D) (%d)", ibc1); } if ( !( ibc2 == BC_SIMPLE || ibc2 == BC_CYCLIC ) ) { rb_raise(rb_eArgError,"Undefined boundary condision(2nd D) (%d)", ibc2); } for (k2=0; k2= nminvalid) { zo[ID5(i,j1,k1,j2,k2)] /= wsum; } else { zo[ID5(i,j1,k1,j2,k2)] = zmiss; } } } } } } } // driver of running_mean_2D_miss // (no-missing version is not needed) // static VALUE running_mean_2D(obj, vi, dim1, len_or_wgt1, bc1, dim2, len_or_wgt2, bc2, missv, nminvalid) VALUE obj; VALUE vi; // input NArray VALUE dim1; // Integer VALUE len_or_wgt1; // weight 1D NArray or Integer (length of uniform wgt) VALUE bc1 ; // boundary condition (Integer) VALUE dim2; // Integer VALUE len_or_wgt2; // weight 1D NArray or Integer (length of uniform wgt) VALUE bc2 ; // boundary condition (Integer) VALUE missv; // missing value in vi VALUE nminvalid; // miminum count of non-missing vales { struct NARRAY *na; VALUE wgt; na_shape_t *shi, wlen1, wlen2; int with_w1, with_w2; na_shape_t ni,nd1,no1,nd2,no2; double *zi, *w1, *w2, zmiss; int d1, d2, i, rank, ibc1, ibc2; VALUE vo; na_shape_t *sho, n1o; int nminvalid_; // miminum count of non-missing vales double *zo; // < process arguments > if (!IsNArray(vi)) { rb_raise(rb_eArgError,"1st arg must be a NArray"); } vi = na_cast_object(vi, NA_DFLOAT); rank = NA_RANK(vi); if (rank < 2) { rb_raise(rb_eArgError,"rank 1st arg must be >= 2"); } zi = NA_PTR_TYPE(vi, double *); GetNArray(vi, na); shi = na->shape; d1 = NUM2INT( dim1 ); d2 = NUM2INT( dim2 ); nminvalid_ = NUM2INT( nminvalid ); if (d1 >= d2) { rb_raise(rb_eArgError,"d1 < d2 is required"); } if (d2 >= rank) { rb_raise(rb_eArgError,"d2 must be < rank"); } with_w1 = IsNArray(len_or_wgt1); if (with_w1) { wgt = len_or_wgt1; if (NA_RANK(wgt)!=1){rb_raise(rb_eArgError,"wgt1 must be a 1D NArray");} wgt = na_cast_object(wgt, NA_DFLOAT); wlen1 = NA_TOTAL(wgt); w1 = NA_PTR_TYPE(wgt, double *); } else { wlen1 = NUM2INT( len_or_wgt1 ); w1 == NULL; /* will not be used */ } with_w2 = IsNArray(len_or_wgt2); if (with_w2) { wgt = len_or_wgt2; if (NA_RANK(wgt)!=1){rb_raise(rb_eArgError,"wgt2 must be a 1D NArray");} wgt = na_cast_object(wgt, NA_DFLOAT); wlen2 = NA_TOTAL(wgt); w2 = NA_PTR_TYPE(wgt, double *); } else { wlen2 = NUM2INT( len_or_wgt2 ); w2 == NULL; /* will not be used */ } ibc1 = NUM2INT( bc1 ); ibc2 = NUM2INT( bc2 ); zmiss = NUM2DBL(missv); // < shape as 5D > nd1 = shi[d1]; // length of the dim to filter if (wlen1 >= nd1) {rb_raise(rb_eArgError,"filter len >= len of the dim1");} nd2 = shi[d2]; // length of the dim to filter if (wlen2 >= nd1) {rb_raise(rb_eArgError,"filter len >= len of the dim2");} ni = no1 = no2 = 1; for (i=0; i sho = shi; // Limited to shape conserved cases vo = na_make_object(NA_DFLOAT, rank, sho, cNArray); GetNArray(vo, na); na_clear_data(na); zo = NA_PTR_TYPE(vo, double *); // < do the job > running_mean_2D_miss(zi, ni,nd1,no1,nd2,no2, with_w1, w1, wlen1, ibc1, with_w2, w2, wlen2, ibc2, zmiss, nminvalid_, zo); return vo; } static void bin_mean_nomiss(zi,n0,n1,n2, len, zo) // IN double *zi; na_shape_t n0; na_shape_t n1; na_shape_t n2; na_shape_t len; // OUT double *zo; // must have been set to 0 everywhere { na_shape_t n1o; na_shape_t i,j,k,m; double fact; n1o = n1 / len; fact = 1.0 / len; for (k=0; k= nminvalid) { zo[ID3o(i,j,k)] /= cnt; } else { zo[ID3o(i,j,k)] = zmiss; } } } } } static void bin_sum_miss(zi,n0,n1,n2, len, zmiss,nminvalid, zo) // IN double *zi; na_shape_t n0; na_shape_t n1; na_shape_t n2; na_shape_t len; double zmiss; int nminvalid; // OUT double *zo; // must have been set to 0 everywhere { na_shape_t n1o; na_shape_t i,j,k,m; na_shape_t cnt; n1o = n1 / len; for (k=0; k= nminvalid) ) { zo[ID3o(i,j,k)] = zmiss; } } } } } // bin_mean or bin_sum depeiding on the last arg static VALUE bin_mean_sum(int argc, VALUE *argv, VALUE self, int mean) { VALUE vi; // 1st arg; input NArray VALUE dim; // 2nd arg na_shape_t len; // 3rd arg VALUE missv=Qnil; // optional 4th arg; if present(Float) vi may have missing int nminvalid=1; // optional 5th arg int with_miss; struct NARRAY *na; double *zi, zmiss; na_shape_t *shi; na_shape_t n0, n1, n2; int d, i, rank; VALUE vo; na_shape_t *sho, n1o; double *zo; // < process arguments > if ( argc<3 || argc>5 ) { rb_raise(rb_eArgError,"Need 2 or 3 arguments"); } vi = argv[0]; dim = argv[1]; d = NUM2INT( dim ); len = NUM2INT( argv[2] ); if (len<1) {rb_raise(rb_eArgError,"len must be >= 1");} with_miss = (argc > 3) && (!NIL_P(argv[3])); if ( with_miss ) { missv = argv[3]; zmiss = NUM2DBL(missv); } if (argc==5) { nminvalid = NUM2INT( argv[4] ); if (nminvalid > len) {rb_raise(rb_eArgError,"nminvalid > bin length");} } // < NArray elements > vi = na_cast_object(vi, NA_DFLOAT); rank = NA_RANK(vi); zi = NA_PTR_TYPE(vi, double *); GetNArray(vi, na); shi = na->shape; // < shape as 3D > n1 = shi[d]; // length of the dim if (len >= n1) {rb_raise(rb_eArgError,"filter len >= len of the dim");} n0 = n2 = 1; for (i=0; i n1o = n1 / len; // <- BC_TRIM (currently this is the only available bc) sho = ALLOCA_N(int, rank); for(i=0; i if ( mean ) { if ( with_miss ) { bin_mean_miss(zi,n0,n1,n2, len, zmiss,nminvalid, zo); } else { bin_mean_nomiss(zi,n0,n1,n2, len, zo); } } else { if ( with_miss ) { bin_sum_miss(zi,n0,n1,n2, len, zmiss,nminvalid, zo); } else { bin_sum_nomiss(zi,n0,n1,n2, len, zo); } } return vo; } static VALUE bin_mean(int argc, VALUE *argv, VALUE self) { bin_mean_sum(argc, argv, self, 1); } static VALUE bin_sum(int argc, VALUE *argv, VALUE self) { bin_mean_sum(argc, argv, self, 0); } /* cum_sum_dfloat_bang : cumulative summation along a dimension zdim (FLOAT) (bang method: overwrite the input by the result) */ static VALUE cum_sum_dfloat_bang(obj, f, zdim) VALUE obj; VALUE f; VALUE zdim; { int rank, zd, d; struct NARRAY *na; na_shape_t *shape; double *v; na_shape_t n0, nz, n2; na_shape_t j, k, l; if ( NA_TYPE(f) != NA_DFLOAT ){ rb_raise(rb_eArgError, "expects a DFLOAT NArray"); } rank = NA_RANK(f); GetNArray(f, na); shape = na->shape; v = (double *)NA_PTR(na, 0); zd = NUM2INT(zdim); if (zd < 0) zd += rank; // negative: count from the last dim if (zd < 0 || zd >= rank){ rb_raise(rb_eArgError, "Invalid dimension (%d) for a rank %d NArray", NUM2INT(zdim), rank); } for (d=0, n0=1 ; d= rank){ rb_raise(rb_eArgError, "Invalid dimension (%d) since f.rank==%d", NUM2INT(zdim), rank); } GetNArray(f, na); shape = na->shape; for (d=0, n0=1 ; d wc[nc-1]){ rb_raise(rb_eArgError, "ccell must be alined in the increasing order"); } if(w != Qnil){ GetNArray(w, na); wv = (double *)NA_PTR(na, 0); } else { wv = zv; } // main loop #pragma omp parallel for private(nzw,m,k,wa,wb,fa,fb,dz,wac,wbc,a,b,fi) for (l=0; lnz) nzw=nz; } m=0; // m: index of the new cooridnate for (k=0; k0 && wa < wc[m-1] ){ m--; } } else { while( wa >= wc[m] && m fe[:,nz+1,:], where ":" respresent arbitrary number of dimensions. The elements of fe are equal to those of f where inside the domain (simple copies), and they are equal to the elements of fs at the bondary (simple copies if fs is given; if not, guessed by interpolation or naive extension). * ze: grid points of fe along zdim. It is a mixture of zcrd and zs; it is zcrd inside the domain (where f is copied), and it is zs at the boundary (where fs is copied). Same shape as fe. * nze: The number of valid data along zdim of fe. Shaped as ze[:,:], according to the notation above. For example, when fe is 4D and zdim==2, fe[i,j,k,l] is valid for k = 0,1,...,nze[i,j,l]-1, where the boundary is at nze[i,j,l]-1. Thus, nze is always smaller than or equal to the length of zdim of fe (which is nz+1) */ static VALUE cap_by_boundary(obj, f, zdim, zcrd, upper, zb, fb) VALUE obj; VALUE f; // [NArray] multi-D data VALUE zdim; // [Integer] dimension of zcrd in f VALUE zcrd; // [NArray] 1D coordinate values of the zdim dimension of f VALUE upper; // true/false to cap the upper/lower side (with z) VALUE zb; // [NArray] the "surface" z; zb.rank must be f.rank-1 VALUE fb; // [nil or NArray] the f value at surface (zb.shape==fb.shape) { VALUE fe; // [NArray] return value, extended f by fb VALUE ze; // [NArray] return value, grid points of fe along zdim VALUE nze; // [NArray] return value, valid data lengths along zdim in fe VALUE result; // [Array] [fe, ze, nze] (return the two in an Array) struct NARRAY *na; int rank, zd, d; na_shape_t n0, nz, n2, nc; double *fv, *zcv, *zbv, *fbv, *fev, *zev; int32_t *nzev; int zcincr, capupper, sgn; na_shape_t j, k, l; na_shape_t *shape, *oshape, *oshape2; // cast to ensure pointer types if(!IsNArray(f)) rb_raise(rb_eArgError, "f is not a NArray"); if(!IsNArray(zcrd)) rb_raise(rb_eArgError, "zcrd is not a NArray"); if(!IsNArray(zb)) rb_raise(rb_eArgError, "zb is not a NArray"); if(fb!=Qnil && !IsNArray(fb)) rb_raise(rb_eArgError, "fb must be nil or a NArray"); f = na_cast_object(f, NA_DFLOAT); zcrd = na_cast_object(zcrd, NA_DFLOAT); zb = na_cast_object(zb, NA_DFLOAT); if(fb != Qnil){ fb = na_cast_object(fb, NA_DFLOAT); } // read & check rank = NA_RANK(f); if (NA_RANK(zb)!=rank-1){rb_raise(rb_eArgError, "zb.rank must f.rank-1");} zd = NUM2INT(zdim); if (zd < 0) zd += rank; // negative: count from the last dim if (zd < 0 || zd >= rank){ rb_raise(rb_eArgError, "Invalid dimension (%d) since f.rank==%d", NUM2INT(zdim), rank); } GetNArray(f, na); shape = na->shape; for (d=0, n0=1 ; d zd) { oshape2[d-1] = shape[d]; } } nze = na_make_object(NA_LINT, rank-1, oshape2, cNArray); nzev = NA_PTR_TYPE(nze, int32_t *); // initialize the output data for (l=0; l 0 ){ fev[ID3c(j,k,l)] = fbv[ID2(j,l)]; zev[ID3c(j,k,l)] = zbv[ID2(j,l)]; nzev[ID2(j,l)] = k+1; break; } } if (k==nz) { // didn't break fev[ID3c(j,k,l)] = fbv[ID2(j,l)]; zev[ID3c(j,k,l)] = zbv[ID2(j,l)]; nzev[ID2(j,l)] = k+1; } } } } else { for (l=0; l 0 ){ //fev[ID3c(j,k,l)] = fv[ID3z(j,k-1,l)];//naive extension fev[ID3c(j,k,l)] = ( fv[ID3z(j,k-1,l)]*(zcv[k]-zbv[ID2(j,l)]) + fv[ID3z(j,k,l)]*(zbv[ID2(j,l)]-zcv[k-1]) ) / (zcv[k] - zcv[k-1]); zev[ID3c(j,k,l)] = zbv[ID2(j,l)]; nzev[ID2(j,l)] = k+1; break; } } if (k==nz) { // didn't break fev[ID3c(j,k,l)] = fv[ID3z(j,k-1,l)]; // naive extension zev[ID3c(j,k,l)] = zbv[ID2(j,l)]; nzev[ID2(j,l)] = k+1; } } } } // output result = rb_ary_new3(3, fe, ze, nze); return result; } void init_gphys_dim_op() { static VALUE mNumRu; static VALUE cGPhys; static VALUE cVArray; // rb_require("narray"); // it does not work ?? mNumRu = rb_define_module("NumRu"); cGPhys = rb_define_class_under(mNumRu, "GPhys", rb_cObject); rb_define_private_method(cGPhys, "c_running_mean", running_mean, -1); rb_define_private_method(cGPhys, "c_running_mean_2D", running_mean_2D, 9); rb_define_singleton_method(cGPhys, "c_running_mean", running_mean, -1); rb_define_singleton_method(cGPhys, "c_running_mean_2D", running_mean_2D, 9); cVArray = rb_define_class_under(mNumRu, "VArray", rb_cObject); rb_define_private_method(cVArray, "c_bin_mean", bin_mean, -1); rb_define_private_method(cVArray, "c_bin_sum", bin_sum, -1); rb_define_singleton_method(cGPhys, "c_bin_mean", bin_mean, -1); rb_define_singleton_method(cGPhys, "c_bin_sum", bin_sum, -1); rb_define_private_method(cVArray, "c_cum_sum", cum_sum, 2); rb_define_singleton_method(cGPhys, "c_cell_integ_irreg", cell_integ_irreg, 6); rb_define_singleton_method(cGPhys, "c_cum_integ_irreg", cum_integ_irreg, 6); rb_define_singleton_method(cGPhys, "c_cap_by_boundary", cap_by_boundary, 6); }