cdf_write.c 48.2 KB
Newer Older
1 2
#ifdef HAVE_CONFIG_H
#include "config.h"
3 4 5 6 7 8 9 10 11 12 13
#endif

#ifdef HAVE_LIBNETCDF

#include "dmemory.h"
#include "cdi.h"
#include "cdi_int.h"
#include "stream_cdf.h"
#include "cdf.h"
#include "cdf_int.h"
#include "vlist.h"
14
#include "vlist_var.h"
15 16


Uwe Schulzweida's avatar
Uwe Schulzweida committed
17
void cdfDefVarDeflate(int ncid, int ncvarID, int deflate_level)
18
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
19
#ifdef HAVE_NETCDF4
20 21
  // Set chunking, shuffle, and deflate.
  const int shuffle = 1, deflate = 1;
22

23
  if (deflate_level < 1 || deflate_level > 9) deflate_level = 1;
24

25 26
  int status;
  if ((status = nc_def_var_deflate(ncid, ncvarID, shuffle, deflate, deflate_level)))
27
    {
28
      Error("nc_def_var_deflate failed; %s", nc_strerror(status));
29 30
    }
#else
31
  static bool lwarn = true;
32
  if (lwarn)
33
    {
34
      lwarn = false;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
35
      Warning("Deflate compression failed, NetCDF4 not available!");
36 37 38 39
    }
#endif
}

40 41
void cdfDefVarSzip(int ncid, int ncvarID, int pixels_per_block)
{
42
#ifdef HAVE_NC_DEF_VAR_SZIP
43 44 45 46 47 48 49 50 51
  // Set options_mask.
  /*
    H5_SZIP_ALLOW_K13_OPTION_MASK   1
    H5_SZIP_CHIP_OPTION_MASK        2
    H5_SZIP_EC_OPTION_MASK          4
    H5_SZIP_NN_OPTION_MASK          32
    H5_SZIP_ALL_MASKS (H5_SZIP_CHIP_OPTION_MASK|H5_SZIP_EC_OPTION_MASK|H5_SZIP_NN_OPTION_MASK)
  */
  int options_mask = 38;
52 53 54
  int status;
  if ((status = nc_def_var_szip(ncid, ncvarID, options_mask, pixels_per_block)))
    Error("nc_def_var_szip failed; %s", nc_strerror(status));
55 56 57 58 59 60 61 62 63 64
#else
  static bool lwarn = true;
  if (lwarn)
    {
      lwarn = false;
      Warning("Szip compression failed, NetCDF4/szlib not available!");
    }
#endif
}

65 66 67 68 69 70 71
#ifdef HAVE_NETCDF4
static
nc_type cdfTypeComplexFloat(stream_t *streamptr)
{
  if (streamptr->nc_complex_float_id == CDI_UNDEFID)
    {
      typedef struct complex_float { float r, i; } complex_float;
72
      const int fileID = streamptr->fileID;
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
      int nc_complex_id;
      int status;
      status = nc_def_compound(fileID, sizeof(complex_float), "complex_float", &nc_complex_id);
      if (status != NC_NOERR) Error("%s", nc_strerror(status));
      status = nc_insert_compound(fileID, nc_complex_id, "r", NC_COMPOUND_OFFSET(complex_float, r), NC_FLOAT);
      if (status != NC_NOERR) Error("%s", nc_strerror(status));
      status = nc_insert_compound(fileID, nc_complex_id, "i", NC_COMPOUND_OFFSET(complex_float, i), NC_FLOAT);
      if (status != NC_NOERR) Error("%s", nc_strerror(status));
      streamptr->nc_complex_float_id = nc_complex_id;
    }

  return (nc_type) streamptr->nc_complex_float_id;
}

static
nc_type cdfTypeComplexDouble(stream_t *streamptr)
{
  if (streamptr->nc_complex_double_id == CDI_UNDEFID)
    {
      typedef struct complex_double { double r, i; } complex_double;
      const int fileID  = streamptr->fileID;
      int nc_complex_id;
      int status;
      status = nc_def_compound(fileID, sizeof(complex_double), "complex_double", &nc_complex_id);
      if (status != NC_NOERR) Error("%s", nc_strerror(status));
      status = nc_insert_compound(fileID, nc_complex_id, "r", NC_COMPOUND_OFFSET(complex_double, r), NC_DOUBLE);
      if (status != NC_NOERR) Error("%s", nc_strerror(status));
      status = nc_insert_compound(fileID, nc_complex_id, "i", NC_COMPOUND_OFFSET(complex_double, i), NC_DOUBLE);
      if (status != NC_NOERR) Error("%s", nc_strerror(status));
      streamptr->nc_complex_double_id = nc_complex_id;
    }

  return (nc_type) streamptr->nc_complex_double_id;
}
#endif

