From d3f5b6aa27bc3d60118a3cc9748d12f608eca76d Mon Sep 17 00:00:00 2001 From: Ben Ryan Date: Tue, 9 Apr 2024 14:42:42 -0600 Subject: [PATCH] Add features to history output (#1024) * first pass * Appears to work * reduction example? * CHANGELOG/copyright * Remove printf * breaking changelog * Fix iterator, unify output file block name * lint * Update history output testing * Update docs, guard fence appropriately. * oops bad conflict resolution * restrict is a keyword in C * Test for history column labels as well * formatting * Remove an ignored TODO * Fix ascent update modification (h/t pgrete) * Provide error message if ascent output not found * trigger pipeline --- .github/workflows/ci-extended.yml | 7 +- CHANGELOG.md | 2 + doc/sphinx/src/interface/state.rst | 16 +- doc/sphinx/src/outputs.rst | 12 +- example/advection/advection_package.cpp | 54 ++++++- example/advection/advection_package.hpp | 5 +- example/advection/parthinput.advection | 7 +- src/outputs/history.cpp | 152 ++++++++++++------ src/outputs/outputs.cpp | 14 +- src/outputs/outputs.hpp | 13 ++ .../test_suites/output_hdf5/output_hdf5.py | 30 +++- 11 files changed, 239 insertions(+), 73 deletions(-) diff --git a/.github/workflows/ci-extended.yml b/.github/workflows/ci-extended.yml index 44b2ad9d8b48..268abcabb0b4 100644 --- a/.github/workflows/ci-extended.yml +++ b/.github/workflows/ci-extended.yml @@ -85,10 +85,13 @@ jobs: export CUDA_VISIBLE_DEVICES=$(nvidia-smi --query-gpu=memory.free,index --format=csv,nounits,noheader | sort -nr | head -1 | awk '{ print $NF }') mpirun -np 2 ../../build-ascent/example/advection/advection-example \ -i parthinput.advection \ - parthenon/output4/dt=0.05 \ + parthenon/output5/dt=0.05 \ parthenon/time/tlim=0.1 # check if file exists - test -f ascent_render_57.png + if [ ! -f "ascent_render_57.png" ]; then + echo "'ascent_render_57.png' does not exist." + exit 1 + fi - uses: actions/upload-artifact@v3 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 84396f6aeb79..550505c1f7f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,11 @@ ### Changed (changing behavior/API/variables/...) +- [[PR 1024]](https://github.com/parthenon-hpc-lab/parthenon/pull/1024) Add .outN. to history output filenames ### Fixed (not changing behavior/API/variables/...) +- [[PR 1024]](https://github.com/parthenon-hpc-lab/parthenon/pull/1024) Add features to history output - [[PR 1031]](https://github.com/parthenon-hpc-lab/parthenon/pull/1031) Fix bug in non-cell centered AMR ### Infrastructure (changes irrelevant to downstream codes) diff --git a/doc/sphinx/src/interface/state.rst b/doc/sphinx/src/interface/state.rst index 6a4cfaf840e1..6f8c4f5052e8 100644 --- a/doc/sphinx/src/interface/state.rst +++ b/doc/sphinx/src/interface/state.rst @@ -159,7 +159,7 @@ functions that are all called at the interval according to the input parameters, see :ref:`output documention `. To enroll functions create a list of callback function with the -appropriate reduction operation: +appropriate reduction operations for either scalar data: .. code:: cpp @@ -172,6 +172,19 @@ appropriate reduction operation: // add callbacks for HST output identified by the `hist_param_key` pkg->AddParam<>(parthenon::hist_param_key, hst_vars); +or vector data: + +.. code:: cpp + + // List (vector) of HistoryOutputVar that will all be enrolled as output variables + parthenon::HstVec_list hst_vecs = {}; + + // Add a callback function + hst_vecs.emplace_back(parthenon::HistoryOutputVec(UserHistoryOperation::sum, MyHstVecFunction, "my vector label")); + + // add callbacks for HST output identified by the `hist_vec_param_key` + pkg->AddParam<>(parthenon::hist_vec_param_key, hst_vecs); + Here, ``HistoryOutputVar`` is a ``struct`` containing the global (over all blocks of all ranks) reduction operation, ``MyHstFunction`` is a callback function (see below), and ``"my label"`` is the string to be @@ -194,6 +207,7 @@ Callback functions need to have the following signature .. code:: cpp Real MyHstFunction(MeshData *md); + std::vector MyHstVecFunction(MeshData *md); i.e., they will always work on ``MeshData``. *Note*, currently history output will always be calculated for the “base” container. More diff --git a/doc/sphinx/src/outputs.rst b/doc/sphinx/src/outputs.rst index 2c274f04c078..4a8bce33244a 100644 --- a/doc/sphinx/src/outputs.rst +++ b/doc/sphinx/src/outputs.rst @@ -149,7 +149,10 @@ History Files In the input file, include a ```` block and specify ``file_type = hst``. A ``dt`` parameter controls the frequency of -outputs for simulations involving evolution. A ```` +outputs for simulations involving evolution. The default behavior is to provide +all enrolled history outputs, but output can be limited to a specific set of +packages with an optional comma-separated list argument +``packages = package_a, package_b``. A ```` block might look like :: @@ -157,10 +160,11 @@ block might look like file_type = hst dt = 1.0 + packages = advection_app This will produce a text file (``.hst``) output file every 1 units of simulation time. The content of the file is determined by the functions -enrolled by a specific package, see :ref:`state history output`. +enrolled by specific packages, see :ref:`state history output`. Histograms ---------- @@ -171,7 +175,7 @@ Currently supported are - 1D and 2D histograms (see examples below) - binning by variable or coordinate (x1, x2, x3 and radial distance) -- counting samples and or summing a variable +- counting samples and or summing a variable - weighting by volume and/or variable The output format follows ``numpy`` convention, so that plotting data @@ -322,7 +326,7 @@ The following is a minimal example to plot a 1D and 2D histogram from the output y = infile["other_name/y_edges"][:] z = infile["other_name/data"][:].T # note the transpose here (so that the data matches the axis for the pcolormesh) plt.pcolormesh(x,y,z,) - plt.show() + plt.show() Ascent (optional) ----------------- diff --git a/example/advection/advection_package.cpp b/example/advection/advection_package.cpp index 3008f457d8c9..f799a2909262 100644 --- a/example/advection/advection_package.cpp +++ b/example/advection/advection_package.cpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2023. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -213,8 +213,15 @@ std::shared_ptr Initialize(ParameterInput *pin) { UserHistoryOperation::min, AdvectionHst>, "min_advected")); + // Enroll example vector history output + parthenon::HstVec_list hst_vecs = {}; + hst_vecs.emplace_back(parthenon::HistoryOutputVec( + UserHistoryOperation::sum, AdvectionVecHst>, + "advected_powers")); + // add callbacks for HST output identified by the `hist_param_key` pkg->AddParam<>(parthenon::hist_param_key, hst_vars); + pkg->AddParam<>(parthenon::hist_vec_param_key, hst_vecs); if (fill_derived) { pkg->FillDerivedBlock = SquareIt; @@ -375,6 +382,45 @@ void PostFill(MeshBlockData *rc) { } } +template +std::vector AdvectionVecHst(MeshData *md) { + auto pmb = md->GetBlockData(0)->GetBlockPointer(); + + const auto ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); + const auto jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); + const auto kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); + + // Packing variable over MeshBlock as the function is called for MeshData, i.e., a + // collection of blocks + const auto &advected_pack = md->PackVariables(std::vector{"advected"}); + + const int nvec = 3; + + std::vector result(nvec, 0); + + // We choose to apply volume weighting when using the sum reduction. + // Downstream this choice will be done on a variable by variable basis and volume + // weighting needs to be applied in the reduction region. + const bool volume_weighting = std::is_same>::value; + + for (int n = 0; n < nvec; n++) { + T reducer(result[n]); + parthenon::par_reduce( + parthenon::loop_pattern_mdrange_tag, PARTHENON_AUTO_LABEL, DevExecSpace(), 0, + advected_pack.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lresult) { + const auto &coords = advected_pack.GetCoords(b); + // `join` is a function of the Kokkos::ReducerConecpt that allows to use the + // same call for different reductions + const Real vol = volume_weighting ? coords.CellVolume(k, j, i) : 1.0; + reducer.join(lresult, pow(advected_pack(b, 0, k, j, i), n + 1) * vol); + }, + reducer); + } + + return result; +} + // Example of how to enroll a history function. // Templating is *NOT* required and just implemented here to reuse this function // for testing of the UserHistoryOperations curently available in Parthenon (Sum, Min, @@ -400,9 +446,9 @@ Real AdvectionHst(MeshData *md) { // weighting needs to be applied in the reduction region. const bool volume_weighting = std::is_same>::value; - pmb->par_reduce( - PARTHENON_AUTO_LABEL, 0, advected_pack.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, - ib.e, + parthenon::par_reduce( + parthenon::loop_pattern_mdrange_tag, PARTHENON_AUTO_LABEL, DevExecSpace(), 0, + advected_pack.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lresult) { const auto &coords = advected_pack.GetCoords(b); // `join` is a function of the Kokkos::ReducerConecpt that allows to use the same diff --git a/example/advection/advection_package.hpp b/example/advection/advection_package.hpp index 85d1d6371e5a..92e312158387 100644 --- a/example/advection/advection_package.hpp +++ b/example/advection/advection_package.hpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2023. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -14,6 +14,7 @@ #define EXAMPLE_ADVECTION_ADVECTION_PACKAGE_HPP_ #include +#include #include @@ -30,6 +31,8 @@ Real EstimateTimestepBlock(MeshBlockData *rc); TaskStatus CalculateFluxes(std::shared_ptr> &rc); template Real AdvectionHst(MeshData *md); +template +std::vector AdvectionVecHst(MeshData *md); } // namespace advection_package #endif // EXAMPLE_ADVECTION_ADVECTION_PACKAGE_HPP_ diff --git a/example/advection/parthinput.advection b/example/advection/parthinput.advection index bdc6def403be..48098d33109d 100644 --- a/example/advection/parthinput.advection +++ b/example/advection/parthinput.advection @@ -3,7 +3,7 @@ # Copyright(C) 2014 James M. Stone and other code contributors # Licensed under the 3-clause BSD License, see LICENSE file for details # ======================================================================================== -# (C) (or copyright) 2020-2021. Triad National Security, LLC. All rights reserved. +# (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. # # This program was produced under U.S. Government contract 89233218CNA000001 for Los # Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -83,6 +83,11 @@ file_type = hst dt = 0.05 +file_type = hst +dt = 0.1 +packages = advection_app + + file_type = ascent dt = -0.05 # soft disabled by default, as Ascent is an optional dependency actions_file = custom_ascent_actions.yaml diff --git a/src/outputs/history.cpp b/src/outputs/history.cpp index 3f6117f6adf2..eab7668d298d 100644 --- a/src/outputs/history.cpp +++ b/src/outputs/history.cpp @@ -3,7 +3,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -49,12 +49,39 @@ void HistoryOutput::WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm, if (signal == SignalHandler::OutputSignal::now) { return; } - std::vector all_labels = {}; - std::vector all_results = {}; + + std::map> results; + std::map> labels; + std::vector ops = { + UserHistoryOperation::sum, UserHistoryOperation::max, UserHistoryOperation::min}; + for (auto &op : ops) { + results[op] = {}; + labels[op] = {}; + } + + // If "packages" not provided, output history for all packages + auto &requested_packages = output_params.packages; + Dictionary> packages; + if (requested_packages.empty()) { + packages = pm->packages.AllPackages(); + } else { + const auto &all_packages = pm->packages.AllPackages(); + for (const auto &pkg_name : requested_packages) { + const auto &it = all_packages.find(pkg_name); + if (it != all_packages.end()) { + packages[(*it).first] = (*it).second; + } else { + std::stringstream msg; + msg << "History output for package \"" << pkg_name + << "\" requested but package is not available!" << std::endl; + PARTHENON_FAIL(msg); + } + } + } // Loop over all packages of the application - for (const auto &pkg : pm->packages.AllPackages()) { - // Check if the package has enrolled functions which are stored in the + for (const auto &pkg : packages) { + // Check if the package has enrolled scalar history functions which are stored in the // Params under the `hist_param_key` name. const auto ¶ms = pkg.second->AllParams(); if (!params.hasKey(hist_param_key)) { @@ -62,55 +89,78 @@ void HistoryOutput::WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm, } const auto &hist_vars = params.Get(hist_param_key); + // Get "base" MeshData, which always exists but may not be populated yet + auto &md_base = pm->mesh_data.Get(); + // Populated with all blocks + if (md_base->NumBlocks() == 0) { + md_base->Set(pm->block_list, pm); + } else if (md_base->NumBlocks() != pm->block_list.size()) { + PARTHENON_WARN( + "Resetting \"base\" MeshData to contain all blocks. This indicates that " + "the \"base\" MeshData container has been modified elsewhere. Double check " + "that the modification was intentional and is compatible with this reset.") + md_base->Set(pm->block_list, pm); + } + for (const auto &hist_var : hist_vars) { - // Get "base" MeshData, which always exists but may not be populated yet - auto &md_base = pm->mesh_data.Get(); - // Populated with all blocks - if (md_base->NumBlocks() == 0) { - md_base->Set(pm->block_list, pm); - } else if (md_base->NumBlocks() != pm->block_list.size()) { - PARTHENON_WARN( - "Resetting \"base\" MeshData to contain all blocks. This indicates that " - "the \"base\" MeshData container has been modified elsewhere. Double check " - "that the modification was intentional and is compatible with this reset.") - md_base->Set(pm->block_list, pm); - } auto result = hist_var.hst_fun(md_base.get()); -#ifdef MPI_PARALLEL - // need fence so that the result is ready prior to the MPI call - Kokkos::fence(); - // apply separate chosen operations to each user-defined history output - MPI_Op usr_op; - switch (hist_var.hst_op) { - case UserHistoryOperation::sum: - usr_op = MPI_SUM; - break; - case UserHistoryOperation::max: - usr_op = MPI_MAX; - break; - case UserHistoryOperation::min: - usr_op = MPI_MIN; - break; - } - if (Globals::my_rank == 0) { - PARTHENON_MPI_CHECK(MPI_Reduce(MPI_IN_PLACE, &result, 1, MPI_PARTHENON_REAL, - usr_op, 0, MPI_COMM_WORLD)); - } else { - PARTHENON_MPI_CHECK(MPI_Reduce(&result, &result, 1, MPI_PARTHENON_REAL, usr_op, 0, - MPI_COMM_WORLD)); + results[hist_var.hst_op].push_back(result); + labels[hist_var.hst_op].push_back(hist_var.label); + } + + // Check if the package has enrolled vector history functions which are stored in the + // Params under the `hist_vec_param_key` name. + if (!params.hasKey(hist_vec_param_key)) { + continue; + } + const auto &hist_vecs = params.Get(hist_vec_param_key); + for (const auto &hist_vec : hist_vecs) { + auto result = hist_vec.hst_vec_fun(md_base.get()); + for (int n = 0; n < result.size(); n++) { + std::string label = hist_vec.label + "_" + std::to_string(n); + results[hist_vec.hst_op].push_back(result[n]); + labels[hist_vec.hst_op].push_back(label); } -#endif + } + } + +#ifdef MPI_PARALLEL + // Need fence so result is ready prior to MPI call + Kokkos::fence(); - all_results.emplace_back(result); - all_labels.emplace_back(hist_var.label); + for (auto &op : ops) { + if (results[op].size() == 0) { + continue; + } + + MPI_Op usr_op; + if (op == UserHistoryOperation::sum) { + usr_op = MPI_SUM; + } else if (op == UserHistoryOperation::max) { + usr_op = MPI_MAX; + } else if (op == UserHistoryOperation::min) { + usr_op = MPI_MIN; + } + + if (Globals::my_rank == 0) { + PARTHENON_MPI_CHECK(MPI_Reduce(MPI_IN_PLACE, &(results[op])[0], results[op].size(), + MPI_PARTHENON_REAL, usr_op, 0, MPI_COMM_WORLD)); + } else { + PARTHENON_MPI_CHECK(MPI_Reduce(&(results[op])[0], &(results[op])[0], + results[op].size(), MPI_PARTHENON_REAL, usr_op, 0, + MPI_COMM_WORLD)); } } +#endif // MPI_PARALLEL - // only the master rank writes the file - // create filename: "file_basename" + ".hst". There is no file number. + // Only the master rank writes the file. + // Create filename: "file_basename.out" + [output number] + ".hst". There is no file + // number. if (Globals::my_rank == 0) { std::string fname; fname.assign(output_params.file_basename); + fname.append("."); + fname.append(output_params.file_id); fname.append(".hst"); // open file for output @@ -124,14 +174,14 @@ void HistoryOutput::WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm, // If this is the first output, write header if (output_params.file_number == 0) { - // NEW_OUTPUT_TYPES: - int iout = 1; std::fprintf(pfile, "# History data\n"); // descriptor is first line std::fprintf(pfile, "# [%d]=time ", iout++); std::fprintf(pfile, "[%d]=dt ", iout++); - for (auto &lbl : all_labels) { - std::fprintf(pfile, "[%d]=%-8s", iout++, lbl.c_str()); + for (auto &op : ops) { + for (auto &label : labels[op]) { + std::fprintf(pfile, "[%d]=%-8s ", iout++, label.c_str()); + } } std::fprintf(pfile, "\n"); // terminate line } @@ -139,8 +189,10 @@ void HistoryOutput::WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm, // write history variables std::fprintf(pfile, output_params.data_format.c_str(), tm->time); std::fprintf(pfile, output_params.data_format.c_str(), tm->dt); - for (auto &val : all_results) { - std::fprintf(pfile, output_params.data_format.c_str(), val); + for (auto &op : ops) { + for (auto &result : results[op]) { + std::fprintf(pfile, output_params.data_format.c_str(), result); + } } std::fprintf(pfile, "\n"); // terminate line std::fclose(pfile); diff --git a/src/outputs/outputs.cpp b/src/outputs/outputs.cpp index 1c6c967c9c0c..8054ae8a8bbc 100644 --- a/src/outputs/outputs.cpp +++ b/src/outputs/outputs.cpp @@ -7,7 +7,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020-2023. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -196,6 +196,11 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { } } + if (op.file_type == "hst") { + op.packages = pin->GetOrAddVector(pib->block_name, "packages", + std::vector()); + } + // set output variable and optional data format string used in formatted writes if ((op.file_type != "hst") && (op.file_type != "rst") && (op.file_type != "ascent") && (op.file_type != "histogram")) { @@ -291,11 +296,10 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { pib = pib->pnext; // move to next input block name } - // check there were no more than one history or restart files requested - if (num_hst_outputs > 1 || num_rst_outputs > 1) { + // check there were no more than one restart file requested + if (num_rst_outputs > 1) { msg << "### FATAL ERROR in Outputs constructor" << std::endl - << "More than one history or restart output block detected in input file" - << std::endl; + << "More than one restart output block detected in input file" << std::endl; PARTHENON_FAIL(msg); } diff --git a/src/outputs/outputs.hpp b/src/outputs/outputs.hpp index ff268926d7e1..98dbb486695e 100644 --- a/src/outputs/outputs.hpp +++ b/src/outputs/outputs.hpp @@ -58,6 +58,7 @@ struct OutputParameters { std::vector swarm_vars; std::string file_type; std::string data_format; + std::vector packages; Real next_time, dt; int file_number; bool include_ghost_zones, cartesian_vector; @@ -136,6 +137,7 @@ class OutputType { // Function signature for currently supported user output functions using HstFun_t = std::function *md)>; +using HstVecFun_t = std::function(MeshData *md)>; // Container struct HistoryOutputVar { @@ -147,9 +149,20 @@ struct HistoryOutputVar { : hst_op(hst_op_), hst_fun(hst_fun_), label(label_) {} }; +struct HistoryOutputVec { + UserHistoryOperation hst_op; + HstVecFun_t hst_vec_fun; + std::string label; + HistoryOutputVec(const UserHistoryOperation &hst_op_, const HstVecFun_t &hst_vec_fun_, + const std::string &label_) + : hst_op(hst_op_), hst_vec_fun(hst_vec_fun_), label(label_) {} +}; + using HstVar_list = std::vector; +using HstVec_list = std::vector; // Hardcoded global entry to be used by each package to enroll user output functions const char hist_param_key[] = "HistoryFunctions"; +const char hist_vec_param_key[] = "HistoryVectorFunctions"; //---------------------------------------------------------------------------------------- //! \class HistoryFile diff --git a/tst/regression/test_suites/output_hdf5/output_hdf5.py b/tst/regression/test_suites/output_hdf5/output_hdf5.py index efe7eca5b6ba..da8d2695c465 100644 --- a/tst/regression/test_suites/output_hdf5/output_hdf5.py +++ b/tst/regression/test_suites/output_hdf5/output_hdf5.py @@ -119,19 +119,39 @@ def Analyse(self, parameters): if ret_2d != 0 or ret_3d != 0: analyze_status = False - hst_2d = np.genfromtxt("advection_2d.hst") - hst_3d = np.genfromtxt("advection_3d.hst") + hst_2d = np.genfromtxt("advection_2d.out1.hst") + hst_3d = np.genfromtxt("advection_3d.out1.hst") ref_results = [ ["time", 1.0, 1.0], ["dt", 1.75781e-03, 3.12500e-03], - ["total", 7.06177e-02, 1.39160e-02], - ["max", 9.43685e-01, 4.80914e-01], + ["total_advected", 7.06177e-02, 1.39160e-02], + ["advected_powers_0", 7.06177e-02, 1.39160e-02], + ["advected_powers_1", 3.88112e-02, 2.59597e-03], + ["advected_powers_2", 2.65948e-02, 7.19427e-04], + ["max_advected", 9.43685e-01, 4.80914e-01], [ - "min", + "min_advected", 1.69755e-10, 1.45889e-07, ], ] + # check header labels + for fname in ["advection_2d.out1.hst", "advection_3d.out1.hst"]: + with open(fname, "r") as f: + f.readline() + header = f.readline()[1:].split() + for i, val in enumerate(ref_results): + col_label = header[i].strip()[4:] + if col_label != val[0]: + print( + "Wrong", + val[0], + "label in hst output of", + fname, + ":", + col_label, + ) + analyze_status = False # check results in last row (at the final time of the sim) for i, val in enumerate(ref_results): if hst_2d[-1:, i] != val[1]: