bes  Updated for version 3.20.13
FONcArray.cc
1 // FONcArray.cc
2 
3 // This file is part of BES Netcdf File Out Module
4 
5 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
6 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact University Corporation for Atmospheric Research at
23 // 3080 Center Green Drive, Boulder, CO 80301
24 
25 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
26 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
27 //
28 // Authors:
29 // pwest Patrick West <pwest@ucar.edu>
30 // jgarcia Jose Garcia <jgarcia@ucar.edu>
31 // Kent Yang <myang6@hdfgroup.org> (for DAP4/netCDF-4 enhancement)
32 
33 #include <sstream>
34 #include <algorithm>
35 
36 #include <libdap/Array.h>
37 
38 #include <BESInternalError.h>
39 #include <BESSyntaxUserError.h>
40 #include <BESDebug.h>
41 
42 #include "FONcRequestHandler.h" // For access to the handler's keys
43 #include "FONcArray.h"
44 #include "FONcDim.h"
45 #include "FONcGrid.h"
46 #include "FONcMap.h"
47 #include "FONcUtils.h"
48 #include "FONcAttributes.h"
49 
50 using namespace libdap;
51 
52 // This controls whether variables' data values are deleted as soon
53 // as they are written (except for DAP2 Grid Maps, which may be shared).
54 #define CLEAR_LOCAL_DATA 1
55 
56 vector<FONcDim *> FONcArray::Dimensions;
57 
58 const int MAX_CHUNK_SIZE = 1024;
59 
68 FONcArray::FONcArray(BaseType *b) :
69  FONcBaseType(), d_a(0), d_array_type(NC_NAT), d_ndims(0), d_actual_ndims(0), d_nelements(1), d4_dim_ids(0),
70  d_dim_ids(0), d_dim_sizes(0), /* FIXME d_str_data(0),*/ d_dont_use_it(false), d_chunksizes(0),
71  d_grid_maps(0), d4_def_dim(false) {
72  d_a = dynamic_cast<Array *>(b);
73  if (!d_a) {
74  string s = "File out netcdf, FONcArray was passed a variable that is not a DAP Array";
75  throw BESInternalError(s, __FILE__, __LINE__);
76  }
77 
78  for (unsigned int i = 0; i < d_a->dimensions(); i++)
79  use_d4_dim_ids.push_back(false);
80 }
81 
82 FONcArray::FONcArray(BaseType *b, const vector<int> &fd4_dim_ids, const vector<bool> &fuse_d4_dim_ids,
83  const vector<int> &rds_nums) :
84  FONcBaseType(), d_a(0), d_array_type(NC_NAT), d_ndims(0), d_actual_ndims(0), d_nelements(1), d_dim_ids(0),
85  d_dim_sizes(0), /* FIXME d_str_data(0),*/ d_dont_use_it(false), d_chunksizes(0), d_grid_maps(0) {
86  d_a = dynamic_cast<Array *>(b);
87  if (!d_a) {
88  string s = "File out netcdf, FONcArray was passed a variable that is not a DAP Array";
89  throw BESInternalError(s, __FILE__, __LINE__);
90  }
91  if (d_a->is_dap4()) {
92  BESDEBUG("fonc", "FONcArray() - constructor is dap4 " << endl);
93  d4_dim_ids = fd4_dim_ids;
94  use_d4_dim_ids = fuse_d4_dim_ids;
95  d4_def_dim = true;
96  d4_rds_nums = rds_nums;
97  }
98 }
99 
111  // Added jhrg 8/28/13
112  vector<FONcDim *>::iterator d = d_dims.begin();
113  while (d != d_dims.end()) {
114  (*d)->decref();
115  ++d;
116  }
117 
118  // Added jhrg 8/28/13
119  vector<FONcMap *>::iterator i = d_grid_maps.begin();
120  while (i != d_grid_maps.end()) {
121  (*i)->decref();
122  ++i;
123  }
124 }
125 
140 void FONcArray::convert(vector<string> embed, bool _dap4, bool is_dap4_group) {
141  FONcBaseType::convert(embed, _dap4, is_dap4_group);
142 
143  _varname = FONcUtils::gen_name(embed, _varname, _orig_varname);
144 
145  BESDEBUG("fonc", "FONcArray::convert() - converting array " << _varname << endl);
146 
147  d_array_type = FONcUtils::get_nc_type(d_a->var(), isNetCDF4_ENHANCED());
148 
149  if(d_array_type == NC_NAT) {
150 
151  string err = "fileout_netcdf: The datatype of this variable '" + _varname;
152  err += "' is not supported. It is very possible that you try to obtain ";
153  err += "a netCDF file that follows the netCDF classic model. ";
154  err += "The unsigned 32-bit integer and signed/unsigned 64-bit integer ";
155  err += "are not supported by the netCDF classic model. Downloading this file as the netCDF-4 file that ";
156  err += "follows the netCDF enhanced model should solve the problem.";
157 
158  throw BESInternalError(err, __FILE__, __LINE__);
159  }
160 
161 #if !NDEBUG
162  if (d4_dim_ids.size() > 0) {
163  BESDEBUG("fonc", "FONcArray::convert() - d4_dim_ids size is " << d4_dim_ids.size() << endl);
164  }
165 #endif
166 
167  d_ndims = d_a->dimensions();
168  d_actual_ndims = d_ndims; //replace this with _a->dimensions(); below TODO
169  if (d_array_type == NC_CHAR) {
170  // if we have an array of strings then we need to add the string length
171  // dimension, so add one more to ndims
172  d_ndims++;
173  }
174 
175  // See HYRAX-805. When assigning values using [], set the size using resize
176  // not reserve. THe reserve method works to optimize push_back(), but apparently
177  // does not work with []. jhrg 8/3/18
178  d_dim_ids.resize(d_ndims);
179  d_dim_sizes.resize(d_ndims);
180 
181  Array::Dim_iter di = d_a->dim_begin();
182  Array::Dim_iter de = d_a->dim_end();
183  int dimnum = 0;
184  for (; di != de; di++) {
185  int size = d_a->dimension_size(di, true);
186  d_dim_sizes[dimnum] = size;
187  d_nelements *= size;
188 
189  // Set COMPRESSION CHUNK SIZE for each dimension.
190  d_chunksizes.push_back(size <= MAX_CHUNK_SIZE ? size : MAX_CHUNK_SIZE);
191 
192  BESDEBUG("fonc", "FONcArray::convert() - dim num: " << dimnum << ", dim size: " << size << ", chunk size: "
193  << d_chunksizes[dimnum] << endl);
194  BESDEBUG("fonc", "FONcArray::convert() - dim name: " << d_a->dimension_name(di) << endl);
195 
196  // If this dimension is a D4 dimension defined in its group, just obtain the dimension ID.
197  if (true == d4_def_dim && use_d4_dim_ids[dimnum] == true) {
198  d_dim_ids[dimnum] = d4_dim_ids[dimnum];
199  BESDEBUG("fonc", "FONcArray::convert() - has dap4 group" << endl);
200 
201  }
202  else {
203  // See if this dimension has already been defined. If it has the
204  // same name and same size as another dimension, then it is a
205  // shared dimension. Create it only once and share the FONcDim
206  int ds_num = FONcDim::DimNameNum + 1;
207  while (find(d4_rds_nums.begin(), d4_rds_nums.end(), ds_num) != d4_rds_nums.end()) {
208  // Note: the following #if 0 #endif block is only for future development.
209  // Don't delete or change it for debuggging.
210 #if 0
211  // This may be an optimization for rare cases. May do this when performance issue hurts
212  //d4_rds_nums_visited.push_back(ds_num);
213 #endif
214  // Now the following line ensure this dimension name dimds_num(ds_num is a number)
215  // is NOT created for the dimension that doesn't have a name in DAP4.
216  ds_num++;
217  }
218  FONcDim::DimNameNum = ds_num - 1;
219 
220  FONcDim *use_dim = find_dim(embed, d_a->dimension_name(di), size);
221  d_dims.push_back(use_dim);
222  }
223 
224  dimnum++;
225  }
226 
227  // if this array is a string array, then add the length dimension
228  if (d_array_type == NC_CHAR) {
229  // Calling intern_data() here is part of the 'streaming' refactoring.
230  // For the other types, the call can go in the write() implementations,
231  // but because strings in netCDF are arrays of char, a string array
232  // must have an added dimension (so a 1d string array becomes a 2d char
233  // array). To build the netCDF file, we need to know the dimensions of
234  // the array when the file is defined, not when the data are written.
235  // To know the size of the extra dimension used to hold the chars, we
236  // need to look at all the strings and find the biggest one. Thus, in
237  // order to define the variable for the netCDF file, we need to read
238  // string data long before we actually write it out. Kind of a drag,
239  // but not the end of the world. jhrg 5/18/21
240  if (is_dap4)
241  d_a->intern_data();
242  else
243  d_a->intern_data(*get_eval(), *get_dds());
244 
245  // get the data from the dap array
246  int array_length = d_a->length();
247 #if 0
248  d_str_data.reserve(array_length);
249  d_a->value(d_str_data);
250 
251  // determine the max length of the strings
252  size_t max_length = 0;
253  for (int i = 0; i < array_length; i++) {
254  if (d_str_data[i].length() > max_length) {
255  max_length = d_str_data[i].length();
256  }
257  }
258  max_length++;
259 #endif
260  size_t max_length = 0;
261  for (int i = 0; i < array_length; i++) {
262  if (d_a->get_str()[i].length() > max_length) {
263  max_length = d_a->get_str()[i].length();
264  }
265  }
266  max_length++;
267 
268  vector<string> empty_embed;
269  string lendim_name;
270  if (is_dap4_group == true) {
271  // Here is a quick implementation.
272  // We just append the DimNameNum(globally defined)
273  // and then increase the number by 1.
274  ostringstream dim_suffix_strm;
275  dim_suffix_strm << "_len" << FONcDim::DimNameNum + 1;
276  FONcDim::DimNameNum++;
277  lendim_name = _varname + dim_suffix_strm.str();
278 
279  }
280  else
281  lendim_name = _varname + "_len";
282 
283 
284  FONcDim *use_dim = find_dim(empty_embed, lendim_name, max_length, true);
285  // Added static_cast to suppress warning. 12.27.2011 jhrg
286  if (use_dim->size() < static_cast<int>(max_length)) {
287  use_dim->update_size(max_length);
288  }
289 
290  d_dim_sizes[d_ndims - 1] = use_dim->size();
291  d_dim_ids[d_ndims - 1] = use_dim->dimid();
292 
293  //DAP4 dimension ID is false.
294  use_d4_dim_ids.push_back(false);
295  d_dims.push_back(use_dim);
296 
297  // Adding this fixes the bug reported by GSFC where arrays of strings
298  // caused the handler to throw an error stating that 'Bad chunk sizes'
299  // were used. When the dimension of the string array was extended (because
300  // strings become char arrays in netcdf3/4), the numbers of dimensions
301  // in 'chunksizes' was not bumped up. The code below in convert() that
302  // set the chunk sizes then tried to access data that had never been set.
303  // jhrg 11/25/15
304  d_chunksizes.push_back(max_length <= MAX_CHUNK_SIZE ? max_length : MAX_CHUNK_SIZE);
305  }
306 
307  // If this array has a single dimension, and the name of the array
308  // and the name of that dimension are the same, then this array
309  // might be used as a map for a grid defined elsewhere.
310  // Notice: DAP4 doesn't have Grid and the d_dont_use_it=true causes some
311  // variables not written to the netCDF-4 file with group hierarchy.
312  // So need to have the if check. KY 2021-06-21
313  if(is_dap4 == false) {
314  if (!FONcGrid::InGrid && d_actual_ndims == 1 && d_a->name() == d_a->dimension_name(d_a->dim_begin())) {
315  // is it already in there?
316  FONcMap *map = FONcGrid::InMaps(d_a);
317  if (!map) {
318  // This memory is/was leaked. jhrg 8/28/13
319  FONcMap *new_map = new FONcMap(this);
320  d_grid_maps.push_back(new_map); // save it here so we can free it later. jhrg 8/28/13
321  FONcGrid::Maps.push_back(new_map);
322  }
323  else {
324  d_dont_use_it = true;
325  }
326  }
327  }
328 
329  BESDEBUG("fonc", "FONcArray::convert() - done converting array " << _varname << endl);
330 }
331 
345 FONcDim *
346 FONcArray::find_dim(vector<string> &embed, const string &name, int size, bool ignore_size) {
347  string oname;
348  string ename = FONcUtils::gen_name(embed, name, oname);
349  FONcDim *ret_dim = 0;
350  vector<FONcDim *>::iterator i = FONcArray::Dimensions.begin();
351  vector<FONcDim *>::iterator e = FONcArray::Dimensions.end();
352  for (; i != e && !ret_dim; i++) {
353  if (!((*i)->name().empty()) && ((*i)->name() == name)) {
354  if (ignore_size) {
355  ret_dim = (*i);
356  }
357  else if ((*i)->size() == size) {
358  ret_dim = (*i);
359  }
360  else {
361  if (embed.size() > 0) {
362  vector<string> tmp;
363  return find_dim(tmp, ename, size);
364  }
365  string err = "fileout_netcdf: dimension found with the same name, but different size";
366  throw BESInternalError(err, __FILE__, __LINE__);
367  }
368  }
369  }
370  if (!ret_dim) {
371  ret_dim = new FONcDim(name, size);
372  FONcArray::Dimensions.push_back(ret_dim);
373  }
374  else {
375  ret_dim->incref();
376  }
377  return ret_dim;
378 }
379 
394 void FONcArray::define(int ncid) {
395  BESDEBUG("fonc", "FONcArray::define() - defining array '" << _varname << "'" << endl);
396 
397  if (!_defined && !d_dont_use_it) {
398 
399  BESDEBUG("fonc", "FONcArray::define() - defining array ' defined already: " << _varname << "'" << endl);
400 
401  // Note: the following #if 0 #endif block is only for future development.
402  // Don't delete or change it for debuggging.
403 
404 #if 0
405  if(d4_dim_ids.size() >0) {
406  if(d_array_type == NC_CHAR) {
407  if(d_dims.size() == 1) {
408  FONcDim *fd = *(d_dims.begin());
409  fd->define(ncid);
410  d_dim_ids[d_ndims-1] = fd->dimid();
411 
412  }
413  else {
414 
415  }
416  }
417  }
418  else {
419 #endif
420  // If not defined DAP4 dimensions(mostly DAP2 or DAP4 no groups)
421  if (false == d4_def_dim) {
422  vector<FONcDim *>::iterator i = d_dims.begin();
423  vector<FONcDim *>::iterator e = d_dims.end();
424  int dimnum = 0;
425  for (; i != e; i++) {
426  FONcDim *fd = *i;
427  fd->define(ncid);
428  //d_dim_ids.at(dimnum) = fd->dimid();
429  d_dim_ids[dimnum] = fd->dimid();
430  BESDEBUG("fonc", "FONcArray::define() - dim_id: " << fd->dimid() << " size:" << fd->size() << endl);
431  dimnum++;
432  }
433  }
434  else {// Maybe some dimensions are not DAP4 dimensions, will still generate those dimensions.
435  int j = 0;
436  for (unsigned int i = 0; i < use_d4_dim_ids.size(); i++) {
437  if (use_d4_dim_ids[i] == false) {
438  FONcDim *fd = d_dims[j];
439  fd->define(ncid);
440  d_dim_ids[i] = fd->dimid();
441  j++;
442  }
443  }
444  }
445 
446  int stax = nc_def_var(ncid, _varname.c_str(), d_array_type, d_ndims, d_dim_ids.data(), &_varid);
447  if (stax != NC_NOERR) {
448  string err = (string) "fileout.netcdf - Failed to define variable " + _varname;
449  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
450  }
451 
452  stax = nc_def_var_fill(ncid, _varid, NC_NOFILL, NULL );
453  if (stax != NC_NOERR) {
454  string err = (string) "fileout.netcdf - " + "Failed to clear fill value for " + _varname;
455  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
456  }
457 
458  BESDEBUG("fonc", "FONcArray::define() netcdf-4 version is " << _ncVersion << endl);
459  if (isNetCDF4()) {
460  BESDEBUG("fonc", "FONcArray::define() Working netcdf-4 branch " << endl);
461  if (FONcRequestHandler::chunk_size == 0)
462  // I have no idea if chunksizes is needed in this case.
463  stax = nc_def_var_chunking(ncid, _varid, NC_CONTIGUOUS, d_chunksizes.data());
464  else
465  stax = nc_def_var_chunking(ncid, _varid, NC_CHUNKED, d_chunksizes.data());
466 
467  if (stax != NC_NOERR) {
468  string err = "fileout.netcdf - Failed to define chunking for variable " + _varname;
469  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
470  }
471 
472  // TODO Make this more adaptable to the Array's data type. Find out when it's
473  // best to use shuffle, et c. jhrg 7/22/18
474  if (FONcRequestHandler::use_compression) {
475 
476  int shuffle = 0;
477  // For integer, if the type size is >= 2, turn on the shuffle key always.
478  // For other types, turn off the shuffle key by default.
479  if (NC_SHORT == d_array_type || NC_USHORT == d_array_type || NC_INT == d_array_type ||
480  NC_UINT == d_array_type || NC_INT64 == d_array_type || NC_UINT64 == d_array_type ||
481  FONcRequestHandler::use_shuffle)
482  shuffle = 1;
483 
484  int deflate = 1;
485  int deflate_level = 4;
486  stax = nc_def_var_deflate(ncid, _varid, shuffle, deflate, deflate_level);
487 
488  if (stax != NC_NOERR) {
489  string err = (string) "fileout.netcdf - Failed to define compression (deflate) level for variable "
490  + _varname;
491  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
492  }
493  }
494  }
495 
496  // Largely revised the fillvalue check code and add the check for the DAP4 case. KY 2021-05-10
497  if (is_dap4) {
498  D4Attributes *d4_attrs = d_a->attributes();
499  updateD4AttrType(d4_attrs, d_array_type);
500  }
501  else {
502  AttrTable &attrs = d_a->get_attr_table();
503  updateAttrType(attrs, d_array_type);
504  }
505 
506  BESDEBUG("fonc", "FONcArray::define() - Adding attributes " << endl);
507  FONcAttributes::add_variable_attributes(ncid, _varid, d_a, isNetCDF4_ENHANCED(), is_dap4);
508  FONcAttributes::add_original_name(ncid, _varid, _varname, _orig_varname);
509  _defined = true;
510  }
511  else {
512  if (_defined) {
513  BESDEBUG("fonc", "FONcArray::define() - variable " << _varname << " is already defined" << endl);
514  }
515  if (d_dont_use_it) {
516  BESDEBUG("fonc", "FONcArray::define() - variable " << _varname << " is not being used" << endl);
517  }
518  }
519 
520  BESDEBUG("fonc", "FONcArray::define() - done defining array '" << _varname << "'" << endl);
521 }
522 
528 void FONcArray::write_nc_variable(int ncid, nc_type var_type) {
529  if (is_dap4)
530  d_a->intern_data();
531  else
532  d_a->intern_data(*get_eval(), *get_dds());
533 
534  int stax;
535 
536  switch (var_type) {
537  case NC_UBYTE:
538  stax = nc_put_var_uchar(ncid, _varid, reinterpret_cast<unsigned char *>(d_a->get_buf()));
539  break;
540  case NC_BYTE:
541  stax = nc_put_var_schar(ncid, _varid, reinterpret_cast<signed char *>(d_a->get_buf()));
542  break;
543  case NC_SHORT:
544  stax = nc_put_var_short(ncid, _varid, reinterpret_cast<short *>(d_a->get_buf()));
545  break;
546  case NC_INT:
547  stax = nc_put_var_int(ncid, _varid, reinterpret_cast<int *>(d_a->get_buf()));
548  break;
549  case NC_INT64:
550  stax = nc_put_var_longlong(ncid, _varid, reinterpret_cast<long long *>(d_a->get_buf()));
551  break;
552  case NC_FLOAT:
553  stax = nc_put_var_float(ncid, _varid, reinterpret_cast<float *>(d_a->get_buf()));
554  break;
555  case NC_DOUBLE:
556  stax = nc_put_var_double(ncid, _varid, reinterpret_cast<double *>(d_a->get_buf()));
557  break;
558  case NC_USHORT:
559  stax = nc_put_var_ushort(ncid, _varid, reinterpret_cast<unsigned short *>(d_a->get_buf()));
560  break;
561  case NC_UINT:
562  stax = nc_put_var_uint(ncid, _varid, reinterpret_cast<unsigned int *>(d_a->get_buf()));
563  break;
564  case NC_UINT64:
565  stax = nc_put_var_ulonglong(ncid, _varid, reinterpret_cast<unsigned long long *>(d_a->get_buf()));
566  break;
567 
568  default:
569  throw BESInternalError("Failed to transform array of unknown type in file out netcdf (1)",
570  __FILE__, __LINE__);
571  }
572 
573  if (stax != NC_NOERR) {
574  string err = "fileout.netcdf - Failed to create array of " + d_a->var()->type_name() + " for " + _varname;
575  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
576  }
577 
578  // This frees the local storage. jhrg 5/14/21
579 #if CLEAR_LOCAL_DATA
580  if (!FONcGrid::InMaps(d_a))
581  d_a->clear_local_data();
582 #endif
583 }
584 
594 void FONcArray::write(int ncid) {
595  BESDEBUG("fonc", "FONcArray::write() BEGIN var: " << _varname << "[" << d_nelements << "]" << endl);
596  BESDEBUG("fonc", "FONcArray::write() BEGIN var type: " << d_array_type << " " << endl);
597 
598  if (d_dont_use_it) {
599  BESDEBUG("fonc", "FONcTransform::write not using variable " << _varname << endl);
600  return;
601  }
602 
603  // Writing out array is complex. There are three cases:
604  // 1. Arrays of NC_CHAR, which are written the same for both the netCDF
605  // classic and enhanced data models;
606  // 2. All the other types, written for the enhanced data model
607  // 3. All the other types, written for the classic data model
608  if (d_array_type == NC_CHAR) {
609  // Note that String data are not read here but in FONcArray::convert() because
610  // that code needs to know that actual length of the individual strings in the
611  // array. jhrg 6/4/21
612 
613  vector<size_t> var_count(d_ndims);
614  vector<size_t> var_start(d_ndims);
615  int dim = 0;
616  for (dim = 0; dim < d_ndims; dim++) {
617  // the count for each of the dimensions will always be 1 except
618  // for the string length dimension
619  var_count[dim] = 1;
620 
621  // the start for each of the dimensions will start at 0. We will
622  // bump this up in the while loop below
623  var_start[dim] = 0;
624  }
625 
626  for (int element = 0; element < d_nelements; element++) {
627  var_count[d_ndims - 1] = d_a->get_str()[element].size() + 1; // d_str_data[element].size() + 1;
628  var_start[d_ndims - 1] = 0;
629 
630  // write out the string
631  int stax = nc_put_vara_text(ncid, _varid, var_start.data(), var_count.data(), d_a->get_str()[element].c_str()); //d_str_data[element].c_str());
632  if (stax != NC_NOERR) {
633  string err = (string) "fileout.netcdf - Failed to create array of strings for " + _varname;
634  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
635  }
636 
637  // bump up the start.
638  if (element + 1 < d_nelements) {
639  bool done = false;
640  dim = d_ndims - 2;
641  while (!done) {
642  var_start[dim] = var_start[dim] + 1;
643  if (var_start[dim] == d_dim_sizes[dim]) {
644  var_start[dim] = 0;
645  dim--;
646  }
647  else {
648  done = true;
649  }
650  }
651  }
652  }
653 
654  d_a->get_str().clear();
655  }
656  // If we support the netCDF-4 enhanced model, the unsigned integer
657  // can be directly mapped to the netcdf-4 unsigned integer.
658  else if (isNetCDF4_ENHANCED()) {
659  write_for_nc4_types(ncid);
660  }
661  else {
662  libdap::Type element_type = d_a->var()->type();
663  // create array to hold data hyperslab
664  switch (d_array_type) {
665  case NC_BYTE:
666  case NC_FLOAT:
667  case NC_DOUBLE:
668  write_nc_variable(ncid, d_array_type);
669  break;
670 
671  case NC_SHORT:
672  // Given Byte/UInt8 will always be unsigned they must map
673  // to a NetCDF type that will support unsigned bytes. This
674  // detects the original variable was of type Byte and typecasts
675  // each data value to a short.
676  if (element_type == libdap::dods_byte_c || element_type == libdap::dods_uint8_c) {
677  if (is_dap4)
678  d_a->intern_data();
679  else
680  d_a->intern_data(*get_eval(), *get_dds());
681 
682  // There's no practical way to get rid of the value copy, be here we
683  // read directly from libdap::Array object's memory.
684  vector<short> data(d_nelements);
685  for (int d_i = 0; d_i < d_nelements; d_i++)
686  data[d_i] = *(reinterpret_cast<unsigned char *>(d_a->get_buf()) + d_i);
687 
688  int stax = nc_put_var_short(ncid, _varid, data.data());
689  if (stax != NC_NOERR) {
690  string err = (string) "fileout.netcdf - Failed to create array of shorts for " + _varname;
691  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
692  }
693 
694  // Once we've written an array, reclaim its space _unless_ it is a Grid map.
695  // It might be shared and other code here expects it to be resident in memory.
696  // jhrg 6/4/21
697  if (!FONcGrid::InMaps(d_a))
698  d_a->clear_local_data();
699  }
700  else {
701  write_nc_variable(ncid, NC_SHORT);
702  }
703  break;
704 
705  case NC_INT:
706  // Added as a stop-gap measure to alert SAs and inform users of a misconfigured server.
707  // jhrg 6/15/20
708  if (element_type == libdap::dods_int64_c || element_type == libdap::dods_uint64_c) {
709  // We should not be here. The server configuration is wrong since the netcdf classic
710  // model is being used (either a netCDf3 response is requested OR a netCDF4 with the
711  // classic model). Tell the user and the SA.
712  string msg;
713  if (FONcRequestHandler::classic_model == false) {
714  msg = "You asked for one or more 64-bit integer values returned using a netCDF3 file. "
715  "Try asking for netCDF4 enhanced and/or contact the server administrator.";
716  }
717  else {
718  msg = "You asked for one or more 64-bit integer values, but either returned using a netCDF3 file or "
719  "from a server that is configured to use the 'classic' netCDF data model with netCDF4. "
720  "Try netCDF4 and/or contact the server administrator.";
721  }
722  throw BESInternalError(msg, __FILE__, __LINE__);
723  }
724 
725  if (element_type == libdap::dods_uint16_c) {
726  if (is_dap4)
727  d_a->intern_data();
728  else
729  d_a->intern_data(*get_eval(), *get_dds());
730 
731  vector<int> data(d_nelements);
732  for (int d_i = 0; d_i < d_nelements; d_i++)
733  data[d_i] = *(reinterpret_cast<unsigned short *>(d_a->get_buf()) + d_i);
734 
735  int stax = nc_put_var_int(ncid, _varid, data.data());
736  if (stax != NC_NOERR) {
737  string err = (string) "fileout.netcdf - Failed to create array of ints for " + _varname;
738  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
739  }
740 
741  if (!FONcGrid::InMaps(d_a))
742  d_a->clear_local_data();
743  }
744  else {
745  write_nc_variable(ncid, NC_INT);
746  }
747  break;
748 
749  default:
750  throw BESInternalError("Failed to transform array of unknown type in file out netcdf (2)",
751  __FILE__, __LINE__);
752  } // switch(d_array_type)
753  }
754 
755  BESDEBUG("fonc", "FONcArray::write() END var: " << _varname << "[" << d_nelements << "]" << endl);
756 }
757 
762 string FONcArray::name() {
763  return d_a->name();
764 }
765 
774 void FONcArray::dump(ostream &strm) const {
775  strm << BESIndent::LMarg << "FONcArray::dump - (" << (void *) this << ")" << endl;
776  BESIndent::Indent();
777  strm << BESIndent::LMarg << "name = " << _varname << endl;
778  strm << BESIndent::LMarg << "ndims = " << d_ndims << endl;
779  strm << BESIndent::LMarg << "actual ndims = " << d_actual_ndims << endl;
780  strm << BESIndent::LMarg << "nelements = " << d_nelements << endl;
781  if (d_dims.size()) {
782  strm << BESIndent::LMarg << "dimensions:" << endl;
783  BESIndent::Indent();
784  vector<FONcDim *>::const_iterator i = d_dims.begin();
785  vector<FONcDim *>::const_iterator e = d_dims.end();
786  for (; i != e; i++) {
787  (*i)->dump(strm);
788  }
789  BESIndent::UnIndent();
790  }
791  else {
792  strm << BESIndent::LMarg << "dimensions: none" << endl;
793  }
794  BESIndent::UnIndent();
795 }
796 
806 void FONcArray::write_for_nc4_types(int ncid) {
807 
808  is_dap4 = true;
809 
810  // create array to hold data hyperslab
811  // DAP2 only supports unsigned BYTE. So here
812  // we don't include NC_BYTE (the signed BYTE, the same
813  // as 64-bit integer). KY 2020-03-20
814  // Actually 64-bit integer is supported.
815  switch (d_array_type) {
816  case NC_BYTE:
817  case NC_UBYTE:
818  case NC_SHORT:
819  case NC_INT:
820  case NC_INT64:
821  case NC_FLOAT:
822  case NC_DOUBLE:
823  case NC_USHORT:
824  case NC_UINT:
825  case NC_UINT64:
826  write_nc_variable(ncid, d_array_type);
827  break;
828 
829  default:
830  string err = (string) "Failed to transform array of unknown type in file out netcdf";
831  throw BESInternalError(err, __FILE__, __LINE__);
832  }
833 }
834 
exception thrown if internal error encountered
virtual void dump(std::ostream &strm) const override
dumps information about this object for debugging purposes
Definition: FONcArray.cc:774
virtual void define(int ncid) override
define the DAP Array in the netcdf file
Definition: FONcArray.cc:394
virtual ~FONcArray() override
Destructor that cleans up the array.
Definition: FONcArray.cc:110
virtual void convert(std::vector< std::string > embed, bool _dap4=false, bool is_dap4_group=false) override
Converts the DAP Array to a FONcArray.
Definition: FONcArray.cc:140
virtual void write(int ncid) override
Write the array out to the netcdf file.
Definition: FONcArray.cc:594
std::string name() override
returns the name of the DAP Array
Definition: FONcArray.cc:762
static void add_original_name(int ncid, int varid, const string &var_name, const string &orig)
Adds an attribute for the variable if the variable name had to be modified in any way.
static void add_variable_attributes(int ncid, int varid, BaseType *b, bool is_netCDF_enhanced, bool is_dap4)
Add the attributes for an OPeNDAP variable to the netcdf file.
A DAP BaseType with file out netcdf information included.
Definition: FONcBaseType.h:60
virtual bool isNetCDF4()
Returns true if NetCDF4 features will be required.
A class that represents the dimension of an array.
Definition: FONcDim.h:45
virtual void define(int ncid)
define the DAP dimension in the netcdf file
Definition: FONcDim.cc:79
static vector< FONcMap * > Maps
global list of maps that could be shared amongst the different grids
Definition: FONcGrid.h:80
static bool InGrid
tells whether we are converting or defining a grid.
Definition: FONcGrid.h:82
A map of a DAP Grid with file out netcdf information included.
Definition: FONcMap.h:52
static void handle_error(int stax, const string &err, const string &file, int line)
handle any netcdf errors
Definition: FONcUtils.cc:424
static nc_type get_nc_type(libdap::BaseType *element, bool isNC4_ENHANCED)
translate the OPeNDAP data type to a netcdf data type
Definition: FONcUtils.cc:112
static string gen_name(const vector< string > &embed, const string &name, string &original)
generate a new name for the embedded variable
Definition: FONcUtils.cc:179
Type
Type of JSON value.
Definition: rapidjson.h:664