109
nc_type cdfDefDatatype(int datatype, stream_t *streamptr)
110
{
111
  nc_type xtype = NC_FLOAT;
112

113
  if ( streamptr->filetype == CDI_FILETYPE_NC4 )
114
    {
115 116 117
      if      ( datatype == CDI_DATATYPE_INT8   ) xtype = NC_BYTE;
      else if ( datatype == CDI_DATATYPE_INT16  ) xtype = NC_SHORT;
      else if ( datatype == CDI_DATATYPE_INT32  ) xtype = NC_INT;
118
#ifdef  HAVE_NETCDF4
119 120 121
      else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_UBYTE;
      else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_USHORT;
      else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_UINT;
122 123
      else if ( datatype == CDI_DATATYPE_CPX32  ) xtype = cdfTypeComplexFloat(streamptr);
      else if ( datatype == CDI_DATATYPE_CPX64  ) xtype = cdfTypeComplexDouble(streamptr);
124
#else
125 126 127
      else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_SHORT;
      else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_INT;
      else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_INT;
128 129
      else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
        Error("CDI library does not support complex numbers with NetCDF4 classic!");
130
#endif
131
      else if ( datatype == CDI_DATATYPE_FLT64  ) xtype = NC_DOUBLE;
132
      else if ( datatype == CDI_DATATYPE_FLT32  ) xtype = NC_FLOAT;
133 134 135
    }
  else
    {
136 137 138 139 140 141 142
      if      ( datatype == CDI_DATATYPE_INT8   ) xtype = NC_BYTE;
      else if ( datatype == CDI_DATATYPE_INT16  ) xtype = NC_SHORT;
      else if ( datatype == CDI_DATATYPE_INT32  ) xtype = NC_INT;
      else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_SHORT;
      else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_INT;
      else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_INT;
      else if ( datatype == CDI_DATATYPE_FLT64  ) xtype = NC_DOUBLE;
143 144 145
      else if ( datatype == CDI_DATATYPE_FLT32  ) xtype = NC_FLOAT;
      else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
        Error("CDI library does not support complex numbers with NetCDF classic!");
146 147 148 149 150 151 152 153
    }

  return xtype;
}

static
void cdfDefVarMissval(stream_t *streamptr, int varID, int dtype, int lcheck)
{
154
  if ( streamptr->vars[varID].defmiss == false )
155
    {
156 157
      const int vlistID = streamptr->vlistID;
      const int fileID  = streamptr->fileID;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
158
      const int ncvarID = streamptr->vars[varID].ncvarid;
159
      const double missval = vlistInqVarMissval(vlistID, varID);
160 161 162

      if ( lcheck && streamptr->ncmode == 2 ) cdf_redef(fileID);

163
      nc_type xtype = cdfDefDatatype(dtype, streamptr);
164 165
      if ( xtype == NC_BYTE && missval > 127 && missval < 256 ) xtype = NC_INT;

166 167
      if ( lcheck == 0 ||
           streamptr->ncmode != 2 ||
168
           streamptr->filetype == CDI_FILETYPE_NC ||
Uwe Schulzweida's avatar
Uwe Schulzweida committed
169 170
           streamptr->filetype == CDI_FILETYPE_NC2||
           streamptr->filetype == CDI_FILETYPE_NC5 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
171
        cdf_put_att_double(fileID, ncvarID, "_FillValue", xtype, 1, &missval);
172

Uwe Schulzweida's avatar
Uwe Schulzweida committed
173
      cdf_put_att_double(fileID, ncvarID, "missing_value", xtype, 1, &missval);
174 175 176

      if ( lcheck && streamptr->ncmode == 2 ) cdf_enddef(fileID);

177
      streamptr->vars[varID].defmiss = true;
178 179 180 181
    }
}

static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
182
void cdfDefInstituteGlobal(const stream_t *streamptr)
183
{
184 185 186
  const int vlistID = streamptr->vlistID;
  const int fileID  = streamptr->fileID;
  const int instID  = vlistInqInstitut(vlistID);
187

188
  if ( instID != CDI_UNDEFID )
189 190 191 192
    {
      const char *longname = institutInqLongnamePtr(instID);
      if ( longname )
	{
193
	  const size_t len = strlen(longname);
194 195 196 197 198 199 200 201 202 203 204
	  if ( len > 0 )
	    {
	      if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
	      cdf_put_att_text(fileID, NC_GLOBAL, "institution", len, longname);
	      if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
	    }
	}
    }
}

static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
205
void cdfDefSourceGlobal(const stream_t *streamptr)
206
{
207 208 209
  const int vlistID = streamptr->vlistID;
  const int fileID  = streamptr->fileID;
  const int modelID = vlistInqModel(vlistID);
210

211
  if ( modelID != CDI_UNDEFID )
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    {
      const char *longname = modelInqNamePtr(modelID);
      if ( longname )
	{
          size_t len = strlen(longname);
	  if ( len > 0 )
	    {
	      if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
	      cdf_put_att_text(fileID, NC_GLOBAL, "source", len, longname);
	      if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
	    }
	}
    }
}

static inline
void *resizeBuf(void **buf, size_t *bufSize, size_t reqSize)
{
230
  if ( reqSize > *bufSize )
231 232 233 234 235 236 237
    {
      *buf = Realloc(*buf, reqSize);
      *bufSize = reqSize;
    }
  return *buf;
}

238 239 240 241 242 243
static
void cdfDefineCellMethods(stream_t *streamptr, int cdiID, int varID, int fileID, int ncvarID)
{
  taxis_t *taxis = &streamptr->tsteps[0].taxis;
  if (!taxis->has_bounds) return;

244
  const int time_varid = streamptr->basetime.ncvarid;
245 246 247
  char timeVarName[CDI_MAX_NAME];
  cdf_inq_varname(fileID, time_varid, timeVarName);

248
  const int stepType = vlistInqVarTsteptype(cdiID, varID);
249

Uwe Schulzweida's avatar
Uwe Schulzweida committed
250
  const char *cellMethod = NULL;
251 252 253 254 255 256 257 258
  if      (stepType == TSTEP_AVG)   cellMethod = "mean";
  else if (stepType == TSTEP_SUM)   cellMethod = "sum";
  else if (stepType == TSTEP_RANGE) cellMethod = "range";
  else if (stepType == TSTEP_MIN)   cellMethod = "minimum";
  else if (stepType == TSTEP_MAX)   cellMethod = "maximum";

  if (cellMethod)
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
259
      const char *attname = "cell_methods";
Uwe Schulzweida's avatar
Uwe Schulzweida committed
260
      char atttxt[CDI_MAX_NAME+10];
261 262 263 264
      sprintf(atttxt, "%s: %s", timeVarName, cellMethod);
      cdf_put_att_text(fileID, ncvarID, attname, strlen(atttxt), atttxt);
    }
}
265 266

