bes  Updated for version 3.20.10
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 (excpet 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 !NDEBUG
150  if (d4_dim_ids.size() > 0) {
151  BESDEBUG("fonc", "FONcArray::convert() - d4_dim_ids size is " << d4_dim_ids.size() << endl);
152  }
153 #endif
154 
155  d_ndims = d_a->dimensions();
156  d_actual_ndims = d_ndims; //replace this with _a->dimensions(); below TODO
157  if (d_array_type == NC_CHAR) {
158  // if we have array of strings then we need to add the string length
159  // dimension, so add one more to ndims
160  d_ndims++;
161  }
162 
163  // See HYRAX-805. When assigning values using [], set the size using resize
164  // not reserve. THe reserve method works to optimize push_back(), but apparently
165  // does not work with []. jhrg 8/3/18
166  d_dim_ids.resize(d_ndims);
167  d_dim_sizes.resize(d_ndims);
168 
169  Array::Dim_iter di = d_a->dim_begin();
170  Array::Dim_iter de = d_a->dim_end();
171  int dimnum = 0;
172  for (; di != de; di++) {
173  int size = d_a->dimension_size(di, true);
174  d_dim_sizes[dimnum] = size;
175  d_nelements *= size;
176 
177  // Set COMPRESSION CHUNK SIZE for each dimension.
178  d_chunksizes.push_back(size <= MAX_CHUNK_SIZE ? size : MAX_CHUNK_SIZE);
179 
180  BESDEBUG("fonc", "FONcArray::convert() - dim num: " << dimnum << ", dim size: " << size << ", chunk size: "
181  << d_chunksizes[dimnum] << endl);
182  BESDEBUG("fonc", "FONcArray::convert() - dim name: " << d_a->dimension_name(di) << endl);
183 
184  // If this dimension is a D4 dimension defined in its group, just obtain the dimension ID.
185  if (true == d4_def_dim && use_d4_dim_ids[dimnum] == true) {
186  d_dim_ids[dimnum] = d4_dim_ids[dimnum];
187  BESDEBUG("fonc", "FONcArray::convert() - has dap4 group" << endl);
188 
189  }
190  else {
191  // See if this dimension has already been defined. If it has the
192  // same name and same size as another dimension, then it is a
193  // shared dimension. Create it only once and share the FONcDim
194  int ds_num = FONcDim::DimNameNum + 1;
195  while (find(d4_rds_nums.begin(), d4_rds_nums.end(), ds_num) != d4_rds_nums.end()) {
196  // Note: the following #if 0 #endif block is only for future development.
197  // Don't delete or change it for debuggging.
198 #if 0
199  // This may be an optimization for rare cases. May do this when performance issue hurts
200  //d4_rds_nums_visited.push_back(ds_num);
201 #endif
202  // Now the following line ensure this dimension name dimds_num(ds_num is a number)
203  // is NOT created for the dimension that doesn't have a name in DAP4.
204  ds_num++;
205  }
206  FONcDim::DimNameNum = ds_num - 1;
207 
208  FONcDim *use_dim = find_dim(embed, d_a->dimension_name(di), size);
209  d_dims.push_back(use_dim);
210  }
211 
212  dimnum++;
213  }
214 
215  // if this array is a string array, then add the length dimension
216  if (d_array_type == NC_CHAR) {
217  // Calling intern_data() here is part of the 'streaming' refactoring.
218  // For the other types, the call can go in the write() implementations,
219  // but because strings in netCDF are arrays of char, a string array
220  // must have an added dimension (so a 1d string array becomes a 2d char
221  // array). To build the netCDF file, we need to know the dimensions of
222  // the array when the file is defined, not when the data are written.
223  // To know the size of the extra dimension used to hold the chars, we
224  // need to look at all the strings and find the biggest one. Thus, in
225  // order to define the variable for the netCDF file, we need to read
226  // string data long before we actually write it out. Kind of a drag,
227  // but not the end of the world. jhrg 5/18/21
228  if (is_dap4)
229  d_a->intern_data();
230  else
231  d_a->intern_data(*get_eval(), *get_dds());
232 
233  // get the data from the dap array
234  int array_length = d_a->length();
235 #if 0
236  d_str_data.reserve(array_length);
237  d_a->value(d_str_data);
238 
239  // determine the max length of the strings
240  size_t max_length = 0;
241  for (int i = 0; i < array_length; i++) {
242  if (d_str_data[i].length() > max_length) {
243  max_length = d_str_data[i].length();
244  }
245  }
246  max_length++;
247 #endif
248  size_t max_length = 0;
249  for (int i = 0; i < array_length; i++) {
250  if (d_a->get_str()[i].length() > max_length) {
251  max_length = d_a->get_str()[i].length();
252  }
253  }
254  max_length++;
255 
256  vector<string> empty_embed;
257  string lendim_name;
258  if (is_dap4_group == true) {
259  // Here is a quick implementation.
260  // We just append the DimNameNum(globally defined)
261  // and then increase the number by 1.
262  ostringstream dim_suffix_strm;
263  dim_suffix_strm << "_len" << FONcDim::DimNameNum + 1;
264  FONcDim::DimNameNum++;
265  lendim_name = _varname + dim_suffix_strm.str();
266 
267  }
268  else
269  lendim_name = _varname + "_len";
270 
271 
272  FONcDim *use_dim = find_dim(empty_embed, lendim_name, max_length, true);
273  // Added static_cast to suppress warning. 12.27.2011 jhrg
274  if (use_dim->size() < static_cast<int>(max_length)) {
275  use_dim->update_size(max_length);
276  }
277 
278  d_dim_sizes[d_ndims - 1] = use_dim->size();
279  d_dim_ids[d_ndims - 1] = use_dim->dimid();
280 
281  //DAP4 dimension ID is false.
282  use_d4_dim_ids.push_back(false);
283  d_dims.push_back(use_dim);
284 
285  // Adding this fixes the bug reported by GSFC where arrays of strings
286  // caused the handler to throw an error stating that 'Bad chunk sizes'
287  // were used. When the dimension of the string array was extended (because
288  // strings become char arrays in netcdf3/4), the numbers of dimensions
289  // in 'chunksizes' was not bumped up. The code below in convert() that
290  // set the chunk sizes then tried to access data that had never been set.
291  // jhrg 11/25/15
292  d_chunksizes.push_back(max_length <= MAX_CHUNK_SIZE ? max_length : MAX_CHUNK_SIZE);
293  }
294 
295  // If this array has a single dimension, and the name of the array
296  // and the name of that dimension are the same, then this array
297  // might be used as a map for a grid defined elsewhere.
298  // Notice: DAP4 doesn't have Grid and the d_dont_use_it=true causes some
299  // variables not written to the netCDF-4 file with group hierarchy.
300  // So need to have the if check. KY 2021-06-21
301  if(is_dap4 == false) {
302  if (!FONcGrid::InGrid && d_actual_ndims == 1 && d_a->name() == d_a->dimension_name(d_a->dim_begin())) {
303  // is it already in there?
304  FONcMap *map = FONcGrid::InMaps(d_a);
305  if (!map) {
306  // This memory is/was leaked. jhrg 8/28/13
307  FONcMap *new_map = new FONcMap(this);
308  d_grid_maps.push_back(new_map); // save it here so we can free it later. jhrg 8/28/13
309  FONcGrid::Maps.push_back(new_map);
310  }
311  else {
312  d_dont_use_it = true;
313  }
314  }
315  }
316 
317  BESDEBUG("fonc", "FONcArray::convert() - done converting array " << _varname << endl);
318 }
319 
333 FONcDim *
334 FONcArray::find_dim(vector<string> &embed, const string &name, int size, bool ignore_size) {
335  string oname;
336  string ename = FONcUtils::gen_name(embed, name, oname);
337  FONcDim *ret_dim = 0;
338  vector<FONcDim *>::iterator i = FONcArray::Dimensions.begin();
339  vector<FONcDim *>::iterator e = FONcArray::Dimensions.end();
340  for (; i != e && !ret_dim; i++) {
341  if (!((*i)->name().empty()) && ((*i)->name() == name)) {
342  if (ignore_size) {
343  ret_dim = (*i);
344  }
345  else if ((*i)->size() == size) {
346  ret_dim = (*i);
347  }
348  else {
349  if (embed.size() > 0) {
350  vector<string> tmp;
351  return find_dim(tmp, ename, size);
352  }
353  string err = "fileout_netcdf: dimension found with the same name, but different size";
354  throw BESInternalError(err, __FILE__, __LINE__);
355  }
356  }
357  }
358  if (!ret_dim) {
359  ret_dim = new FONcDim(name, size);
360  FONcArray::Dimensions.push_back(ret_dim);
361  }
362  else {
363  ret_dim->incref();
364  }
365  return ret_dim;
366 }
367 
382 void FONcArray::define(int ncid) {
383  BESDEBUG("fonc", "FONcArray::define() - defining array '" << _varname << "'" << endl);
384 
385  if (!_defined && !d_dont_use_it) {
386 
387  BESDEBUG("fonc", "FONcArray::define() - defining array ' defined already: " << _varname << "'" << endl);
388 
389  // Note: the following #if 0 #endif block is only for future development.
390  // Don't delete or change it for debuggging.
391 
392 #if 0
393  if(d4_dim_ids.size() >0) {
394  if(d_array_type == NC_CHAR) {
395  if(d_dims.size() == 1) {
396  FONcDim *fd = *(d_dims.begin());
397  fd->define(ncid);
398  d_dim_ids[d_ndims-1] = fd->dimid();
399 
400  }
401  else {
402 
403  }
404  }
405  }
406  else {
407 #endif
408  // If not defined DAP4 dimensions(mostly DAP2 or DAP4 no groups)
409  if (false == d4_def_dim) {
410  vector<FONcDim *>::iterator i = d_dims.begin();
411  vector<FONcDim *>::iterator e = d_dims.end();
412  int dimnum = 0;
413  for (; i != e; i++) {
414  FONcDim *fd = *i;
415  fd->define(ncid);
416  //d_dim_ids.at(dimnum) = fd->dimid();
417  d_dim_ids[dimnum] = fd->dimid();
418  BESDEBUG("fonc", "FONcArray::define() - dim_id: " << fd->dimid() << " size:" << fd->size() << endl);
419  dimnum++;
420  }
421  }
422  else {// Maybe some dimensions are not DAP4 dimensions, will still generate those dimensions.
423  int j = 0;
424  for (unsigned int i = 0; i < use_d4_dim_ids.size(); i++) {
425  if (use_d4_dim_ids[i] == false) {
426  FONcDim *fd = d_dims[j];
427  fd->define(ncid);
428  d_dim_ids[i] = fd->dimid();
429  j++;
430  }
431  }
432  }
433 
434  int stax = nc_def_var(ncid, _varname.c_str(), d_array_type, d_ndims, &d_dim_ids[0], &_varid);
435  if (stax != NC_NOERR) {
436  string err = (string) "fileout.netcdf - Failed to define variable " + _varname;
437  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
438  }
439 
440  stax = nc_def_var_fill(ncid, _varid, NC_NOFILL, NULL );
441  if (stax != NC_NOERR) {
442  string err = (string) "fileout.netcdf - " + "Failed to clear fill value for " + _varname;
443  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
444  }
445 
446  BESDEBUG("fonc", "FONcArray::define() netcdf-4 version is " << _ncVersion << endl);
447  if (isNetCDF4()) {
448  BESDEBUG("fonc", "FONcArray::define() Working netcdf-4 branch " << endl);
449  if (FONcRequestHandler::chunk_size == 0)
450  // I have no idea if chunksizes is needed in this case.
451  stax = nc_def_var_chunking(ncid, _varid, NC_CONTIGUOUS, &d_chunksizes[0]);
452  else
453  stax = nc_def_var_chunking(ncid, _varid, NC_CHUNKED, &d_chunksizes[0]);
454 
455  if (stax != NC_NOERR) {
456  string err = "fileout.netcdf - Failed to define chunking for variable " + _varname;
457  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
458  }
459 
460  // TODO Make this more adaptable to the Array's data type. Find out when it's
461  // best to use shuffle, et c. jhrg 7/22/18
462  if (FONcRequestHandler::use_compression) {
463  int shuffle = 0;
464  int deflate = 1;
465  int deflate_level = 4;
466  stax = nc_def_var_deflate(ncid, _varid, shuffle, deflate, deflate_level);
467 
468  if (stax != NC_NOERR) {
469  string err = (string) "fileout.netcdf - Failed to define compression (deflate) level for variable "
470  + _varname;
471  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
472  }
473  }
474  }
475 
476  // Largely revised the fillvalue check code and add the check for the DAP4 case. KY 2021-05-10
477  if (is_dap4) {
478  D4Attributes *d4_attrs = d_a->attributes();
479  updateD4AttrType(d4_attrs, d_array_type);
480  }
481  else {
482  AttrTable &attrs = d_a->get_attr_table();
483  updateAttrType(attrs, d_array_type);
484  }
485 
486  BESDEBUG("fonc", "FONcArray::define() - Adding attributes " << endl);
487  FONcAttributes::add_variable_attributes(ncid, _varid, d_a, isNetCDF4_ENHANCED(), is_dap4);
488  FONcAttributes::add_original_name(ncid, _varid, _varname, _orig_varname);
489  _defined = true;
490  }
491  else {
492  if (_defined) {
493  BESDEBUG("fonc", "FONcArray::define() - variable " << _varname << " is already defined" << endl);
494  }
495  if (d_dont_use_it) {
496  BESDEBUG("fonc", "FONcArray::define() - variable " << _varname << " is not being used" << endl);
497  }
498  }
499 
500  BESDEBUG("fonc", "FONcArray::define() - done defining array '" << _varname << "'" << endl);
501 }
502 
508 void FONcArray::write_nc_variable(int ncid, nc_type var_type) {
509  if (is_dap4)
510  d_a->intern_data();
511  else
512  d_a->intern_data(*get_eval(), *get_dds());
513 
514  int stax;
515 
516  switch (var_type) {
517  case NC_UBYTE:
518  stax = nc_put_var_uchar(ncid, _varid, reinterpret_cast<unsigned char *>(d_a->get_buf()));
519  break;
520  case NC_BYTE:
521  stax = nc_put_var_schar(ncid, _varid, reinterpret_cast<signed char *>(d_a->get_buf()));
522  break;
523  case NC_SHORT:
524  stax = nc_put_var_short(ncid, _varid, reinterpret_cast<short *>(d_a->get_buf()));
525  break;
526  case NC_INT:
527  stax = nc_put_var_int(ncid, _varid, reinterpret_cast<int *>(d_a->get_buf()));
528  break;
529  case NC_INT64:
530  stax = nc_put_var_longlong(ncid, _varid, reinterpret_cast<long long *>(d_a->get_buf()));
531  break;
532  case NC_FLOAT:
533  stax = nc_put_var_float(ncid, _varid, reinterpret_cast<float *>(d_a->get_buf()));
534  break;
535  case NC_DOUBLE:
536  stax = nc_put_var_double(ncid, _varid, reinterpret_cast<double *>(d_a->get_buf()));
537  break;
538  case NC_USHORT:
539  stax = nc_put_var_ushort(ncid, _varid, reinterpret_cast<unsigned short *>(d_a->get_buf()));
540  break;
541  case NC_UINT:
542  stax = nc_put_var_uint(ncid, _varid, reinterpret_cast<unsigned int *>(d_a->get_buf()));
543  break;
544  case NC_UINT64:
545  stax = nc_put_var_ulonglong(ncid, _varid, reinterpret_cast<unsigned long long *>(d_a->get_buf()));
546  break;
547 
548  default:
549  throw BESInternalError("Failed to transform array of unknown type in file out netcdf (1)",
550  __FILE__, __LINE__);
551  }
552 
553  if (stax != NC_NOERR) {
554  string err = "fileout.netcdf - Failed to create array of " + d_a->var()->type_name() + " for " + _varname;
555  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
556  }
557 
558  // This frees the local storage. jhrg 5/14/21
559 #if CLEAR_LOCAL_DATA
560  if (!FONcGrid::InMaps(d_a))
561  d_a->clear_local_data();
562 #endif
563 }
564 
574 void FONcArray::write(int ncid) {
575  BESDEBUG("fonc", "FONcArray::write() BEGIN var: " << _varname << "[" << d_nelements << "]" << endl);
576  BESDEBUG("fonc", "FONcArray::write() BEGIN var type: " << d_array_type << " " << endl);
577 
578  if (d_dont_use_it) {
579  BESDEBUG("fonc", "FONcTransform::write not using variable " << _varname << endl);
580  return;
581  }
582 
583  // Writing out array is complex. There are three cases:
584  // 1. Arrays of NC_CHAR, which are written the same for both the netCDF
585  // classic and enhanced data models;
586  // 2. All the other types, written for the enhanced data model
587  // 3. All the other types, written for the classic data model
588  if (d_array_type == NC_CHAR) {
589  // Note that String data are not read here but in FONcArray::convert() because
590  // that code needs to know that actual length of the individual strings in the
591  // array. jhrg 6/4/21
592 
593  vector<size_t> var_count(d_ndims);
594  vector<size_t> var_start(d_ndims);
595  int dim = 0;
596  for (dim = 0; dim < d_ndims; dim++) {
597  // the count for each of the dimensions will always be 1 except
598  // for the string length dimension
599  var_count[dim] = 1;
600 
601  // the start for each of the dimensions will start at 0. We will
602  // bump this up in the while loop below
603  var_start[dim] = 0;
604  }
605 
606  for (int element = 0; element < d_nelements; element++) {
607  var_count[d_ndims - 1] = d_a->get_str()[element].size() + 1; // d_str_data[element].size() + 1;
608  var_start[d_ndims - 1] = 0;
609 
610  // write out the string
611  int stax = nc_put_vara_text(ncid, _varid, &var_start[0], &var_count[0], d_a->get_str()[element].c_str()); //d_str_data[element].c_str());
612  if (stax != NC_NOERR) {
613  string err = (string) "fileout.netcdf - Failed to create array of strings for " + _varname;
614  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
615  }
616 
617  // bump up the start.
618  if (element + 1 < d_nelements) {
619  bool done = false;
620  dim = d_ndims - 2;
621  while (!done) {
622  var_start[dim] = var_start[dim] + 1;
623  if (var_start[dim] == d_dim_sizes[dim]) {
624  var_start[dim] = 0;
625  dim--;
626  }
627  else {
628  done = true;
629  }
630  }
631  }
632  }
633 
634  d_a->get_str().clear();
635  }
636  // If we support the netCDF-4 enhanced model, the unsigned integer
637  // can be directly mapped to the netcdf-4 unsigned integer.
638  else if (isNetCDF4_ENHANCED()) {
639  write_for_nc4_types(ncid);
640  }
641  else {
642  libdap::Type element_type = d_a->var()->type();
643  // create array to hold data hyperslab
644  switch (d_array_type) {
645  case NC_BYTE:
646  case NC_FLOAT:
647  case NC_DOUBLE:
648  write_nc_variable(ncid, d_array_type);
649  break;
650 
651  case NC_SHORT:
652  // Given Byte/UInt8 will always be unsigned they must map
653  // to a NetCDF type that will support unsigned bytes. This
654  // detects the original variable was of type Byte and typecasts
655  // each data value to a short.
656  if (element_type == libdap::dods_byte_c || element_type == libdap::dods_uint8_c) {
657  if (is_dap4)
658  d_a->intern_data();
659  else
660  d_a->intern_data(*get_eval(), *get_dds());
661 
662  // There's no practical way to get rid of the value copy, be here we
663  // read directly from libdap::Array object's memory.
664  vector<short> data(d_nelements);
665  for (int d_i = 0; d_i < d_nelements; d_i++)
666  data[d_i] = *(reinterpret_cast<unsigned char *>(d_a->get_buf()) + d_i);
667 
668  int stax = nc_put_var_short(ncid, _varid, &data[0]);
669  if (stax != NC_NOERR) {
670  string err = (string) "fileout.netcdf - Failed to create array of shorts for " + _varname;
671  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
672  }
673 
674  // Once we've written an array, reclaim its space _unless_ it is a Grid map.
675  // It might be shared and other code here expects it to be resident in memory.
676  // jhrg 6/4/21
677  if (!FONcGrid::InMaps(d_a))
678  d_a->clear_local_data();
679  }
680  else {
681  write_nc_variable(ncid, NC_SHORT);
682  }
683  break;
684 
685  case NC_INT:
686  // Added as a stop-gap measure to alert SAs and inform users of a misconfigured server.
687  // jhrg 6/15/20
688  if (element_type == libdap::dods_int64_c || element_type == libdap::dods_uint64_c) {
689  // We should not be here. The server configuration is wrong since the netcdf classic
690  // model is being used (either a netCDf3 response is requested OR a netCDF4 with the
691  // classic model). Tell the user and the SA.
692  string msg;
693  if (FONcRequestHandler::classic_model == false) {
694  msg = "You asked for one or more 64-bit integer values returned using a netCDF3 file. "
695  "Try asking for netCDF4 enhanced and/or contact the server administrator.";
696  }
697  else {
698  msg = "You asked for one or more 64-bit integer values, but either returned using a netCDF3 file or "
699  "from a server that is configured to use the 'classic' netCDF data model with netCDF4. "
700  "Try netCDF4 and/or contact the server administrator.";
701  }
702  throw BESInternalError(msg, __FILE__, __LINE__);
703  }
704 
705  if (element_type == libdap::dods_uint16_c) {
706  if (is_dap4)
707  d_a->intern_data();
708  else
709  d_a->intern_data(*get_eval(), *get_dds());
710 
711  vector<int> data(d_nelements);
712  for (int d_i = 0; d_i < d_nelements; d_i++)
713  data[d_i] = *(reinterpret_cast<unsigned short *>(d_a->get_buf()) + d_i);
714 
715  int stax = nc_put_var_int(ncid, _varid, &data[0]);
716  if (stax != NC_NOERR) {
717  string err = (string) "fileout.netcdf - Failed to create array of ints for " + _varname;
718  FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
719  }
720 
721  if (!FONcGrid::InMaps(d_a))
722  d_a->clear_local_data();
723  }
724  else {
725  write_nc_variable(ncid, NC_INT);
726  }
727  break;
728 
729  default:
730  throw BESInternalError("Failed to transform array of unknown type in file out netcdf (2)",
731  __FILE__, __LINE__);
732  } // switch(d_array_type)
733  }
734 
735  BESDEBUG("fonc", "FONcArray::write() END var: " << _varname << "[" << d_nelements << "]" << endl);
736 }
737 
742 string FONcArray::name() {
743  return d_a->name();
744 }
745 
754 void FONcArray::dump(ostream &strm) const {
755  strm << BESIndent::LMarg << "FONcArray::dump - (" << (void *) this << ")" << endl;
756  BESIndent::Indent();
757  strm << BESIndent::LMarg << "name = " << _varname << endl;
758  strm << BESIndent::LMarg << "ndims = " << d_ndims << endl;
759  strm << BESIndent::LMarg << "actual ndims = " << d_actual_ndims << endl;
760  strm << BESIndent::LMarg << "nelements = " << d_nelements << endl;
761  if (d_dims.size()) {
762  strm << BESIndent::LMarg << "dimensions:" << endl;
763  BESIndent::Indent();
764  vector<FONcDim *>::const_iterator i = d_dims.begin();
765  vector<FONcDim *>::const_iterator e = d_dims.end();
766  for (; i != e; i++) {
767  (*i)->dump(strm);
768  }
769  BESIndent::UnIndent();
770  }
771  else {
772  strm << BESIndent::LMarg << "dimensions: none" << endl;
773  }
774  BESIndent::UnIndent();
775 }
776 
786 void FONcArray::write_for_nc4_types(int ncid) {
787 
788  is_dap4 = true;
789 
790  // create array to hold data hyperslab
791  // DAP2 only supports unsigned BYTE. So here
792  // we don't inlcude NC_BYTE (the signed BYTE, the same
793  // as 64-bit integer). KY 2020-03-20
794  // Actually 64-bit integer is supported.
795  switch (d_array_type) {
796  case NC_BYTE:
797  case NC_UBYTE:
798  case NC_SHORT:
799  case NC_INT:
800  case NC_INT64:
801  case NC_FLOAT:
802  case NC_DOUBLE:
803  case NC_USHORT:
804  case NC_UINT:
805  case NC_UINT64:
806  write_nc_variable(ncid, d_array_type);
807  break;
808 
809  default:
810  string err = (string) "Failed to transform array of unknown type in file out netcdf";
811  throw BESInternalError(err, __FILE__, __LINE__);
812  }
813 }
814 
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:754
virtual void define(int ncid) override
define the DAP Array in the netcdf file
Definition: FONcArray.cc:382
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:574
virtual std::string name() override
returns the name of the DAP Array
Definition: FONcArray.cc:742
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:64
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