Beispiel #1
0
int /* [rcd] Successful conversion returns NCO_NOERR */
nco_cln_clc_org /* [fnc] Difference between two generic co-ordinate units */
(const char *fl_unt_sng, /* I [ptr] Source value (optional) and source units */
 const char *fl_bs_sng, /* I [ptr] Target units */
 nco_cln_typ lmt_cln, /* [enm] Calendar type, if any, of coordinate variable */ 
 double *og_val) /* O [ptr] Target value in units stored on disk */
{
  /* Purpose:
     Given a value expressed source units (fl_unit_sng) and target units to switch to, 
     determine and return the value expressed in the target units. */

  int rcd;
  
  char *usr_unt_sng;   
  double crr_val;
  
  /* If units contain date or timestamp call special time-conversion routine */
  if(strcasestr(fl_bs_sng," from ") || strcasestr(fl_bs_sng," since ") || strcasestr(fl_bs_sng," after ")){
    rcd=nco_cln_clc_tm(fl_unt_sng,fl_bs_sng,lmt_cln,og_val);   
    return rcd;
  } /* endif */
  
  /* Regular conversion of fl_unt_sng of form <double_value units>, e.g., '10 inches', '100 ft' */
  usr_unt_sng=(char *)nco_calloc(strlen(fl_unt_sng)+1L, sizeof(char));
  sscanf(fl_unt_sng,"%lg %s",&crr_val,usr_unt_sng);
  rcd=nco_cln_clc_dff(usr_unt_sng,fl_bs_sng,crr_val,og_val);
  usr_unt_sng=(char *)nco_free(usr_unt_sng);
  return rcd;  
} /* end nco_cln_clc_org() */
Beispiel #2
0
int                                  /* O [rcd] Return code */
nco_cnv_cf_cll_mth_add               /* [fnc] Add cell_methods attributes */
(const int out_id,                   /* I [id] netCDF file ID */
 var_sct * const * const var,        /* I [sct] Variable to reduce (e.g., average) (destroyed) */
 const int var_nbr,                  /* I [nbr] Number of variables to be defined */
 dmn_sct * const * const dmn_rdc,    /* I [sct] Dimensions over which to reduce variable */
 const int dmn_nbr_rdc,              /* I [sct] Number of dimensions to reduce variable over */
 const int nco_op_typ,               /* I [enm] Operation type, default is average */
 gpe_sct *gpe,                       /* I [sng] Group Path Editing (GPE) structure */
 const clm_bnd_sct * const cb,       /* I [sct] Climatology bounds structure */
 const trv_tbl_sct * const trv_tbl)  /* I [sct] Traversal table */
{
  /* Purpose: Add/modify CF cell_methods attribute
     http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/build/cf-conventions.html#cell-methods
     
     cell_methods values and description:
     point	: Data values are representative of points in space or time (instantaneous)
     sum	: Data values are representative of a sum or accumulation over the cell
     maximum_absolute_value	: Maximum absolute value
     maximum	: Maximum
     median	: Median
     mid_range	: Average of maximum and minimum
     minimum	: Minimum
     mean	: Mean (average value)
     mode	: Mode (most common value)
     range	: Absolute difference between maximum and minimum
     standard_deviation : Standard deviation
     variance	: Variance
     
     NCO operation types:
     avg Mean value
     avgsqr Mean of sum of squares
     mabs Maximum absolute value
     max Maximum value
     mebs Mean absolute value
     mibs Minimum absolute value
     min Minimum value
     rms Root-mean-square (normalized by N)
     rmssdn Root-mean square (normalized by N-1)
     sqravg Square of the mean
     sqrt Square root of the mean
     ttl Sum of values */

  const char fnc_nm[]="nco_cnv_cf_cll_mth_add()"; /* [sng] Function name */

  aed_sct aed; /* [sct] Attribute-edit information */

  char att_op_sng[23]; /* [sng] Operation type (longest is nco_op_mabs which translates to "maximum_absolute_value") */

  char *att_val=NULL; /* [sng] Coordinates attribute */
  char *att_val_cpy; /* [sng] Copy of attribute */
  char *grp_out_fll=NULL; /* [sng] Group name */
  char *sbs_ptr; /* [sng] Pointer to substring */
  char *cll_mth_clm; /* [sng] Cell methods for climatology */
  
  int *dmn_mch; /* [idx] Indices of dimensions reduced in this variable */

  int dmn_idx_rdc;
  int dmn_idx_var;
  int dmn_nbr_mch; /* [nbr] Number of dimension names to add to cell_methods */
  int grp_out_id; /* [ID] Group ID (output) */
  int nco_op_typ_lcl; /* [enm] Operation type, default is average */
  int rcd=NC_NOERR; /* [rcd] Return code */
  int var_idx;
  int var_out_id; /* [ID] Variable ID (output) */

  long int att_lng; /* [nbr] Length of attribute string */
  
  nc_type att_typ; /* [nbr] Attribute type */

  nco_bool FIRST_WARNING=True;
  nco_bool flg_dpl; /* [flg] New cell_methods attribute duplicates old */
  nco_bool mlt_dmn_rdc; /* [flg] Multiple dimension reduction flag */

  size_t dmn_sng_lng; /* [nbr] Length of dimension string */
  size_t sbs_sng_lng; /* [nbr] Length of substring */

  trv_sct *var_trv=NULL;  /* [sct] Variable GTT object */

  /* Initialize unchanging structure members */
  aed.att_nm=strdup("cell_methods");
  aed.type=NC_CHAR;

  /* Allocate space for maximum number of matching dimensions */
  dmn_mch=(int *)nco_calloc(dmn_nbr_rdc,sizeof(int));

  if(cb){
    if(cb->bnd2clm || cb->clm2clm) cll_mth_clm=strdup("time: mean within years time: mean over years");
    if(cb->clm2bnd) cll_mth_clm=strdup("time: mean");
  } /* !cb */

  /* Process all variables */
  for(var_idx=0;var_idx<var_nbr;var_idx++){ 

    /* Obtain variable GTT object using full variable name */
    var_trv=trv_tbl_var_nm_fll(var[var_idx]->nm_fll,trv_tbl);

    /* Edit group name for output */
    if(gpe) grp_out_fll=nco_gpe_evl(gpe,var_trv->grp_nm_fll); else grp_out_fll=(char *)strdup(var_trv->grp_nm_fll);

    /* Obtain output group ID */
    (void)nco_inq_grp_full_ncid(out_id,grp_out_fll,&grp_out_id);

    /* Memory management after current extracted group */
    if(grp_out_fll) grp_out_fll=(char *)nco_free(grp_out_fll);

    /* Get variable ID */
    (void)nco_inq_varid(grp_out_id,var_trv->nm,&var_out_id);

    /* Initialize attribute-edit structure for this variable */
    aed.var_nm=var_trv->nm;
    aed.id=var_out_id;
    aed.sz=0L;
    dmn_nbr_mch=0;
    flg_dpl=False;

    if(cb){
      /* Does variable use time coordinate? */
      for(dmn_idx_var=0;dmn_idx_var<var_trv->nbr_dmn;dmn_idx_var++)
	if(!strcmp(var_trv->var_dmn[dmn_idx_var].dmn_nm,cb->tm_crd_nm)) break;
      if(dmn_idx_var < var_trv->nbr_dmn){
	/* Stamp with appropriate cell_methods temporal attribute */
	att_val=strdup(cll_mth_clm);
	aed.sz=strlen(att_val);
	aed.type=NC_CHAR;
	aed.val.cp=att_val;
	aed.mode=aed_overwrite;
	(void)nco_aed_prc(grp_out_id,var_out_id,aed);
	if(att_val) att_val=(char *)nco_free(att_val);
	continue;
      } /* !dmn_idx_var */
    } /* !cb */

    /* cell_methods format: blank-separated phrases of form "dmn1[, dmn2[...]]: op_typ", e.g., "lat, lon: mean" */ 
    for(dmn_idx_var=0;dmn_idx_var<var_trv->nbr_dmn;dmn_idx_var++){
      for(dmn_idx_rdc=0;dmn_idx_rdc<dmn_nbr_rdc;dmn_idx_rdc++){
        assert(dmn_rdc[dmn_idx_rdc]->nm_fll);
        /* Compare full names */
        if(!strcmp(var_trv->var_dmn[dmn_idx_var].dmn_nm_fll,dmn_rdc[dmn_idx_rdc]->nm_fll)){
          /* Add length of each matching dimension to accumulating attribute size */
          aed.sz+=strlen(dmn_rdc[dmn_idx_rdc]->nm);
          dmn_mch[dmn_nbr_mch++]=dmn_idx_rdc;
        } /* !match */
      } /* dmn_idx_rdc */
    } /* dmn_idx_var */

    assert(dmn_nbr_mch > 0);

    /* Preserve rule to always return averages (never extrema or other statistics) of coordinates */
    if(var[var_idx]->is_crd_var) nco_op_typ_lcl=nco_op_avg; else nco_op_typ_lcl=nco_op_typ;

    /* NUL-terminate before concatenation */
    att_op_sng[0]='\0';
    switch(nco_op_typ_lcl){
      /* Next five operations are defined in CF Conventions */
    case nco_op_avg: strcpy(att_op_sng,"mean"); break;
    case nco_op_min: strcpy(att_op_sng,"minimum"); break;
    case nco_op_max: strcpy(att_op_sng,"maximum"); break;
    case nco_op_ttl: strcpy(att_op_sng,"sum"); break;
    case nco_op_avgsqr: strcpy(att_op_sng,"variance"); break; /* Mean of sum of squares */
      /* Remaining operations are supported by NCO yet are not in CF Conventions */
    case nco_op_mabs: strcpy(att_op_sng,"maximum_absolute_value"); break; /* Maximum absolute value */
    case nco_op_mebs: strcpy(att_op_sng,"mean_absolute_value"); break; /* Mean absolute value */
    case nco_op_mibs: strcpy(att_op_sng,"minimum_absolute_value"); break; /* Minimum absolute value */
    case nco_op_sqravg: strcpy(att_op_sng,"square_of_mean"); break; /* Square of mean */
    case nco_op_sqrt: strcpy(att_op_sng,"square_root_of_mean"); break; /* Square root of mean */ 
    case nco_op_rms: strcpy(att_op_sng,"root_mean_square"); break; /* Root-mean-square (normalized by N) */
    case nco_op_rmssdn: strcpy(att_op_sng,"root_mean_square_nm1"); break; /* Root-mean square normalized by N-1 */
    case nco_op_nil: /* nco_op_nil, Undefined operation type */
      if(nco_dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stdout,"%s: DEBUG %s reports variable %s cell_method not implemented for operation %d\n",nco_prg_nm_get(),fnc_nm,var_trv->nm_fll,nco_op_typ);
      continue;
    } /* End switch */

    /* Initialize to size of ": " plus length of operation string */
    aed.sz+=2L+strlen(att_op_sng);
    /* Add room for commas and spaces, i.e., "dmn1, dmn2, dmn3" */
    if(dmn_nbr_mch > 1) aed.sz+=2*(dmn_nbr_mch-1);
    /* Add room for NUL-terminator */
    aed.val.cp=(char *)nco_malloc((aed.sz+1L)*sizeof(char));
    aed.val.cp[0]='\0';

    /* Build single string by concatenating known matches */
    for(int dmn_idx_mch=0;dmn_idx_mch<dmn_nbr_mch;dmn_idx_mch++){
      (void)strcat(aed.val.cp,dmn_rdc[dmn_mch[dmn_idx_mch]]->nm);
      if(dmn_idx_mch<dmn_nbr_mch-1) (void)strcat(aed.val.cp,", ");
    } /* dmn_idx_mch>=dmn_nbr_mch */
    (void)strcat(aed.val.cp,": ");
    (void)strcat(aed.val.cp,att_op_sng);

    /* 20150625: Older versions of CAM, e.g., CAM3, used "cell_method" instead of "cell_methods" 
       If old attribute is not deleted then the output file will contain both attributes
       Does variable already have "cell_method" attribute? */
    strcpy(aed.att_nm,"cell_method");
    rcd=nco_inq_att_flg(grp_out_id,var_out_id,aed.att_nm,&att_typ,&att_lng);
    if(rcd == NC_NOERR){
      if(FIRST_WARNING) (void)fprintf(stderr,"%s: WARNING: Variable \"%s\" uses the non-standard attribute name \"cell_method\" instead of \"cell_methods\", the correct attribute name. The CAM3 model (and others?) have this problem. Expect \"double attributes\" in output. This message is printed only once per invocation, although the problem likely occurs in more variables.\n",nco_prg_nm_get(),aed.var_nm);
      FIRST_WARNING=False;
    } /* endif attribute exists */

    /* Does variable already have "cell_methods" attribute? */
    strcpy(aed.att_nm,"cell_methods");
    rcd=nco_inq_att_flg(grp_out_id,var_out_id,aed.att_nm,&att_typ,&att_lng);
    if(rcd == NC_NOERR){
      if(att_typ == NC_STRING) (void)fprintf(stderr,"%s: WARNING %s reports existing cell_methods attribute for variable %s is type NC_STRING. Unpredictable results...\n",nco_prg_nm_get(),fnc_nm,aed.var_nm);
      if(att_typ != NC_STRING && att_typ != NC_CHAR) (void)fprintf(stderr,"%s: WARNING %s reports existing cell_methods attribute for variable %s is type %s. Unpredictable results...\n",nco_prg_nm_get(),fnc_nm,aed.var_nm,nco_typ_sng(att_typ));

      /* Often climatologies are multiply-averaged over time
	 NCO's treatment of this has changed with time
	 pre-20140131: 
	 NCO has no special treatment of cell_methods
	 20140131: 
	 First NCO implementation (ncra, ncea, ncwa) of cell_methods with 
	 20150625: 
	 For example, climate model output is often archived as monthly means in each gridcell
	 cell_methods attributes of these monthly data begin as "time: mean" (i.e., monthly mean).
	 We then create a climatology by a sequence of one or two more temporal-averaging steps
	 The one-step method puts all the months in the hopper and averages those
	 Variables in the resultiing file may have cell_methods = "time: mean time: mean"
	 The two-step method first averages the months into four climatological seasons
	 Then it averages those four seasons into the climatological annual mean
	 Variables in the resultiing file may have cell_methods = "time: mean time: mean time: mean"
	 To avoid this redundancy, we check that the new cell_method does not duplicate the old 
	 If it would, then skip adding the new
	 20160418: 
	 Treatment of multiply-time-averaged quantities requires climatology bounds attribute
	 One-step methods (e.g., monthly mean) should have time-bounds attribute
	 cell_methods = "time: mean"
	 Two-step methods (e.g., climatological March) should have climatology-bounds attribute
	 cell_methods = "time: mean within years time: mean over years"
	 Three-step methods (e.g., climatological MAM) should have climatology-bounds attribute
	 cell_methods = "time: mean within years time: mean over years"
	 Four-step methods (e.g., climatological ANN) should have time-bounds attribute
	 cell_methods = "time: mean" */
      ptr_unn val_old; /* [sng] Old cell_methods attribute */
      val_old.vp=(void *)nco_malloc((att_lng+1L)*sizeof(char));
      (void)nco_get_att(grp_out_id,var_out_id,aed.att_nm,val_old.vp,NC_CHAR);
      val_old.cp[att_lng]='\0';
      if(strstr(val_old.cp,aed.val.cp)) flg_dpl=True;
      if(val_old.vp) val_old.vp=(void *)nco_free(val_old.vp);
      
      aed.mode=aed_append;
      /* Insert space between existing attribute and appended attribute */
      att_val_cpy=(char *)strdup(aed.val.cp);
      /* Add one for space character */
      aed.sz++;
      /* Add one for NUL-terminator */
      aed.val.cp=(char *)nco_realloc(aed.val.cp,(aed.sz+1L)*sizeof(char));
      aed.val.cp[0]=' ';
      aed.val.cp[1]='\0';
      (void)strncat(aed.val.cp,att_val_cpy,aed.sz-1L);
      if(att_val_cpy) att_val_cpy=(char *)nco_free(att_val_cpy);
    }else{ /* !cell_methods attribute already exists */
      aed.mode=aed_create;
    } /* !cell_methods attribute already exists */

    /* Edit attribute */
    if(!flg_dpl) (void)nco_aed_prc(grp_out_id,var_out_id,aed);

    /* 20150308 */
    /* Does variable already have "coordinates" attribute?
       NB: This reuses att_nm which has only enough space to hold "cell_methods" */
    strcpy(aed.att_nm,"coordinates");
    rcd=nco_inq_att_flg(grp_out_id,var_out_id,aed.att_nm,&att_typ,&att_lng);
    if(rcd == NC_NOERR && att_typ == NC_CHAR){
      /* Remove reduced dimensions from coordinates string
	 coordinates format: blank-separated names of form "dmn1 [dmn2 [...]] dmnN", e.g., "time lat lon" */ 
      /* Add room for NUL-terminator */
      att_val=(char *)nco_malloc((att_lng+1L)*sizeof(char));
      rcd=nco_get_att(grp_out_id,var_out_id,aed.att_nm,att_val,att_typ);
      /* Reset value from previous use */
      aed.val.cp[0]='\0';
      att_val[att_lng]='\0';
      mlt_dmn_rdc=False;
      assert(rcd == NC_NOERR);
      for(dmn_idx_var=0;dmn_idx_var<var_trv->nbr_dmn;dmn_idx_var++){
	for(dmn_idx_rdc=0;dmn_idx_rdc<dmn_nbr_rdc;dmn_idx_rdc++){
	  /* Is reduced dimension in variable? */
	  if(!strcmp(var_trv->var_dmn[dmn_idx_var].dmn_nm_fll,dmn_rdc[dmn_idx_rdc]->nm_fll)){
	    if(mlt_dmn_rdc){
	      /* At least one other dimension has already been reduced/excised
		 Hence multiple dimensions of this variable may be reduced
		 Start next excision from ending point of last excision, not from disk-values */
	      strcpy(att_val,aed.val.cp);
	      att_lng=strlen(aed.val.cp);
	    } /* endif */
	    /* Is dimension in current (possibly locally-modified) "coordinates" attribute? NB: Assume short name not full name */
	    if((sbs_ptr=strstr(att_val,dmn_rdc[dmn_idx_rdc]->nm))){
	      /* Is this the only dimension in "coordinates" attribute? */
	      if(!strcmp(dmn_rdc[dmn_idx_rdc]->nm,att_val)){
		/* Variable will become scalar so delete "coordinates" attribute */
		aed.mode=aed_delete;
	      }else{ /* endif scalar */
		/* Excise dimension from "coordinates" attribute */
		dmn_sng_lng=strlen(dmn_rdc[dmn_idx_rdc]->nm);
		sbs_sng_lng=(size_t)(sbs_ptr-att_val);
		aed.mode=aed_overwrite;
		/* Remove whitespace immediately following excised dimension, i.e., count it as part of dimension string
		   True for all dimensions except final dimension (trailed by a NUL, not a space) */
		if(sbs_ptr[dmn_sng_lng] == ' ') dmn_sng_lng++;
		aed.sz=att_lng-dmn_sng_lng;
		/* Add one for NUL-terminator */
		aed.val.cp=(char *)nco_realloc(aed.val.cp,(aed.sz+1L)*sizeof(char));
		strncpy(aed.val.cp,att_val,sbs_sng_lng);
		aed.val.cp[sbs_sng_lng]='\0';
		strcat(aed.val.cp,sbs_ptr+dmn_sng_lng);
	      } /* endelse scalar */
	      /* Edit attribute */
	      (void)nco_aed_prc(grp_out_id,var_out_id,aed);
	      mlt_dmn_rdc=True;
	    } /* !match attribute */
	  } /* !match variable */
	} /* dmn_idx_rdc */
      } /* dmn_idx_var */
    } /* endif attribute exists */

    if(att_val) att_val=(char *)nco_free(att_val);
    if(aed.val.cp) aed.val.cp=(char *)nco_free(aed.val.cp);

  } /* var_idx >= var_nbr */

  if(aed.att_nm) aed.att_nm=(char *)nco_free(aed.att_nm);
  if(dmn_mch) dmn_mch=(int *)nco_free(dmn_mch);

  return NC_NOERR;

} /* end nco_cnv_cf_cll_mth_add() */