void cdfDefineAttributes(int cdiID, int varID, int fileID, int ncvarID)
267 268 269 270 271 272 273 274
{
  int atttype, attlen;
  size_t len;
  char attname[CDI_MAX_NAME+1];
  void *attBuf = NULL;
  size_t attBufSize = 0;

  int natts;
275
  cdiInqNatts(cdiID, varID, &natts);
276

277
  for ( int iatt = 0; iatt < natts; ++iatt )
278
    {
279
      cdiInqAtt(cdiID, varID, iatt, attname, &atttype, &attlen);
280 281 282

      if ( attlen == 0 ) continue;

283
      if ( atttype == CDI_DATATYPE_TXT )
284
        {
285
          const size_t attSize = (size_t)attlen*sizeof(char);
286
          char *atttxt = (char *)resizeBuf(&attBuf, &attBufSize, attSize);
287
          cdiInqAttTxt(cdiID, varID, attname, attlen, atttxt);
288 289 290
          len = (size_t)attlen;
          cdf_put_att_text(fileID, ncvarID, attname, len, atttxt);
        }
291 292 293
      else if ( atttype == CDI_DATATYPE_INT8  || atttype == CDI_DATATYPE_UINT8  ||
                atttype == CDI_DATATYPE_INT16 || atttype == CDI_DATATYPE_UINT16 ||
                atttype == CDI_DATATYPE_INT32 || atttype == CDI_DATATYPE_UINT32 )
294
        {
295
          const size_t attSize = (size_t)attlen*sizeof(int);
296
          int *attint = (int *)resizeBuf(&attBuf, &attBufSize, attSize);
297
          cdiInqAttInt(cdiID, varID, attname, attlen, &attint[0]);
298
          len = (size_t)attlen;
299 300
          nc_type xtype = (atttype == CDI_DATATYPE_INT8)   ? NC_BYTE :
                          (atttype == CDI_DATATYPE_INT16)  ? NC_SHORT :
Uwe Schulzweida's avatar
Uwe Schulzweida committed
301
#ifdef  HAVE_NETCDF4
302 303 304
                          (atttype == CDI_DATATYPE_UINT8)  ? NC_UBYTE :
                          (atttype == CDI_DATATYPE_UINT16) ? NC_USHORT :
                          (atttype == CDI_DATATYPE_UINT32) ? NC_UINT :
305 306 307
#endif
                          NC_INT;
          cdf_put_att_int(fileID, ncvarID, attname, xtype, len, attint);
308
        }
309
      else if ( atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64 )
310
        {
311
          const size_t attSize = (size_t)attlen * sizeof(double);
312
          double *attflt = (double *)resizeBuf(&attBuf, &attBufSize, attSize);
313
          cdiInqAttFlt(cdiID, varID, attname, attlen, attflt);
314
          len = (size_t)attlen;
315
          if ( atttype == CDI_DATATYPE_FLT32 )
316
            {
317 318 319 320 321
              float attflt_sp[8];
              float *pattflt_sp = (len > 8) ? (float*) malloc(len*sizeof(float)) : attflt_sp;
              for ( size_t i = 0; i < len; ++i ) pattflt_sp[i] = (float)attflt[i];
              cdf_put_att_float(fileID, ncvarID, attname, NC_FLOAT, len, pattflt_sp);
              if (len > 8) free(pattflt_sp);
322 323 324 325 326
            }
          else
            cdf_put_att_double(fileID, ncvarID, attname, NC_DOUBLE, len, attflt);
        }
    }
327

328 329 330
  Free(attBuf);
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
331 332 333 334
static
void cdfDefineInstituteName(int vlistID, int varID, int fileID, int ncvarID)
{
  const int instID = vlistInqVarInstitut(vlistID, varID);
335
  if (instID != CDI_UNDEFID)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
336 337
    {
      const char *name = institutInqNamePtr(instID);
338
      if (name) cdf_put_att_text(fileID, ncvarID, "institution", strlen(name), name);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
339 340 341
    }
}

342 343 344 345 346
static
void cdfDefGlobalAtts(stream_t *streamptr)
{
  if ( streamptr->globalatts ) return;

347 348
  const int vlistID = streamptr->vlistID;
  const int fileID  = streamptr->fileID;
349

Uwe Schulzweida's avatar
Uwe Schulzweida committed
350 351
  cdfDefSourceGlobal(streamptr);
  cdfDefInstituteGlobal(streamptr);
352 353

  int natts;
354
  cdiInqNatts(vlistID, CDI_GLOBAL, &natts);
355 356 357

  if ( natts > 0 && streamptr->ncmode == 2 ) cdf_redef(fileID);

358
  cdfDefineAttributes(vlistID, CDI_GLOBAL, fileID, NC_GLOBAL);
359 360 361 362 363 364

  if ( natts > 0 && streamptr->ncmode == 2 ) cdf_enddef(fileID);

  streamptr->globalatts = 1;
}

365 366 367
static
void cdf_get_gmapvarname(int gridID, char *gmapvarname)
{
368
  int pgridID = gridID;
369 370 371
  char gmapname[CDI_MAX_NAME];
  int length = CDI_MAX_NAME;
  cdiInqKeyString(pgridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME, gmapname, &length);
372

373
  if ( !gmapname[0] )
374
    {
375
      const int projID = gridInqProj(gridID);
376 377 378
      if ( projID != CDI_UNDEFID )
        {
          pgridID = projID;
379 380
          length = CDI_MAX_NAME;
          cdiInqKeyString(pgridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME, gmapname, &length);
381
        }
382 383
    }

384 385 386 387 388
  if ( gmapname[0] )
    {
      length = CDI_MAX_NAME;
      cdiInqKeyString(pgridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_VARNAME, gmapvarname, &length);
    }
389 390
}

391 392 393 394
static
int nc_grid_index(stream_t *streamptr, int gridID)
{
  int index = 0;
395 396
  const int vlistID = streamptr->vlistID;
  const int ngrids = vlistNgrids(vlistID);
397 398 399 400 401 402 403 404
  for ( index = 0; index < ngrids; ++index )
    if ( streamptr->ncgrid[index].gridID == gridID ) break;

  assert(index < ngrids);

  return index;
}

405
static
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
int xtype2ppb(nc_type xtype)
{
  int ppb = 32;

  if      (xtype == NC_BYTE)   ppb = 8;
  else if (xtype == NC_SHORT)  ppb = 16;
#ifdef  HAVE_NETCDF4
  else if (xtype == NC_UBYTE)  ppb = 8;
  else if (xtype == NC_USHORT) ppb = 16;
#endif

  return ppb;
}

static
void cdfDefVarCompression(const stream_t *streamptr, int ncvarID, bool lchunk, nc_type xtype)
422 423 424 425 426
{
  if ( streamptr->comptype == CDI_COMPRESS_ZIP )
    {
      if ( lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C) )
        {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
427
          cdfDefVarDeflate(streamptr->fileID, ncvarID, streamptr->complevel);
428 429 430 431 432 433 434 435 436 437 438 439 440 441
        }
      else
        {
          if ( lchunk )
            {
              static bool lwarn = true;
              if ( lwarn )
                {
                  lwarn = false;
                  Warning("Deflate compression is only available for NetCDF4!");
                }
            }
        }
    }
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
  else if ( streamptr->comptype == CDI_COMPRESS_SZIP )
    {
      if ( lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C) )
        {
          cdfDefVarSzip(streamptr->fileID, ncvarID, xtype2ppb(xtype));
        }
      else
        {
          if ( lchunk )
            {
              static bool lwarn = true;
              if ( lwarn )
                {
                  lwarn = false;
                  Warning("SZIP compression is only available for NetCDF4!");
                }
            }
        }
    }
