nco/nco_cnf_dmn.h File Reference

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netcdf.h>
#include "nco_netcdf.h"
#include "nco.h"
#include "nco_ctl.h"
#include "nco_lst_utl.h"
#include "nco_mmr.h"
#include "nco_rth_utl.h"
#include "nco_var_utl.h"

Include dependency graph for nco_cnf_dmn.h:

This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

var_sctnco_var_cnf_dmn (const var_sct *const var, var_sct *const wgt, var_sct *wgt_crr, const nco_bool MUST_CONFORM, nco_bool *DO_CONFORM)
bool ncap_var_cnf_dmn (var_sct **var_1, var_sct **var_2)
dmn_sct ** nco_dmn_avg_rdr_prp (dmn_sct **const dmn_in, char **dmn_rdr_lst, const int dmn_rdr_nbr)
char * nco_var_dmn_rdr_mtd (const var_sct *const var_in, var_sct *const var_out, CST_X_PTR_CST_PTR_CST_Y(dmn_sct, dmn_rdr), const int dmn_rdr_nbr, int *const dmn_idx_out_in, const nco_bool *const dmn_rvr_rdr, nco_bool *const dmn_rvr_in)
int nco_var_dmn_rdr_val (const var_sct *const var_in, var_sct *const var_out, const int *const dmn_idx_out_in, const nco_bool *const dmn_rvr_in)


Function Documentation

bool ncap_var_cnf_dmn var_sct **  var_1,
var_sct **  var_2
 

Definition at line 331 of file nco_cnf_dmn.c.

References EXIT_FAILURE, var_sct_tag::nbr_dim, nco_bool, nco_exit(), nco_var_cnf_dmn(), nco_var_free(), prg_nm_get(), and True.

Referenced by main(), ncap_var_var_add(), ncap_var_var_dvd(), ncap_var_var_mlt(), ncap_var_var_mod(), ncap_var_var_op(), ncap_var_var_pwr(), and ncap_var_var_sub().

00333 {
00334   /* Purpose: Conform lesser ranked to greater ranked variable, so that
00335      both variables are equal size on return.
00336      If this is possible then return true, otherwise die.
00337      Routine wraps nco_var_cnf_dmn(), which does the hard work
00338      If an input variable is replaced by a broadcast version of itself,
00339      then calling routine must free original version or it will leak. */
00340 
00341   nco_bool DO_CONFORM; /* [flg] Do var_1 and var_2 conform after processing? */
00342   nco_bool MUST_CONFORM=True; /* [flg] Must var_1 and var_2 conform? */
00343   var_sct *var_1_org; /* [ptr] Original location of var_1 */
00344   var_sct *var_2_org; /* [ptr] Original location of var_2 */
00345   var_sct *var_tmp=NULL;
00346 
00347   var_1_org=*var_1; /* [ptr] Original location of var_1 */
00348   var_2_org=*var_2; /* [ptr] Original location of var_2 */
00349   
00350   if(var_1_org->nbr_dim > var_2_org->nbr_dim){
00351     var_tmp=nco_var_cnf_dmn(var_1_org,var_2_org,var_tmp,MUST_CONFORM,&DO_CONFORM);
00352     if(var_2_org != var_tmp){
00353       var_2_org=nco_var_free(var_2_org);
00354       *var_2=var_tmp;
00355     } /* endif replace var_2 */
00356   }else{ 
00357     var_tmp=nco_var_cnf_dmn(var_2_org,var_1_org,var_tmp,MUST_CONFORM,&DO_CONFORM);
00358     if(var_1_org != var_tmp){
00359       var_1_org=nco_var_free(var_1_org);
00360       *var_1=var_tmp;
00361     } /* endif replace var_1 */
00362   } /* endif var_1 > var_2 */
00363 
00364   if(!DO_CONFORM){
00365     (void)fprintf(stderr,"%s: ncap_var_cnf_dmn() reports that variables %s and %s do not have have conforming dimensions. Cannot proceed with operation\n",prg_nm_get(),(*var_1)->nm,(*var_2)->nm);
00366     nco_exit(EXIT_FAILURE);
00367   } /* endif */
00368 
00369   return DO_CONFORM; /* [flg] Do var_1 and var_2 conform after processing? */
00370 } /* end ncap_var_cnf_dmn() */

dmn_sct** nco_dmn_avg_rdr_prp dmn_sct **const   dmn_in,
char **  dmn_rdr_lst,
const int  dmn_rdr_nbr
 

var_sct* nco_var_cnf_dmn const var_sct *const   var,
var_sct *const   wgt,
var_sct wgt_crr,
const nco_bool  MUST_CONFORM,
nco_bool *  DO_CONFORM
 

Definition at line 13 of file nco_cnf_dmn.c.

References var_sct_tag::cnt, dbg_lvl_get(), var_sct_tag::dim, EXIT_FAILURE, False, var_sct_tag::id, var_sct_tag::nbr_dim, NC_MAX_DIMS, nco_bool, nco_exit(), nco_free(), nco_malloc(), nco_typ_lng(), nco_var_dpl(), nco_var_free(), nco_xrf_var(), var_sct_tag::nm, dmn_sct_tag::nm, prg_nm_get(), var_sct_tag::sz, True, var_sct_tag::type, var_sct_tag::val, vec_set(), and ptr_unn::vp.

Referenced by main(), and ncap_var_cnf_dmn().

