1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #ifdef HAVE_LIBNETCDF
6 
7 #include "dmemory.h"
8 #include "cdi.h"
9 #include "cdi_int.h"
10 #include "stream_cdf.h"
11 #include "cdf.h"
12 #include "cdf_int.h"
13 #include "vlist.h"
14 #include "vlist_var.h"
15 
16 
cdfDefVarDeflate(int ncid,int ncvarID,int deflate_level)17 void cdfDefVarDeflate(int ncid, int ncvarID, int deflate_level)
18 {
19 #ifdef HAVE_NETCDF4
20   // Set chunking, shuffle, and deflate.
21   const int shuffle = 1, deflate = 1;
22 
23   if (deflate_level < 1 || deflate_level > 9) deflate_level = 1;
24 
25   int status;
26   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;
35       Warning("Deflate compression failed, NetCDF4 not available!");
36     }
37 #endif
38 }
39 
cdfDefVarSzip(int ncid,int ncvarID,int pixels_per_block)40 void cdfDefVarSzip(int ncid, int ncvarID, int pixels_per_block)
41 {
42 #ifdef HAVE_NC_DEF_VAR_SZIP
43   // Set options_mask.
44   /*
45     H5_SZIP_ALLOW_K13_OPTION_MASK   1
46     H5_SZIP_CHIP_OPTION_MASK        2
47     H5_SZIP_EC_OPTION_MASK          4
48     H5_SZIP_NN_OPTION_MASK          32
49     H5_SZIP_ALL_MASKS (H5_SZIP_CHIP_OPTION_MASK|H5_SZIP_EC_OPTION_MASK|H5_SZIP_NN_OPTION_MASK)
50   */
51   int options_mask = 38;
52   int status;
53   if ((status = nc_def_var_szip(ncid, ncvarID, options_mask, pixels_per_block)))
54     Error("nc_def_var_szip failed; %s", nc_strerror(status));
55 #else
56   static bool lwarn = true;
57   if (lwarn)
58     {
59       lwarn = false;
60       Warning("Szip compression failed, NetCDF4/szlib not available!");
61     }
62 #endif
63 }
64 
65 #ifdef HAVE_NETCDF4
66 static
cdfTypeComplexFloat(stream_t * streamptr)67 nc_type cdfTypeComplexFloat(stream_t *streamptr)
68 {
69   if (streamptr->nc_complex_float_id == CDI_UNDEFID)
70     {
71       typedef struct complex_float { float r, i; } complex_float;
72       const int fileID = streamptr->fileID;
73       int nc_complex_id;
74       int status;
75       status = nc_def_compound(fileID, sizeof(complex_float), "complex_float", &nc_complex_id);
76       if (status != NC_NOERR) Error("%s", nc_strerror(status));
77       status = nc_insert_compound(fileID, nc_complex_id, "r", NC_COMPOUND_OFFSET(complex_float, r), NC_FLOAT);
78       if (status != NC_NOERR) Error("%s", nc_strerror(status));
79       status = nc_insert_compound(fileID, nc_complex_id, "i", NC_COMPOUND_OFFSET(complex_float, i), NC_FLOAT);
80       if (status != NC_NOERR) Error("%s", nc_strerror(status));
81       streamptr->nc_complex_float_id = nc_complex_id;
82     }
83 
84   return (nc_type) streamptr->nc_complex_float_id;
85 }
86 
87 static
cdfTypeComplexDouble(stream_t * streamptr)88 nc_type cdfTypeComplexDouble(stream_t *streamptr)
89 {
90   if (streamptr->nc_complex_double_id == CDI_UNDEFID)
91     {
92       typedef struct complex_double { double r, i; } complex_double;
93       const int fileID  = streamptr->fileID;
94       int nc_complex_id;
95       int status;
96       status = nc_def_compound(fileID, sizeof(complex_double), "complex_double", &nc_complex_id);
97       if (status != NC_NOERR) Error("%s", nc_strerror(status));
98       status = nc_insert_compound(fileID, nc_complex_id, "r", NC_COMPOUND_OFFSET(complex_double, r), NC_DOUBLE);
99       if (status != NC_NOERR) Error("%s", nc_strerror(status));
100       status = nc_insert_compound(fileID, nc_complex_id, "i", NC_COMPOUND_OFFSET(complex_double, i), NC_DOUBLE);
101       if (status != NC_NOERR) Error("%s", nc_strerror(status));
102       streamptr->nc_complex_double_id = nc_complex_id;
103     }
104 
105   return (nc_type) streamptr->nc_complex_double_id;
106 }
107 #endif
108 
cdfDefDatatype(int datatype,stream_t * streamptr)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       if      ( datatype == CDI_DATATYPE_INT8   ) xtype = NC_BYTE;
116       else if ( datatype == CDI_DATATYPE_INT16  ) xtype = NC_SHORT;
117       else if ( datatype == CDI_DATATYPE_INT32  ) xtype = NC_INT;
118 #ifdef  HAVE_NETCDF4
119       else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_UBYTE;
120       else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_USHORT;
121       else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_UINT;
122       else if ( datatype == CDI_DATATYPE_CPX32  ) xtype = cdfTypeComplexFloat(streamptr);
123       else if ( datatype == CDI_DATATYPE_CPX64  ) xtype = cdfTypeComplexDouble(streamptr);
124 #else
125       else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_SHORT;
126       else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_INT;
127       else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_INT;
128       else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
129         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   else
135     {
136       if      ( datatype == CDI_DATATYPE_INT8   ) xtype = NC_BYTE;
137       else if ( datatype == CDI_DATATYPE_INT16  ) xtype = NC_SHORT;
138       else if ( datatype == CDI_DATATYPE_INT32  ) xtype = NC_INT;
139       else if ( datatype == CDI_DATATYPE_UINT8  ) xtype = NC_SHORT;
140       else if ( datatype == CDI_DATATYPE_UINT16 ) xtype = NC_INT;
141       else if ( datatype == CDI_DATATYPE_UINT32 ) xtype = NC_INT;
142       else if ( datatype == CDI_DATATYPE_FLT64  ) xtype = NC_DOUBLE;
143       else if ( datatype == CDI_DATATYPE_FLT32  ) xtype = NC_FLOAT;
144       else if ( datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64 )
145         Error("CDI library does not support complex numbers with NetCDF classic!");
146     }
147 
148   return xtype;
149 }
150 
151 static
cdfDefVarMissval(stream_t * streamptr,int varID,int dtype,int lcheck)152 void cdfDefVarMissval(stream_t *streamptr, int varID, int dtype, int lcheck)
153 {
154   if ( streamptr->vars[varID].defmiss == false )
155     {
156       const int vlistID = streamptr->vlistID;
157       const int fileID  = streamptr->fileID;
158       const int ncvarID = streamptr->vars[varID].ncvarid;
159       const double missval = vlistInqVarMissval(vlistID, varID);
160 
161       if ( lcheck && streamptr->ncmode == 2 ) cdf_redef(fileID);
162 
163       nc_type xtype = cdfDefDatatype(dtype, streamptr);
164       if ( xtype == NC_BYTE && missval > 127 && missval < 256 ) xtype = NC_INT;
165 
166       if ( lcheck == 0 ||
167            streamptr->ncmode != 2 ||
168            streamptr->filetype == CDI_FILETYPE_NC ||
169            streamptr->filetype == CDI_FILETYPE_NC2||
170            streamptr->filetype == CDI_FILETYPE_NC5 )
171         cdf_put_att_double(fileID, ncvarID, "_FillValue", xtype, 1, &missval);
172 
173       cdf_put_att_double(fileID, ncvarID, "missing_value", xtype, 1, &missval);
174 
175       if ( lcheck && streamptr->ncmode == 2 ) cdf_enddef(fileID);
176 
177       streamptr->vars[varID].defmiss = true;
178     }
179 }
180 
181 static
cdfDefInstituteGlobal(const stream_t * streamptr)182 void cdfDefInstituteGlobal(const stream_t *streamptr)
183 {
184   const int vlistID = streamptr->vlistID;
185   const int fileID  = streamptr->fileID;
186   const int instID  = vlistInqInstitut(vlistID);
187 
188   if ( instID != CDI_UNDEFID )
189     {
190       const char *longname = institutInqLongnamePtr(instID);
191       if ( longname )
192 	{
193 	  const size_t len = strlen(longname);
194 	  if ( len > 0 )
195 	    {
196 	      if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
197 	      cdf_put_att_text(fileID, NC_GLOBAL, "institution", len, longname);
198 	      if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
199 	    }
200 	}
201     }
202 }
203 
204 static
cdfDefSourceGlobal(const stream_t * streamptr)205 void cdfDefSourceGlobal(const stream_t *streamptr)
206 {
207   const int vlistID = streamptr->vlistID;
208   const int fileID  = streamptr->fileID;
209   const int modelID = vlistInqModel(vlistID);
210 
211   if ( modelID != CDI_UNDEFID )
212     {
213       const char *longname = modelInqNamePtr(modelID);
214       if ( longname )
215 	{
216           size_t len = strlen(longname);
217 	  if ( len > 0 )
218 	    {
219 	      if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
220 	      cdf_put_att_text(fileID, NC_GLOBAL, "source", len, longname);
221 	      if ( streamptr->ncmode == 2 ) cdf_enddef(fileID);
222 	    }
223 	}
224     }
225 }
226 
227 static inline
resizeBuf(void ** buf,size_t * bufSize,size_t reqSize)228 void *resizeBuf(void **buf, size_t *bufSize, size_t reqSize)
229 {
230   if ( reqSize > *bufSize )
231     {
232       *buf = Realloc(*buf, reqSize);
233       *bufSize = reqSize;
234     }
235   return *buf;
236 }
237 
238 static
cdfDefineCellMethods(stream_t * streamptr,int cdiID,int varID,int fileID,int ncvarID)239 void cdfDefineCellMethods(stream_t *streamptr, int cdiID, int varID, int fileID, int ncvarID)
240 {
241   taxis_t *taxis = &streamptr->tsteps[0].taxis;
242   if (!taxis->has_bounds) return;
243 
244   const int time_varid = streamptr->basetime.ncvarid;
245   char timeVarName[CDI_MAX_NAME];
246   cdf_inq_varname(fileID, time_varid, timeVarName);
247 
248   const int stepType = vlistInqVarTsteptype(cdiID, varID);
249 
250   const char *cellMethod = NULL;
251   if      (stepType == TSTEP_AVG)   cellMethod = "mean";
252   else if (stepType == TSTEP_SUM)   cellMethod = "sum";
253   else if (stepType == TSTEP_RANGE) cellMethod = "range";
254   else if (stepType == TSTEP_MIN)   cellMethod = "minimum";
255   else if (stepType == TSTEP_MAX)   cellMethod = "maximum";
256 
257   if (cellMethod)
258     {
259       const char *attname = "cell_methods";
260       char atttxt[CDI_MAX_NAME+10];
261       sprintf(atttxt, "%s: %s", timeVarName, cellMethod);
262       cdf_put_att_text(fileID, ncvarID, attname, strlen(atttxt), atttxt);
263     }
264 }
265 
cdfDefineAttributes(int cdiID,int varID,int fileID,int ncvarID)266 void cdfDefineAttributes(int cdiID, int varID, int fileID, int ncvarID)
267 {
268   int atttype, attlen;
269   size_t len;
270   char attname[CDI_MAX_NAME+1];
271   void *attBuf = NULL;
272   size_t attBufSize = 0;
273 
274   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       if ( attlen == 0 ) continue;
282 
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           len = (size_t)attlen;
289           cdf_put_att_text(fileID, ncvarID, attname, len, atttxt);
290         }
291       else if ( atttype == CDI_DATATYPE_INT8  || atttype == CDI_DATATYPE_UINT8  ||
292                 atttype == CDI_DATATYPE_INT16 || atttype == CDI_DATATYPE_UINT16 ||
293                 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           nc_type xtype = (atttype == CDI_DATATYPE_INT8)   ? NC_BYTE :
300                           (atttype == CDI_DATATYPE_INT16)  ? NC_SHORT :
301 #ifdef  HAVE_NETCDF4
302                           (atttype == CDI_DATATYPE_UINT8)  ? NC_UBYTE :
303                           (atttype == CDI_DATATYPE_UINT16) ? NC_USHORT :
304                           (atttype == CDI_DATATYPE_UINT32) ? NC_UINT :
305 #endif
306                           NC_INT;
307           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               float attflt_sp[8];
318               float *pattflt_sp = (len > 8) ? (float*) malloc(len*sizeof(float)) : attflt_sp;
319               for ( size_t i = 0; i < len; ++i ) pattflt_sp[i] = (float)attflt[i];
320               cdf_put_att_float(fileID, ncvarID, attname, NC_FLOAT, len, pattflt_sp);
321               if (len > 8) free(pattflt_sp);
322             }
323           else
324             cdf_put_att_double(fileID, ncvarID, attname, NC_DOUBLE, len, attflt);
325         }
326     }
327 
328   Free(attBuf);
329 }
330 
331 static
cdfDefineInstituteName(int vlistID,int varID,int fileID,int ncvarID)332 void cdfDefineInstituteName(int vlistID, int varID, int fileID, int ncvarID)
333 {
334   const int instID = vlistInqVarInstitut(vlistID, varID);
335   if (instID != CDI_UNDEFID)
336     {
337       const char *name = institutInqNamePtr(instID);
338       if (name) cdf_put_att_text(fileID, ncvarID, "institution", strlen(name), name);
339     }
340 }
341 
342 static
cdfDefGlobalAtts(stream_t * streamptr)343 void cdfDefGlobalAtts(stream_t *streamptr)
344 {
345   if ( streamptr->globalatts ) return;
346 
347   const int vlistID = streamptr->vlistID;
348   const int fileID  = streamptr->fileID;
349 
350   cdfDefSourceGlobal(streamptr);
351   cdfDefInstituteGlobal(streamptr);
352 
353   int natts;
354   cdiInqNatts(vlistID, CDI_GLOBAL, &natts);
355 
356   if ( natts > 0 && streamptr->ncmode == 2 ) cdf_redef(fileID);
357 
358   cdfDefineAttributes(vlistID, CDI_GLOBAL, fileID, NC_GLOBAL);
359 
360   if ( natts > 0 && streamptr->ncmode == 2 ) cdf_enddef(fileID);
361 
362   streamptr->globalatts = 1;
363 }
364 
365 static
cdf_get_gmapvarname(int gridID,char * gmapvarname)366 void cdf_get_gmapvarname(int gridID, char *gmapvarname)
367 {
368   int length = CDI_MAX_NAME;
369   int pgridID = gridID;
370   cdiInqKeyString(pgridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_VARNAME, gmapvarname, &length);
371 
372   if (!gmapvarname[0])
373     {
374       length = CDI_MAX_NAME;
375       pgridID = gridInqProj(gridID);
376       if (pgridID != CDI_UNDEFID) cdiInqKeyString(pgridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_VARNAME, gmapvarname, &length);
377     }
378 }
379 
380 static
nc_grid_index(stream_t * streamptr,int gridID)381 int nc_grid_index(stream_t *streamptr, int gridID)
382 {
383   int index = 0;
384   const int vlistID = streamptr->vlistID;
385   const int ngrids = vlistNgrids(vlistID);
386   for ( index = 0; index < ngrids; ++index )
387     if ( streamptr->ncgrid[index].gridID == gridID ) break;
388 
389   assert(index < ngrids);
390 
391   return index;
392 }
393 
394 static
xtype2ppb(nc_type xtype)395 int xtype2ppb(nc_type xtype)
396 {
397   int ppb = 32;
398 
399   if      (xtype == NC_BYTE)   ppb = 8;
400   else if (xtype == NC_SHORT)  ppb = 16;
401 #ifdef  HAVE_NETCDF4
402   else if (xtype == NC_UBYTE)  ppb = 8;
403   else if (xtype == NC_USHORT) ppb = 16;
404 #endif
405 
406   return ppb;
407 }
408 
409 static
cdfDefVarCompression(const stream_t * streamptr,int ncvarID,bool lchunk,nc_type xtype)410 void cdfDefVarCompression(const stream_t *streamptr, int ncvarID, bool lchunk, nc_type xtype)
411 {
412   if ( streamptr->comptype == CDI_COMPRESS_ZIP )
413     {
414       if ( lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C) )
415         {
416           cdfDefVarDeflate(streamptr->fileID, ncvarID, streamptr->complevel);
417         }
418       else
419         {
420           if ( lchunk )
421             {
422               static bool lwarn = true;
423               if ( lwarn )
424                 {
425                   lwarn = false;
426                   Warning("Deflate compression is only available for NetCDF4!");
427                 }
428             }
429         }
430     }
431   else if ( streamptr->comptype == CDI_COMPRESS_SZIP )
432     {
433       if ( lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C) )
434         {
435           cdfDefVarSzip(streamptr->fileID, ncvarID, xtype2ppb(xtype));
436         }
437       else
438         {
439           if ( lchunk )
440             {
441               static bool lwarn = true;
442               if ( lwarn )
443                 {
444                   lwarn = false;
445                   Warning("SZIP compression is only available for NetCDF4!");
446                 }
447             }
448         }
449     }
450 }
451 
452 static
cdfDefVarPacking(const stream_t * streamptr,int ncvarID,nc_type xtype,int vlistID,int varID)453 void cdfDefVarPacking(const stream_t *streamptr, int ncvarID, nc_type xtype, int vlistID, int varID)
454 {
455   //  if ( xtype == NC_BYTE || xtype == NC_SHORT || xtype == NC_INT )
456     {
457       const double addoffset   = vlistInqVarAddoffset(vlistID, varID);
458       const double scalefactor = vlistInqVarScalefactor(vlistID, varID);
459       const bool laddoffset   = IS_NOT_EQUAL(addoffset, 0);
460       const bool lscalefactor = IS_NOT_EQUAL(scalefactor, 1);
461 
462       if ( laddoffset || lscalefactor )
463         {
464           nc_type astype = (xtype == NC_FLOAT) ? NC_FLOAT : NC_DOUBLE;
465           if ( (astype == NC_DOUBLE) &&
466                IS_EQUAL(addoffset,   (double) ((float) addoffset)) &&
467                IS_EQUAL(scalefactor, (double) ((float) scalefactor)) )
468             {
469               astype = NC_FLOAT;
470             }
471 
472           cdf_put_att_double(streamptr->fileID, ncvarID, "add_offset",   astype, 1, &addoffset);
473           cdf_put_att_double(streamptr->fileID, ncvarID, "scale_factor", astype, 1, &scalefactor);
474         }
475     }
476 }
477 
478 static
cdfAppendCoordinates(int fileID,int ncvarID,char coordinates[CDI_MAX_NAME])479 void cdfAppendCoordinates(int fileID, int ncvarID, char coordinates[CDI_MAX_NAME])
480 {
481   if (ncvarID != CDI_UNDEFID)
482     {
483       size_t len = strlen(coordinates);
484       if (len) coordinates[len++] = ' ';
485       cdf_inq_varname(fileID, ncvarID, coordinates+len);
486     }
487 }
488 
489 static
cdfDefineCoordinates(const stream_t * streamptr,int ncvarID,int nczvarID,int gridtype,int gridID,int gridindex,int xid,int yid,size_t gridsize,char axis[5],size_t iax)490 void cdfDefineCoordinates(const stream_t *streamptr, int ncvarID, int nczvarID, int gridtype, int gridID, int gridindex,
491                           int xid, int yid, size_t gridsize, char axis[5], size_t iax)
492 {
493   const int fileID  = streamptr->fileID;
494 
495   if ( gridtype != GRID_GENERIC && gridtype != GRID_LONLAT &&
496        gridtype != GRID_PROJECTION && gridtype != GRID_CURVILINEAR && gridtype != GRID_CHARXY )
497     {
498       const size_t len = strlen(gridNamePtr(gridtype));
499       if ( len > 0 ) cdf_put_att_text(fileID, ncvarID, "CDI_grid_type", len, gridNamePtr(gridtype));
500     }
501 
502   char gmapvarname[CDI_MAX_NAME]; gmapvarname[0] = 0;
503   cdf_get_gmapvarname(gridID, gmapvarname);
504   if ( gmapvarname[0] ) cdf_put_att_text(fileID, ncvarID, "grid_mapping", strlen(gmapvarname), gmapvarname);
505 
506   if ( gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED )
507     {
508       const int numLPE = gridInqNP(gridID);
509       if (numLPE > 0)
510         cdf_put_att_int(fileID, ncvarID, "CDI_grid_num_LPE", NC_INT, 1, &numLPE);
511     }
512 
513   if ( gridtype == GRID_GAUSSIAN_REDUCED )
514     {
515       const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
516       if (ncyvarID != CDI_UNDEFID)
517         {
518           char name[CDI_MAX_NAME]; name[0] = 0;
519           cdf_inq_varname(fileID, ncyvarID, name);
520           const size_t len = strlen(name);
521           cdf_put_att_text(fileID, ncvarID, "CDI_grid_latitudes", len, name);
522         }
523 
524       const int ncrpvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_RP];
525       if (ncrpvarID != CDI_UNDEFID)
526         {
527           char name[CDI_MAX_NAME]; name[0] = 0;
528           cdf_inq_varname(fileID, ncrpvarID, name);
529           const size_t len = strlen(name);
530           cdf_put_att_text(fileID, ncvarID, "CDI_grid_reduced_points", len, name);
531         }
532     }
533 
534   // define coordinates attribute
535 
536   char coordinates[CDI_MAX_NAME]; coordinates[0] = 0;
537 
538   if (nczvarID != CDI_UNDEFID) cdfAppendCoordinates(fileID, nczvarID, coordinates);
539 
540   if ( gridtype == GRID_TRAJECTORY )
541     {
542       cdf_put_att_text(fileID, ncvarID, "coordinates", 9, "tlon tlat");
543     }
544   else if ( gridtype == GRID_LONLAT && xid == CDI_UNDEFID && yid == CDI_UNDEFID && gridsize == 1 )
545     {
546       const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
547       const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
548       cdfAppendCoordinates(fileID, ncyvarID, coordinates);
549       cdfAppendCoordinates(fileID, ncxvarID, coordinates);
550     }
551   else if ( gridtype == GRID_GAUSSIAN_REDUCED )
552     {
553       /*
554       const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
555       const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
556       cdfAppendCoordinates(fileID, ncyvarID, coordinates);
557       cdfAppendCoordinates(fileID, ncxvarID, coordinates);
558       */
559     }
560   else if ( gridtype == GRID_UNSTRUCTURED || gridtype == GRID_CURVILINEAR )
561     {
562       const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
563       const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
564       const int ncavarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_A];
565       // CMOR order: coordinates = "lat lon"
566       if ( CDI_Coordinates_Lon_Lat )
567         {
568           cdfAppendCoordinates(fileID, ncxvarID, coordinates);
569           cdfAppendCoordinates(fileID, ncyvarID, coordinates);
570         }
571       else
572         {
573           cdfAppendCoordinates(fileID, ncyvarID, coordinates);
574           cdfAppendCoordinates(fileID, ncxvarID, coordinates);
575         }
576 
577       if ( ncavarID != CDI_UNDEFID )
578         {
579           char cellarea[CDI_MAX_NAME] = "area: ";
580           size_t len = strlen(cellarea);
581           cdf_inq_varname(fileID, ncavarID, cellarea+len);
582           len = strlen(cellarea);
583           cdf_put_att_text(fileID, ncvarID, "cell_measures", len, cellarea);
584         }
585 
586       if ( gridtype == GRID_UNSTRUCTURED )
587         {
588           int position = gridInqPosition(gridID);
589           if ( position > 0 )
590             cdf_put_att_int(fileID, ncvarID, "number_of_grid_in_reference", NC_INT, 1, &position);
591         }
592     }
593   else if ( gridtype == GRID_SPECTRAL || gridtype == GRID_FOURIER )
594     {
595       axis[iax++] = '-';
596       axis[iax++] = '-';
597       cdf_put_att_text(fileID, ncvarID, "axis", iax, axis);
598       int gridTruncation = gridInqTrunc(gridID);
599       cdf_put_att_int(fileID, ncvarID, "truncation", NC_INT, 1, &gridTruncation);
600     }
601   else if ( gridtype == GRID_CHARXY )
602     {
603       if ( gridInqXIsc(gridID) )
604         {
605           const int ncxvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_X];
606           cdfAppendCoordinates(fileID, ncxvarID, coordinates);
607         }
608       else if ( gridInqYIsc(gridID) )
609         {
610           const int ncyvarID = streamptr->ncgrid[gridindex].ncIDs[CDF_VARID_Y];
611           cdfAppendCoordinates(fileID, ncyvarID, coordinates);
612         }
613     }
614 
615   const size_t len = strlen(coordinates);
616   if ( len ) cdf_put_att_text(fileID, ncvarID, "coordinates", len, coordinates);
617 }
618 
619 static
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)620 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)
621 {
622   const int fileID  = streamptr->fileID;
623   const int vlistID = streamptr->vlistID;
624 
625   size_t iax = *piax;
626   int ndims = 0;
627 
628   for (int i = 0; i < 4; ++i) chunks[i] = 0;
629 
630   size_t xsize = 0, ysize = 0;
631   if ( xid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, xid, &xsize);
632   if ( yid != CDI_UNDEFID ) cdf_inq_dimlen(fileID, yid, &ysize);
633 
634   const int timetype = vlistInqVarTimetype(vlistID, varID);
635   if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
636     {
637       const int tid = streamptr->basetime.ncdimid;
638       if (tid == CDI_UNDEFID) Error("Internal problem, time undefined!");
639       axis[iax++] = 'T';
640       chunks[ndims] = 1;
641       dims[ndims] = tid;
642       ndims++;
643     }
644 
645   int chunktype = vlistInqVarChunkType(vlistID, varID);
646   const size_t chunk_size_max = 65536;
647   const size_t chunk_size_lim = 1073741823;
648   if ( chunktype != CDI_CHUNK_LINES && gridsize > chunk_size_lim )
649     {
650       if ( CDI_Debug ) fprintf(stderr, "gridsize > %zu, changed chunktype to CDI_CHUNK_LINES!\n", chunk_size_lim);
651       chunktype = CDI_CHUNK_LINES;
652     }
653 
654   for ( int id = 0; id < 3; ++id )
655     {
656       if ( dimorder[id] == 3 && zid != CDI_UNDEFID )
657         {
658           axis[iax++] = 'Z';
659           chunks[ndims] = 1;
660           dims[ndims] = zid;
661           ndims++;
662         }
663       else if ( dimorder[id] == 2 && yid != CDI_UNDEFID )
664         {
665           if ( chunktype == CDI_CHUNK_AUTO )
666             chunks[ndims] = (chunk_size_max > gridsize) ? ysize : chunk_size_max/xsize;
667           else
668             chunks[ndims] = (chunktype == CDI_CHUNK_LINES) ? 1 : ysize;
669           dims[ndims] = yid;
670           ndims++;
671         }
672       else if ( dimorder[id] == 1 && xid != CDI_UNDEFID )
673         {
674           if ( chunktype == CDI_CHUNK_AUTO && yid == CDI_UNDEFID )
675             chunks[ndims] = (chunk_size_max > xsize) ? xsize : chunk_size_max;
676           else
677             chunks[ndims] = (xsize > chunk_size_lim) ? chunk_size_lim : xsize;
678           dims[ndims] = xid;
679           ndims++;
680         }
681     }
682 
683   if ( CDI_Debug )
684     fprintf(stderr, "lchunk %d chunktype %d  chunks %zu %zu %zu %zu\n", lchunk, chunktype, chunks[0], chunks[1], chunks[2], chunks[3]);
685 
686   *piax = iax;
687   return ndims;
688 }
689 
690 static
cdfDefineAttrLeveltype(int fileID,int ncvarID,int zaxisID,int zaxistype)691 void cdfDefineAttrLeveltype(int fileID, int ncvarID, int zaxisID, int zaxistype)
692 {
693   if ( zaxistype == ZAXIS_CLOUD_BASE          ||
694        zaxistype == ZAXIS_CLOUD_TOP           ||
695        zaxistype == ZAXIS_ISOTHERM_ZERO       ||
696        zaxistype == ZAXIS_TROPOPAUSE          ||
697        zaxistype == ZAXIS_TOA                 ||
698        zaxistype == ZAXIS_SEA_BOTTOM          ||
699        zaxistype == ZAXIS_LAKE_BOTTOM         ||
700        zaxistype == ZAXIS_SEDIMENT_BOTTOM     ||
701        zaxistype == ZAXIS_SEDIMENT_BOTTOM_TA  ||
702        zaxistype == ZAXIS_SEDIMENT_BOTTOM_TW  ||
703        zaxistype == ZAXIS_MIX_LAYER           ||
704        zaxistype == ZAXIS_ATMOSPHERE )
705     {
706       char varname[CDI_MAX_NAME];
707       int length = CDI_MAX_NAME;
708       cdiInqKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_NAME, varname, &length);
709       cdf_put_att_text(fileID, ncvarID, "level_type", strlen(varname), varname);
710     }
711 }
712 
713 static
cdfDefineAttrEnsemble(int fileID,int ncvarID,int vlistID,int varID)714 void cdfDefineAttrEnsemble(int fileID, int ncvarID, int vlistID, int varID)
715 {
716   int perturbationNumber, numberOfForecastsInEnsemble, typeOfEnsembleForecast;
717   if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_PERTURBATIONNUMBER, &perturbationNumber) == 0 )
718     cdf_put_att_int(fileID, ncvarID, "realization", NC_INT, 1, &perturbationNumber);
719   if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, &numberOfForecastsInEnsemble) == 0 )
720     cdf_put_att_int(fileID, ncvarID, "ensemble_members", NC_INT, 1, &numberOfForecastsInEnsemble);
721   if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFENSEMBLEFORECAST, &typeOfEnsembleForecast) == 0 )
722     cdf_put_att_int(fileID, ncvarID, "forecast_init_type", NC_INT, 1, &typeOfEnsembleForecast);
723 }
724 
725 static
cdfCheckVarname(int fileID,char name[CDI_MAX_NAME])726 void cdfCheckVarname(int fileID, char name[CDI_MAX_NAME])
727 {
728   if ( name[0] )
729     {
730       int ncvarID;
731       char varname[CDI_MAX_NAME];
732       sprintf(varname, "%s", name);
733       char *varname2 = varname+strlen(varname);
734       unsigned iz = 0;
735 
736       do
737         {
738           if ( iz ) sprintf(varname2, "_%u", iz+1);
739 
740           if ( nc_inq_varid(fileID, varname, &ncvarID) != NC_NOERR ) break;
741 
742           ++iz;
743         }
744       while ( iz <= 99 );
745 
746       if (iz > 99) Error("Variable name %s already exsist!", name);
747 
748       if ( strcmp(name, varname) != 0 )
749         Warning("Changed %s entry of variable name '%s' to '%s'!", (iz==1)?"double":"multiple", name, varname);
750 
751       strcpy(name, varname);
752     }
753 }
754 
755 static
cdfGenVarname(int fileID,char name[CDI_MAX_NAME],int pnum,int pcat,int * pdis,int * pcode)756 void cdfGenVarname(int fileID, char name[CDI_MAX_NAME], int pnum, int pcat, int *pdis, int *pcode)
757 {
758   char varname[CDI_MAX_NAME];
759 
760   int code = *pcode;
761   if ( code < 0 ) code = -code;
762   if ( pnum < 0 ) pnum = -pnum;
763 
764   if ( *pdis == 255 )
765     sprintf(varname, "var%d", code);
766   else
767     sprintf(varname, "param%d.%d.%d", pnum, pcat, *pdis);
768 
769   char *varname2 = varname+strlen(varname);
770   int ncvarID;
771   unsigned iz = 0;
772 
773   do
774     {
775       if ( iz ) sprintf(varname2, "_%u", iz+1);
776 
777       if ( nc_inq_varid(fileID, varname, &ncvarID) != NC_NOERR ) break;
778 
779       ++iz;
780     }
781   while ( iz <= 99 );
782 
783   if (iz > 99) Error("Variable name %s already exsist!", name);
784 
785   strcpy(name, varname);
786   *pcode = 0;
787   *pdis = 255;
788 }
789 
790 static
cdfDefVar(stream_t * streamptr,int varID)791 int cdfDefVar(stream_t *streamptr, int varID)
792 {
793   if (streamptr->vars[varID].ncvarid != CDI_UNDEFID)
794     return streamptr->vars[varID].ncvarid;
795 
796   const int fileID = streamptr->fileID;
797   if (CDI_Debug) Message("streamID = %d, fileID = %d, varID = %d", streamptr->self, fileID, varID);
798 
799   const int vlistID = streamptr->vlistID;
800   const int param = vlistInqVarParam(vlistID, varID);
801   int code = vlistInqVarCode(vlistID, varID);
802   int pnum, pcat, pdis;
803   cdiDecodeParam(param, &pnum, &pcat, &pdis);
804 
805   const int gridID = vlistInqVarGrid(vlistID, varID);
806   const size_t gridsize = gridInqSize(gridID);
807   const int gridtype = gridInqType(gridID);
808   const int gridindex = nc_grid_index(streamptr, gridID);
809   const int xid = (gridtype != GRID_TRAJECTORY) ? streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X] : CDI_UNDEFID;
810   const int yid = (gridtype != GRID_TRAJECTORY && gridtype != GRID_GAUSSIAN_REDUCED) ?
811     streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y] : CDI_UNDEFID;
812 
813   const int zaxisID = vlistInqVarZaxis(vlistID, varID);
814   const int zaxistype = zaxisInqType(zaxisID);
815   const int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
816   const int zid = streamptr->zaxisID[zaxisindex];
817 
818   int dimorder[3]; // ZYX and ZXY
819   vlistInqVarDimorder(vlistID, varID, &dimorder);
820   const bool lchunk = (dimorder[0] == 3) ? (gridsize >= 32) : false;
821 
822   if ( ((dimorder[0]>0)+(dimorder[1]>0)+(dimorder[2]>0)) < ((xid!=CDI_UNDEFID)+(yid!=CDI_UNDEFID)+(zid!=CDI_UNDEFID)) )
823     {
824       printf("zid=%d  yid=%d  xid=%d\n", zid, yid, xid);
825       Error("Internal problem, dimension order missing!");
826     }
827 
828   size_t iax = 0;
829   char axis[5];
830   int dims[4];
831   size_t chunks[4];
832   int ndims = cdfDefineDimsAndChunks(streamptr, varID, xid, yid, zid, gridsize, dimorder, dims, lchunk, chunks, axis, &iax);
833 
834   char name[CDI_MAX_NAME];
835   int length = CDI_MAX_NAME;
836   cdiInqKeyString(vlistID, varID, CDI_KEY_NAME, name, &length);
837 
838   char longname[CDI_MAX_NAME];
839   vlistInqVarLongname(vlistID, varID, longname);
840 
841   char units[CDI_MAX_NAME];
842   vlistInqVarUnits(vlistID, varID, units);
843 
844   char stdname[CDI_MAX_NAME];
845   length = CDI_MAX_NAME;
846   cdiInqKeyString(vlistID, varID, CDI_KEY_STDNAME, stdname, &length);
847 
848   const int tableID  = vlistInqVarTable(vlistID, varID);
849   if (!name[0]) tableInqEntry(tableID, code, -1, name, longname, units);
850   if (name[0]) cdfCheckVarname(fileID, name);
851   else         cdfGenVarname(fileID, name, pnum, pcat, &pdis, &code);
852 
853   const int dtype = vlistInqVarDatatype(vlistID, varID);
854   const nc_type xtype = cdfDefDatatype(dtype, streamptr);
855 
856   int ncvarID = -1;
857   cdf_def_var(fileID, name, xtype, ndims, dims, &ncvarID);
858 
859 #ifdef  HAVE_NETCDF4
860   if (lchunk && (streamptr->filetype == CDI_FILETYPE_NC4 || streamptr->filetype == CDI_FILETYPE_NC4C))
861     cdf_def_var_chunking(fileID, ncvarID, NC_CHUNKED, chunks);
862 #endif
863 
864   cdfDefVarCompression(streamptr, ncvarID, lchunk, xtype);
865 
866   if ( *stdname )  cdf_put_att_text(fileID, ncvarID, "standard_name", strlen(stdname), stdname);
867   if ( *longname ) cdf_put_att_text(fileID, ncvarID, "long_name", strlen(longname), longname);
868   if ( *units )    cdf_put_att_text(fileID, ncvarID, "units", strlen(units), units);
869 
870   if ( code > 0 && pdis == 255 )
871     cdf_put_att_int(fileID, ncvarID, "code", NC_INT, 1, &code);
872 
873   if ( pdis != 255 )
874     {
875       char paramstr[32];
876       cdiParamToString(param, paramstr, sizeof(paramstr));
877       cdf_put_att_text(fileID, ncvarID, "param", strlen(paramstr), paramstr);
878     }
879 
880   if ( tableID != CDI_UNDEFID )
881     {
882       int tablenum = tableInqNum(tableID);
883       if (tablenum > 0) cdf_put_att_int(fileID, ncvarID, "table", NC_INT, 1, &tablenum);
884     }
885 
886   const bool zaxisIsScalar = (zid == CDI_UNDEFID) ? (zaxisInqScalar(zaxisID) > 0) : false;
887   const int nczvarID = (zaxisIsScalar || zaxistype == ZAXIS_CHAR) ? streamptr->nczvarID[zaxisindex] : CDI_UNDEFID;
888 
889   cdfDefineCoordinates(streamptr, ncvarID, nczvarID, gridtype, gridID, gridindex, xid, yid, gridsize, axis, iax);
890 
891   cdfDefVarPacking(streamptr, ncvarID, xtype, vlistID, varID);
892 
893   if ( dtype == CDI_DATATYPE_UINT8 && xtype == NC_BYTE )
894     {
895       const int validrange[2] = {0, 255};
896       cdf_put_att_int(fileID, ncvarID, "valid_range", NC_SHORT, 2, validrange);
897       cdf_put_att_text(fileID, ncvarID, "_Unsigned", 4, "true");
898     }
899 
900   streamptr->vars[varID].ncvarid = ncvarID;
901 
902   if ( vlistInqVarMissvalUsed(vlistID, varID) )
903     cdfDefVarMissval(streamptr, varID, vlistInqVarDatatype(vlistID, varID), 0);
904 
905   if (zid == CDI_UNDEFID) cdfDefineAttrLeveltype(fileID, ncvarID, zaxisID, zaxistype);
906 
907   cdfDefineAttrEnsemble(fileID, ncvarID, vlistID, varID);
908 
909   // Attribute: cell_methods
910   cdfDefineCellMethods(streamptr, vlistID, varID, fileID, ncvarID);
911 
912   // Attributes
913   cdfDefineAttributes(vlistID, varID, fileID, ncvarID);
914 
915   // Institute
916   if (vlistInqInstitut(vlistID) == CDI_UNDEFID) cdfDefineInstituteName(vlistID, varID, fileID, ncvarID);
917 
918   return ncvarID;
919 }
920 
921 
cdfEndDef(stream_t * streamptr)922 void cdfEndDef(stream_t *streamptr)
923 {
924   cdfDefGlobalAtts(streamptr);
925 
926   if ( streamptr->accessmode == 0 )
927     {
928       const int fileID = streamptr->fileID;
929       if ( streamptr->ncmode == 2 ) cdf_redef(fileID);
930 
931       const int nvars = streamptr->nvars;
932       for ( int varID = 0; varID < nvars; varID++ )
933 	cdfDefVar(streamptr, varID);
934 
935       if ( streamptr->ncmode == 2 )
936         {
937           if ( CDI_Netcdf_Hdr_Pad == 0UL )
938             cdf_enddef(fileID);
939           else
940             cdf__enddef(fileID, CDI_Netcdf_Hdr_Pad);
941         }
942 
943       streamptr->accessmode = 1;
944     }
945 }
946 
947 static
cdfWriteGridTraj(stream_t * streamptr,int gridID)948 void cdfWriteGridTraj(stream_t *streamptr, int gridID)
949 {
950   const int gridindex = nc_grid_index(streamptr, gridID);
951   const int lonID = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
952   const int latID = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
953   const size_t index = (size_t)streamptr->curTsID;
954 
955   const double xlon = gridInqXval(gridID, 0);
956   const double xlat = gridInqYval(gridID, 0);
957 
958   cdf_put_var1_double(streamptr->fileID, lonID, &index, &xlon);
959   cdf_put_var1_double(streamptr->fileID, latID, &index, &xlat);
960 }
961 
962 static
cdf_write_var_data(int fileID,int vlistID,int varID,int ncvarID,int dtype,size_t nvals,size_t xsize,size_t ysize,bool swapxy,size_t * start,size_t * count,int memtype,const void * data,size_t nmiss)963 void cdf_write_var_data(int fileID, int vlistID, int varID, int ncvarID, int dtype, size_t nvals, size_t xsize, size_t ysize,
964                         bool swapxy, size_t *start, size_t *count, int memtype, const void *data, size_t nmiss)
965 {
966   const double *pdata_dp = (const double *) data;
967   double *mdata_dp = NULL;
968   double *sdata_dp = NULL;
969   const float *pdata_sp = (const float *) data;
970   float *mdata_sp = NULL;
971   float *sdata_sp = NULL;
972 
973   /*  if ( dtype == CDI_DATATYPE_INT8 || dtype == CDI_DATATYPE_INT16 || dtype == CDI_DATATYPE_INT32 ) */
974     {
975       const double missval      = vlistInqVarMissval(vlistID, varID);
976       const double addoffset    = vlistInqVarAddoffset(vlistID, varID);
977       const double scalefactor  = vlistInqVarScalefactor(vlistID, varID);
978       const bool laddoffset     = IS_NOT_EQUAL(addoffset, 0);
979       const bool lscalefactor   = IS_NOT_EQUAL(scalefactor, 1);
980 
981       if ( laddoffset || lscalefactor )
982         {
983           if ( memtype == MEMTYPE_FLOAT )
984             {
985               mdata_sp = (float *) Malloc(nvals*sizeof(float));
986               memcpy(mdata_sp, pdata_sp, nvals*sizeof(float));
987               pdata_sp = mdata_sp;
988 
989               if ( nmiss > 0 )
990                 {
991                   for ( size_t i = 0; i < nvals; i++ )
992                     {
993                       double temp = mdata_sp[i];
994                       if ( !DBL_IS_EQUAL(temp, missval) )
995                         {
996                           if ( laddoffset )   temp -= addoffset;
997                           if ( lscalefactor ) temp /= scalefactor;
998                           mdata_sp[i] = (float)temp;
999                         }
1000                     }
1001                 }
1002               else
1003                 {
1004                   for ( size_t i = 0; i < nvals; i++ )
1005                     {
1006                       double temp = mdata_sp[i];
1007                       if ( laddoffset )   temp -= addoffset;
1008                       if ( lscalefactor ) temp /= scalefactor;
1009                       mdata_sp[i] = (float)temp;
1010                     }
1011                 }
1012             }
1013           else
1014             {
1015               mdata_dp = (double *) Malloc(nvals*sizeof(double));
1016               memcpy(mdata_dp, pdata_dp, nvals*sizeof(double));
1017               pdata_dp = mdata_dp;
1018 
1019               if ( nmiss > 0 )
1020                 {
1021                   for ( size_t i = 0; i < nvals; i++ )
1022                     {
1023                       if ( !DBL_IS_EQUAL(mdata_dp[i], missval) )
1024                         {
1025                           if ( laddoffset )   mdata_dp[i] -= addoffset;
1026                           if ( lscalefactor ) mdata_dp[i] /= scalefactor;
1027                         }
1028                     }
1029                 }
1030               else
1031                 {
1032                   for ( size_t i = 0; i < nvals; i++ )
1033                     {
1034                       if ( laddoffset )   mdata_dp[i] -= addoffset;
1035                       if ( lscalefactor ) mdata_dp[i] /= scalefactor;
1036                     }
1037                 }
1038             }
1039         }
1040 
1041       if ( dtype == CDI_DATATYPE_UINT8 || dtype == CDI_DATATYPE_INT8 ||
1042            dtype == CDI_DATATYPE_INT16 || dtype == CDI_DATATYPE_INT32 )
1043         {
1044           if ( memtype == MEMTYPE_FLOAT )
1045             {
1046               if ( mdata_sp == NULL )
1047                 {
1048                   mdata_sp = (float *) Malloc(nvals*sizeof(float));
1049                   memcpy(mdata_sp, pdata_sp, nvals*sizeof(float));
1050                   pdata_sp = mdata_sp;
1051                 }
1052 
1053               for ( size_t i = 0; i < nvals; i++ ) mdata_sp[i] = roundf(mdata_sp[i]);
1054 
1055               if ( dtype == CDI_DATATYPE_UINT8 )
1056                 {
1057                   nc_type xtype;
1058                   cdf_inq_vartype(fileID, ncvarID, &xtype);
1059                   if ( xtype == NC_BYTE )
1060                     {
1061                       for ( size_t i = 0; i < nvals; ++i )
1062                         if ( mdata_sp[i] > 127 ) mdata_sp[i] -= 256;
1063                     }
1064                 }
1065             }
1066           else
1067             {
1068               if ( mdata_dp == NULL )
1069                 {
1070                   mdata_dp = (double *) Malloc(nvals*sizeof(double));
1071                   memcpy(mdata_dp, pdata_dp, nvals*sizeof(double));
1072                   pdata_dp = mdata_dp;
1073                 }
1074 
1075               for ( size_t i = 0; i < nvals; i++ ) mdata_dp[i] = round(mdata_dp[i]);
1076 
1077               if ( dtype == CDI_DATATYPE_UINT8 )
1078                 {
1079                   nc_type xtype;
1080                   cdf_inq_vartype(fileID, ncvarID, &xtype);
1081                   if ( xtype == NC_BYTE )
1082                     {
1083                       for ( size_t i = 0; i < nvals; ++i )
1084                         if ( mdata_dp[i] > 127 ) mdata_dp[i] -= 256;
1085                     }
1086                 }
1087             }
1088         }
1089 
1090       if ( CDF_Debug && memtype != MEMTYPE_FLOAT )
1091         {
1092           double fmin =  1.0e200;
1093           double fmax = -1.0e200;
1094           for ( size_t i = 0; i < nvals; ++i )
1095             {
1096               if ( !DBL_IS_EQUAL(pdata_dp[i], missval) )
1097                 {
1098                   if ( pdata_dp[i] < fmin ) fmin = pdata_dp[i];
1099                   if ( pdata_dp[i] > fmax ) fmax = pdata_dp[i];
1100                 }
1101             }
1102           Message("nvals = %zu, nmiss = %d, missval = %g, minval = %g, maxval = %g",
1103                   nvals, nmiss, missval, fmin, fmax);
1104         }
1105     }
1106 
1107   if ( swapxy ) // implemented only for cdf_write_var_slice()
1108     {
1109       size_t gridsize = xsize*ysize;
1110       if ( memtype == MEMTYPE_FLOAT )
1111         {
1112           sdata_sp = (float *) Malloc(gridsize*sizeof(float));
1113           for ( size_t j = 0; j < ysize; ++j )
1114             for ( size_t i = 0; i < xsize; ++i )
1115               sdata_sp[i*ysize+j] = pdata_sp[j*xsize+i];
1116           pdata_sp = sdata_sp;
1117         }
1118       else
1119         {
1120           sdata_dp = (double *) Malloc(gridsize*sizeof (double));
1121           for ( size_t j = 0; j < ysize; ++j )
1122             for ( size_t i = 0; i < xsize; ++i )
1123               sdata_dp[i*ysize+j] = pdata_dp[j*xsize+i];
1124           pdata_dp = sdata_dp;
1125         }
1126     }
1127 
1128   if (dtype == CDI_DATATYPE_CPX32 || dtype == CDI_DATATYPE_CPX64)
1129     {
1130       void *pdata = (memtype == MEMTYPE_FLOAT) ? (void*)pdata_sp : (void*)pdata_dp;
1131       float *cdata_sp = NULL;
1132       double *cdata_dp = NULL;
1133       if (memtype == MEMTYPE_FLOAT && dtype == CDI_DATATYPE_CPX64)
1134         {
1135           cdata_dp = (double *) Malloc(2*nvals*sizeof(double));
1136           for (size_t i = 0; i < nvals; i++)
1137             {
1138               cdata_dp[2*i] = (double)(pdata_sp[2*i]);
1139               cdata_dp[2*i+1] = (double)(pdata_sp[2*i+1]);
1140             }
1141           pdata = cdata_dp;
1142         }
1143       else if (memtype == MEMTYPE_DOUBLE && dtype == CDI_DATATYPE_CPX32)
1144         {
1145           cdata_sp = (float *) Malloc(2*nvals*sizeof(float));
1146           for (size_t i = 0; i < nvals; i++)
1147             {
1148               cdata_sp[2*i] = (float)(pdata_dp[2*i]);
1149               cdata_sp[2*i+1] = (float)(pdata_dp[2*i+1]);
1150             }
1151           pdata = cdata_sp;
1152         }
1153 
1154       cdf_put_vara(fileID, ncvarID, start, count, pdata);
1155       if (cdata_sp) Free(cdata_sp);
1156       if (cdata_dp) Free(cdata_dp);
1157     }
1158   else
1159     {
1160       if ( memtype == MEMTYPE_FLOAT )
1161         cdf_put_vara_float(fileID, ncvarID, start, count, pdata_sp);
1162       else
1163         cdf_put_vara_double(fileID, ncvarID, start, count, pdata_dp);
1164     }
1165 
1166   if ( mdata_dp ) Free(mdata_dp);
1167   if ( sdata_dp ) Free(sdata_dp);
1168   if ( mdata_sp ) Free(mdata_sp);
1169   if ( sdata_sp ) Free(sdata_sp);
1170 }
1171 
1172 static
cdfGetXYZid(stream_t * streamptr,int gridID,int zaxisID,int * xid,int * yid,int * zid)1173 void cdfGetXYZid(stream_t *streamptr, int gridID, int zaxisID, int *xid, int *yid, int *zid)
1174 {
1175   *xid = CDI_UNDEFID;
1176   *yid = CDI_UNDEFID;
1177 
1178   const int gridtype = gridInqType(gridID);
1179   if (gridtype == GRID_TRAJECTORY)
1180     {
1181       cdfWriteGridTraj(streamptr, gridID);
1182     }
1183   else
1184     {
1185       const int gridindex = nc_grid_index(streamptr, gridID);
1186       *xid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_X];
1187       if (gridtype != GRID_GAUSSIAN_REDUCED)
1188         *yid = streamptr->ncgrid[gridindex].ncIDs[CDF_DIMID_Y];
1189     }
1190 
1191   const int vlistID = streamptr->vlistID;
1192   const int zaxisindex = vlistZaxisIndex(vlistID, zaxisID);
1193   *zid = streamptr->zaxisID[zaxisindex];
1194 }
1195 
1196 static
cdfDefineStartAndCount(stream_t * streamptr,int varID,int xid,int yid,int zid,size_t start[5],size_t count[5],size_t * xsize,size_t * ysize)1197 void cdfDefineStartAndCount(stream_t *streamptr, int varID, int xid, int yid, int zid, size_t start[5], size_t count[5], size_t *xsize, size_t *ysize)
1198 {
1199   size_t ndims = 0;
1200   *xsize = 0;
1201   *ysize = 0;
1202 
1203   const int vlistID = streamptr->vlistID;
1204   const int fileID  = streamptr->fileID;
1205 
1206   const long ntsteps = streamptr->ntsteps;
1207   if ( CDI_Debug ) Message("ntsteps = %ld", ntsteps);
1208 
1209   const int timetype = vlistInqVarTimetype(vlistID, varID);
1210 
1211   if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
1212     {
1213       start[ndims] = (size_t)ntsteps - 1;
1214       count[ndims] = 1;
1215       ndims++;
1216     }
1217 
1218   if ( zid != CDI_UNDEFID )
1219     {
1220       const int zaxisID = vlistInqVarZaxis(vlistID, varID);
1221       start[ndims] = 0;
1222       count[ndims] = (size_t)zaxisInqSize(zaxisID);
1223       ndims++;
1224     }
1225 
1226   if ( yid != CDI_UNDEFID )
1227     {
1228       start[ndims] = 0;
1229       size_t size;
1230       cdf_inq_dimlen(fileID, yid, &size);
1231       /*      count[ndims] = gridInqYsize(gridID); */
1232       count[ndims] = size;
1233       ndims++;
1234     }
1235 
1236   if ( xid != CDI_UNDEFID )
1237     {
1238       start[ndims] = 0;
1239       size_t size;
1240       cdf_inq_dimlen(fileID, xid, &size);
1241       /*      count[ndims] = gridInqXsize(gridID); */
1242       count[ndims] = size;
1243       ndims++;
1244     }
1245 
1246   if ( CDI_Debug )
1247     for (size_t idim = 0; idim < ndims; idim++)
1248       Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);
1249 }
1250 
cdf_write_var(stream_t * streamptr,int varID,int memtype,const void * data,size_t nmiss)1251 void cdf_write_var(stream_t *streamptr, int varID, int memtype, const void *data, size_t nmiss)
1252 {
1253   if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);
1254 
1255   if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);
1256 
1257   const int vlistID = streamptr->vlistID;
1258   const int fileID  = streamptr->fileID;
1259 
1260   const int ncvarID = cdfDefVar(streamptr, varID);
1261 
1262   const int gridID   = vlistInqVarGrid(vlistID, varID);
1263   const int zaxisID  = vlistInqVarZaxis(vlistID, varID);
1264 
1265   int xid, yid, zid;
1266   cdfGetXYZid(streamptr, gridID, zaxisID, &xid, &yid, &zid);
1267 
1268   size_t xsize, ysize;
1269   size_t start[5], count[5];
1270   cdfDefineStartAndCount(streamptr, varID, xid, yid, zid, start, count, &xsize, &ysize);
1271 
1272   if ( streamptr->ncmode == 1 )
1273     {
1274       cdf_enddef(fileID);
1275       streamptr->ncmode = 2;
1276     }
1277 
1278   const int dtype = vlistInqVarDatatype(vlistID, varID);
1279 
1280   if ( nmiss > 0 ) cdfDefVarMissval(streamptr, varID, dtype, 1);
1281 
1282   const size_t nvals = gridInqSize(gridID) * (size_t)(zaxisInqSize(zaxisID));
1283 
1284   bool swapxy = false;
1285   cdf_write_var_data(fileID, vlistID, varID, ncvarID, dtype, nvals, xsize, ysize, swapxy, start, count, memtype, data, nmiss);
1286 }
1287 
1288 static
cdfDefineStartAndCountChunk(stream_t * streamptr,const int rect[][2],int varID,int xid,int yid,int zid,size_t start[5],size_t count[5],size_t * xsize,size_t * ysize)1289 void cdfDefineStartAndCountChunk(stream_t *streamptr, const int rect[][2], int varID, int xid, int yid, int zid, size_t start[5], size_t count[5], size_t *xsize, size_t *ysize)
1290 {
1291   size_t ndims = 0;
1292   *xsize = 0;
1293   *ysize = 0;
1294 
1295   const int vlistID = streamptr->vlistID;
1296   const int fileID  = streamptr->fileID;
1297 
1298   const long ntsteps = streamptr->ntsteps;
1299   if ( CDI_Debug ) Message("ntsteps = %ld", ntsteps);
1300 
1301   const int timetype = vlistInqVarTimetype(vlistID, varID);
1302 
1303   if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
1304     {
1305       start[ndims] = (size_t)ntsteps - 1;
1306       count[ndims] = 1;
1307       ndims++;
1308     }
1309 
1310   if ( zid != CDI_UNDEFID )
1311     {
1312       const int zaxisID  = vlistInqVarZaxis(vlistID, varID);
1313       int size = zaxisInqSize(zaxisID);
1314       xassert(rect[2][0] >= 0 && rect[2][0] <= rect[2][1] && rect[2][1] <= size);
1315       start[ndims] = (size_t)rect[2][0];
1316       count[ndims] = (size_t)rect[2][1] - (size_t)rect[2][0] + 1;
1317       ndims++;
1318     }
1319 
1320   if ( yid != CDI_UNDEFID )
1321     {
1322       size_t size;
1323       cdf_inq_dimlen(fileID, yid, &size);
1324       xassert(rect[1][0] >= 0 && rect[1][0] <= rect[1][1] && (size_t)rect[1][1] <= size);
1325       start[ndims] = (size_t)rect[1][0];
1326       count[ndims] = (size_t)rect[1][1] - (size_t)rect[1][0] + 1;
1327       ndims++;
1328     }
1329 
1330   if ( xid != CDI_UNDEFID )
1331     {
1332       size_t size;
1333       cdf_inq_dimlen(fileID, xid, &size);
1334       xassert(rect[0][0] >= 0 && rect[0][0] <= rect[0][1] && (size_t)rect[0][1] <= size);
1335       start[ndims] = (size_t)rect[0][0];
1336       count[ndims] = (size_t)rect[0][1] - (size_t)rect[0][0] + 1;
1337       ndims++;
1338     }
1339 
1340   if ( CDI_Debug )
1341     for (size_t idim = 0; idim < ndims; idim++)
1342       Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);
1343 }
1344 
cdf_write_var_chunk(stream_t * streamptr,int varID,int memtype,const int rect[][2],const void * data,size_t nmiss)1345 void cdf_write_var_chunk(stream_t *streamptr, int varID, int memtype,
1346                          const int rect[][2], const void *data, size_t nmiss)
1347 {
1348   if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);
1349 
1350   const int streamID = streamptr->self;
1351 
1352   if ( CDI_Debug )
1353     Message("streamID = %d  varID = %d", streamID, varID);
1354 
1355   const int vlistID = streamInqVlist(streamID);
1356   const int fileID  = streamInqFileID(streamID);
1357 
1358   const int ncvarID = cdfDefVar(streamptr, varID);
1359 
1360   const int gridID   = vlistInqVarGrid(vlistID, varID);
1361   const int zaxisID  = vlistInqVarZaxis(vlistID, varID);
1362 
1363   int xid, yid, zid;
1364   cdfGetXYZid(streamptr, gridID, zaxisID, &xid, &yid, &zid);
1365 
1366   size_t xsize, ysize;
1367   size_t start[5], count[5];
1368   cdfDefineStartAndCountChunk(streamptr, rect, varID, xid, yid, zid, start, count, &xsize, &ysize);
1369 
1370   if ( streamptr->ncmode == 1 )
1371     {
1372       cdf_enddef(fileID);
1373       streamptr->ncmode = 2;
1374     }
1375 
1376   const int dtype = vlistInqVarDatatype(vlistID, varID);
1377 
1378   if ( nmiss > 0 ) cdfDefVarMissval(streamptr, varID, dtype, 1);
1379 
1380   const size_t nvals = gridInqSize(gridID) * (size_t)(zaxisInqSize(zaxisID));
1381 
1382   bool swapxy = false;
1383   cdf_write_var_data(fileID, vlistID, varID, ncvarID, dtype, nvals,
1384                      xsize, ysize, swapxy, start, count, memtype, data, nmiss);
1385 }
1386 
1387 static
cdfDefineStartAndCountSlice(stream_t * streamptr,int varID,int levelID,int dimorder[3],int xid,int yid,int zid,size_t start[5],size_t count[5],size_t * xsize,size_t * ysize)1388 void cdfDefineStartAndCountSlice(stream_t *streamptr, int varID, int levelID, int dimorder[3], int xid, int yid, int zid, size_t start[5], size_t count[5], size_t *xsize, size_t *ysize)
1389 {
1390   size_t ndims = 0;
1391   *xsize = 0;
1392   *ysize = 0;
1393 
1394   const int vlistID = streamptr->vlistID;
1395   const int fileID  = streamptr->fileID;
1396 
1397   const long ntsteps = streamptr->ntsteps;
1398   if ( CDI_Debug ) Message("ntsteps = %ld", ntsteps);
1399 
1400   const int timetype = vlistInqVarTimetype(vlistID, varID);
1401 
1402   if ( vlistHasTime(vlistID) && timetype != TIME_CONSTANT )
1403     {
1404       start[ndims] = (size_t)ntsteps - 1;
1405       count[ndims] = 1;
1406       ndims++;
1407     }
1408 
1409   for ( int id = 0; id < 3; ++id )
1410     {
1411       if ( dimorder[id] == 3 && zid != CDI_UNDEFID )
1412         {
1413           start[ndims] = (size_t)levelID;
1414           count[ndims] = 1;
1415           ndims++;
1416         }
1417       else if ( dimorder[id] == 2 && yid != CDI_UNDEFID )
1418         {
1419           start[ndims] = 0;
1420           cdf_inq_dimlen(fileID, yid, ysize);
1421           count[ndims] = *ysize;
1422           ndims++;
1423         }
1424       else if ( dimorder[id] == 1 && xid != CDI_UNDEFID )
1425         {
1426           start[ndims] = 0;
1427           cdf_inq_dimlen(fileID, xid, xsize);
1428           count[ndims] = *xsize;
1429           ndims++;
1430         }
1431     }
1432 
1433   if ( CDI_Debug )
1434     for (size_t idim = 0; idim < ndims; idim++)
1435       Message("dim = %d  start = %d  count = %d", idim, start[idim], count[idim]);
1436 }
1437 
1438 
cdf_write_var_slice(stream_t * streamptr,int varID,int levelID,int memtype,const void * data,size_t nmiss)1439 void cdf_write_var_slice(stream_t *streamptr, int varID, int levelID, int memtype, const void *data, size_t nmiss)
1440 {
1441   if ( streamptr->accessmode == 0 ) cdfEndDef(streamptr);
1442 
1443   if ( CDI_Debug ) Message("streamID = %d  varID = %d", streamptr->self, varID);
1444 
1445   const int vlistID = streamptr->vlistID;
1446   const int fileID  = streamptr->fileID;
1447 
1448   const int ncvarID = cdfDefVar(streamptr, varID);
1449 
1450   const int gridID   = vlistInqVarGrid(vlistID, varID);
1451   const int zaxisID  = vlistInqVarZaxis(vlistID, varID);
1452 
1453   int xid, yid, zid;
1454   cdfGetXYZid(streamptr, gridID, zaxisID, &xid, &yid, &zid);
1455 
1456   int dimorder[3];
1457   vlistInqVarDimorder(vlistID, varID, &dimorder);
1458   const bool swapxy = (dimorder[2] == 2 || dimorder[0] == 1) && xid != CDI_UNDEFID && yid != CDI_UNDEFID;
1459 
1460   size_t xsize, ysize;
1461   size_t start[5], count[5];
1462   cdfDefineStartAndCountSlice(streamptr, varID, levelID, dimorder, xid, yid, zid, start, count, &xsize, &ysize);
1463 
1464   const int dtype = vlistInqVarDatatype(vlistID, varID);
1465 
1466   if ( nmiss > 0 ) cdfDefVarMissval(streamptr, varID, dtype, 1);
1467 
1468   const size_t nvals = gridInqSize(gridID);
1469 
1470   cdf_write_var_data(fileID, vlistID, varID, ncvarID, dtype, nvals, xsize, ysize, swapxy, start, count, memtype, data, nmiss);
1471 }
1472 
1473 
cdf_write_record(stream_t * streamptr,int memtype,const void * data,size_t nmiss)1474 void cdf_write_record(stream_t *streamptr, int memtype, const void *data, size_t nmiss)
1475 {
1476   const int varID   = streamptr->record->varID;
1477   const int levelID = streamptr->record->levelID;
1478   cdf_write_var_slice(streamptr, varID, levelID, memtype, data, nmiss);
1479 }
1480 
1481 #endif
1482 
1483 /*
1484  * Local Variables:
1485  * c-file-style: "Java"
1486  * c-basic-offset: 2
1487  * indent-tabs-mode: nil
1488  * show-trailing-whitespace: t
1489  * require-trailing-newline: t
1490  * End:
1491  */
1492