461 462
}

463
static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
464
void cdfDefVarPacking(const stream_t *streamptr, int ncvarID, nc_type xtype, int vlistID, int varID)
465
{
466
  //  if ( xtype == NC_BYTE || xtype == NC_SHORT || xtype == NC_INT )
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
    {
      const double addoffset   = vlistInqVarAddoffset(vlistID, varID);
      const double scalefactor = vlistInqVarScalefactor(vlistID, varID);
      const bool laddoffset   = IS_NOT_EQUAL(addoffset, 0);
      const bool lscalefactor = IS_NOT_EQUAL(scalefactor, 1);

      if ( laddoffset || lscalefactor )
        {
          nc_type astype = (xtype == NC_FLOAT) ? NC_FLOAT : NC_DOUBLE;
          if ( (astype == NC_DOUBLE) &&
               IS_EQUAL(addoffset,   (double) ((float) addoffset)) &&
               IS_EQUAL(scalefactor, (double) ((float) scalefactor)) )
            {
              astype = NC_FLOAT;
            }

Uwe Schulzweida's avatar
Uwe Schulzweida committed
483 484
          cdf_put_att_double(streamptr->fileID, ncvarID, "add_offset",   astype, 1, &addoffset);
          cdf_put_att_double(streamptr->fileID, ncvarID, "scale_factor", astype, 1, &scalefactor);
485 486 487 488
        }
    }
}

489
static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
490
void cdfAppendCoordinates(int fileID, int ncvarID, char coordinates[CDI_MAX_NAME])
491
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
492
  if (ncvarID != CDI_UNDEFID)
493
    {
494 495
      size_t len = strlen(coordinates);
      if (len) coordinates[len++] = ' ';
Uwe Schulzweida's avatar
Uwe Schulzweida committed
496
      cdf_inq_varname(fileID, ncvarID, coordinates+len);
497 498 499
    }
}