00018 {
00019   /* Threads: Routine is thread safe and calls no unsafe routines */
00020   /* fxm: TODO 226. Is xrf in nco_var_cnf_dmn() really necessary? If not, remove it and make wgt arg const var_sct * const */
00021 
00022   /* Purpose: Stretch second variable to match dimensions of first variable
00023      Dimensions in var which are not in wgt will be present in wgt_out, with values
00024      replicated from existing dimensions in wgt.
00025      By default, wgt's dimensions must be subset of var's dimensions (MUST_CONFORM=true)
00026      Calling routine should set MUST_CONFORM=false if wgt and var need not conform
00027      When wgt and var do not conform then then nco_var_cnf_dmn sets *DO_CONFORM=False and returns copy of var with all values set to 1.0
00028      Calling procedure then decides what to do with unity output
00029      MUST_CONFORM is True for ncbo: Variables of like name to be, e.g., differenced, must be same rank
00030      MUST_CONFORM is False false for ncap, ncflint, ncwa: Some variables to be averaged may not conform to the specified weight, e.g., lon will not conform to gw. This is fine and returned wgt_out may be discarded. */
00031 
00032   /* There are many inelegant ways to accomplish this (without using C++): */  
00033 
00034   /* Perhaps most efficient method in general case is to expand weight array until
00035      it is same size as variable array, and then multiply these arrays together 
00036      element-by-element in highly vectorized loop (possibly in Fortran or BLAS). 
00037      To enhance speed, (enlarged) weight-values array could be static, only re-made
00038      when dimensions of incoming variables change. */
00039 
00040   /* Another general method, though more expensive, is to use C to figure out the 
00041      multidimensional indices into the one dimensional hyperslab, a la ncks. 
00042      Knowing these indices, routine could loop over the one-dimensional array
00043      element by element, choosing the appropriate index into the weight array from 
00044      those same multidimensional indices. 
00045      This method can also create a static weight-value array that is only destroyed 
00046      when an incoming variable changes dimensions from the previous variable. */
00047 
00048   /* Another method, which is not completely general, but which may be good enough for
00049      governement work, is to create Fortran subroutines which expect variables of 
00050      a given number of dimensions as input. 
00051      Creating these functions for up to five dimensions would satisfy most situations
00052      C code would determine which branch to call based on number of dimensions
00053      C++ or Fortran9x overloading could construct this interface more elegantly */
00054 
00055   /* An (untested) simplification to some of these methods is to copy the 1-D array
00056      value pointer of variable and cast it to an N-D array pointer
00057      Then C could handle indexing 
00058      This method easily produce working, but non-general code
00059      Implementation would require ugly branches or hard-to-understand recursive function calls */
00060   
00061   /* Routine assumes weight will never have more dimensions than variable
00062      (otherwise which hyperslab of weight to use would be ill-defined). 
00063      However, weight may (and often will) have fewer dimensions than variable */
00064 
00065   nco_bool CONFORMABLE=False; /* [flg] wgt can be made to conform to var */
00066   nco_bool USE_DUMMY_WGT=False; /* [flg] Fool NCO into thinking wgt conforms to var */
00067 
00068   int idx; /* [idx] Counting index */
00069   int idx_dmn; /* [idx] Dimension index */
00070   int wgt_var_dmn_shr_nbr=0; /* [nbr] Number of dimensions wgt and var share */
00071 
00072   var_sct *wgt_out=NULL;
00073 
00074   /* Initialize flag to false. Overwrite by true after successful conformance */
00075   *DO_CONFORM=False;
00076   
00077   /* Does current weight (wgt_crr) conform to variable's dimensions? */
00078   if(wgt_crr != NULL){
00079     /* Test rank first because wgt_crr because of 19960218 bug (invalid dmn_id in old wgt_crr leads to match) */
00080     if(var->nbr_dim == wgt_crr->nbr_dim){
00081       /* Test whether all wgt and var dimensions match in sequence */
00082       for(idx=0;idx<var->nbr_dim;idx++){
00083         if(strcmp(wgt_crr->dim[idx]->nm,var->dim[idx]->nm)) break;
00084       } /* end loop over dimensions */
00085       if(idx == var->nbr_dim) *DO_CONFORM=True;
00086     } /* end if ranks are equal */
00087     if(*DO_CONFORM){
00088       wgt_out=wgt_crr;
00089     }else{
00090       wgt_crr=nco_var_free(wgt_crr);
00091       wgt_out=NULL;
00092     } /* !*DO_CONFORM */
00093   } /* wgt_crr == NULL */
00094 
00095   /* Does original weight (wgt) conform to variable's dimensions? */
00096   if(wgt_out == NULL){
00097     if(var->nbr_dim > 0){
00098       /* Test that all dimensions in wgt appear in var */
00099       for(idx=0;idx<wgt->nbr_dim;idx++){
00100         for(idx_dmn=0;idx_dmn<var->nbr_dim;idx_dmn++){
00101           /* Compare names, not dimension IDs */
00102           if(!strcmp(wgt->dim[idx]->nm,var->dim[idx_dmn]->nm)){
00103             wgt_var_dmn_shr_nbr++; /* wgt and var share this dimension */
00104             break;
00105           } /* endif */
00106         } /* end loop over var dimensions */
00107       } /* end loop over wgt dimensions */
00108       /* Decide whether wgt and var dimensions conform, are mutually exclusive, or are partially exclusive (an error) */ 
00109       if(wgt_var_dmn_shr_nbr == wgt->nbr_dim){
00110         /* wgt and var conform */
00111         CONFORMABLE=True;
00112       }else if(wgt_var_dmn_shr_nbr == 0){
00113         /* Dimensions in wgt and var are mutually exclusive */
00114         CONFORMABLE=False;
00115         if(MUST_CONFORM){
00116           (void)fprintf(stdout,"%s: ERROR %s and template %s share no dimensions\n",prg_nm_get(),wgt->nm,var->nm);
00117           nco_exit(EXIT_FAILURE);
00118         }else{
00119           if(dbg_lvl_get() > 2) (void)fprintf(stdout,"\n%s: DEBUG %s and template %s share no dimensions: Not broadcasting %s to %s\n",prg_nm_get(),wgt->nm,var->nm,wgt->nm,var->nm);
00120           USE_DUMMY_WGT=True;
00121         } /* endif */
00122       }else if(wgt->nbr_dim > var->nbr_dim){
00123         /* wgt is larger rank than var---no possibility of conforming */
00124         CONFORMABLE=False;
00125         if(MUST_CONFORM){
00126           (void)fprintf(stdout,"%s: ERROR %s is rank %d but template %s is rank %d: Impossible to broadcast\n",prg_nm_get(),wgt->nm,wgt->nbr_dim,var->nm,var->nbr_dim);
00127           nco_exit(EXIT_FAILURE);
00128         }else{
00129           if(dbg_lvl_get() > 2) (void)fprintf(stdout,"\n%s: DEBUG %s is rank %d but template %s is rank %d: Not broadcasting %s to %s\n",prg_nm_get(),wgt->nm,wgt->nbr_dim,var->nm,var->nbr_dim,wgt->nm,var->nm);
00130           USE_DUMMY_WGT=True;
00131         } /* endif */
00132       }else if(wgt_var_dmn_shr_nbr > 0 && wgt_var_dmn_shr_nbr < wgt->nbr_dim){
00133         /* Some, but not all, of wgt dimensions are in var */
00134         CONFORMABLE=False;
00135         if(MUST_CONFORM){
00136           (void)fprintf(stdout,"%s: ERROR %d dimensions of %s belong to template %s but %d dimensions do not\n",prg_nm_get(),wgt_var_dmn_shr_nbr,wgt->nm,var->nm,wgt->nbr_dim-wgt_var_dmn_shr_nbr);
00137           nco_exit(EXIT_FAILURE);
00138         }else{
00139           if(dbg_lvl_get() > 2) (void)fprintf(stdout,"\n%s: DEBUG %d dimensions of %s belong to template %s but %d dimensions do not: Not broadcasting %s to %s\n",prg_nm_get(),wgt_var_dmn_shr_nbr,wgt->nm,var->nm,wgt->nbr_dim-wgt_var_dmn_shr_nbr,wgt->nm,var->nm);
00140           USE_DUMMY_WGT=True;
00141         } /* endif */
00142       } /* end if */
00143       if(USE_DUMMY_WGT){
00144         /* Variables do not truly conform, but this might be OK, depending on the application, so set DO_CONFORM flag to false and ... */
00145         *DO_CONFORM=False;
00146         /* ... return a dummy weight of 1.0, which allows program logic to pretend variable is weighted, but does not change answers */ 
00147         wgt_out=nco_var_dpl(var);
00148         (void)vec_set(wgt_out->type,wgt_out->sz,wgt_out->val,1.0);
00149       } /* endif */
00150       if(CONFORMABLE){
00151         if(var->nbr_dim == wgt->nbr_dim){
00152           /* var and wgt conform and are same rank */
00153           /* Test whether all wgt and var dimensions match in sequence */
00154           for(idx=0;idx<var->nbr_dim;idx++){
00155             if(strcmp(wgt->dim[idx]->nm,var->dim[idx]->nm)) break;
00156                /*           if(wgt->dmn_id[idx] != var->dmn_id[idx]) break;*/
00157           } /* end loop over dimensions */
00158           /* If so, take shortcut and copy wgt to wgt_out */
00159           if(idx == var->nbr_dim) *DO_CONFORM=True;
00160         }else{
00161           /* var and wgt conform but are not same rank, set flag to proceed to generic conform routine */
00162           *DO_CONFORM=False;
00163         } /* end else */
00164       } /* endif CONFORMABLE */
00165     }else{
00166       /* var is scalar, if wgt is also then set flag to copy wgt to wgt_out else proceed to generic conform routine */
00167       if(wgt->nbr_dim == 0) *DO_CONFORM=True; else *DO_CONFORM=False;
00168     } /* end else */
00169     if(CONFORMABLE && *DO_CONFORM){
00170       wgt_out=nco_var_dpl(wgt);
00171       (void)nco_xrf_var(wgt,wgt_out);
00172     } /* end if */
00173   } /* end if */
00174 
00175   if(wgt_out == NULL){
00176     /* Expand original weight (wgt) to match size of current variable */
00177     char * restrict wgt_cp;
00178     char * restrict wgt_out_cp;
00179 
00180     int idx_wgt_var[NC_MAX_DIMS];
00181     /*    int idx_var_wgt[NC_MAX_DIMS];*/
00182     int wgt_nbr_dim;
00183     int var_nbr_dmn_m1;
00184 
00185     long * restrict var_cnt;
00186     long dmn_ss[NC_MAX_DIMS];
00187     long dmn_var_map[NC_MAX_DIMS];
00188     long dmn_wgt_map[NC_MAX_DIMS];
00189     long var_lmn;
00190     long wgt_lmn;
00191     long var_sz;
00192 
00193     size_t wgt_typ_sz;
00194 
00195     /* Copy main attributes of variable into ouput weight */
00196     wgt_out=nco_var_dpl(var);
00197     (void)nco_xrf_var(wgt,wgt_out);
00198 
00199     /* wgt_out variable was copied from template var
00200        Modify key fields so its name and type are based on wgt, not var
00201        wgt_out will then be hybrid between wgt and var 
00202        Remainder of routine fills wgt_out's var-dimensionality with wgt-values */
00203     wgt_out->nm=(char *)nco_free(wgt_out->nm);
00204     wgt_out->nm=(char *)strdup(wgt->nm);
00205     wgt_out->id=wgt->id;
00206     wgt_out->type=wgt->type;
00207     wgt_out->val.vp=(void *)nco_free(wgt_out->val.vp);
00208     wgt_out->val.vp=(void *)nco_malloc(wgt_out->sz*nco_typ_lng(wgt_out->type));
00209     wgt_cp=(char *)wgt->val.vp;
00210     wgt_out_cp=(char *)wgt_out->val.vp;
00211     wgt_typ_sz=nco_typ_lng(wgt_out->type);
00212 
00213     if(wgt_out->nbr_dim == 0){
00214       /* Variable (and weight) are scalars, not arrays */
00215       (void)memcpy(wgt_out_cp,wgt_cp,wgt_typ_sz);
00216     }else if(wgt->nbr_dim == 0){
00217       /* Lesser-ranked input variable is scalar 
00218          Expansion in this degenerate case needs no index juggling (reverse-mapping)
00219          Code as special case to speed-up important applications of ncap
00220          for synthetic file creation */
00221       var_sz=var->sz;
00222       for(var_lmn=0;var_lmn<var_sz;var_lmn++){
00223         (void)memcpy(wgt_out_cp+var_lmn*wgt_typ_sz,wgt_cp,wgt_typ_sz);      
00224       } /* end loop over var_lmn */
00225     }else{
00226       /* Variable (and therefore wgt_out) are arrays, not scalars */
00227       
00228       /* Create forward and reverse mappings from variable's dimensions to weight's dimensions:
00229 
00230          dmn_var_map[i] is number of elements between one value of i_th 
00231          dimension of variable and next value of i_th dimension, i.e., 
00232          number of elements in memory between indicial increments in i_th dimension. 
00233          This is computed as product of one (1) times size of all dimensions (if any) after i_th 
00234          dimension in variable.
00235 
00236          dmn_wgt_map[i] contains analogous information, except for original weight variable
00237 
00238          idx_wgt_var[i] contains index into variable's dimensions of i_th dimension of original weight
00239          idx_var_wgt[i] contains index into original weight's dimensions of i_th dimension of variable 
00240 
00241          Since weight is a subset of variable, some elements of idx_var_wgt may be "empty", or unused
00242 
00243          Since mapping arrays (dmn_var_map and dmn_wgt_map) are ultimately used for a
00244          memcpy() operation, they could (read: should) be computed as byte offsets, not type offsets. 
00245          This is why netCDF generic hyperslab routines (ncvarputg(), ncvargetg())
00246          request imap vector to specify offset (imap) vector in bytes. */
00247 
00248       for(idx=0;idx<wgt->nbr_dim;idx++){
00249         for(idx_dmn=0;idx_dmn<var->nbr_dim;idx_dmn++){
00250           /* Compare names, not dimension IDs */
00251           if(!strcmp(var->dim[idx_dmn]->nm,wgt->dim[idx]->nm)){
00252             idx_wgt_var[idx]=idx_dmn;
00253             /*      idx_var_wgt[idx_dmn]=idx;*/
00254             break;
00255           } /* end if */
00256           /* Sanity check */
00257           if(idx_dmn == var->nbr_dim-1){
00258             (void)fprintf(stdout,"%s: ERROR wgt %s has dimension %s but var %s does not deep in nco_var_cnf_dmn()\n",prg_nm_get(),wgt->nm,wgt->dim[idx]->nm,var->nm);
00259             nco_exit(EXIT_FAILURE);
00260           } /* end if err */
00261         } /* end loop over variable dimensions */
00262       } /* end loop over weight dimensions */
00263       
00264       /* Figure out map for each dimension of variable */
00265       for(idx=0;idx<var->nbr_dim;idx++) dmn_var_map[idx]=1L;
00266       for(idx=0;idx<var->nbr_dim-1;idx++)
00267         for(idx_dmn=idx+1;idx_dmn<var->nbr_dim;idx_dmn++)
00268           dmn_var_map[idx]*=var->cnt[idx_dmn];
00269       
00270       /* Figure out map for each dimension of weight */
00271       for(idx=0;idx<wgt->nbr_dim;idx++) dmn_wgt_map[idx]=1L;
00272       for(idx=0;idx<wgt->nbr_dim-1;idx++)
00273         for(idx_dmn=idx+1;idx_dmn<wgt->nbr_dim;idx_dmn++)
00274           dmn_wgt_map[idx]*=wgt->cnt[idx_dmn];
00275       
00276       /* Define convenience variables to avoid repetitive indirect addressing */
00277       wgt_nbr_dim=wgt->nbr_dim;
00278       var_sz=var->sz;
00279       var_cnt=var->cnt;
00280       var_nbr_dmn_m1=var->nbr_dim-1;
00281 
00282       /* var_lmn is offset into 1-D array corresponding to N-D indices dmn_ss */
00283       for(var_lmn=0;var_lmn<var_sz;var_lmn++){
00284         /* dmn_ss are corresponding indices (subscripts) into N-D array */
00285         /* Operations: 1 modulo, 1 pointer offset, 1 user memory fetch
00286            Repetitions: \lmnnbr
00287            Total Counts: \rthnbr=2\lmnnbr, \mmrusrnbr=\lmnnbr
00288            NB: LHS assumed compact and cached, counted RHS offsets and fetches only */
00289         dmn_ss[var_nbr_dmn_m1]=var_lmn%var_cnt[var_nbr_dmn_m1];
00290         for(idx=0;idx<var_nbr_dmn_m1;idx++){
00291           /* Operations: 1 divide, 1 modulo, 2 pointer offset, 2 user memory fetch
00292              Repetitions: \lmnnbr(\dmnnbr-1)
00293              Counts: \rthnbr=4\lmnnbr(\dmnnbr-1), \mmrusrnbr=2\lmnnbr(\dmnnbr-1)
00294              NB: LHS assumed compact and cached, counted RHS offsets and fetches only
00295              NB: Neglected loop arithmetic/compare */
00296           dmn_ss[idx]=(long)(var_lmn/dmn_var_map[idx]);
00297           dmn_ss[idx]%=var_cnt[idx];
00298         } /* end loop over dimensions */
00299         
00300         /* Map (shared) N-D array indices into 1-D index into original weight data */
00301         wgt_lmn=0L;
00302         /* Operations: 1 add, 1 multiply, 3 pointer offset, 3 user memory fetch
00303            Repetitions: \lmnnbr\rnkwgt
00304            Counts: \rthnbr=5\lmnnbr\rnkwgt, \mmrusrnbr=3\lmnnbr\rnkwgt */
00305         for(idx=0;idx<wgt_nbr_dim;idx++) wgt_lmn+=dmn_ss[idx_wgt_var[idx]]*dmn_wgt_map[idx];
00306         
00307         /* Operations: 2 add, 2 multiply, 0 pointer offset, 1 system memory copy
00308            Repetitions: \lmnnbr
00309            Counts: \rthnbr=4\lmnnbr, \mmrusrnbr=0, \mmrsysnbr=1 */
00310         (void)memcpy(wgt_out_cp+var_lmn*wgt_typ_sz,wgt_cp+wgt_lmn*wgt_typ_sz,wgt_typ_sz);
00311         
00312       } /* end loop over var_lmn */
00313       
00314     } /* end if variable (and weight) are arrays, not scalars */
00315     
00316     *DO_CONFORM=True;
00317   } /* end if we had to stretch weight to fit variable */
00318   
00319   if(*DO_CONFORM == -1){
00320     (void)fprintf(stdout,"%s: ERROR *DO_CONFORM == -1 on exit from nco_var_cnf_dmn()\n",prg_nm_get());
00321     nco_exit(EXIT_FAILURE);
00322   } /* endif */
00323   
00324   /* Current weight (wgt_out) now conforms to current variable */
00325   return wgt_out;
00326   
00327 } /* end nco_var_cnf_dmn() */

