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
50using 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
56vector<FONcDim *> FONcArray::Dimensions;
57
58const int MAX_CHUNK_SIZE = 1024;
59
68FONcArray::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
82FONcArray::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
140void 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
333FONcDim *
334FONcArray::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
382void 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
508void 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
574void 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
743 return d_a->name();
744}
745
754void 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
786void 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