500
static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
501
void cdfDefineCoordinates(const stream_t *streamptr, int ncvarID, int nczvarID, int gridtype, int gridID, int gridindex,
502
                          int xid, int yid, size_t gridsize, char axis[5], size_t iax)
503
{
504 505
  const int fileID  = streamptr->fileID;

506
  if ( gridtype != GRID_GENERIC && gridtype != GRID_LONLAT &&
507 508 509
       gridtype != GRID_PROJECTION && gridtype != GRID_CURVILINEAR && gridtype != GRID_CHARXY )
    {
      const size_t len = strlen(gridNamePtr(gridtype));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
510
      if ( len > 0 ) cdf_put_att_text(fileID, ncvarID, "CDI_grid_type", len, gridNamePtr(gridtype));
511 512 513 514
    }

  char gmapvarname[CDI_MAX_NAME]; gmapvarname[0] = 0;
  cdf_get_gmapvarname(gridID, gmapvarname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
515
  if ( gmapvarname[0] ) cdf_put_att_text(fileID, ncvarID, "grid_mapping", strlen(gmapvarname), gmapvarname);
516

517
  if ( gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED )
518 519 520 521
    {
      const int numLPE = gridInqNP(gridID);
      if (numLPE > 0)
        cdf_put_att_int(fileID, ncvarID, "CDI_grid_num_LPE", NC_INT, 1, &numLPE);
522
    }
523

524 525
  if ( gridtype == GRID_GAUSSIAN_REDUCED )
    {
526 527 528 529 530
      const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
      if (ncyvarID != CDI_UNDEFID)
        {
          char name[CDI_MAX_NAME]; name[0] = 0;
          cdf_inq_varname(fileID, ncyvarID, name);
531
          const size_t len = strlen(name);
532 533 534
          cdf_put_att_text(fileID, ncvarID, "CDI_grid_latitudes", len, name);
        }

535 536 537 538 539
      const int ncrpvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_RP];
      if (ncrpvarID != CDI_UNDEFID)
        {
          char name[CDI_MAX_NAME]; name[0] = 0;
          cdf_inq_varname(fileID, ncrpvarID, name);
540
          const size_t len = strlen(name);
541 542 543 544
          cdf_put_att_text(fileID, ncvarID, "CDI_grid_reduced_points", len, name);
        }
    }

545 546 547 548 549 550
  // define coordinates attribute

  char coordinates[CDI_MAX_NAME]; coordinates[0] = 0;

  if (nczvarID != CDI_UNDEFID) cdfAppendCoordinates(fileID, nczvarID, coordinates);

551 552
  if ( gridtype == GRID_TRAJECTORY )
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
553
      cdf_put_att_text(fileID, ncvarID, "coordinates", 9, "tlon tlat");
554 555 556 557 558 559 560 561
    }
  else if ( gridtype == GRID_LONLAT && xid == CDI_UNDEFID && yid == CDI_UNDEFID && gridsize == 1 )
    {
      const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
      const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
      cdfAppendCoordinates(fileID, ncyvarID, coordinates);
      cdfAppendCoordinates(fileID, ncxvarID, coordinates);
    }
562 563
  else if ( gridtype == GRID_GAUSSIAN_REDUCED )
    {
564
      /*
565 566 567 568
      const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
      const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
      cdfAppendCoordinates(fileID, ncyvarID, coordinates);
      cdfAppendCoordinates(fileID, ncxvarID, coordinates);
569
      */
570
    }
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
  else if ( gridtype == GRID_UNSTRUCTURED || gridtype == GRID_CURVILINEAR )
    {
      const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
      const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
      const int ncavarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_A];
      // CMOR order: coordinates = "lat lon"
      if ( cdiCoordinatesLonLat )
        {
          cdfAppendCoordinates(fileID, ncxvarID, coordinates);
          cdfAppendCoordinates(fileID, ncyvarID, coordinates);
        }
      else
        {
          cdfAppendCoordinates(fileID, ncyvarID, coordinates);
          cdfAppendCoordinates(fileID, ncxvarID, coordinates);
        }

      if ( ncavarID != CDI_UNDEFID )
        {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
590
          char cellarea[CDI_MAX_NAME] = "area: ";
591 592 593
          size_t len = strlen(cellarea);
          cdf_inq_varname(fileID, ncavarID, cellarea+len);
          len = strlen(cellarea);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
594
          cdf_put_att_text(fileID, ncvarID, "cell_measures", len, cellarea);
595 596 597 598 599 600
        }

      if ( gridtype == GRID_UNSTRUCTURED )
        {
          int position = gridInqPosition(gridID);
          if ( position > 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
601
            cdf_put_att_int(fileID, ncvarID, "number_of_grid_in_reference", NC_INT, 1, &position);
602 603 604 605 606 607
        }
    }
  else if ( gridtype == GRID_SPECTRAL || gridtype == GRID_FOURIER )
    {
      axis[iax++] = '-';
      axis[iax++] = '-';
Uwe Schulzweida's avatar
Uwe Schulzweida committed
608
      cdf_put_att_text(fileID, ncvarID, "axis", iax, axis);
609
      int gridTruncation = gridInqTrunc(gridID);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
610
      cdf_put_att_int(fileID, ncvarID, "truncation", NC_INT, 1, &gridTruncation);
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
    }
  else if ( gridtype == GRID_CHARXY )
    {
      if ( gridInqXIsc(gridID) )
        {
          const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
          cdfAppendCoordinates(fileID, ncxvarID, coordinates);
        }
      else if ( gridInqYIsc(gridID) )
        {
          const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
          cdfAppendCoordinates(fileID, ncyvarID, coordinates);
        }
    }