char* nco_var_dmn_rdr_mtd const var_sct *const   var_in,
var_sct *const   var_out,
CST_X_PTR_CST_PTR_CST_Y(dmn_sct, dmn_rdr)  ,
const int  dmn_rdr_nbr,
int *const   dmn_idx_out_in,
const nco_bool *const   dmn_rvr_rdr,
nco_bool *const   dmn_rvr_in
 

Definition at line 374 of file nco_cnf_dmn.c.

References dmn_sct_tag::cnt, dbg_lvl_get(), var_sct_tag::dim, var_sct_tag::dmn_id, dmn_sct_tag::end, EXIT_FAILURE, False, dmn_sct_tag::id, dmn_sct_tag::is_rec_dmn, var_sct_tag::nbr_dim, NC_MAX_DIMS, nco_cmp_int(), nco_exit(), nco_free(), nco_malloc(), NCO_REC_DMN_UNDEFINED, var_sct_tag::nm, dmn_sct_tag::nm, prg_nm_get(), dmn_sct_tag::srd, dmn_sct_tag::srt, and dmn_sct_tag::xrf.

Referenced by main().

00381 {
00382   /* Purpose: Re-order dimensions in a given variable
00383      dmn_rdr contains new dimension order for dimensions
00384      Currently routine allows only dimension permutations, i.e., 
00385      re-arranging dimensions without changing their number (variable rank).
00386 
00387      Routine keeps track of two variables var_* whose abbreviations are:
00388      in: Input variable (already hyperslabbed) with old dimension ordering
00389      rdr: User-specified re-ordered dimension list. Possibly subset of dmn_in
00390      out: Output (re-ordered) dimensionality specific to each variable
00391 
00392      At first it seemed this routine could re-order input variable in place without copying it
00393      Multiple constraints keep this from being practical
00394      Constraints are dictated by the architectural decision to call nco_var_dmn_rdr_mtd() twice
00395      Decision to call nco_var_dmn_rdr_mtd() twice is based on:
00396      1. Want to parallelize loop over variables to increase throughput
00397         Parallel writes to output file only possible if output file is defined in shape, order
00398         Output file only definable once variable shapes, i.e., re-ordered dimensions known
00399         Alternatives to calling nco_var_dmn_rdr_mtd() twice:
00400         A. Each thread enters redefine() mode and adds its variable to output file
00401            Internal data re-copying would be expensive and unnecessary
00402            Hence Alternative A is not viable
00403         B. Perform output file definition and all writes after all variable re-ordering
00404            Memory consumption would increase to O(fl_in_sz) to keep all re-ordered data in memory
00405            Hence Alternative B is not viable
00406      2. The two calls to nco_var_dmn_rdr_mtd() accomplish the following
00407         A. First call: Create var_out->dim for call to nco_var_dfn() 
00408            Main thread makes first call in serial mode just prior to nco_var_dfn() 
00409            No input data (AOT metadata) have been allocated or read in at this point
00410            Routine exits after modifying var_out metadata for new dimension geometry
00411         B. Second call: Re-order var_in->val data and place in var_out
00412            Although var_out->dmn is retained between calls, intermediate information such as
00413            in_out dimension mapping arrays are lost and must be re-created
00414            Hence second call must re-do most of first call, then begin re-ordering
00415      Routine must access un-touched var_in->dim input structure during both parts of second call
00416      Hence var_in must be unmodified between first and second call
00417 
00418      dmn_rdr is user-specified list of dimensions to be re-arranged 
00419      User specifies all or only a subset of all dimensions in input file
00420      For example, say user specifies -d lat,lon
00421      This ensures lat precedes lon in all variables in output file
00422      In this case dmn_rdr is (user-specified) list [lat,lon]
00423      Input 0-D variables dimensioned [] output with dmn_out=[] (unaltered)
00424      Input 1-D variables dimensioned [lat] output with dmn_out=[lat] (unaltered)
00425      Input 2-D variables dimensioned [lat,lon] output with dmn_out=[lat,lon] (unaltered)
00426      Input 2-D variables dimensioned [time,lev] output with dmn_out=[time,lev] (unaltered)
00427      Input 2-D variables dimensioned [lon,lat] output with dmn_out=[lon,lat] (transposed)
00428      Input 3-D variables dimensioned [lon,lat,time] output with dmn_out=[lat,lon,time]
00429      Input 3-D variables dimensioned [time,lon,lat] output with dmn_out=[time,lat,lon]
00430      Input 3-D variables dimensioned [lon,lev,lat] output with dmn_out=[lat,lev,lon]
00431      Input 4-D variables dimensioned [lon,lev,lat,time] output with dmn_out=[lat,lev,lon,time]
00432      Hence output dimension dmn_out list depends on each particular variable 
00433      Some, or even all, dimensions in dmn_rdr may not be in dmn_in
00434      Re-ordering is only necessary for variables where dmn_in and dmn_rdr share at least two dimensions
00435      
00436      Dimension reversal:
00437      Users specify dimension reversal by prefixing dimension name with negative sign
00438      Host routine passes dimension reversing flags in dmn_rvr_rdr
00439      Dimensions may be re-ordered, reversed, or both */
00440   
00441   const char fnc_nm[]="nco_var_dmn_rdr_mtd()"; /* [sng] Function name */
00442   
00443   char *rec_dmn_nm_out=NULL; /* [sng] Name of record dimension, if any, required by re-order */
00444 
00445   dmn_sct **dmn_in=NULL; /* [sct] List of dimension structures in input order */
00446   dmn_sct **dmn_out; /* [sct] List of dimension structures in output order */
00447   
00448   int dmn_idx_in_shr[NC_MAX_DIMS]; /* [idx] Dimension correspondence, input->share  Purely diagnostic */
00449   int dmn_idx_in_out[NC_MAX_DIMS]; /* [idx] Dimension correspondence, input->output */
00450   int dmn_idx_in_rdr[NC_MAX_DIMS]; /* [idx] Dimension correspondence, input->re-order NB: Purely diagnostic */
00451   int dmn_idx_rdr_in[NC_MAX_DIMS]; /* [idx] Dimension correspondence, re-order->input NB: Purely diagnostic */
00452   int dmn_idx_shr_rdr[NC_MAX_DIMS]; /* [idx] Dimension correspondence, share->re-order */         
00453   int dmn_idx_shr_in[NC_MAX_DIMS]; /* [idx] Dimension correspondence, share->input */     
00454   int dmn_idx_shr_out[NC_MAX_DIMS]; /* [idx] Dimension correspondence, share->output */   
00455   int dmn_idx_rec_out=NCO_REC_DMN_UNDEFINED; /* [idx] Record dimension index in output variable */
00456   int dmn_shr_nbr=0; /* [nbr] Number of dimensions dmn_in and dmn_rdr share */
00457   int dmn_in_idx; /* [idx] Counting index for dmn_in */
00458   int dmn_in_nbr; /* [nbr] Number of dimensions in input variable */
00459   int dmn_out_idx; /* [idx] Counting index for dmn_out */
00460   int dmn_out_nbr; /* [nbr] Number of dimensions in output variable */
00461   int dmn_rdr_idx; /* [idx] Counting index for dmn_rdr */
00462   int dmn_shr_idx; /* [idx] Counting index for dmn_shr */
00463   int idx_err=-99999; /* [idx] Invalid index for debugging */
00464   
00465   /* Initialize variables to reduce indirection */
00466   /* NB: Number of input and output dimensions are equal for pure re-orders
00467      However, keep dimension numbers in separate variables to ease relax this rule in future */
00468   dmn_in_nbr=var_in->nbr_dim;
00469   dmn_out_nbr=var_out->nbr_dim;
00470 
00471   /* Initialize dimension maps to missing_value to aid debugging */
00472   if(dbg_lvl_get() > 3){
00473     for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++)
00474       dmn_idx_out_in[dmn_out_idx]=idx_err;
00475     for(dmn_rdr_idx=0;dmn_rdr_idx<dmn_rdr_nbr;dmn_rdr_idx++)
00476       dmn_idx_rdr_in[dmn_rdr_idx]=idx_err;
00477     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++){
00478       dmn_idx_in_shr[dmn_in_idx]=idx_err;
00479       dmn_idx_in_rdr[dmn_in_idx]=idx_err;
00480       dmn_idx_shr_rdr[dmn_in_idx]=idx_err; /* fxm: initialize up to dmn_shr_nbr which is currently unknown */
00481       dmn_idx_shr_in[dmn_in_idx]=idx_err; /* fxm: initialize up to dmn_shr_nbr which is currently unknown */
00482       dmn_idx_shr_out[dmn_in_idx]=idx_err; /* fxm: initialize up to dmn_shr_nbr which is currently unknown */
00483     } /* end loop over dmn_in */
00484   } /* end if dbg */
00485   
00486   /* Initialize default correspondence and record dimension in case early return desired */
00487   if(var_out->is_rec_var) rec_dmn_nm_out=var_in->dim[0]->nm;
00488   for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++){
00489     dmn_idx_out_in[dmn_in_idx]=dmn_in_idx;
00490     dmn_rvr_in[dmn_in_idx]=False;
00491   } /* end if */
00492 
00493   /* Scalars are never altered by dimension re-ordering or reversal */
00494   if(dmn_in_nbr < 1) return rec_dmn_nm_out;
00495   
00496   /* On entry to this section of code, we assume:
00497      1. var_out duplicates var_in */
00498   
00499   /* Create complete 1-to-1 ordered list of dimensions in new output variable */
00500   /* For each dimension in re-ordered dimension list... */
00501   for(dmn_rdr_idx=0;dmn_rdr_idx<dmn_rdr_nbr;dmn_rdr_idx++){
00502     /* ...see if re-order dimension exists in dmn_in dimension list... */
00503     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++){
00504       /* ...by comparing names, not dimension IDs... */
00505       if(!strcmp(var_in->dim[dmn_in_idx]->nm,dmn_rdr[dmn_rdr_idx]->nm)){
00506         dmn_idx_in_rdr[dmn_in_idx]=dmn_rdr_idx;
00507         dmn_idx_rdr_in[dmn_rdr_idx]=dmn_in_idx;
00508         dmn_idx_shr_rdr[dmn_shr_nbr]=dmn_rdr_idx;
00509         dmn_idx_shr_in[dmn_shr_nbr]=dmn_in_idx;
00510         dmn_idx_in_shr[dmn_in_idx]=dmn_shr_nbr;
00511         dmn_shr_nbr++; /* dmn_in and dmn_rdr share this dimension */
00512         break;
00513       } /* endif */
00514     } /* end loop over dmn_in */
00515   } /* end loop over dmn_rdr */
00516   
00517   /* Map permanent list of reversed dimensions to input variable */
00518   for(dmn_shr_idx=0;dmn_shr_idx<dmn_shr_nbr;dmn_shr_idx++)
00519     dmn_rvr_in[dmn_idx_shr_in[dmn_shr_idx]]=dmn_rvr_rdr[dmn_idx_shr_rdr[dmn_shr_idx]];
00520 
00521   /* No dimension re-ordering is necessary if dmn_in and dmn_rdr share fewer than two dimensions
00522      Dimension reversal must be done with even one shared dimension
00523      Single dimension reversal, however, uses default dimension maps and return values */
00524   if(dmn_shr_nbr < 2) return rec_dmn_nm_out;
00525   
00526   /* dmn_idx_shr_out is sorted version of dmn_idx_shr_in */
00527   (void)memcpy((void *)(dmn_idx_shr_out),(void *)(dmn_idx_shr_in),dmn_shr_nbr*sizeof(dmn_idx_shr_in[0]));
00528   qsort(dmn_idx_shr_out,(size_t)dmn_shr_nbr,sizeof(dmn_idx_shr_out[0]),nco_cmp_int);
00529 
00530   /* Initialize final map to no re-ordering */
00531   for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++) 
00532     dmn_idx_in_out[dmn_in_idx]=dmn_in_idx;
00533 
00534   /* Splice in re-ordered dimension location for each shared dimension */
00535   for(dmn_shr_idx=0;dmn_shr_idx<dmn_shr_nbr;dmn_shr_idx++)
00536     dmn_idx_in_out[dmn_idx_shr_in[dmn_shr_idx]]=dmn_idx_shr_out[dmn_shr_idx];
00537   
00538   if(dbg_lvl_get() > 3){
00539     (void)fprintf(stdout,"%s: DEBUG %s variable %s shares %d of its %d dimensions with the %d dimensions in the re-order list\n",prg_nm_get(),fnc_nm,var_in->nm,dmn_shr_nbr,var_in->nbr_dim,dmn_rdr_nbr);
00540     (void)fprintf(stdout,"shr_idx\tshr_rdr\tshr_in\tshr_out\n");
00541     for(dmn_shr_idx=0;dmn_shr_idx<dmn_shr_nbr;dmn_shr_idx++)
00542       (void)fprintf(stdout,"%d\t%d\t%d\t%d\n",dmn_shr_idx,dmn_idx_shr_rdr[dmn_shr_idx],dmn_idx_shr_in[dmn_shr_idx],dmn_idx_shr_out[dmn_shr_idx]);
00543     (void)fprintf(stdout,"in_idx\tin_shr\tin_rdr\tin_out\trvr_flg\n");
00544     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++)
00545       (void)fprintf(stdout,"%d\t%d\t%d\t%d\t%s\n",dmn_in_idx,dmn_idx_in_shr[dmn_in_idx],dmn_idx_in_rdr[dmn_in_idx],dmn_idx_in_out[dmn_in_idx],(dmn_rvr_in[dmn_in_idx]) ? "true" : "false");
00546   } /* end if dbg */
00547 
00548   /* Create reverse correspondence */
00549   for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++)
00550     dmn_idx_out_in[dmn_idx_in_out[dmn_in_idx]]=dmn_in_idx;
00551   
00552   /* Create full dmn_out list */
00553   dmn_in=var_in->dim;
00554   dmn_out=(dmn_sct **)nco_malloc(dmn_out_nbr*sizeof(dmn_sct *));
00555   
00556   /* Assign dimension structures to new dimension list in correct order
00557      Remember: dmn_in has dimension IDs relative to input file 
00558      Copy dmn_in->xrf to get dimension IDs relative to output file (once they are defined) 
00559      Oh come on, it only seems like cheating! */
00560   for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++)
00561     dmn_out[dmn_out_idx]=dmn_in[dmn_idx_out_in[dmn_out_idx]]->xrf;
00562 
00563   /* Re-ordered output dimension list dmn_out now comprises correctly ordered but 
00564      otherwise verbatim copies of dmn_out structures in calling routine */
00565   
00566   /* Free var_out's old dimension list */
00567   var_out->dim=(dmn_sct **)nco_free(var_out->dim);
00568   /* Replace old with new dimension list */
00569   var_out->dim=dmn_out;
00570   
00571   /* NB: var_out is now in an inconsistent state 
00572      var_out->dim refers to re-ordered dimensions 
00573      However, var_out->dmn_id,cnt,srt,end,srd refer still duplicate var_in members
00574      They refer to old dimension ordering in input file
00575      nco_cnf_dmn_rdr_mtd() implicitly assumes that only nco_cnf_dmn_rdr_mtd() modifies var_out 
00576      The call to nco_cnf_dmn_rdr_val() for this variable performs the actual re-ordering
00577      The interim inconsistent state is required for dimension IDs because 
00578      output dimension IDs are not known until nco_dmn_dfn() which cannot (or, at least, should not)
00579      occur until output record dimension is known
00580      Interim modifications of var_out by any other routine are dangerous! */
00581 
00582   /* This is clear at date written (20040727), but memories are short
00583      Hence we modify var_out->dmn_id,cnt,srt,end,srd to contain re-ordered values now
00584      This makes it safer to var_out->dmn_id,cnt,srt,end,srd before second call to nco_cnf_dmn_rdr()
00585      If dmn_out->id does depend on record dimension identity, then this update will do no good
00586      Hence, we must re-update dmn_out->id after nco_dmn_dfn() in nco_cnf_dmn_rdr_val()
00587      Structures should be completely consisten at that point
00588      Not updating these structures (at least dmn_out->id) is equivalent to assuming that
00589      dmn_out->id does not depend on record dimension identity, which is an ASSUMPTION
00590      that may currently be true, but is not guaranteed by the netCDF API to always be true. */
00591   for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++){
00592     /* NB: Change dmn_id,cnt,srt,end,srd together to minimize chances of forgetting one */
00593     var_out->dmn_id[dmn_out_idx]=dmn_out[dmn_out_idx]->id;
00594     var_out->cnt[dmn_out_idx]=dmn_out[dmn_out_idx]->cnt;
00595     var_out->srt[dmn_out_idx]=dmn_out[dmn_out_idx]->srt;
00596     var_out->end[dmn_out_idx]=dmn_out[dmn_out_idx]->end;
00597     var_out->srd[dmn_out_idx]=dmn_out[dmn_out_idx]->srd;
00598   } /* end loop over dmn_out */
00599   
00600   if(var_out->is_rec_var){
00601     /* Which dimension in output dimension list is scheduled to be record dimension? */
00602     for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++)
00603       if(dmn_out[dmn_out_idx]->is_rec_dmn) break;
00604     if(dmn_out_idx != dmn_out_nbr){
00605       dmn_idx_rec_out=dmn_out_idx;
00606     }else{
00607       (void)fprintf(stdout,"%s: ERROR %s did not find record dimension in variable %s which claims to be record variable\n",prg_nm_get(),fnc_nm,var_in->nm);
00608       nco_exit(EXIT_FAILURE);
00609     } /* end else */
00610     /* Request that first dimension be record dimension */
00611     rec_dmn_nm_out=dmn_out[0]->nm;
00612     if(dmn_idx_rec_out != 0) (void)fprintf(stdout,"%s: INFO %s for variable %s reports old input record dimension %s is now ordinal dimension %d, new record dimension must be %s\n",prg_nm_get(),fnc_nm,var_in->nm,dmn_out[dmn_idx_rec_out]->nm,dmn_idx_rec_out,dmn_out[0]->nm);
00613   } /* endif record variable */
00614 
00615   if(dbg_lvl_get() > 3){
00616     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++)
00617       (void)fprintf(stdout,"%s: DEBUG %s variable %s re-order maps dimension %s from (ordinal,ID)=(%d,%d) to (%d,unknown)\n",prg_nm_get(),fnc_nm,var_in->nm,var_in->dim[dmn_in_idx]->nm,dmn_in_idx,var_in->dmn_id[dmn_in_idx],dmn_idx_in_out[dmn_in_idx]);
00618   } /* endif dbg */
00619   
00620   return rec_dmn_nm_out;
00621 } /* end nco_var_dmn_rdr_mtd() */ 

