// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. // // -------------------------------------------------------------------- // Get Values Miniapp: Extract field values via DataCollection classes // -------------------------------------------------------------------- // // This miniapp loads previously saved data using DataCollection sub-classes, // see e.g. load-dc miniapp and Example 5/5p, and output field values at a // set of points. Currently, only the VisItDataCollection class is supported. // // Compile with: make get-values // // Serial sample runs: // > get-values -r ../../examples/Example5 -p "0 0 0.1 0" -fn pressure // // Parallel sample runs: // > mpirun -np 4 get-values -r ../electromagnetics/Volta-AMR-Parallel // -c 1 -p "0 0 0 0.1 0.1 0.1" -fn ALL // // Point locations can be specified on the command line using -p or within a // data file whose name can be given with option -pf. The data file format is: // // number_of_points space_dimension // x_0 y_0 ... // x_1 y_1 ... // etc. // // By default all available fields are evaluated. The list of fields can be // reduced by specifying the desired field names with -fn. The -fn option // takes a space separated list of field names surrounded by quotes. Field // names containing spaces, such as "Field 1" and "Field 2", can be entered as: // get-values -fn "Field\ 1 Field\ 2" // // By default the data is written to standard out. This can be overwritten // with the -o [filename] option. // // The output format contains comments as well as sizing information to aid in // subsequent processing. The bulk of the data consists of one line per point // with a 0-based integer index followed by the point coordinates and then the // field data. A legend, appearing before the bulk data, shows the order of // the fields along with the number of values per field (for vector data). #include "mfem.hpp" #include #include #include using namespace std; using namespace mfem; void parseFieldNames(const char * field_name_c_str, set &field_names); void parsePoints(int spaceDim, const char *pts_file_c_str, Vector &pts); /// @cond Suppress_Doxygen_warnings void writeLegend(const DataCollection::FieldMapType &fields, const set & field_names, int spaceDim); /// @endcond int main(int argc, char *argv[]) { #ifdef MFEM_USE_MPI Mpi::Init(); if (!Mpi::Root()) { mfem::out.Disable(); mfem::err.Disable(); } Hypre::Init(); #endif // Parse command-line options. const char *coll_name = NULL; int cycle = 0; int pad_digits_cycle = 6; int pad_digits_rank = 6; const char *field_name_c_str = "ALL"; const char *pts_file_c_str = ""; Vector pts; const char *out_file_c_str = ""; OptionsParser args(argc, argv); args.AddOption(&coll_name, "-r", "--root-file", "Set the VisIt data collection root file prefix.", true); args.AddOption(&cycle, "-c", "--cycle", "Set the cycle index to read."); args.AddOption(&pad_digits_cycle, "-pdc", "--pad-digits-cycle", "Number of digits in cycle."); args.AddOption(&pad_digits_rank, "-pdr", "--pad-digits-rank", "Number of digits in MPI rank."); args.AddOption(&pts, "-p", "--points", "List of points."); args.AddOption(&field_name_c_str, "-fn", "--field-names", "List of field names to get values from."); args.AddOption(&pts_file_c_str, "-pf", "--point-file", "Filename containing a list of points."); args.AddOption(&out_file_c_str, "-o", "--output-file", "Output filename."); args.Parse(); if (!args.Good()) { args.PrintUsage(mfem::out); return 1; } args.PrintOptions(mfem::out); #ifdef MFEM_USE_MPI VisItDataCollection dc(MPI_COMM_WORLD, coll_name); #else VisItDataCollection dc(coll_name); #endif dc.SetPadDigitsCycle(pad_digits_cycle); dc.SetPadDigitsRank(pad_digits_rank); dc.Load(cycle); if (dc.Error() != DataCollection::No_Error) { mfem::out << "Error loading VisIt data collection: " << coll_name << endl; return 1; } int spaceDim = dc.GetMesh()->SpaceDimension(); mfem::out << endl; mfem::out << "Collection Name: " << dc.GetCollectionName() << endl; mfem::out << "Space Dimension: " << spaceDim << endl; mfem::out << "Cycle: " << dc.GetCycle() << endl; mfem::out << "Time: " << dc.GetTime() << endl; mfem::out << "Time Step: " << dc.GetTimeStep() << endl; mfem::out << endl; typedef DataCollection::FieldMapType fields_t; const fields_t &fields = dc.GetFieldMap(); // Print the names of all fields. mfem::out << "fields: [ "; for (fields_t::const_iterator it = fields.begin(); it != fields.end(); ++it) { if (it != fields.begin()) { mfem::out << ", "; } mfem::out << it->first; } mfem::out << " ]" << endl; // Parsing desired field names set field_names; parseFieldNames(field_name_c_str, field_names); // Print field names to be extracted mfem::out << "Extracting fields: "; for (set::iterator it=field_names.begin(); it!=field_names.end(); it++) { mfem::out << " \"" << *it << "\""; } mfem::out << endl; parsePoints(spaceDim, pts_file_c_str, pts); int npts = pts.Size() / spaceDim; DenseMatrix pt_mat(pts.GetData(), spaceDim, npts); Array elem_ids; Array ip; int nfound = dc.GetMesh()->FindPoints(pt_mat, elem_ids, ip); mfem::out << "Found " << nfound << " points." << endl; ofstream ofs; if (strcmp(out_file_c_str,"") != 0 #ifdef MFEM_USE_MPI && Mpi::Root() #endif ) { ofs.open(out_file_c_str); if (!ofs) { MFEM_ABORT("Failed to open output file: " << out_file_c_str << '\n'); } mfem::out.SetStream(ofs); } // Write legend showing the order of the fields and their sizes writeLegend(fields, field_names, spaceDim); mfem::out << "# Number of points\n" << nfound << endl; for (int e=0; e= 0) { mfem::out << e; for (int d=0; dfirst) != field_names.end()) { if (it->second->VectorDim() == 1) { mfem::out << ' ' << it->second->GetValue(elem_ids[e], ip[e]); } else { Vector val; it->second->GetVectorValue(elem_ids[e], ip[e], val); for (int d=0; dsecond->VectorDim(); d++) { mfem::out << ' ' << val[d]; } } } } mfem::out << endl; } } if (ofs) { ofs.close(); } return 0; } void parseFieldNames(const char * field_name_c_str, set &field_names) { string field_name_str(field_name_c_str); string field_name; for (string::iterator it=field_name_str.begin(); it!=field_name_str.end(); it++) { if (*it == '\\') { it++; field_name.push_back(*it); } else if (*it == ' ') { if (!field_name.empty()) { field_names.insert(field_name); } field_name.clear(); } else if (it == field_name_str.end() - 1) { field_name.push_back(*it); field_names.insert(field_name); } else { field_name.push_back(*it); } } if (field_names.size() == 0) { field_names.insert("ALL"); } } void parsePoints(int spaceDim, const char *pts_file_c_str, Vector &pts) { if (strcmp(pts_file_c_str,"") == 0) { return; } ifstream ifs(pts_file_c_str); if (!ifs) { MFEM_ABORT("Failed to open point file: " << pts_file_c_str << '\n'); } int o, n, dim; ifs >> n >> dim; MFEM_VERIFY(dim == spaceDim, "Mismatch in mesh's space dimension " "and point dimension."); if (pts.Size() > 0 && pts.Size() % spaceDim == 0) { int size = pts.Size() + n * dim; Vector tmp(size); for (int i=0; i> pts[o + i * dim + d]; } } ifs.close(); } void writeLegend(const DataCollection::FieldMapType &fields, const set & field_names, int spaceDim) { typedef DataCollection::FieldMapType fields_t; // Count the number of fields to be output int nfields = 1; for (fields_t::const_iterator it = fields.begin(); it != fields.end(); ++it) { if (field_names.find("ALL") != field_names.end() || field_names.find(it->first) != field_names.end()) { nfields++; } } // Write the legend showing each field name and its number of entries mfem::out << "# Number of fields" << endl << nfields << endl; mfem::out << "# Legend" << endl; mfem::out << "# \"Index\" \"Location\":" << spaceDim; for (fields_t::const_iterator it = fields.begin(); it != fields.end(); ++it) { if (field_names.find("ALL") != field_names.end() || field_names.find(it->first) != field_names.end()) { mfem::out << " \"" << it->first << "\":" << it->second->VectorDim(); } } mfem::out << endl; // Write the number of entries for each field without the names // which should be more convenient for parsing the output file. mfem::out << spaceDim; for (fields_t::const_iterator it = fields.begin(); it != fields.end(); ++it) { if (field_names.find("ALL") != field_names.end() || field_names.find(it->first) != field_names.end()) { mfem::out << " " << it->second->VectorDim(); } } mfem::out << endl; }