626
  const size_t len = strlen(coordinates);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
627
  if ( len ) cdf_put_att_text(fileID, ncvarID, "coordinates", len, coordinates);
628 629
}

630
static
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
int cdfDefineDimsAndChunks(const stream_t *streamptr, int varID, int xid, int yid, int zid, size_t gridsize, const int dimorder[3], int dims[4], bool lchunk, size_t chunks[4], char axis[5], size_t *piax)
{
  const int fileID  = streamptr->fileID;
  const int vlistID = streamptr->vlistID;

  size_t iax = *piax;
  int ndims = 0;

  for (int i = 0; i < 4; ++i) chunks[i] = 0;

  size_t xsize = 0, ysize = 0;
  if ( xid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, xid, &xsize);
  if ( yid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, yid, &ysize);

  const int timetype = vlistInqVarTimetype(vlistID, varID);
  if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
    {
      const int tid = streamptr->basetime.ncdimid;
      if (tid == CDI_UNDEFID) Error("Internal problem, time undefined!");
      axis[iax++] = 'T';
      chunks[ndims] = 1;
      dims[ndims] = tid;
      ndims++;
    }

  int chunktype = vlistInqVarChunkType(vlistID, varID);
657 658 659
  const size_t chunk_size_max = 65536;
  const size_t chunk_size_lim = 1073741823;
  if ( chunktype != CDI_CHUNK_LINES && gridsize > chunk_size_lim )
660
    {
661
      if ( CDI_Debug ) fprintf(stderr, "gridsize > %zu, changed chunktype to CDI_CHUNK_LINES!\n", chunk_size_lim);
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
      chunktype = CDI_CHUNK_LINES;
    }

  for ( int id = 0; id < 3; ++id )
    {
      if ( dimorder[id] == 3 && zid != CDI_UNDEFID )
        {
          axis[iax++] = 'Z';
          chunks[ndims] = 1;
          dims[ndims] = zid;
          ndims++;
        }
      else if ( dimorder[id] == 2 && yid != CDI_UNDEFID )
        {
          if ( chunktype == CDI_CHUNK_AUTO )
            chunks[ndims] = (chunk_size_max > gridsize) ? ysize : chunk_size_max/xsize;
          else
            chunks[ndims] = (chunktype == CDI_CHUNK_LINES) ? 1 : ysize;
          dims[ndims] = yid;
          ndims++;
        }
      else if ( dimorder[id] == 1 && xid != CDI_UNDEFID )
        {
          if ( chunktype == CDI_CHUNK_AUTO && yid == CDI_UNDEFID )
            chunks[ndims] = (chunk_size_max > xsize) ? xsize : chunk_size_max;
          else
688
            chunks[ndims] = (xsize > chunk_size_lim) ? chunk_size_lim : xsize;
689 690 691 692 693 694 695 696
          dims[ndims] = xid;
          ndims++;
        }
    }

  if ( CDI_Debug )
    fprintf(stderr, "lchunk %d chunktype %d  chunks %zu %zu %zu %zu\n", lchunk, chunktype, chunks[0], chunks[1], chunks[2], chunks[3]);

697
  *piax = iax;
698 699 700 701
  return ndims;
}

static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
702
void cdfDefineAttrLeveltype(int fileID, int ncvarID, int zaxisID, int zaxistype)
703 704 705 706
{
  if ( zaxistype == ZAXIS_CLOUD_BASE          ||
       zaxistype == ZAXIS_CLOUD_TOP           ||
       zaxistype == ZAXIS_ISOTHERM_ZERO       ||
707
       zaxistype == ZAXIS_TROPOPAUSE          ||
708 709 710 711 712 713 714 715 716
       zaxistype == ZAXIS_TOA                 ||
       zaxistype == ZAXIS_SEA_BOTTOM          ||
       zaxistype == ZAXIS_LAKE_BOTTOM         ||
       zaxistype == ZAXIS_SEDIMENT_BOTTOM     ||
       zaxistype == ZAXIS_SEDIMENT_BOTTOM_TA  ||
       zaxistype == ZAXIS_SEDIMENT_BOTTOM_TW  ||
       zaxistype == ZAXIS_MIX_LAYER           ||
       zaxistype == ZAXIS_ATMOSPHERE )
    {
717
      char varname[CDI_MAX_NAME];
718 719
      int length = CDI_MAX_NAME;
      cdiInqKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_NAME, varname, &length);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
720
      cdf_put_att_text(fileID, ncvarID, "level_type", strlen(varname), varname);
721 722 723
    }
}

724
static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
725
void cdfDefineAttrEnsemble(int fileID, int ncvarID, int vlistID, int varID)
726 727 728
{
  int perturbationNumber, numberOfForecastsInEnsemble, typeOfEnsembleForecast;
  if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_PERTURBATIONNUMBER, &perturbationNumber) == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
729
    cdf_put_att_int(fileID, ncvarID, "realization", NC_INT, 1, &perturbationNumber);
730
  if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, &numberOfForecastsInEnsemble) == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