int nco_var_dmn_rdr_val const var_sct *const   var_in,
var_sct *const   var_out,
const int *const   dmn_idx_out_in,
const nco_bool *const   dmn_rvr_in
 

Definition at line 625 of file nco_cnf_dmn.c.

References dmn_sct_tag::cnt, var_sct_tag::cnt, dbg_lvl_get(), var_sct_tag::dim, var_sct_tag::dmn_id, dmn_sct_tag::end, False, var_sct_tag::has_dpl_dmn, dmn_sct_tag::id, var_sct_tag::nbr_dim, NC_MAX_DIMS, nco_bool, nco_typ_lng(), dmn_sct_tag::nm, var_sct_tag::nm, prg_nm_get(), dmn_sct_tag::srd, dmn_sct_tag::srt, var_sct_tag::sz, True, var_sct_tag::val, and ptr_unn::vp.

Referenced by main().

00629 {
00630   /* Purpose: Re-order values in given variable according to supplied dimension map
00631      Description of re-ordering concepts is in nco_var_dmn_rdr_mtd()
00632      Description of actual re-ordering algorithm is in nco_var_dmn_rdr_val() */
00633 
00634   nco_bool IDENTITY_REORDER=False; /* [flg] User requested identity re-ordering */
00635 
00636   char *val_in_cp; /* [ptr] Input data location as char pointer */
00637   char *val_out_cp; /* [ptr] Output data location as char pointer */
00638   
00639   const char fnc_nm[]="nco_var_dmn_rdr_val()"; /* [sng] Function name */
00640 
00641   dmn_sct **dmn_in=NULL; /* [sct] List of dimension structures in input order */
00642   dmn_sct **dmn_out; /* [sct] List of dimension structures in output order */
00643   
00644   int *dmn_id_out; /* [id] Contiguous vector of dimension IDs */
00645   int dmn_idx; /* [idx] Index over dimensions */
00646   int dmn_in_idx; /* [idx] Counting index for dmn_in */
00647   int dmn_in_nbr; /* [nbr] Number of dimensions in input variable */
00648   int dmn_in_nbr_m1; /* [nbr] Number of dimensions in input variable, less one */
00649   int dmn_out_idx; /* [idx] Counting index for dmn_out */
00650   int dmn_out_nbr; /* [nbr] Number of dimensions in output variable */
00651   int rcd=0; /* [rcd] Return code */
00652   int typ_sz; /* [B] Size of data element in memory */
00653   
00654   long dmn_in_map[NC_MAX_DIMS]; /* [idx] Map for each dimension of input variable */
00655   long dmn_out_map[NC_MAX_DIMS]; /* [idx] Map for each dimension of output variable */
00656   long dmn_in_sbs[NC_MAX_DIMS]; /* [idx] Dimension subscripts into N-D input array */
00657   long var_in_lmn; /* [idx] Offset into 1-D input array */
00658   long var_out_lmn; /* [idx] Offset into 1-D output array */
00659   long *var_in_cnt; /* [nbr] Number of valid elements in this dimension (including effects of stride and wrapping) */
00660   long var_sz; /* [nbr] Number of elements (NOT bytes) in hyperslab (NOT full size of variable in input file!) */
00661   
00662   /* Initialize variables to reduce indirection */
00663   /* NB: Number of input and output dimensions are equal for pure re-orders
00664      However, keep dimension numbers in separate variables to ease relax this rule in future */
00665   dmn_in_nbr=var_in->nbr_dim;
00666   dmn_out_nbr=var_out->nbr_dim;
00667   
00668   /* On entry to this section of code, we assume:
00669      1. var_out metadata are re-ordered
00670      2. var_out->val buffer has been allocated (calling routine must do this) */
00671 
00672   /* Get ready to re-order */
00673   dmn_id_out=var_out->dmn_id;
00674   dmn_in_nbr_m1=dmn_in_nbr-1;
00675   dmn_out=var_out->dim;
00676   dmn_in=var_in->dim;
00677   typ_sz=nco_typ_lng(var_out->type);
00678   val_in_cp=(char *)var_in->val.vp;
00679   val_out_cp=(char *)var_out->val.vp;
00680   var_in_cnt=var_in->cnt;
00681   var_sz=var_in->sz;
00682   
00683   /* As explained in nco_var_dmn_rdr_mtd(),
00684      "Hence, we must re-update dmn_out->id after nco_dmn_dfn() in nco_cnf_dmn_rdr_val()
00685      Structures should be completely consisten at that point
00686      Not updating these structures (at least dmn_out->id) is equivalent to assuming that
00687      dmn_out->id does not depend on record dimension identity, which is an ASSUMPTION
00688      that may currently be true, but is not guaranteed by the netCDF API to always be true." */
00689   for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++){
00690     /* NB: Change dmn_id,cnt,srt,end,srd together to minimize chances of forgetting one */
00691     var_out->dmn_id[dmn_out_idx]=dmn_out[dmn_out_idx]->id;
00692     var_out->cnt[dmn_out_idx]=dmn_out[dmn_out_idx]->cnt;
00693     var_out->srt[dmn_out_idx]=dmn_out[dmn_out_idx]->srt;
00694     var_out->end[dmn_out_idx]=dmn_out[dmn_out_idx]->end;
00695     var_out->srd[dmn_out_idx]=dmn_out[dmn_out_idx]->srd;
00696   } /* end loop over dmn_out */
00697   
00698   /* Report full metadata re-order, if requested */
00699   if(dbg_lvl_get() > 3){
00700     int dmn_idx_in_out[NC_MAX_DIMS]; /* [idx] Dimension correspondence, input->output */
00701     /* Create reverse correspondence */
00702     for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++)
00703       dmn_idx_in_out[dmn_idx_out_in[dmn_out_idx]]=dmn_out_idx;
00704   
00705     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++)
00706       (void)fprintf(stdout,"%s: DEBUG %s variable %s re-order maps dimension %s from (ordinal,ID)=(%d,%d) to (%d,%d)\n",prg_nm_get(),fnc_nm,var_in->nm,var_in->dim[dmn_in_idx]->nm,dmn_in_idx,var_in->dmn_id[dmn_in_idx],dmn_idx_in_out[dmn_in_idx],var_out->dmn_id[dmn_idx_in_out[dmn_in_idx]]);
00707   } /* endif dbg */
00708   
00709   /* Is identity re-ordering requested? */
00710   for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++)
00711     if(dmn_out_idx != dmn_idx_out_in[dmn_out_idx]) break;
00712   if(dmn_out_idx == dmn_out_nbr) IDENTITY_REORDER=True;
00713 
00714   /* Dimension reversal breaks identity re-ordering */
00715   if(IDENTITY_REORDER){
00716     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++)
00717       if(dmn_rvr_in[dmn_in_idx]) break;
00718     if(dmn_in_idx != dmn_in_nbr) IDENTITY_REORDER=False;
00719   } /* !IDENTITY_REORDER */
00720 
00721   if(IDENTITY_REORDER){
00722     if(dbg_lvl_get() > 2) (void)fprintf(stdout,"%s: INFO %s reports re-order is identity transformation for variable %s\n",prg_nm_get(),fnc_nm,var_in->nm);
00723     /* Copy in one fell swoop then return */
00724     (void)memcpy((void *)(var_out->val.vp),(void *)(var_in->val.vp),var_out->sz*nco_typ_lng(var_out->type));
00725     return rcd;
00726   } /* !IDENTITY_REORDER */
00727 
00728   if(var_in->has_dpl_dmn) (void)fprintf(stdout,"%s: WARNING %s reports non-identity re-order for variable with duplicate dimensions %s.\n%s does not support non-identity re-orders of variables with duplicate dimensions\n",prg_nm_get(),fnc_nm,var_in->nm,prg_nm_get());
00729 
00730   /* Compute map for each dimension of input variable */
00731   for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++) dmn_in_map[dmn_in_idx]=1L;
00732   for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr-1;dmn_in_idx++)
00733     for(dmn_idx=dmn_in_idx+1;dmn_idx<dmn_in_nbr;dmn_idx++)
00734       dmn_in_map[dmn_in_idx]*=var_in->cnt[dmn_idx];
00735   
00736   /* Compute map for each dimension of output variable */
00737   for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++) dmn_out_map[dmn_out_idx]=1L;
00738   for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr-1;dmn_out_idx++)
00739     for(dmn_idx=dmn_out_idx+1;dmn_idx<dmn_out_nbr;dmn_idx++)
00740       dmn_out_map[dmn_out_idx]*=var_out->cnt[dmn_idx];
00741   
00742   /* There is more than one method to re-order dimensions
00743      Output dimensionality is known in advance, unlike nco_var_avg()
00744      Hence outer loop may be over dimensions or over elements
00745      Method 1: Loop over input elements 
00746      1a. Loop over 1-D input array offsets
00747      1b. Invert 1-D input array offset to get N-D input subscripts
00748      1c. Turn N-D input subscripts into N-D output subscripts
00749      1d. Map N-D output subscripts to get 1-D output element
00750      1e. Copy input element to output element
00751      This method is simplified from method used in nco_var_avg()
00752      Method 2: Loop over input dimensions
00753      1a. Loop over input dimensions, from slowest to fastest varying
00754      1b. 
00755    */
00756 
00757   /* Begin Method 1: Loop over input elements */
00758   /* var_in_lmn is offset into 1-D array */
00759   for(var_in_lmn=0;var_in_lmn<var_sz;var_in_lmn++){
00760 
00761     /* dmn_in_sbs are corresponding indices (subscripts) into N-D array */
00762     dmn_in_sbs[dmn_in_nbr_m1]=var_in_lmn%var_in_cnt[dmn_in_nbr_m1];
00763     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr_m1;dmn_in_idx++){
00764       dmn_in_sbs[dmn_in_idx]=(long)(var_in_lmn/dmn_in_map[dmn_in_idx]);
00765       dmn_in_sbs[dmn_in_idx]%=var_in_cnt[dmn_in_idx];
00766     } /* end loop over dimensions */
00767 
00768     /* Dimension reversal:
00769        Reversing a dimension changes subscripts along that dimension
00770        Consider dimension of size N indexed by [0,1,2,...k-1,k,k+1,...,N-2,N-1] 
00771        Reversal maps element k to element N-1-k=N-k-1 
00772        Enhance speed by using that all elements along dimension share reversal */
00773     for(dmn_in_idx=0;dmn_in_idx<dmn_in_nbr;dmn_in_idx++)
00774       if(dmn_rvr_in[dmn_in_idx]) dmn_in_sbs[dmn_in_idx]=var_in_cnt[dmn_in_idx]-dmn_in_sbs[dmn_in_idx]-1;
00775 
00776     /* Map variable's N-D array indices to get 1-D index into output data */
00777     var_out_lmn=0L;
00778     for(dmn_out_idx=0;dmn_out_idx<dmn_out_nbr;dmn_out_idx++) 
00779       var_out_lmn+=dmn_in_sbs[dmn_idx_out_in[dmn_out_idx]]*dmn_out_map[dmn_out_idx];
00780 
00781     /* Copy current input element into its slot in output array */
00782     (void)memcpy(val_out_cp+var_out_lmn*typ_sz,val_in_cp+var_in_lmn*typ_sz,(size_t)typ_sz);
00783   } /* end loop over var_in_lmn */
00784   /* End Method 1: Loop over input elements */
00785 
00786   /* Begin Method 2: Loop over input dimensions */
00787   /* End Method 2: Loop over input dimensions */
00788 
00789   return rcd;
00790 } /* end nco_var_dmn_rdr_val() */ 


Generated on Thu Mar 16 18:15:19 2006 for nco by  doxygen 1.4.4