OGS
NetCdfConverter.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include <tclap/CmdLine.h>
5
6#include <cctype>
7#include <iostream>
8#include <limits>
9#include <memory>
10#include <netcdf>
11#include <numeric>
12#include <sstream>
13#include <string>
14#include <utility>
15
16#include "BaseLib/FileTools.h"
17#include "BaseLib/Logging.h"
18#include "BaseLib/MPI.h"
21#include "GeoLib/Raster.h"
22#include "InfoLib/GitInfo.h"
24#include "MeshLib/Mesh.h"
27
28using namespace netCDF;
29
30static const double no_data_output = -9999;
31static double no_data_input = -9999;
32
40
41static void checkExit(std::string const& str)
42{
43 if (str == "x" || str == "exit")
44 exit(0);
45}
46
47static void showErrorMessage(std::size_t const error_id,
48 std::size_t const max = 0)
49{
50 if (error_id == 0)
51 {
52 ERR("Input not valid.");
53 }
54 else if (error_id == 1)
55 {
56 ERR("Index not valid. Valid indices are in [0,{:d}].", max);
57 }
58 else if (error_id == 2)
59 {
60 ERR("Input not valid.");
61 std::cout << "Type \"info\" to display the available options again. "
62 "\"exit\" will exit the programme.\n";
63 }
64}
65
66static std::size_t parseInput(std::string const& request_str,
67 std::size_t const max_val,
68 bool const has_info = false)
69{
70 while (true)
71 {
72 std::cout << request_str;
73 std::string str;
74 std::size_t val;
75 std::getline(std::cin, str);
76 checkExit(str);
77 if (has_info && str == "info")
78 return (max_val);
79 std::stringstream str_stream(str);
80 if (!(str_stream >> val))
81 {
82 std::size_t const error_val = (has_info) ? 2 : 0;
83 showErrorMessage(error_val);
84 continue;
85 }
86 if (val > max_val - 1)
87 {
88 showErrorMessage(1, max_val - 1);
89 continue;
90 }
91 return val;
92 }
93 return std::numeric_limits<std::size_t>::max();
94}
95
96static NcVar getDimVar(NcFile const& dataset, NcVar const& var,
97 std::size_t const dim)
98{
99 NcDim const& dim_obj = var.getDim(dim);
100 return dataset.getVar(dim_obj.getName());
101}
102
103static std::pair<double, double> getDimLength(NcVar const& var,
104 std::size_t const dim)
105{
106 return std::make_pair(0.0, static_cast<double>(var.getDim(dim).getSize()));
107}
108
109static std::vector<std::string> getArrays(NcFile const& dataset)
110{
111 auto const& names = dataset.getVars();
112 std::vector<std::string> var_names;
113 for (auto [name, var] : names)
114 {
115 (void)var;
116 var_names.push_back(name);
117 }
118 return var_names;
119}
120
121static void showArrays(NcFile const& dataset)
122{
123 std::size_t const n_vars(dataset.getDimCount());
124 std::cout << "The NetCDF file contains the following " << n_vars
125 << " arrays:\n\n";
126 std::cout << "\tIndex\tArray Name\t#Dimensions\n";
127 std::cout << "-------------------------------------------\n";
128 auto const& names = dataset.getVars();
129 std::size_t count = 0;
130 for (auto [name, var] : names)
131 {
132 std::cout << "\t" << count++ << "\t" << name << "\t("
133 << var.getDimCount() << "D array)\n";
134 }
135 std::cout << "\n";
136}
137
138static void showArraysDims(NcVar const& var)
139{
140 std::cout << "Data array \"" << var.getName()
141 << "\" contains the following dimensions:\n";
142 std::size_t const n_dims(var.getDimCount());
143 for (std::size_t i = 0; i < n_dims; ++i)
144 std::cout << "\t" << i << "\t" << var.getDim(i).getName() << "\t("
145 << var.getDim(i).getSize() << " values)\n";
146 std::cout << "\n";
147}
148
149static std::pair<double, double> getBoundaries(NcVar const& var)
150{
151 if (var.getDimCount() == 1)
152 {
153 double start, end;
154 std::size_t const size = var.getDim(0).getSize();
155 var.getVar({0}, {1}, &start);
156 var.getVar({size - 1}, {1}, &end);
157 return std::make_pair(start, end);
158 }
159 return std::make_pair(0, 0);
160}
161
162static MathLib::Point3d getOrigin(NcFile const& dataset, NcVar const& var,
163 std::vector<std::size_t> const& dim_idx_map,
164 std::size_t const time_offset)
165{
167 std::size_t const n_dims = var.getDimCount();
168 for (std::size_t i = time_offset; i < n_dims; ++i)
169 {
170 NcVar const& dim = getDimVar(dataset, var, dim_idx_map[i]);
171 auto const bounds = (dim.isNull()) ? getDimLength(var, dim_idx_map[i])
172 : getBoundaries(dim);
173 origin[i - time_offset] =
174 (bounds.first < bounds.second) ? bounds.first : bounds.second;
175 }
176 return origin;
177}
178
179static void flipRaster(std::vector<double>& data, std::size_t const layers,
180 std::size_t const width, std::size_t const height)
181{
182 std::size_t const length(data.size());
183 std::vector<double> tmp_vec;
184 tmp_vec.reserve(length);
185 for (std::size_t k = 0; k < layers; k++)
186 {
187 std::size_t const layer_end = (k + 1) * height * width;
188 for (std::size_t i = 0; i < height; i++)
189 {
190 std::size_t const line_idx(layer_end - (width * (i + 1)));
191 for (std::size_t j = 0; j < width; j++)
192 {
193 tmp_vec.push_back(data[line_idx + j]);
194 }
195 }
196 }
197 std::copy(tmp_vec.cbegin(), tmp_vec.cend(), data.begin());
198}
199
200static bool canConvert(NcVar const& var)
201{
202 bool ret(var.getDimCount() < 2);
203 if (ret)
204 ERR("Only 2+ dimensional variables can be converted into OGS "
205 "Meshes.\n");
206 return !ret;
207}
208
209static std::string arraySelectionLoop(NcFile const& dataset)
210{
211 std::vector<std::string> const& names = getArrays(dataset);
212 showArrays(dataset);
213 std::size_t const idx =
214 parseInput("Enter data array index: ", dataset.getVarCount(), true);
215
216 if (static_cast<int>(idx) == dataset.getVarCount() ||
217 !canConvert(dataset.getVar(names[idx])))
218 return arraySelectionLoop(dataset);
219
220 return names[idx];
221}
222
223static bool dimensionSelectionLoop(NcVar const& var,
224 std::vector<std::size_t>& dim_idx_map)
225{
226 showArraysDims(var);
227 std::size_t const n_dims(var.getDimCount());
228 dim_idx_map[0] = std::numeric_limits<std::size_t>::max();
229 bool is_time_dep(true);
230
231 // get temporal dimension
232 if (n_dims > 1)
233 {
234 std::string temp_str("");
235 cout << "Is the parameter time-dependent?\n";
236 while (dim_idx_map[0] == std::numeric_limits<std::size_t>::max() &&
237 is_time_dep == true)
238 {
239 cout << "Enter ID for temporal dimension or \"c\" to continue: ";
240 std::getline(std::cin, temp_str);
241 std::stringstream str_stream(temp_str);
242 if (str_stream.str() == "c" || str_stream.str() == "continue")
243 is_time_dep = false;
244 else
245 {
246 if (!(str_stream >> dim_idx_map[0]))
247 {
249 dim_idx_map[0] = std::numeric_limits<std::size_t>::max();
250 continue;
251 }
252 if (dim_idx_map[0] > n_dims - 1)
253 {
254 showErrorMessage(1, var.getDimCount() - 1);
255 dim_idx_map[0] = std::numeric_limits<std::size_t>::max();
256 }
257 }
258 }
259 }
260 else
261 is_time_dep = false;
262
263 // get spatial dimension(s)
264 std::size_t const start_idx = (is_time_dep) ? 1 : 0;
265 std::array<std::string, 4> const dim_comment{
266 "(x / longitude)", "(y / latitude)", "(z / height / depth)",
267 "[Error: 4-dimensional non-temporal arrays are not supported]"};
268 for (std::size_t i = start_idx; i < n_dims; ++i)
269 {
270 dim_idx_map[i] = std::numeric_limits<std::size_t>::max();
271
272 std::string const request_str("Enter ID for dimension " +
273 std::to_string(i) + " " +
274 dim_comment[i - start_idx] + ": ");
275 std::size_t const idx =
276 parseInput(request_str, var.getDimCount(), true);
277
278 if (static_cast<int>(idx) == var.getDimCount())
279 {
280 showArraysDims(var);
281 i--;
282 continue;
283 }
284 dim_idx_map[i] = idx;
285 }
286
287 return is_time_dep;
288}
289
290static std::pair<std::size_t, std::size_t> timestepSelectionLoop(
291 NcVar const& var, std::size_t const dim_idx)
292{
293 std::size_t const n_time_steps = var.getDim(dim_idx).getSize();
294 std::size_t const max_val = std::numeric_limits<std::size_t>::max();
295 std::pair<std::size_t, std::size_t> bounds(max_val, max_val);
296 std::cout << "\nThe dataset contains " << n_time_steps << " time steps.\n";
297 while (bounds.first == max_val)
298 {
299 bounds.first = parseInput(
300 "Specify first time step to export: ", n_time_steps, false);
301 }
302 while (bounds.first > bounds.second || bounds.second > n_time_steps)
303 {
304 bounds.second = parseInput(
305 "Specify last time step to export: ", n_time_steps, false);
306 }
307 return bounds;
308}
309
310static MeshLib::MeshElemType elemSelectionLoop(std::size_t const dim)
311{
312 if (dim == 1)
314
317 {
318 std::cout << "\nSelect element type for result, choose ";
319
320 if (dim == 2)
321 std::cout << "(t)riangle or (q)uadliteral: ";
322 if (dim == 3)
323 std::cout << "(p)rism or (h)exahedron: ";
324 std::string type("");
325 std::getline(std::cin, type);
326 checkExit(type);
327 if (dim == 2)
328 {
329 if (type != "t" && type != "q" && type != "tri" && type != "quad" &&
330 type != "triangle" && type != "quatliteral")
331 continue;
332 if (type == "t" || type == "tri" || type == "triangle")
335 }
336
337 if (dim == 3)
338 {
339 if (type != "p" && type != "h" && type != "prism" &&
340 type != "hex" && type != "hexahedron")
341 continue;
342 if (type == "p" || type == "prism")
345 }
346 }
347 return t;
348}
349
351 std::pair<std::size_t, std::size_t> const& time_bounds)
352{
354 while (t == OutputType::INVALID)
355 {
356 std::size_t const n_time_steps(time_bounds.second - time_bounds.first +
357 1);
358 std::cout << "\nThe selection includes " << n_time_steps
359 << " time steps.\n";
360 std::cout << "0. Save data in " << n_time_steps
361 << " mesh files with one scalar array each.\n";
362 std::cout << "1. Save data in one mesh file with " << n_time_steps
363 << " scalar arrays.\n";
364 std::cout << "2. Save data as " << n_time_steps << " ASC images.\n";
365
366 std::size_t const ret =
367 parseInput("Select preferred method: ", 3, false);
368
369 if (ret == 0)
371 else if (ret == 1)
373 else if (ret == 2)
375 }
376 return t;
377}
378
379static std::string getIterationString(std::size_t i, std::size_t max)
380{
381 std::size_t const max_length(std::to_string(max).length());
382 std::string const current_str(std::to_string(i));
383 return std::string(max_length - current_str.length(), '0') + current_str;
384}
385
386static double getResolution(NcFile const& dataset, NcVar const& var)
387{
388 std::size_t const dim_idx = var.getDimCount() - 1;
389 NcVar const dim_var(getDimVar(dataset, var, dim_idx));
390 auto const bounds = (dim_var.isNull()) ? getDimLength(var, dim_idx)
391 : getBoundaries(dim_var);
392 std::size_t const dim_size = var.getDim(dim_idx).getSize();
393 if (dim_size == 0)
394 {
395 OGS_FATAL("Dimension '{:s}' has size 0. Aborting...",
396 var.getDim(dim_idx).getName());
397 }
398 return std::fabs(bounds.second - bounds.first) /
399 static_cast<double>(dim_size);
400}
401
403 NcFile const& dataset, NcVar const& var,
404 std::vector<std::size_t> const& dim_idx_map,
405 std::vector<std::size_t> const& length, std::size_t const time_offset)
406{
407 MathLib::Point3d const origin =
408 getOrigin(dataset, var, dim_idx_map, time_offset);
409 double const res = getResolution(dataset, var);
410 std::size_t n_dims = var.getDimCount();
411 std::size_t z_length =
412 (n_dims - time_offset == 3) ? length[dim_idx_map.back()] : 1;
413 return {length[dim_idx_map[0 + time_offset]],
414 length[dim_idx_map[1 + time_offset]],
415 z_length,
416 origin,
417 res,
419}
420
421static std::vector<std::size_t> getLength(NcVar const& var,
422 std::size_t const time_offset)
423{
424 std::size_t const n_dims = (var.getDimCount());
425 std::vector<std::size_t> length(n_dims, 1);
426 for (std::size_t i = time_offset; i < n_dims; ++i)
427 {
428 length[i] = var.getDim(i).getSize();
429 }
430 return length;
431}
432
433static std::vector<double> getData(NcVar const& var,
434 std::size_t const total_length,
435 std::size_t const time_step,
436 std::vector<std::size_t> const& length)
437{
438 std::size_t const n_dims(var.getDimCount());
439 std::vector<std::size_t> offset(n_dims, 0);
440 offset[0] = time_step;
441 std::vector<double> data_vec(total_length, 0);
442 var.getVar(offset, length, data_vec.data());
443
444 std::replace_if(
445 data_vec.begin(), data_vec.end(),
446 [](double const& x) { return x == no_data_input; }, no_data_output);
447
448 return data_vec;
449}
450
451static bool assignDimParams(NcVar const& var,
452 std::vector<std::size_t>& dim_idx_map,
453 TCLAP::ValueArg<std::size_t>& arg_dim_time,
454 TCLAP::ValueArg<std::size_t>& arg_dim1,
455 TCLAP::ValueArg<std::size_t>& arg_dim2,
456 TCLAP::ValueArg<std::size_t>& arg_dim3)
457{
458 std::size_t dim_param_count = 0;
459 if (arg_dim_time.isSet())
460 dim_param_count++;
461 if (arg_dim1.isSet())
462 dim_param_count++;
463 if (arg_dim2.isSet())
464 dim_param_count++;
465 if (arg_dim3.isSet())
466 dim_param_count++;
467
468 std::size_t const n_dims = var.getDimCount();
469 if (dim_param_count != n_dims)
470 {
471 ERR("Number of parameters set does not fit number of parameters for "
472 "specified variable.");
473 return false;
474 }
475
476 if (arg_dim_time.getValue() >= n_dims || arg_dim1.getValue() >= n_dims ||
477 arg_dim2.getValue() >= n_dims || arg_dim3.getValue() >= n_dims)
478 {
479 ERR("Maximum allowed dimension for variable \"{:s}\" is {:d}.",
480 var.getName(), n_dims - 1);
481 return false;
482 }
483
484 if (arg_dim_time.isSet())
485 dim_idx_map[0] = arg_dim_time.getValue();
486 std::size_t const temp_offset = (arg_dim_time.isSet()) ? 1 : 0;
487 dim_idx_map[0 + temp_offset] = arg_dim1.getValue();
488 dim_idx_map[1 + temp_offset] = arg_dim2.getValue();
489 if (n_dims == (3 + temp_offset))
490 dim_idx_map[2 + temp_offset] = arg_dim3.getValue();
491
492 return true;
493}
494
495static std::pair<std::size_t, std::size_t> assignTimeBounds(
496 NcVar const& var,
497 TCLAP::ValueArg<std::size_t>& arg_time_start,
498 TCLAP::ValueArg<std::size_t>& arg_time_end)
499{
500 auto const bounds = getBoundaries(var);
501 if (arg_time_start.getValue() > bounds.second)
502 {
503 ERR("Start time step larger than total number of time steps. Resetting "
504 "to 0.");
505 arg_time_start.reset();
506 }
507
508 if (!arg_time_end.isSet())
509 return {arg_time_start.getValue(), arg_time_start.getValue()};
510
511 if (arg_time_end.getValue() > bounds.second)
512 {
513 ERR("End time step larger than total number of time steps. Resetting "
514 "to starting time step");
515 return {arg_time_start.getValue(), arg_time_start.getValue()};
516 }
517
518 if (arg_time_end.getValue() < arg_time_start.getValue())
519 {
520 ERR("End time step larger than starting time step. Swapping values");
521 return {arg_time_end.getValue(), arg_time_start.getValue()};
522 }
523
524 return {arg_time_start.getValue(), arg_time_end.getValue()};
525}
526
528 TCLAP::ValueArg<std::string>& arg_elem_type)
529{
530 if (arg_elem_type.getValue() == "tri")
532 if (arg_elem_type.getValue() == "quad")
534 if (arg_elem_type.getValue() == "prism")
536 if (arg_elem_type.getValue() == "hex")
538 // this should never happen
540}
541
542static bool convert(NcFile const& dataset, NcVar const& var,
543 std::string const& output_name,
544 std::vector<std::size_t> const& dim_idx_map,
545 std::size_t const time_offset,
546 std::pair<std::size_t, std::size_t> const& time_bounds,
547 OutputType const output,
548 MeshLib::MeshElemType const elem_type)
549{
550 std::unique_ptr<MeshLib::Mesh> mesh;
551 std::vector<std::size_t> const length = getLength(var, time_offset);
552 std::size_t const array_length = std::accumulate(
553 length.cbegin(), length.cend(), 1, std::multiplies<std::size_t>());
554 for (std::size_t i = time_bounds.first; i <= time_bounds.second; ++i)
555 {
556 std::string const step_str =
557 (time_bounds.first != time_bounds.second)
558 ? std::string(" time step " + std::to_string(i))
559 : "";
560 std::cout << "Converting" << step_str << "...\n";
561 std::vector<double> data_vec = getData(var, array_length, i, length);
562
563 // reverse lines in vertical direction if file has its origin in the
564 // northwest corner
565 std::size_t const n_dims = length.size();
566 NcVar const dim_var(getDimVar(dataset, var, n_dims - 2));
567 auto const bounds = (dim_var.isNull()) ? getDimLength(var, n_dims - 2)
568 : getBoundaries(dim_var);
569 if (bounds.first > bounds.second)
570 {
571 std::size_t n_layers =
572 (length.size() - time_offset == 3) ? length[n_dims - 3] : 1;
573 flipRaster(data_vec, n_layers, length[n_dims - 1],
574 length[n_dims - 2]);
575 }
576
577 GeoLib::RasterHeader const header =
578 createRasterHeader(dataset, var, dim_idx_map, length, time_offset);
579 MeshLib::UseIntensityAs const useIntensity =
581 if (output == OutputType::MULTIMESH)
582 {
583 mesh = MeshToolsLib::RasterToMesh::convert(data_vec.data(), header,
584 elem_type, useIntensity,
585 var.getName());
586 std::string const output_file_name(
587 BaseLib::dropFileExtension(output_name) +
588 getIterationString(i, time_bounds.second) + ".vtu");
589 MeshLib::IO::VtuInterface vtu(mesh.get());
590 vtu.writeToFile(output_file_name);
591 }
592 else if (output == OutputType::SINGLEMESH)
593 {
594 std::string array_name(var.getName());
595 if (time_bounds.first != time_bounds.second)
596 array_name.append(getIterationString(i, time_bounds.second));
597 if (i == time_bounds.first) // create persistent mesh
599 data_vec.data(), header, elem_type, useIntensity,
600 array_name);
601 else // copy array to mesh
602 {
603 std::unique_ptr<MeshLib::Mesh> const temp(
604 MeshToolsLib::RasterToMesh::convert(data_vec.data(), header,
605 elem_type, useIntensity,
606 array_name));
607 MeshLib::PropertyVector<double> const* const vec =
608 temp->getProperties().getPropertyVector<double>(array_name);
609 if (vec == nullptr)
610 {
611 return false;
612 }
614 *mesh, array_name, MeshLib::MeshItemType::Cell, 1, *vec);
615 }
616 if (i == time_bounds.second)
617 {
618 MeshLib::IO::VtuInterface vtu(mesh.get());
619 std::string const output_file_name =
620 (BaseLib::getFileExtension(output_name) == ".vtu")
621 ? output_name
622 : output_name + ".vtu";
623 vtu.writeToFile(output_file_name);
624 }
625 }
626 else // OutputType::IMAGES
627 {
628 GeoLib::Raster const raster(header, data_vec.data(),
629 data_vec.data() + header.n_cols *
630 header.n_rows *
631 header.n_depth);
632 std::string const output_file_name(
633 BaseLib::dropFileExtension(output_name) +
634 getIterationString(i, time_bounds.second) + ".asc");
636 output_file_name);
637 }
638 }
639 return true;
640}
641
642int main(int argc, char* argv[])
643{
644 TCLAP::CmdLine cmd(
645 "Converts NetCDF data into mesh file(s).\n\n "
646 "OpenGeoSys-6 software, version " +
648 ".\n"
649 "Copyright (c) 2012-2026, OpenGeoSys Community "
650 "(http://www.opengeosys.org)",
652
653 TCLAP::ValueArg<int> arg_nodata(
654 "n", "nodata",
655 "explicitly specifies the no data value used in the dataset (usually "
656 "it is not necessary to set this)",
657 false, no_data_input, "NODATA_VALUE");
658 cmd.add(arg_nodata);
659
660 std::vector<std::string> allowed_elems{"tri", "quad", "prism", "hex"};
661 TCLAP::ValuesConstraint<std::string> allowed_elem_vals(allowed_elems);
662 TCLAP::ValueArg<std::string> arg_elem_type(
663 "e", "elem-type", "the element type used in the resulting OGS mesh, ",
664 false, "", &allowed_elem_vals);
665 cmd.add(arg_elem_type);
666
667 TCLAP::SwitchArg arg_images(
668 "", "images",
669 "if set, all time steps will be written as ESRI image files (*.asc)");
670 cmd.add(arg_images);
671
672 TCLAP::SwitchArg arg_multi_files(
673 "", "multi-file",
674 "if set, each time step will be written to a separate mesh file");
675 cmd.add(arg_multi_files);
676
677 TCLAP::SwitchArg arg_single_file(
678 "", "single-file",
679 "if set, all time steps will be written to a single mesh file (with "
680 "one scalar array per time step)");
681 cmd.add(arg_single_file);
682
683 TCLAP::ValueArg<std::size_t> arg_time_end(
684 "", "timestep-last",
685 "last time step to be extracted (only for time-dependent variables!)",
686 false, 0, "TIME_END");
687 cmd.add(arg_time_end);
688
689 TCLAP::ValueArg<std::size_t> arg_time_start(
690 "", "timestep-first",
691 "first time step to be extracted (only for time-dependent variables!)",
692 false, 0, "TIMESTEP_FIRST");
693 cmd.add(arg_time_start);
694
695 std::vector<std::size_t> allowed_dims{0, 1, 2, 3};
696 TCLAP::ValuesConstraint<std::size_t> allowed_dim_vals(allowed_dims);
697 TCLAP::ValueArg<std::size_t> arg_dim3(
698 "", "dim3",
699 "index of third dimension (z/height/depth) for the selected variable",
700 false, 0, &allowed_dim_vals);
701 cmd.add(arg_dim3);
702
703 TCLAP::ValueArg<std::size_t> arg_dim2(
704 "", "dim2",
705 "index of second dimension (y/latitude) for the selected variable",
706 false, 0, &allowed_dim_vals);
707 cmd.add(arg_dim2);
708
709 TCLAP::ValueArg<std::size_t> arg_dim1(
710 "", "dim1",
711 "index of first dimension (x/longitude) for the selected variable",
712 false, 0, &allowed_dim_vals);
713 cmd.add(arg_dim1);
714
715 TCLAP::ValueArg<std::size_t> arg_dim_time(
716 "t", "time",
717 "index of the time-dependent dimension for the selected variable",
718 false, 0, &allowed_dim_vals);
719 cmd.add(arg_dim_time);
720
721 TCLAP::ValueArg<std::string> arg_varname(
722 "v", "var", "variable included in the the netCDF file", false, "",
723 "VAR_NAME");
724 cmd.add(arg_varname);
725
726 TCLAP::ValueArg<std::string> arg_output(
727 "o", "output", "Output (.vtu). The OGS mesh output file", true, "",
728 "OUTPUT_FILE");
729 cmd.add(arg_output);
730 TCLAP::ValueArg<std::string> arg_input("i", "input",
731 "Input (.nc). The netCDF input file",
732 true, "", "INPUT_FILE");
733 cmd.add(arg_input);
734 auto log_level_arg = BaseLib::makeLogLevelArg();
735 cmd.add(log_level_arg);
736 cmd.parse(argc, argv);
737
738 BaseLib::MPI::Setup mpi_setup(argc, argv);
739 BaseLib::initOGSLogger(log_level_arg.getValue());
740
741 NcFile dataset(arg_input.getValue().c_str(), NcFile::read);
742
743 if (dataset.isNull())
744 {
745 ERR("Error opening file.");
746 return -1;
747 }
748
749 std::size_t const mutex =
750 arg_single_file.isSet() + arg_multi_files.isSet() + arg_images.isSet();
751 if (mutex > 1)
752 {
753 ERR("Only one output format can be specified (single-file, multi-file, "
754 "or images)");
755 return EXIT_FAILURE;
756 }
757
758 std::cout << "OpenGeoSys NetCDF Converter\n";
759 if (!arg_varname.isSet())
760 {
761 std::cout << "File " << arg_input.getValue()
762 << " loaded. Press ENTER to display available data arrays.\n";
763 std::cin.ignore();
764 }
765
766 std::string const& output_name(arg_output.getValue());
767 std::string const& var_name = (arg_varname.isSet())
768 ? arg_varname.getValue()
769 : arraySelectionLoop(dataset);
770 NcVar const& var = dataset.getVar(var_name);
771 if (var.isNull())
772 {
773 ERR("Variable \"{:s}\" not found in file.", arg_varname.getValue());
774 return EXIT_FAILURE;
775 }
776
777 std::vector<std::size_t> dim_idx_map(var.getDimCount(), 0);
778 bool is_time_dep(false);
779 if (arg_dim1.isSet() && arg_dim2.isSet())
780 {
781 is_time_dep = arg_dim_time.isSet();
782 if (!assignDimParams(var, dim_idx_map, arg_dim_time, arg_dim1, arg_dim2,
783 arg_dim3))
784 {
785 return EXIT_FAILURE;
786 }
787 }
788 else
789 {
790 is_time_dep = dimensionSelectionLoop(var, dim_idx_map);
791 }
792
793 std::pair<std::size_t, std::size_t> time_bounds(0, 0);
794 if (is_time_dep)
795 time_bounds =
796 (arg_time_start.isSet())
797 ? assignTimeBounds(getDimVar(dataset, var, dim_idx_map[0]),
798 arg_time_start, arg_time_end)
799 : timestepSelectionLoop(var, dim_idx_map[0]);
800
802 if (arg_images.isSet())
803 {
804 output = OutputType::IMAGES;
805 }
806 else if (arg_multi_files.isSet())
807 {
808 output = OutputType::MULTIMESH;
809 }
810 else if (arg_single_file.isSet() || !is_time_dep ||
811 time_bounds.first == time_bounds.second)
812 {
813 output = OutputType::SINGLEMESH;
814 }
815 else
816 {
817 output = multFilesSelectionLoop(time_bounds);
818 }
819
820 std::size_t const temp_offset = (is_time_dep) ? 1 : 0;
821 std::size_t const n_dims = (var.getDimCount());
823
824 if (output != OutputType::IMAGES)
825 {
826 elem_type = (arg_elem_type.isSet())
827 ? assignElemType(arg_elem_type)
828 : elemSelectionLoop(n_dims - temp_offset);
829 if (elem_type == MeshLib::MeshElemType::INVALID)
830 elemSelectionLoop(n_dims - temp_offset);
831 }
832
833 if (arg_nodata.isSet())
834 {
835 no_data_input = arg_nodata.getValue();
836 }
837
838 if (!convert(dataset, var, output_name, dim_idx_map, is_time_dep,
839 time_bounds, output, elem_type))
840 {
841 return EXIT_FAILURE;
842 }
843
844 std::cout << "Conversion finished successfully.\n";
845 return EXIT_SUCCESS;
846}
#define OGS_FATAL(...)
Definition Error.h:19
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
static void showArrays(NcFile const &dataset)
static double no_data_input
static OutputType multFilesSelectionLoop(std::pair< std::size_t, std::size_t > const &time_bounds)
int main(int argc, char *argv[])
static std::string arraySelectionLoop(NcFile const &dataset)
static MathLib::Point3d getOrigin(NcFile const &dataset, NcVar const &var, std::vector< std::size_t > const &dim_idx_map, std::size_t const time_offset)
static GeoLib::RasterHeader createRasterHeader(NcFile const &dataset, NcVar const &var, std::vector< std::size_t > const &dim_idx_map, std::vector< std::size_t > const &length, std::size_t const time_offset)
static void showErrorMessage(std::size_t const error_id, std::size_t const max=0)
static std::pair< double, double > getDimLength(NcVar const &var, std::size_t const dim)
static std::pair< std::size_t, std::size_t > assignTimeBounds(NcVar const &var, TCLAP::ValueArg< std::size_t > &arg_time_start, TCLAP::ValueArg< std::size_t > &arg_time_end)
static std::pair< double, double > getBoundaries(NcVar const &var)
static std::size_t parseInput(std::string const &request_str, std::size_t const max_val, bool const has_info=false)
static std::string getIterationString(std::size_t i, std::size_t max)
static MeshLib::MeshElemType assignElemType(TCLAP::ValueArg< std::string > &arg_elem_type)
static MeshLib::MeshElemType elemSelectionLoop(std::size_t const dim)
static bool assignDimParams(NcVar const &var, std::vector< std::size_t > &dim_idx_map, TCLAP::ValueArg< std::size_t > &arg_dim_time, TCLAP::ValueArg< std::size_t > &arg_dim1, TCLAP::ValueArg< std::size_t > &arg_dim2, TCLAP::ValueArg< std::size_t > &arg_dim3)
static std::pair< std::size_t, std::size_t > timestepSelectionLoop(NcVar const &var, std::size_t const dim_idx)
static const double no_data_output
static void checkExit(std::string const &str)
static void flipRaster(std::vector< double > &data, std::size_t const layers, std::size_t const width, std::size_t const height)
static std::vector< std::size_t > getLength(NcVar const &var, std::size_t const time_offset)
static std::vector< std::string > getArrays(NcFile const &dataset)
static NcVar getDimVar(NcFile const &dataset, NcVar const &var, std::size_t const dim)
static bool dimensionSelectionLoop(NcVar const &var, std::vector< std::size_t > &dim_idx_map)
static double getResolution(NcFile const &dataset, NcVar const &var)
static std::vector< double > getData(NcVar const &var, std::size_t const total_length, std::size_t const time_step, std::vector< std::size_t > const &length)
static bool convert(NcFile const &dataset, NcVar const &var, std::string const &output_name, std::vector< std::size_t > const &dim_idx_map, std::size_t const time_offset, std::pair< std::size_t, std::size_t > const &time_bounds, OutputType const output, MeshLib::MeshElemType const elem_type)
static bool canConvert(NcVar const &var)
static void showArraysDims(NcVar const &var)
static void writeRasterAsASC(GeoLib::Raster const &raster, std::string const &file_name)
Writes an Esri asc-file.
Class Raster is used for managing raster data.
Definition Raster.h:39
Reads and writes VtkXMLUnstructuredGrid-files (vtu) to and from OGS data structures....
bool writeToFile(std::filesystem::path const &file_path)
static std::unique_ptr< MeshLib::Mesh > convert(GeoLib::Raster const &raster, MeshLib::MeshElemType elem_type, MeshLib::UseIntensityAs intensity_type, std::string const &array_name="Colour")
TCLAP::ValueArg< std::string > makeLogLevelArg()
void initOGSLogger(std::string const &log_level)
Definition Logging.cpp:56
std::string getFileExtension(const std::string &path)
std::string dropFileExtension(std::string const &filename)
GITINFOLIB_EXPORT const std::string ogs_version
const Point3d ORIGIN
Definition Point3d.h:89
void addPropertyToMesh(Mesh &mesh, std::string_view name, MeshItemType item_type, std::size_t number_of_components, std::span< T const > values)
UseIntensityAs
Selection of possible interpretations for intensities.
Definition MeshEnums.h:93
MeshElemType
Types of mesh elements supported by OpenGeoSys. Values are from VTKCellType enum.
Definition MeshEnums.h:37
Contains the relevant information when storing a geoscientific raster data.
Definition Raster.h:18
std::size_t n_depth
Definition Raster.h:21
std::size_t n_cols
Definition Raster.h:19
std::size_t n_rows
Definition Raster.h:20