731
    cdf_put_att_int(fileID, ncvarID, "ensemble_members", NC_INT, 1, &numberOfForecastsInEnsemble);
732
  if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFENSEMBLEFORECAST, &typeOfEnsembleForecast) == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
733
    cdf_put_att_int(fileID, ncvarID, "forecast_init_type", NC_INT, 1, &typeOfEnsembleForecast);
734 735
}

736 737 738 739 740
static
void cdfCheckVarname(int fileID, char name[CDI_MAX_NAME])
{
  if ( name[0] )
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
741
      int ncvarID;
742 743 744 745 746 747 748 749 750
      char varname[CDI_MAX_NAME];
      sprintf(varname, "%s", name);
      char *varname2 = varname+strlen(varname);
      unsigned iz = 0;

      do
        {
          if ( iz ) sprintf(varname2, "_%u", iz+1);

Uwe Schulzweida's avatar
Uwe Schulzweida committed
751
          if ( nc_inq_varid(fileID, varname, &ncvarID) != NC_NOERR ) break;
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780

          ++iz;
        }
      while ( iz <= 99 );

      if (iz > 99) Error("Variable name %s already exsist!", name);

      if ( strcmp(name, varname) != 0 )
        Warning("Changed %s entry of variable name '%s' to '%s'!", (iz==1)?"double":"multiple", name, varname);

      strcpy(name, varname);
    }
}

static
void cdfGenVarname(int fileID, char name[CDI_MAX_NAME], int pnum, int pcat, int *pdis, int *pcode)
{
  char varname[CDI_MAX_NAME];

  int code = *pcode;
  if ( code < 0 ) code = -code;
  if ( pnum < 0 ) pnum = -pnum;

  if ( *pdis == 255 )
    sprintf(varname, "var%d", code);
  else
    sprintf(varname, "param%d.%d.%d", pnum, pcat, *pdis);

  char *varname2 = varname+strlen(varname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
781
  int ncvarID;
782 783 784 785 786 787
  unsigned iz = 0;

  do
    {
      if ( iz ) sprintf(varname2, "_%u", iz+1);

Uwe Schulzweida's avatar
Uwe Schulzweida committed
788
      if ( nc_inq_varid(fileID, varname, &ncvarID) != NC_NOERR ) break;
789 790 791 792 793 794 795 796 797 798 799 800

      ++iz;
    }
  while ( iz <= 99 );

  if (iz > 99) Error("Variable name %s already exsist!", name);

  strcpy(name, varname);
  *pcode = 0;
  *pdis = 255;
}

801 802 803
static
int cdfDefVar(stream_t *streamptr, int varID)
{
804 805
  if (streamptr->vars[varID].ncvarid != CDI_UNDEFID)
    return streamptr->vars[varID].ncvarid;
806

807
  const int fileID  = streamptr->fileID;
808
  if (CDI_Debug) Message("streamID = %d, fileID = %d, varID = %d", streamptr->self, fileID, varID);
809

810 811 812
  const int vlistID = streamptr->vlistID;
  const int param = vlistInqVarParam(vlistID, varID);
  int code = vlistInqVarCode(vlistID, varID);
813 814 815
  int pnum, pcat, pdis;
  cdiDecodeParam(param, &pnum, &pcat, &pdis);

816
  const int gridID = vlistInqVarGrid(vlistID, varID);
817 818 819
  const size_t gridsize = gridInqSize(gridID);
  const int gridtype = gridInqType(gridID);
  const int gridindex = nc_grid_index(streamptr, gridID);
820
  const int xid = (gridtype != GRID_TRAJECTORY) ? streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X] : CDI_UNDEFID;
821 822
  const int yid = (gridtype != GRID_TRAJECTORY && gridtype != GRID_GAUSSIAN_REDUCED) ?
    streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y] : CDI_UNDEFID;
823

824
  const int zaxisID = vlistInqVarZaxis(vlistID, varID);
825
  const int zaxistype = zaxisInqType(zaxisID);
826 827
  const int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
  const int zid = streamptr->zaxisID[zaxisindex];
828

829 830
  int dimorder[3]; // ZYX and ZXY
  vlistInqVarDimorder(vlistID, varID, &dimorder);
831
  const bool lchunk = (dimorder[0] == 3) ? (gridsize >= 32) : false;
832

833
  if ( ((dimorder[0]>0)+(dimorder[1]>0)+(dimorder[2]>0)) < ((xid!=CDI_UNDEFID)+(yid!=CDI_UNDEFID)+(zid!=CDI_UNDEFID)) )
834 835 836 837 838
    {
      printf("zid=%d  yid=%d  xid=%d\n", zid, yid, xid);
      Error("Internal problem, dimension order missing!");
    }

839 840 841 842 843
  size_t iax = 0;
  char axis[5];
  int dims[4];
  size_t chunks[4];
  int ndims = cdfDefineDimsAndChunks(streamptr, varID, xid, yid, zid, gridsize, dimorder, dims, lchunk, chunks, axis, &iax);
844

845 846
  char name[CDI_MAX_NAME];
  int length = CDI_MAX_NAME;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
847
  cdiInqKeyString(vlistID, varID, CDI_KEY_NAME, name, &length);
848

849
  char longname[CDI_MAX_NAME];
