From 1996b1c9cfd001cbe16659bd52d926b04eb0c094 Mon Sep 17 00:00:00 2001 From: Hang Qian Date: Wed, 29 Jul 2026 23:25:13 +0200 Subject: [PATCH] Add method to execute selected postprocessors --- include/aspect/postprocess/interface.h | 20 ++++ source/postprocess/interface.cc | 139 +++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/include/aspect/postprocess/interface.h b/include/aspect/postprocess/interface.h index d70dfdbcb76..50538f54686 100644 --- a/include/aspect/postprocess/interface.h +++ b/include/aspect/postprocess/interface.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -143,6 +144,25 @@ namespace aspect std::list> execute (TableHandler &statistics); + /** + * Execute the selected postprocessors and any active postprocessors they + * depend on. The selected postprocessors are identified by the names used + * in parameter files. The function keeps the dependency order established + * during parse_parameters(). + * + * @param requested_postprocessors The names of active postprocessors that + * should be executed. + * @param statistics The statistics object that is passed to each selected + * postprocessor. Selected postprocessors may add entries to this object, + * so callers that need the computed postprocessor state without changing + * the main statistics table should pass a temporary TableHandler. + * @return The concatenated text output returned by the selected + * postprocessors and their dependencies. + */ + std::list> + execute_selected_postprocessors(const std::set &requested_postprocessors, + TableHandler &statistics); + /** * Go through the list of all postprocessors that have been selected * in the input file (and are consequently currently active) and return diff --git a/source/postprocess/interface.cc b/source/postprocess/interface.cc index b4aac34edbd..b7aa35e2c23 100644 --- a/source/postprocess/interface.cc +++ b/source/postprocess/interface.cc @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include @@ -113,6 +116,142 @@ namespace aspect } + + template + std::list> + Manager::execute_selected_postprocessors(const std::set &requested_postprocessors, + TableHandler &statistics) + { + // Create a working set initialized with the explicitly requested + // postprocessors. Dependencies can then be added to this set, while + // std::set prevents the same postprocessor from being added more than once. + std::set postprocessors_to_execute = + requested_postprocessors; + + // Keep a separate work list of postprocessors whose dependencies have + // not yet been inspected. Newly discovered dependencies are appended to + // this list so that dependencies of dependencies are included as well. + std::list postprocessors_to_check( + requested_postprocessors.begin(), + requested_postprocessors.end()); + + // Return the plugin object associated with an active postprocessor name. + // + // plugin_names and plugin_objects are parallel containers, so the + // position of a name in plugin_names identifies the corresponding entry + // in plugin_objects. + const auto find_active_postprocessor = + [this](const std::string &postprocessor_name) + { + const auto name_position = + std::find(this->plugin_names.begin(), + this->plugin_names.end(), + postprocessor_name); + + AssertThrow(name_position != this->plugin_names.end(), + ExcMessage("The selected postprocessor <" + postprocessor_name + + "> is not active.")); + + return std::next(this->plugin_objects.begin(), + std::distance(this->plugin_names.begin(), + name_position)); + }; + + // Traverse the dependency graph starting from the requested + // postprocessors. Each postprocessor is added to postprocessors_to_check + // only when it is first discovered, so its dependencies are inspected at + // most once. + while (postprocessors_to_check.empty() == false) + { + const std::string postprocessor_name = postprocessors_to_check.front(); + postprocessors_to_check.pop_front(); + + const auto plugin = find_active_postprocessor(postprocessor_name); + + for (const std::string &dependency : (*plugin)->required_other_postprocessors()) + { + // Only new dependencies need to be inspected further. + if (postprocessors_to_execute.insert(dependency).second) + postprocessors_to_check.push_back(dependency); + } + } + + // Execute the selected postprocessors in the order established during + // parse_parameters(). That ordering already places dependencies before + // the postprocessors that require them. + std::list> output_list; + auto plugin = this->plugin_objects.begin(); + for (unsigned int i = 0; + i < this->plugin_names.size(); + ++i, ++plugin) + { + if (postprocessors_to_execute.find(this->plugin_names[i]) + == postprocessors_to_execute.end()) + continue; + + const auto &p = *plugin; + + try + { + // first call the update() function. + p->update(); + + // call the execute() function. if it produces any output + // then add it to the list + std::pair output + = p->execute (statistics); + + if (output.first.size() + output.second.size() > 0) + output_list.push_back (output); + } + // postprocessors that throw exceptions usually do not result in + // anything good because they result in an unwinding of the stack + // and, if only one processor triggers an exception, the + // destruction of objects often causes a deadlock. thus, if + // an exception is generated, catch it, print an error message, + // and abort the program + catch (std::exception &exc) + { + std::cerr << std::endl << std::endl + << "----------------------------------------------------" + << std::endl; + std::cerr << "Exception on MPI process <" + << Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + << "> while running postprocessor <" + << typeid(*p).name() + << ">: " << std::endl + << exc.what() << std::endl + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; + + // terminate the program! + MPI_Abort (MPI_COMM_WORLD, 1); + } + catch (...) + { + std::cerr << std::endl << std::endl + << "----------------------------------------------------" + << std::endl; + std::cerr << "Exception on MPI process <" + << Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + << "> while running postprocessor <" + << typeid(*p).name() + << ">: " << std::endl; + std::cerr << "Unknown exception!" << std::endl + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; + + // terminate the program! + MPI_Abort (MPI_COMM_WORLD, 1); + } + } + + return output_list; + } + + // -------------------------------- Deal with registering postprocessors and automating // -------------------------------- their setup and selection at run time