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
50using 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
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(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
345FONcDim *
346FONcArray::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
394void 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
528void 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
594void 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
763 return d_a->name();
764}
765
774void 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
806void 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