bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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#include <cstring>
36
37#include <netcdf.h>
38
39#include <libdap/Array.h>
40#include <libdap/AttrTable.h>
41#include <libdap/D4Attributes.h>
42
43#include <BESInternalError.h>
44#include <BESDebug.h>
45#include <BESUtil.h>
46
47#include "FONcRequestHandler.h" // For access to the handler's keys
48#include "FONcArray.h"
49#include "FONcDim.h"
50#include "FONcGrid.h"
51#include "FONcMap.h"
52#include "FONcUtils.h"
53#include "FONcAttributes.h"
54
55using namespace libdap;
56
57// This controls whether variables' data values are deleted as soon
58// as they are written (except for DAP2 Grid Maps, which may be shared).
59#define CLEAR_LOCAL_DATA 1
60#define STRING_ARRAY_OPT 1
61
62vector<FONcDim *> FONcArray::Dimensions;
63
64const int MAX_CHUNK_SIZE = 1024;
65
66// Set general total maximum chunk sizes to 1M(1024x1024)
67const int GENERAL_MAX_CHUNK_SIZES = 1048576;
68
69// set normal 1-D maximum chunk sizes to 64K(64*1024)
70const int NORMAL_1D_MAX_CHUNK_SIZES = 65536;
71
80FONcArray::FONcArray(BaseType *b) : FONcBaseType() {
81 d_a = dynamic_cast<Array *>(b);
82 if (!d_a) {
83 string s = "File out netcdf, FONcArray was passed a variable that is not a DAP Array";
84 throw BESInternalError(s, __FILE__, __LINE__);
85 }
86
87 for (unsigned int i = 0; i < d_a->dimensions(); i++)
88 use_d4_dim_ids.push_back(false);
89}
90
91FONcArray::FONcArray(BaseType *b, const vector<int> &fd4_dim_ids, const vector<bool> &fuse_d4_dim_ids,
92 const vector<int> &rds_nums) : FONcBaseType() {
93 d_a = dynamic_cast<Array *>(b);
94 if (!d_a) {
95 string s = "File out netcdf, FONcArray was passed a variable that is not a DAP Array";
96 throw BESInternalError(s, __FILE__, __LINE__);
97 }
98 if (d_a->is_dap4()) {
99 BESDEBUG("fonc", "FONcArray() - constructor is dap4 " << endl);
100 d4_dim_ids = fd4_dim_ids;
101 use_d4_dim_ids = fuse_d4_dim_ids;
102 d4_def_dim = true;
103 d4_rds_nums = rds_nums;
104 }
105}
106
118 for (auto &dim: d_dims) {
119 dim->decref();
120 }
121
122 for (auto &map: d_grid_maps) {
123 map->decref();
124 }
125}
126
141void FONcArray::convert(vector<string> embed, bool _dap4, bool is_dap4_group) {
142 FONcBaseType::convert(embed, _dap4, is_dap4_group);
143
144 d_varname = FONcUtils::gen_name(embed, d_varname, d_orig_varname);
145
146 BESDEBUG("fonc", "FONcArray::convert() - converting array " << d_varname << endl);
147
148 d_array_type = FONcUtils::get_nc_type(d_a->var(), isNetCDF4_ENHANCED());
149
150 if(d_array_type == NC_NAT) {
151
152 string err = "fileout_netcdf: The datatype of this variable '" + d_varname;
153 err += "' is not supported. It is very possible that you try to obtain ";
154 err += "a netCDF file that follows the netCDF classic model. ";
155 err += "The unsigned 32-bit integer and signed/unsigned 64-bit integer ";
156 err += "are not supported by the netCDF classic model. Downloading this file as the netCDF-4 file that ";
157 err += "follows the netCDF enhanced model should solve the problem.";
158
159 throw BESInternalError(err, __FILE__, __LINE__);
160 }
161
162#if !NDEBUG
163 if (d4_dim_ids.size() > 0) {
164 BESDEBUG("fonc", "FONcArray::convert() - d4_dim_ids size is " << d4_dim_ids.size() << endl);
165 }
166#endif
167
168 d_ndims = d_a->dimensions();
169 d_actual_ndims = d_ndims; //replace this with _a->dimensions(); below TODO
170 if (d_array_type == NC_CHAR) {
171 // if we have an array of strings then we need to add the string length
172 // dimension, so add one more to ndims
173 d_ndims++;
174 }
175
176 // See HYRAX-805. When assigning values using [], set the size using resize
177 // not reserve. THe reserve method works to optimize push_back(), but apparently
178 // does not work with []. jhrg 8/3/18
179 d_dim_ids.resize(d_ndims);
180 d_dim_sizes.resize(d_ndims);
181
182
183 Array::Dim_iter di = d_a->dim_begin();
184 Array::Dim_iter de = d_a->dim_end();
185 int dimnum = 0;
186 for (; di != de; di++) {
187 int64_t size = d_a->dimension_size_ll(di, true);
188 d_dim_sizes[dimnum] = size;
189 d_nelements *= size;
190 // Set COMPRESSION CHUNK SIZE for each dimension.
191 // Make the chunk size reasonable for 1-D case.
192 // For the 1-D case, the MAX_CHUNK_SIZE(1K) is too small if the array size is too big.
193 // We increase the chunk size to 64K if the number of elements is >64K and less than 16M.
194 // If the number of elements is >16M, we set the chunk size to 1M.
195 // KY 2023-01-31
196 if (d_a->dimensions() == 1) {
197 if (size < NORMAL_1D_MAX_CHUNK_SIZES)
198 d_chunksizes.push_back(size <= MAX_CHUNK_SIZE ? size : MAX_CHUNK_SIZE);
199 else if ( size >= NORMAL_1D_MAX_CHUNK_SIZES && size <=GENERAL_MAX_CHUNK_SIZES*16)
200 d_chunksizes.push_back(NORMAL_1D_MAX_CHUNK_SIZES);
201 else
202 d_chunksizes.push_back(GENERAL_MAX_CHUNK_SIZES);
203 }
204 // For the 2-D case, we keep the original way to set the chunk size as MAX_CHUNK_SIZE for each dimension.
205 // This turns out to be good enough for most NASA files so far. KY 2023-01-31
206 else if (d_a->dimensions() ==2)
207 d_chunksizes.push_back(size <= MAX_CHUNK_SIZE ? size : MAX_CHUNK_SIZE);
208 // We found an array that has 3-D 365x8075*7814 elements. This will make the chunk size 365*1024*1024, which
209 // is too big and will cause potential bad performance for the application that uses the generated
210 // netcdf file. So reduce the chunk size when the similar case occurs.
211 // This will be handled in a separate for-loop below. KY 2023-01-25
212
213 BESDEBUG("fonc", "FONcArray::convert() - dim num: " << dimnum << ", dim size: " << size << endl);
214 BESDEBUG("fonc", "FONcArray::convert() - dim name: " << d_a->dimension_name(di) << endl);
215
216 // If this dimension is a D4 dimension defined in its group, just obtain the dimension ID.
217 if (true == d4_def_dim && use_d4_dim_ids[dimnum] == true) {
218 d_dim_ids[dimnum] = d4_dim_ids[dimnum];
219 BESDEBUG("fonc", "FONcArray::convert() - has dap4 group" << endl);
220
221 }
222 else {
223 // See if this dimension has already been defined. If it has the
224 // same name and same size as another dimension, then it is a
225 // shared dimension. Create it only once and share the FONcDim
226 int ds_num = FONcDim::DimNameNum + 1;
227 while (find(d4_rds_nums.begin(), d4_rds_nums.end(), ds_num) != d4_rds_nums.end()) {
228 // Note: the following #if 0 #endif block is only for future development.
229 // Don't delete or change it for debugging.
230#if 0
231 // This may be an optimization for rare cases. May do this when performance issue hurts
232 //d4_rds_nums_visited.push_back(ds_num);
233#endif
234 // Now the following line ensure this dimension name dimds_num(ds_num is a number)
235 // is NOT created for the dimension that doesn't have a name in DAP4.
236 ds_num++;
237 }
238 FONcDim::DimNameNum = ds_num - 1;
239
240 FONcDim *use_dim = find_dim(embed, d_a->dimension_name(di), size);
241 d_dims.push_back(use_dim);
242 }
243
244 dimnum++;
245 }
246
247 // We found an array that has 3-D 365x8075*7814 elements. This will make the chunk size 365*1024*1024, which
248 // is too big and will cause potential bad performance for the application that uses the generated
249 // netcdf file. So reduce the chunk size when the similar case occurs.
250 // The following rules handle the cases described above.
251 // Given there may be a very large size array and also the chunk size cannot too big,
252 // we modify the maximum chunk size according to the array size if necessary.
253 // The idea is we don't want to have too many chunks and we don't want to have the chunk size too big.
254 // So we do the following.
255 // 1. We start from 1M(1024x1024), if the number of chunks for the array is >64, we increase the chunk size to be 2048x2048.
256 // 2. We can increase the chunk size to 16M(4096x4096) if the number of chunks is still >64.
257 // 3. The maximum chunk size is 16M(4096x4096) no matter how big the array size is.
258 // 4. We can increase the maximum chunk size to a bigger number in the future if necessary.
259 // We can also change the maximum number of chunks.
260 // KY 2023-01-26
261
262 // The above algorithm works well to reduce the number of chunks for a large array size as well as having a decent
263 // access time. However,
264 // it may increase the final file size when the fastest changing dimension size increases from 1024 to 2048 or 4096.
265 // So to find a good balance between the file size and the access time for NASA files,
266 // we fix the sizes of two fastest changing dimensions to be <=1024x1024. We will increase the higher chunk dimension size
267 // from 1 to 2 or higher if the number of higher dimension size is >512. Why 512? Because we find the higher dimension
268 // may be time and one year is 365 days. We want to still keep the one day per slice if possible. 512 is not too big.
269 // The chunk overhead is not that big.
270 // KY 2023-01-29
271 // One more optimization: if the sizes of two fastest changing dimensions are much less than 1M(1024x1024),given
272 // that the higher dimension size may be large, we allow the chunk size grow to 1M and then if we still find the number of
273 // higher dimension size is >512, we then grow the chunk size gradually. This will make the bigger array size not hold
274 // too many chunks.
275
276 // Chunk size is not set when the number of dimension is >2. It is set here.
277 // When the number of higher than 2 dimension sizes is <max_num_higher_chunks(512 now), we start increasing the higher chunk dimension size.
278 // KY 2023-01-29
279 if (d_a->dimensions() >2) {
280
281 // The chunk sizes of the two fastest changing dimensions are fixed(not exceeding the MAX_CHUNK_SIZE). So set them first.
282 size_t two_fastest_chunk_dim_sizes=1;
283 auto d_chunksize_it = d_chunksizes.begin();
284
285 for (size_t i = d_a->dimensions();i>d_a->dimensions()-2; i--) {
286
287 size_t size = d_dim_sizes[i-1];
288 BESDEBUG("fonc", "FONcArray::CHUNK - dim size backward: " << size << endl);
289 d_chunksize_it = d_chunksizes.insert(d_chunksize_it, size <= MAX_CHUNK_SIZE ? size : MAX_CHUNK_SIZE);
290 two_fastest_chunk_dim_sizes *=d_chunksizes[0];
291 }
292 BESDEBUG("fonc", "FONcArray::CHUNK - two fastest dimchunk_sizes " << two_fastest_chunk_dim_sizes << endl);
293
294 // Set the chunk sizes for the rest dimensions if the array size is not too big.
295 // Calculate the dimension index when the size of the total chunk size exceeds 1024x1024.
296 // Without doing this, an extreme case such as a 511x1x1 array will set chunk size to 1x1x1, we don't want this to happen.
297
298 size_t rest_dim_stop_index = d_a->dimensions()-2;
299
300 if (two_fastest_chunk_dim_sizes <= GENERAL_MAX_CHUNK_SIZES) {
301
302 size_t total_chunk_size_so_far = two_fastest_chunk_dim_sizes;
303
304 for (int i = d_a->dimensions()-2;i>0; i--) {
305
306 size_t size = d_dim_sizes[i-1];
307 size_t chunk_size_candidate =((size<=MAX_CHUNK_SIZE)?size:MAX_CHUNK_SIZE);
308
309 if (total_chunk_size_so_far * chunk_size_candidate <= GENERAL_MAX_CHUNK_SIZES) {
310 total_chunk_size_so_far *=chunk_size_candidate;
311 d_chunksize_it = d_chunksizes.insert(d_chunksize_it,chunk_size_candidate);
312 // If i =1, we reach the first dimension. Set the dim stop index to 0.
313 if (i == 1)
314 rest_dim_stop_index = 0;
315 }
316 else {
317 rest_dim_stop_index = i;
318 break;
319 }
320 }
321 BESDEBUG("fonc", "FONcArray::CHUNK - total_chunk_size_so_far: " << total_chunk_size_so_far << endl);
322 }
323 BESDEBUG("fonc", "FONcArray::CHUNK - rest_dim_stop_index: " << rest_dim_stop_index << endl);
324
325
326 // Now if our chunk size is already 1M and there are too many chunks in this big array.
327 // We may increase the chunk size. 512 is the current maximum number of chunks.
328
329 size_t higher_dimension_size = 1;
330 size_t total_higher_dim_chunk_size = 1;
331 int max_num_higher_chunks = 512;
332
333 for (size_t i = 0; i<rest_dim_stop_index; i++)
334 higher_dimension_size *= d_dim_sizes[i];
335
336 if (higher_dimension_size > (size_t)max_num_higher_chunks)
337 total_higher_dim_chunk_size = higher_dimension_size/max_num_higher_chunks;
338
339 size_t left_higher_dim_chunk_size = total_higher_dim_chunk_size;
340
341 // We have to walk the dimension backward to make sure the fastest changing dimension chunk sizes set properly.
342 for (size_t i = rest_dim_stop_index;i>0; i--) {
343
344 size_t size = d_dim_sizes[i-1];
345
346 BESDEBUG("fonc", "FONcArray::CHUNK - left_higher_dim_chunk_size " << left_higher_dim_chunk_size << endl);
347 if (size < left_higher_dim_chunk_size) {
348 d_chunksize_it = d_chunksizes.insert(d_chunksize_it,size);
349 // The if block is not necessary, just make the sonar cloud happy
350 if (size !=0)
351 left_higher_dim_chunk_size = left_higher_dim_chunk_size/size;
352 }
353 else {
354 d_chunksize_it = d_chunksizes.insert(d_chunksize_it,left_higher_dim_chunk_size);
355 // Set the left higher dim chunk size to 1 since all the left higher chunk dimension size(if any) should be 1.
356 left_higher_dim_chunk_size = 1;
357 }
358 }
359 }
360
361#ifndef NDEBUG
362 for( const auto &chunk_size:d_chunksizes)
363 BESDEBUG("fonc", "FONcArray::CHUNK - chunk_size final: " <<chunk_size << endl);
364#endif
365
366 // if this array is a string array, then add the length dimension
367 if (d_array_type == NC_CHAR) {
368 // Calling intern_data() here is part of the 'streaming' refactoring.
369 // For the other types, the call can go in the write() implementations,
370 // but because strings in netCDF are arrays of char, a string array
371 // must have an added dimension (so a 1d string array becomes a 2d char
372 // array). To build the netCDF file, we need to know the dimensions of
373 // the array when the file is defined, not when the data are written.
374 // To know the size of the extra dimension used to hold the chars, we
375 // need to look at all the strings and find the biggest one. Thus, in
376 // order to define the variable for the netCDF file, we need to read
377 // string data long before we actually write it out. Kind of a drag,
378 // but not the end of the world. jhrg 5/18/21
379
380 // For NC_CHAR, the module needs to call the intern_data().
381 // This routine is called in the convert(). So it should not called in define().
382 // FIXME Patch for HYRAX-1334 jhrg 2/14/24
383
384 if (d_is_dap4 || get_eval() == nullptr || get_dds() == nullptr)
385 d_a->intern_data();
386 else
387 d_a->intern_data(*get_eval(), *get_dds());
388
389 // get the data from the dap array
390 int array_length = d_a->length();
391#if 0
392 d_str_data.reserve(array_length);
393 d_a->value(d_str_data);
394
395 // determine the max length of the strings
396 size_t max_length = 0;
397 for (int i = 0; i < array_length; i++) {
398 if (d_str_data[i].size() > max_length) {
399 max_length = d_str_data[i].size();
400 }
401 }
402 max_length++;
403#endif
404 size_t max_length = 0;
405 for (int i = 0; i < array_length; i++) {
406 if (d_a->get_str()[i].size() > max_length) {
407 max_length = d_a->get_str()[i].size();
408 }
409 }
410 max_length++;
411
412 vector<string> empty_embed;
413 string lendim_name;
414 if (is_dap4_group == true) {
415 // Here is a quick implementation.
416 // We just append the DimNameNum(globally defined)
417 // and then increase the number by 1.
418 ostringstream dim_suffix_strm;
419 dim_suffix_strm << "_len" << FONcDim::DimNameNum + 1;
420 FONcDim::DimNameNum++;
421 lendim_name = d_varname + dim_suffix_strm.str();
422
423 }
424 else
425 lendim_name = d_varname + "_len";
426
427
428 FONcDim *use_dim = find_dim(empty_embed, lendim_name, max_length, true);
429 // Added static_cast to suppress warning. 12.27.2011 jhrg
430 if (use_dim->size() < static_cast<int>(max_length)) {
431 use_dim->update_size(max_length);
432 }
433
434 d_dim_sizes[d_ndims - 1] = use_dim->size();
435 d_dim_ids[d_ndims - 1] = use_dim->dimid();
436
437 //DAP4 dimension ID is false.
438 use_d4_dim_ids.push_back(false);
439 d_dims.push_back(use_dim);
440
441 // Adding this fixes the bug reported by GSFC where arrays of strings
442 // caused the handler to throw an error stating that 'Bad chunk sizes'
443 // were used. When the dimension of the string array was extended (because
444 // strings become char arrays in netcdf3/4), the numbers of dimensions
445 // in 'chunksizes' was not bumped up. The code below in convert() that
446 // set the chunk sizes then tried to access data that had never been set.
447 // jhrg 11/25/15
448 d_chunksizes.push_back(max_length <= MAX_CHUNK_SIZE ? max_length : MAX_CHUNK_SIZE);
449 }
450
451 // If this array has a single dimension, and the name of the array
452 // and the name of that dimension are the same, then this array
453 // might be used as a map for a grid defined elsewhere.
454 // Notice: DAP4 doesn't have Grid and the d_dont_use_it=true causes some
455 // variables not written to the netCDF-4 file with group hierarchy.
456 // So need to have the if check. KY 2021-06-21
457 if(d_is_dap4 == false) {
458 if (!FONcGrid::InGrid && d_actual_ndims == 1 && d_a->name() == d_a->dimension_name(d_a->dim_begin())) {
459 // is it already in there?
460 const FONcMap *map = FONcGrid::InMaps(d_a);
461 if (!map) {
462 // This memory is/was leaked. jhrg 8/28/13
463 auto new_map = new FONcMap(this);
464 d_grid_maps.push_back(new_map); // save it here so we can free it later. jhrg 8/28/13
465 FONcGrid::Maps.push_back(new_map);
466 }
467 else {
468 d_dont_use_it = true;
469 }
470 }
471 }
472
473 BESDEBUG("fonc", "FONcArray::convert() - done converting array " << d_varname << endl);
474}
475
489FONcDim *
490FONcArray::find_dim(const vector<string> &embed, const string &name, int64_t size, bool ignore_size) {
491 string oname;
492 string ename = FONcUtils::gen_name(embed, name, oname);
493 FONcDim *ret_dim = nullptr;
494 vector<FONcDim *>::iterator i = FONcArray::Dimensions.begin();
495 vector<FONcDim *>::iterator e = FONcArray::Dimensions.end();
496 for (; i != e && !ret_dim; i++) {
497 if (!((*i)->name().empty()) && ((*i)->name() == name)) {
498 if (ignore_size) {
499 ret_dim = (*i);
500 }
501 else if ((*i)->size() == size) {
502 ret_dim = (*i);
503 }
504 else {
505 if (embed.size() > 0) {
506 vector<string> tmp;
507 return find_dim(tmp, ename, size);
508 }
509 string err = "fileout_netcdf: dimension found with the same name, but different size";
510 throw BESInternalError(err, __FILE__, __LINE__);
511 }
512 }
513 }
514
515 if (!ret_dim) {
516 ret_dim = new FONcDim(name, size);
517 FONcArray::Dimensions.push_back(ret_dim);
518 }
519 else {
520 ret_dim->incref();
521 }
522
523 return ret_dim;
524}
525
540void FONcArray::define(int ncid) {
541 BESDEBUG("fonc", "FONcArray::define() - defining array '" << d_varname << "'" << endl);
542
543 if (!d_defined && !d_dont_use_it) {
544
545 BESDEBUG("fonc", "FONcArray::define() - defining array ' defined already: " << d_varname << "'" << endl);
546
547 // Note: the following #if 0 #endif block is only for future development.
548 // Don't delete or change it for debugging.
549#if 0
550 if(d4_dim_ids.size() >0) {
551 if(d_array_type == NC_CHAR) {
552 if(d_dims.size() == 1) {
553 FONcDim *fd = *(d_dims.begin());
554 fd->define(ncid);
555 d_dim_ids[d_ndims-1] = fd->dimid();
556
557 }
558 else {
559
560 }
561 }
562 }
563 else {
564#endif
565 // If not defined DAP4 dimensions(mostly DAP2 or DAP4 no groups)
566 if (false == d4_def_dim) {
567 vector<FONcDim *>::iterator i = d_dims.begin();
568 vector<FONcDim *>::iterator e = d_dims.end();
569 int dimnum = 0;
570 for (; i != e; i++) {
571 FONcDim *fd = *i;
572 fd->define(ncid);
573 d_dim_ids[dimnum] = fd->dimid();
574 BESDEBUG("fonc", "FONcArray::define() - dim_id: " << fd->dimid() << " size:" << fd->size() << endl);
575 dimnum++;
576 }
577 }
578 else {// Maybe some dimensions are not DAP4 dimensions, will still generate those dimensions.
579 int j = 0;
580 for (unsigned int i = 0; i < use_d4_dim_ids.size(); i++) {
581 if (use_d4_dim_ids[i] == false) {
582 FONcDim *fd = d_dims[j];
583 fd->define(ncid);
584 d_dim_ids[i] = fd->dimid();
585 j++;
586 }
587 }
588 }
589
590 int stax = nc_def_var(ncid, d_varname.c_str(), d_array_type, d_ndims, d_dim_ids.data(), &d_varid);
591 if (stax != NC_NOERR) {
592 string err = (string) "fileout.netcdf - Failed to define variable " + d_varname;
593 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
594 }
595
596 stax = nc_def_var_fill(ncid, d_varid, NC_NOFILL, NULL );
597 if (stax != NC_NOERR) {
598 string err = (string) "fileout.netcdf - " + "Failed to clear fill value for " + d_varname;
599 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
600 }
601#ifndef NBEBUG
602
603 if (fdio_flag) {
604 BESDEBUG("fonc","variable name is "<<d_varname << endl);
605 BESDEBUG("fonc","FONC direct io flag is true before calling the intern_data()"<<endl);
606 }
607 else {
608 BESDEBUG("fonc","variable name is "<<d_varname << endl);
609 BESDEBUG("fonc","FONC direct io flag is false before calling the intern_data()"<<endl);
610 }
611
612 bool d_io_flag_phase_2 = d_a->get_dio_flag();
613 if (d_io_flag_phase_2) {
614 BESDEBUG("fonc","variable name is "<<d_varname << endl);
615 BESDEBUG("fonc","direct io flag is true before calling the intern_data()"<<endl);
616 Array::var_storage_info dmrpp_vs_info = d_a->get_var_storage_info();
617
618 BESDEBUG("fonc", "filters: "<<dmrpp_vs_info.filter<<endl);
619 for (const auto& def_lev:dmrpp_vs_info.deflate_levels)
620 BESDEBUG("fonc", "deflate level: "<<def_lev<<endl);
621
622 for (unsigned int i = 0; i < dmrpp_vs_info.chunk_dims.size(); i++)
623 BESDEBUG("fonc", "chunk_dim["<<i<<"]: "<<dmrpp_vs_info.chunk_dims[i]<<endl);
624
625 BESDEBUG("fonc","End of checking the chunk info. for the define mode. "<<d_varname << endl);
626
627 }
628
629
630#endif
631
632
633#if 0
634 // Check if the direct IO flag is really set. Note intern_data() is called in define() if fdio_flag is true.
635 // TODO: the following should be NOT necessary with further optimization of the memory usage in the future. KY 2023-11-30
636 if (d_array_type != NC_CHAR && fdio_flag == true) {
637 if (d_is_dap4)
638 d_a->intern_data();
639 else
640 d_a->intern_data(*get_eval(), *get_dds());
641 }
642#endif
643
644 // Obtain the direct IO flag
645 bool d_io_flag = d_a->get_dio_flag();
646
647#ifndef NBEBUG
648 BESDEBUG("fonc", "d_io_flag after intern_data(): "<<d_io_flag<<endl);
649
650 if (d_io_flag) {
651
652 Array::var_storage_info dmrpp_vs_info = d_a->get_var_storage_info();
653
654 BESDEBUG("fonc", "filters: "<<dmrpp_vs_info.filter<<endl);
655 for (const auto& def_lev:dmrpp_vs_info.deflate_levels)
656 BESDEBUG("fonc", "deflate level: "<<def_lev<<endl);
657
658 for (unsigned int i = 0; i < dmrpp_vs_info.chunk_dims.size(); i++)
659 BESDEBUG("fonc", "chunk_dim["<<i<<"]: "<<dmrpp_vs_info.chunk_dims[i]<<endl);
660
661 for (unsigned int i = 0; i<dmrpp_vs_info.var_chunk_info.size(); i++) {
662 BESDEBUG("fonc", "chunk index: "<<i<<" filter mask "<<dmrpp_vs_info.var_chunk_info[i].filter_mask<<endl);
663 BESDEBUG("fonc", "chunk index: "<<i<<" chunk_direct_io_offset "<<dmrpp_vs_info.var_chunk_info[i].chunk_direct_io_offset<<endl);
664 BESDEBUG("fonc", "chunk index: "<<i<<" chunk_buffer_size "<<dmrpp_vs_info.var_chunk_info[i].chunk_buffer_size<<endl);
665
666 BESDEBUG("fonc", "chunk index: "<<i<<" coordinates are "<<endl);
667 for (unsigned int j = 0; j<dmrpp_vs_info.var_chunk_info[i].chunk_coords.size(); j++)
668 BESDEBUG("fonc", "coordinate index: "<<j<<" value "<<dmrpp_vs_info.var_chunk_info[i].chunk_coords[j]<<endl);
669 }
670 }
671
672 BESDEBUG("fonc", "FONcArray::define() netcdf-4 version is " << d_ncVersion << endl);
673
674#endif
675
676 if (isNetCDF4()) {
677 BESDEBUG("fonc", "FONcArray::define() Working netcdf-4 branch " << endl);
678
679 if (d_io_flag) {
680 // Use the direct chunk IO settings.
681 define_dio_filters(ncid, d_varid);
682 }
683 else {
684 if (FONcRequestHandler::chunk_size == 0)
685 // I have no idea if chunksizes is needed in this case.
686 stax = nc_def_var_chunking(ncid, d_varid, NC_CONTIGUOUS, d_chunksizes.data());
687 else
688 stax = nc_def_var_chunking(ncid, d_varid, NC_CHUNKED, d_chunksizes.data());
689
690 if (stax != NC_NOERR) {
691 string err = "fileout.netcdf - Failed to define chunking for variable " + d_varname;
692 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
693 }
694
695 // TODO Make this more adaptable to the Array's data type. Find out when it's
696 // best to use shuffle, et c. jhrg 7/22/18
697 // The following code provides a way how to use shuffle. KY 11/2/23
698 if (FONcRequestHandler::use_compression) {
699
700 int shuffle = 0;
701 // For integer, if the type size is >= 2, turn on the shuffle key always.
702 // For other types, turn off the shuffle key by default.
703 if (NC_SHORT == d_array_type || NC_USHORT == d_array_type || NC_INT == d_array_type ||
704 NC_UINT == d_array_type || NC_INT64 == d_array_type || NC_UINT64 == d_array_type ||
705 FONcRequestHandler::use_shuffle)
706 shuffle = 1;
707
708 int deflate = 1;
709 int deflate_level = 4;
710 stax = nc_def_var_deflate(ncid, d_varid, shuffle, deflate, deflate_level);
711
712 if (stax != NC_NOERR) {
713 string err = (string) "fileout.netcdf - Failed to define compression (deflate) level for variable "
714 + d_varname;
715 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
716 }
717 }
718 }
719 }
720
721 // Largely revised the fillvalue check code and add the check for the DAP4 case. KY 2021-05-10
722 if (d_is_dap4) {
723 D4Attributes *d4_attrs = d_a->attributes();
724 updateD4AttrType(d4_attrs, d_array_type);
725 }
726 else {
727 AttrTable &attrs = d_a->get_attr_table();
728 updateAttrType(attrs, d_array_type);
729 }
730
731 BESDEBUG("fonc", "FONcArray::define() - Adding attributes " << endl);
732 FONcAttributes::add_variable_attributes(ncid, d_varid, d_a, isNetCDF4_ENHANCED(), d_is_dap4);
733 FONcAttributes::add_original_name(ncid, d_varid, d_varname, d_orig_varname);
734 d_defined = true;
735 }
736 else {
737 if (d_defined) {
738 BESDEBUG("fonc", "FONcArray::define() - variable " << d_varname << " is already defined" << endl);
739 }
740 if (d_dont_use_it) {
741 BESDEBUG("fonc", "FONcArray::define() - variable " << d_varname << " is not being used" << endl);
742 }
743 }
744
745 BESDEBUG("fonc", "FONcArray::define() - done defining array '" << d_varname << "'" << endl);
746}
747
753void FONcArray::write_nc_variable(int ncid, nc_type var_type) {
754
755 // Note: when fdio_flag is not true, the intern_data needs to be called here.
756 // FIXME Patch for HYRAX-1334 jhrg 2/14/24
757 if (d_is_dap4 || get_eval() == nullptr || get_dds() == nullptr)
758 d_a->intern_data();
759 else
760 d_a->intern_data(*get_eval(), *get_dds());
761
762 // Check if we can use direct IO.
763 bool d_io_flag = d_a->get_dio_flag();
764
765 if (d_io_flag) {
766 // direct IO operation.
767 write_direct_io_data(ncid,d_varid);
768
769 d_a->clear_local_data();
770 return;
771 }
772
773 int stax;
774
775 switch (var_type) {
776 case NC_UBYTE:
777 stax = nc_put_var_uchar(ncid, d_varid, reinterpret_cast<unsigned char *>(d_a->get_buf()));
778 break;
779 case NC_BYTE:
780 stax = nc_put_var_schar(ncid, d_varid, reinterpret_cast<signed char *>(d_a->get_buf()));
781 break;
782 case NC_SHORT:
783 stax = nc_put_var_short(ncid, d_varid, reinterpret_cast<short *>(d_a->get_buf()));
784 break;
785 case NC_INT:
786 stax = nc_put_var_int(ncid, d_varid, reinterpret_cast<int *>(d_a->get_buf()));
787 break;
788 case NC_INT64:
789 stax = nc_put_var_longlong(ncid, d_varid, reinterpret_cast<long long *>(d_a->get_buf()));
790 break;
791 case NC_FLOAT:
792 stax = nc_put_var_float(ncid, d_varid, reinterpret_cast<float *>(d_a->get_buf()));
793 break;
794 case NC_DOUBLE:
795 stax = nc_put_var_double(ncid, d_varid, reinterpret_cast<double *>(d_a->get_buf()));
796 break;
797 case NC_USHORT:
798 stax = nc_put_var_ushort(ncid, d_varid, reinterpret_cast<unsigned short *>(d_a->get_buf()));
799 break;
800 case NC_UINT:
801 stax = nc_put_var_uint(ncid, d_varid, reinterpret_cast<unsigned int *>(d_a->get_buf()));
802 break;
803 case NC_UINT64:
804 stax = nc_put_var_ulonglong(ncid, d_varid, reinterpret_cast<unsigned long long *>(d_a->get_buf()));
805 break;
806
807 default:
808 throw BESInternalError("Failed to transform array of unknown type in file out netcdf (1)",
809 __FILE__, __LINE__);
810 }
811
812 if (stax != NC_NOERR) {
813 string err = "fileout.netcdf - Failed to create array of " + d_a->var()->type_name() + " for " + d_varname;
814 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
815 }
816
817 // This frees the local storage. jhrg 5/14/21
818#if CLEAR_LOCAL_DATA
819 if (!FONcGrid::InMaps(d_a))
820 d_a->clear_local_data();
821#endif
822}
823
835bool FONcArray::equal_length(vector<string> &the_strings)
836{
837 if (the_strings.empty()==true)
838 return false;
839 else {
840 size_t length = the_strings[0].size();
841 if ( std::all_of(the_strings.begin()+1, the_strings.end(),
842 [length](string &s){return s.size() == length;}) )
843 return true;
844 else
845 return false;
846 }
847}
848
859void FONcArray::write(int ncid) {
860 BESDEBUG("fonc", "FONcArray::write() BEGIN var: " << d_varname << "[" << d_nelements << "]" << endl);
861 BESDEBUG("fonc", "FONcArray::write() BEGIN var type: " << d_array_type << " " << endl);
862
863 if (d_dont_use_it) {
864 BESDEBUG("fonc", "FONcTransform::write not using variable " << d_varname << endl);
865 return;
866 }
867
868 // Writing out array is complex. There are three cases:
869 // 1. Arrays of NC_CHAR, which are written the same for both the netCDF
870 // classic and enhanced data models;
871 // 2. All the other types, written for the enhanced data model
872 // 3. All the other types, written for the classic data model
873
874 if (d_array_type == NC_CHAR) {
875 // Note that String data are not read here but in FONcArray::convert() because
876 // that code needs to know that actual length of the individual strings in the
877 // array. jhrg 6/4/21
878
879 // More info: In FONcArray::convert() for arrays of NC_CHAR, even though the
880 // libdap::Array variable has M dimension (e.g., 2) d_ndims will be M+1. The
881 // additional dimension is there because each character is actually a string,
882 // so the code needs to store both the character and a null terminator. This
883 // might be a mistake in the data model - using String for CHAR might not be
884 // the best plan. Right now, it's what we have. jhrg 10/3/22
885
886 // Can we optimize for a special case where all strings are the same length?
887 // jhrg 10/3/22
888 if (equal_length(d_a->get_str())) {
889#if STRING_ARRAY_OPT
890 write_equal_length_string_array(ncid);
891#else
892 write_string_array(ncid);
893#endif
894 }
895 else {
896 write_string_array(ncid);
897 }
898 }
899 else if (isNetCDF4_ENHANCED()) {
900 // If we support the netCDF-4 enhanced model, the unsigned integer
901 // can be directly mapped to the netcdf-4 unsigned integer.
902 write_for_nc4_types(ncid);
903 }
904 else {
905 write_for_nc3_types(ncid);
906 }
907
908 BESDEBUG("fonc", "FONcArray::write() END var: " << d_varname << "[" << d_nelements << "]" << endl);
909}
910
911void FONcArray::write_for_nc3_types(int ncid) {
912 Type element_type = d_a->var()->type();
913 // create array to hold data hyperslab
914 switch (d_array_type) {
915 case NC_BYTE:
916 case NC_FLOAT:
917 case NC_DOUBLE:
918 write_nc_variable(ncid, d_array_type);
919 break;
920
921 case NC_SHORT:
922 // Given Byte/UInt8 will always be unsigned they must map
923 // to a NetCDF type that will support unsigned bytes. This
924 // detects the original variable was of type Byte and typecasts
925 // each data value to a short.
926 if (element_type == dods_byte_c || element_type == dods_uint8_c) {
927 // The data is retrieved in the define mode. So no need to do it here.
928 // Comment out for the time being.
929 // Uncomment to reduce the memory print. the meory is not allocated in the define mode.
930 if (d_is_dap4)
931 d_a->intern_data();
932 else
933 d_a->intern_data(*get_eval(), *get_dds());
934
935 // There's no practical way to get rid of the value copy, be here we
936 // read directly from libdap::Array object's memory.
937 vector<short> data(d_nelements);
938 for (size_t d_i = 0; d_i < d_nelements; d_i++)
939 data[d_i] = *(reinterpret_cast<unsigned char *>(d_a->get_buf()) + d_i);
940
941 int stax = nc_put_var_short(ncid, d_varid, data.data());
942 if (stax != NC_NOERR) {
943 string err = (string) "fileout.netcdf - Failed to create array of shorts for " + d_varname;
944 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
945 }
946
947 // Once we've written an array, reclaim its space _unless_ it is a Grid map.
948 // It might be shared and other code here expects it to be resident in memory.
949 // jhrg 6/4/21
950 if (!FONcGrid::InMaps(d_a))
951 d_a->clear_local_data();
952 }
953 else {
954 write_nc_variable(ncid, NC_SHORT);
955 }
956 break;
957
958 case NC_INT:
959 // Added as a stop-gap measure to alert SAs and inform users of a misconfigured server.
960 // jhrg 6/15/20
961 if (element_type == dods_int64_c || element_type == dods_uint64_c) {
962 // We should not be here. The server configuration is wrong since the netcdf classic
963 // model is being used (either a netCDf3 response is requested OR a netCDF4 with the
964 // classic model). Tell the user and the SA.
965 string msg;
966 if (FONcRequestHandler::classic_model == false) {
967 msg = "You asked for one or more 64-bit integer values returned using a netCDF3 file. "
968 "Try asking for netCDF4 enhanced and/or contact the server administrator.";
969 }
970 else {
971 msg = "You asked for one or more 64-bit integer values, but either returned using a netCDF3 file or "
972 "from a server that is configured to use the 'classic' netCDF data model with netCDF4. "
973 "Try netCDF4 and/or contact the server administrator.";
974 }
975 throw BESInternalError(msg, __FILE__, __LINE__);
976 }
977
978 if (element_type == dods_uint16_c) {
979
980 // The data is retrieved in the define mode. So no need to do it here.
981 // Comment out for the time being.
982 // Uncomment to reduce the memory print. the meory is not allocated in the define mode.
983 if (d_is_dap4)
984 d_a->intern_data();
985 else
986 d_a->intern_data(*get_eval(), *get_dds());
987
988 vector<int> data(d_nelements);
989 for (size_t d_i = 0; d_i < d_nelements; d_i++)
990 data[d_i] = *(reinterpret_cast<unsigned short *>(d_a->get_buf()) + d_i);
991
992 int stax = nc_put_var_int(ncid, d_varid, data.data());
993 if (stax != NC_NOERR) {
994 string err = (string) "fileout.netcdf - Failed to create array of ints for " + d_varname;
995 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
996 }
997
998 if (!FONcGrid::InMaps(d_a))
999 d_a->clear_local_data();
1000 }
1001 else {
1002 write_nc_variable(ncid, NC_INT);
1003 }
1004 break;
1005
1006 default:
1007 throw BESInternalError("Failed to transform array of unknown type in file out netcdf (2)",
1008 __FILE__, __LINE__);
1009 }
1010}
1011
1016void FONcArray::write_string_array(int ncid) {
1017 vector<size_t> var_count(d_ndims);
1018 vector<size_t> var_start(d_ndims);
1019 int dim = 0;
1020 for (dim = 0; dim < d_ndims; dim++) {
1021 // the count for each of the dimensions will always be 1 except
1022 // for the string length dimension.
1023 // The size of the last dimension (var_count[d_ndims-1]) is set
1024 // separately for each element below. jhrg 10/3/22
1025 var_count[dim] = 1;
1026
1027 // the start for each of the dimensions will start at 0. We will
1028 // bump this up in the while loop below
1029 var_start[dim] = 0;
1030 }
1031
1032 auto const &d_a_str = d_a->get_str();
1033 for (size_t element = 0; element < d_nelements; element++) {
1034 var_count[d_ndims - 1] = d_a_str[element].size() + 1;
1035 var_start[d_ndims - 1] = 0;
1036
1037 // write out the string
1038 int stax = nc_put_vara_text(ncid, d_varid, var_start.data(), var_count.data(),
1039 d_a_str[element].c_str());
1040
1041 if (stax != NC_NOERR) {
1042 string err = (string) "fileout.netcdf - Failed to create array of strings for " + d_varname;
1043 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1044 }
1045
1046 // bump up the start.
1047 if (element + 1 < d_nelements) {
1048 bool done = false;
1049 dim = d_ndims - 2;
1050 while (!done) {
1051 var_start[dim] = var_start[dim] + 1;
1052 if (var_start[dim] == d_dim_sizes[dim]) {
1053 var_start[dim] = 0;
1054 dim--;
1055 if (dim <0)
1056 break;
1057 }
1058 else {
1059 done = true;
1060 }
1061 }
1062 }
1063 }
1064
1065 d_a->get_str().clear();
1066}
1067
1075void FONcArray::write_equal_length_string_array(int ncid) {
1076 vector<size_t> var_count(d_ndims);
1077 vector<size_t> var_start(d_ndims);
1078 // The flattened n-dim array as a vector of strings, row major order
1079 auto const &d_a_str = d_a->get_str();
1080
1081 vector<char> text_data;
1082 text_data.reserve(d_a_str.size() * (d_a_str[0].size() + 1));
1083 for (auto &str: d_a_str) {
1084 for (auto c: str)
1085 text_data.emplace_back(c);
1086 text_data.emplace_back('\0');
1087 }
1088
1089 for (int dim = 0; dim < d_ndims; dim++) {
1090 var_start[dim] = 0;
1091 }
1092 for (int dim = 0; dim < d_ndims; dim++) {
1093 var_count[dim] = d_dim_sizes[dim];
1094 }
1095 var_count[d_ndims - 1] = d_a_str[0].size() + 1;
1096
1097 int stax = nc_put_vara_text(ncid, d_varid, var_start.data(), var_count.data(), text_data.data());
1098
1099 if (stax != NC_NOERR) {
1100 string err = (string) "fileout.netcdf - Failed to create array of strings for " + d_varname;
1101 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1102 }
1103
1104 d_a->get_str().clear();
1105}
1106
1107
1113 return d_a->name();
1114}
1115
1124void FONcArray::dump(ostream &strm) const {
1125 strm << BESIndent::LMarg << "FONcArray::dump - (" << (void *) this << ")" << endl;
1126 BESIndent::Indent();
1127 strm << BESIndent::LMarg << "name = " << d_varname << endl;
1128 strm << BESIndent::LMarg << "ndims = " << d_ndims << endl;
1129 strm << BESIndent::LMarg << "actual ndims = " << d_actual_ndims << endl;
1130 strm << BESIndent::LMarg << "nelements = " << d_nelements << endl;
1131 if (d_dims.size()) {
1132 strm << BESIndent::LMarg << "dimensions:" << endl;
1133 BESIndent::Indent();
1134 vector<FONcDim *>::const_iterator i = d_dims.begin();
1135 vector<FONcDim *>::const_iterator e = d_dims.end();
1136 for (; i != e; i++) {
1137 (*i)->dump(strm);
1138 }
1139 BESIndent::UnIndent();
1140 }
1141 else {
1142 strm << BESIndent::LMarg << "dimensions: none" << endl;
1143 }
1144 BESIndent::UnIndent();
1145}
1146
1156void FONcArray::write_for_nc4_types(int ncid) {
1157
1158 d_is_dap4 = true;
1159
1160 // create array to hold data hyperslab
1161 // DAP2 only supports unsigned BYTE. So here
1162 // we don't include NC_BYTE (the signed BYTE, the same
1163 // as 64-bit integer). KY 2020-03-20
1164 // Actually 64-bit integer is supported.
1165 switch (d_array_type) {
1166 case NC_BYTE:
1167 case NC_UBYTE:
1168 case NC_SHORT:
1169 case NC_INT:
1170 case NC_INT64:
1171 case NC_FLOAT:
1172 case NC_DOUBLE:
1173 case NC_USHORT:
1174 case NC_UINT:
1175 case NC_UINT64:
1176 write_nc_variable(ncid, d_array_type);
1177 break;
1178
1179 default:
1180 string err = (string) "Failed to transform array of unknown type in file out netcdf";
1181 throw BESInternalError(err, __FILE__, __LINE__);
1182 }
1183}
1184
1185// Method to define filters of direct IO.
1186void FONcArray::define_dio_filters(int ncid, int d_varid) {
1187
1188 Array::var_storage_info dmrpp_vs_info = d_a->get_var_storage_info();
1189
1190 BESDEBUG("fonc", "filters: "<<dmrpp_vs_info.filter<<endl);
1191
1192 string filters_string = dmrpp_vs_info.filter;
1193
1194 bool has_fletcher_first = false;
1195 bool has_fletcher_last = false;
1196 bool has_shuffle = false;
1197 bool has_2deflates = false;
1198 bool has_1deflate = false;
1199
1200 // Filter orders matter.
1201 obtain_dio_filters_order(filters_string, has_fletcher_first, has_fletcher_last, has_shuffle, has_2deflates, has_1deflate);
1202
1203 int stax = 0;
1204 stax = nc_def_var_chunking_direct_write(ncid, d_varid, NC_CHUNKED, dmrpp_vs_info.chunk_dims.data());
1205 if (stax != NC_NOERR) {
1206 string err = "fileout.netcdf - Failed to define direct_io chunking for variable " + d_varname;
1207 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1208 }
1209
1210 // Allocate filters for the direct IO.
1211 allocate_dio_nc4_def_filters(ncid, d_varid, has_fletcher_first, has_fletcher_last, has_shuffle, has_2deflates, has_1deflate, dmrpp_vs_info.deflate_levels);
1212
1213
1214}
1215
1216// Method that obtains the filter orders for the direct IO case.
1217void FONcArray::obtain_dio_filters_order(const string & filters, bool &has_fle_first, bool &has_fle_last, bool &has_shuffle, bool &has_2defs, bool &has_1def) const {
1218
1219 vector<string> filter_array = BESUtil::split(filters, ' ' );
1220
1221 short num_defs = 0;
1222 for (unsigned int i = 0; i <filter_array.size(); i++) {
1223 if (filter_array[i] == "shuffle")
1224 has_shuffle = true;
1225 else if (filter_array[i] == "deflate")
1226 num_defs++;
1227 else if (filter_array[i] == "fletcher32") {
1228 if (i ==0)
1229 has_fle_first = true;
1230 else
1231 has_fle_last = true;
1232 }
1233 }
1234
1235 if (num_defs == 1)
1236 has_1def = true;
1237 else if (num_defs == 2)
1238 has_2defs = true;
1239 else if (num_defs >2)
1240 throw BESInternalError("Currently we don't support more than 2 deflate filters.", __FILE__, __LINE__);
1241
1242}
1243
1244// Method that allocate netCDF-4 filters for the direct IO case.
1245void FONcArray::allocate_dio_nc4_def_filters(int ncid, int d_varid, bool has_fle_first, bool has_fle_last, bool has_shuffle,
1246 bool has_2defs, bool has_1def, const vector<unsigned int>& def_levs) const {
1247
1248 int stax = 0;
1249
1250 if (has_1def && def_levs.size() !=1)
1251 throw BESInternalError("The size of def_levs should be 1 for one deflate filter", __FILE__, __LINE__);
1252
1253 if (has_2defs && def_levs.size() !=2)
1254 throw BESInternalError("The size of def_levs should be 2 for two deflate filters", __FILE__, __LINE__);
1255
1256 if (has_fle_first) {
1257
1258 stax = nc_def_var_fletcher32(ncid, d_varid, 1);
1259 if (stax != NC_NOERR) {
1260 string err = "fileout.netcdf - Failed to define the fletcher32 filter for variable " + d_varname;
1261 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1262 }
1263 }
1264
1265 if (has_shuffle) {
1266 if (has_1def) {
1267 stax = nc_def_var_deflate(ncid, d_varid, 1, 1, def_levs[0]);
1268 if (stax != NC_NOERR) {
1269 string err = "fileout.netcdf - Failed to define the deflate and the shuffle filters for variable " + d_varname;
1270 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1271 }
1272 }
1273 else if (has_2defs) {
1274 stax = nc_def_var_two_deflates(ncid, d_varid, 1, 1, def_levs[0], def_levs[1]);
1275 if (stax != NC_NOERR) {
1276 string err = "fileout.netcdf - Failed to define the two deflate filters and the shuffle filter for variable " + d_varname;
1277 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1278 }
1279 }
1280 }
1281 else {
1282 if (has_1def) {
1283 stax = nc_def_var_deflate(ncid, d_varid, 0, 1, def_levs[0]);
1284 if (stax != NC_NOERR) {
1285 string err = "fileout.netcdf - Failed to define the deflate filter for variable " + d_varname;
1286 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1287 }
1288 }
1289 else if (has_2defs) {
1290 stax = nc_def_var_two_deflates(ncid, d_varid, 0, 1, def_levs[0], def_levs[1]);
1291 if (stax != NC_NOERR) {
1292 string err = "fileout.netcdf - Failed to define the two deflate filters for variable " + d_varname;
1293 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1294 }
1295 }
1296 }
1297
1298 if (has_fle_last) {
1299 stax = nc_def_var_fletcher32(ncid, d_varid,1);
1300 if (stax != NC_NOERR) {
1301 string err = "fileout.netcdf - Failed to define the filter fletcher32 for variable " + d_varname;
1302 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1303 }
1304 }
1305
1306}
1307
1308// Write data for the direct IO case.
1309void FONcArray::write_direct_io_data(int ncid, int d_varid) {
1310
1311 char dummy_buffer[1];
1312
1313 BESDEBUG("fonc", "FONcArray() - direct IO write " << endl);
1314 // The following call doesn't write any data but set up the necessary operations for sending data directly.
1315 int stax = nc_put_var(ncid, d_varid, dummy_buffer);
1316 if (stax != NC_NOERR) {
1317 string err = "fileout.netcdf - the direct IO version of nc_put_var error for variable " + d_varname;
1318 FONcUtils::handle_error(stax , err, __FILE__, __LINE__);
1319 }
1320
1321 Array::var_storage_info dmrpp_vs_info = d_a->get_var_storage_info();
1322
1323 for (const auto & var_chunk_info:dmrpp_vs_info.var_chunk_info) {
1324
1325 Array::var_chunk_info_t vci = var_chunk_info;
1326 // May use the vector to replace new[] later.
1327 auto chunk_buf = new char[vci.chunk_buffer_size];
1328 memcpy (chunk_buf,d_a->get_buf()+vci.chunk_direct_io_offset,vci.chunk_buffer_size);
1329
1330 stax = nc4_write_chunk(ncid, d_varid, vci.filter_mask, vci.chunk_coords.size(), (const size_t *)(vci.chunk_coords.data()),vci.chunk_buffer_size, chunk_buf);
1331 if (stax != NC_NOERR) {
1332 string err = "fileout.netcdf - nc4_write_chunk error for variable " + d_varname;
1333 FONcUtils::handle_error(stax, err, __FILE__, __LINE__);
1334
1335 }
1336
1337 delete[] chunk_buf;
1338 }
1339
1340}
1341
1342
exception thrown if internal error encountered
static std::vector< std::string > split(const std::string &s, char delim='/', bool skip_empty=true)
Splits the string s into the return vector of tokens using the delimiter delim and skipping empty val...
Definition BESUtil.cc:1068
virtual void dump(std::ostream &strm) const override
dumps information about this object for debugging purposes
virtual void define(int ncid) override
define the DAP Array in the netcdf file
Definition FONcArray.cc:540
~FONcArray() override
Destructor that cleans up the array.
Definition FONcArray.cc:117
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:141
virtual void write(int ncid) override
Write the array out to the netcdf file.
Definition FONcArray.cc:859
std::string name() override
returns the name of the DAP Array
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.
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:85
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:429
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:115
static string gen_name(const vector< string > &embed, const string &name, string &original)
generate a new name for the embedded variable
Definition FONcUtils.cc:182
STL class.
STL class.
STL class.
STL class.
STL iterator class.
STL iterator class.
STL class.
Type
Type of JSON value.
Definition rapidjson.h:664