850
  vlistInqVarLongname(vlistID, varID, longname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
851

852
  char units[CDI_MAX_NAME];
853
  vlistInqVarUnits(vlistID, varID, units);
854

Uwe Schulzweida's avatar
Uwe Schulzweida committed
855 856 857 858
  char stdname[CDI_MAX_NAME];
  length = CDI_MAX_NAME;
  cdiInqKeyString(vlistID, varID, CDI_KEY_STDNAME, stdname, &length);

859
  const int tableID  = vlistInqVarTable(vlistID, varID);
860 861 862
  if (!name[0]) tableInqEntry(tableID, code, -1, name, longname, units);
  if (name[0]) cdfCheckVarname(fileID, name);
  else         cdfGenVarname(fileID, name, pnum, pcat, &pdis, &code);
863

864 865
  const int dtype = vlistInqVarDatatype(vlistID, varID);
  const nc_type xtype = cdfDefDatatype(dtype, streamptr);
866

Uwe Schulzweida's avatar
Uwe Schulzweida committed
867 868
  int ncvarID = -1;
  cdf_def_var(fileID, name, xtype, ndims, dims, &ncvarID);
869

Uwe Schulzweida's avatar
Uwe Schulzweida committed
870
#ifdef  HAVE_NETCDF4
871
  if (lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C))
Uwe Schulzweida's avatar
Uwe Schulzweida committed
872
    cdf_def_var_chunking(fileID, ncvarID, NC_CHUNKED, chunks);
873 874
#endif

875
  cdfDefVarCompression(streamptr, ncvarID, lchunk, xtype);
876

Uwe Schulzweida's avatar
Uwe Schulzweida committed
877 878 879
  if ( *stdname )  cdf_put_att_text(fileID, ncvarID, "standard_name", strlen(stdname), stdname);
  if ( *longname ) cdf_put_att_text(fileID, ncvarID, "long_name", strlen(longname), longname);
  if ( *units )    cdf_put_att_text(fileID, ncvarID, "units", strlen(units), units);
880 881

  if ( code > 0 && pdis == 255 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
882
    cdf_put_att_int(fileID, ncvarID, "code", NC_INT, 1, &code);
883 884 885 886 887

  if ( pdis != 255 )
    {
      char paramstr[32];
      cdiParamToString(param, paramstr, sizeof(paramstr));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
888
      cdf_put_att_text(fileID, ncvarID, "param", strlen(paramstr), paramstr);
889 890
    }

891
  if ( tableID != CDI_UNDEFID )
892
    {
893
      int tablenum = tableInqNum(tableID);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
894
      if (tablenum > 0) cdf_put_att_int(fileID, ncvarID, "table", NC_INT, 1, &tablenum);
895 896
    }

897
  const bool zaxisIsScalar = (zid == CDI_UNDEFID) ? (zaxisInqScalar(zaxisID) > 0) : false;
898
  const int nczvarID = (zaxisIsScalar || zaxistype == ZAXIS_CHAR) ? streamptr->nczvarID[zaxisindex] : CDI_UNDEFID;
899

Uwe Schulzweida's avatar
Uwe Schulzweida committed
900
  cdfDefineCoordinates(streamptr, ncvarID, nczvarID, gridtype, gridID, gridindex, xid, yid, gridsize, axis, iax);
901

Uwe Schulzweida's avatar
Uwe Schulzweida committed
902
  cdfDefVarPacking(streamptr, ncvarID, xtype, vlistID, varID);
903

904
  if ( dtype == CDI_DATATYPE_UINT8 && xtype == NC_BYTE )
905
    {
906
      const int validrange[2] = {0, 255};
Uwe Schulzweida's avatar
Uwe Schulzweida committed
907 908
      cdf_put_att_int(fileID, ncvarID, "valid_range", NC_SHORT, 2, validrange);
      cdf_put_att_text(fileID, ncvarID, "_Unsigned", 4, "true");
909 910
    }

Uwe Schulzweida's avatar
Uwe Schulzweida committed
911
  streamptr->vars[varID].ncvarid = ncvarID;
912 913 914 915

  if ( vlistInqVarMissvalUsed(vlistID, varID) )
    cdfDefVarMissval(streamptr, varID, vlistInqVarDatatype(vlistID, varID), 0);

Uwe Schulzweida's avatar
Uwe Schulzweida committed
916
  if (zid == CDI_UNDEFID) cdfDefineAttrLeveltype(fileID, ncvarID, zaxisID, zaxistype);
917

Uwe Schulzweida's avatar
Uwe Schulzweida committed
918
  cdfDefineAttrEnsemble(fileID, ncvarID, vlistID, varID);
919

920 921 922
  // Attribute: cell_methods
  cdfDefineCellMethods(streamptr, vlistID, varID, fileID, ncvarID);

923
  // Attributes
Uwe Schulzweida's avatar
Uwe Schulzweida committed
924 925 926
  cdfDefineAttributes(vlistID, varID, fileID, ncvarID);

  // Institute
927
  if (vlistInqInstitut(vlistID) == CDI_UNDEFID) cdfDefineInstituteName(vlistID, varID, fileID, ncvarID);
928

Uwe Schulzweida's avatar
Uwe Schulzweida committed
929
  return ncvarID;
930 931 932 933 934 935 936 937 938
}


void cdfEndDef(stream_t *streamptr)
{
  cdfDefGlobalAtts(streamptr);