From 4b530ecb8e87d80b4e26705be411d29b2d245df9 Mon Sep 17 00:00:00 2001
From: Dmitri Naumov <dmitri.naumov@ufz.de>
Date: Tue, 30 Jan 2024 13:28:32 +0100
Subject: [PATCH] [BL] findElementOrError w/ callback

Using in MPL shows no difference in runtime, one assembly
instruction is different.
---
 BaseLib/Algorithm.h                           | 25 ++++++++++++
 ChemistryLib/PhreeqcIO.cpp                    | 39 ++++++++++++-------
 MaterialLib/MPL/Medium.cpp                    | 12 ++----
 MaterialLib/MPL/Phase.cpp                     | 12 ++----
 .../SolidModels/MFront/MFrontGeneric.h        |  6 +--
 5 files changed, 59 insertions(+), 35 deletions(-)

diff --git a/BaseLib/Algorithm.h b/BaseLib/Algorithm.h
index 774146c8ef9..07c86faa2d5 100644
--- a/BaseLib/Algorithm.h
+++ b/BaseLib/Algorithm.h
@@ -12,10 +12,14 @@
 
 #include <algorithm>
 #include <cassert>
+#include <concepts>
 #include <optional>
+#include <range/v3/algorithm/find_if.hpp>
+#include <range/v3/range/concepts.hpp>
 #include <string>
 #include <typeindex>
 #include <typeinfo>
+#include <utility>
 
 #include "Error.h"
 
@@ -65,6 +69,27 @@ void excludeObjectCopy(std::vector<T> const& src_vec,
     dest_vec = excludeObjectCopy(src_vec, exclude_positions);
 }
 
+/// Returns reference to an element in the range satisfying the predicate. If no
+/// such element is found, error_callback is called and reference to
+/// past-the-end of the range is returned.
+template <ranges::input_range Range>
+ranges::range_reference_t<Range> findElementOrError(
+    Range& range,
+    std::predicate<ranges::range_reference_t<Range>> auto&& predicate,
+    std::invocable auto error_callback)
+{
+    auto it =
+        ranges::find_if(range, std::forward<decltype(predicate)>(predicate));
+    if (it == ranges::end(range))
+    {
+        error_callback();
+        OGS_FATAL(
+            "Element not found in the input range. The user provided error "
+            "callback is meant not to return. That has not happened.");
+    }
+    return *it;
+}
+
 template <typename InputIt, typename Predicate>
 typename std::iterator_traits<InputIt>::reference findElementOrError(
     InputIt begin, InputIt end, Predicate predicate,
diff --git a/ChemistryLib/PhreeqcIO.cpp b/ChemistryLib/PhreeqcIO.cpp
index b4404552546..d109707cf2b 100644
--- a/ChemistryLib/PhreeqcIO.cpp
+++ b/ChemistryLib/PhreeqcIO.cpp
@@ -787,9 +787,12 @@ std::istream& operator>>(std::istream& in, PhreeqcIO& phreeqc_io)
                 case ItemType::Component:
                 {
                     // Update component concentrations
-                    auto& component = BaseLib::findElementOrError(
-                        components.begin(), components.end(), compare_by_name,
-                        "Could not find component '" + item_name + "'.");
+                    auto const& component = BaseLib::findElementOrError(
+                        components, compare_by_name,
+                        [&]() {
+                            OGS_FATAL("Could not find component '{:s}'.",
+                                      item_name);
+                        });
                     MathLib::LinAlg::setLocalAccessibleVector(
                         *component.amount);
                     component.amount->set(global_index,
@@ -801,10 +804,14 @@ std::istream& operator>>(std::istream& in, PhreeqcIO& phreeqc_io)
                     // Update amounts of equilibrium reactant
                     auto const& equilibrium_reactant =
                         BaseLib::findElementOrError(
-                            equilibrium_reactants.begin(),
-                            equilibrium_reactants.end(), compare_by_name,
-                            "Could not find equilibrium reactant '" +
-                                item_name + "'.");
+                            equilibrium_reactants, compare_by_name,
+                            [&]()
+                            {
+                                OGS_FATAL(
+                                    "Could not find equilibrium reactant "
+                                    "'{:s}'",
+                                    item_name);
+                            });
                     (*equilibrium_reactant.molality)[chemical_system_id] =
                         accepted_items[item_id];
                     break;
@@ -813,9 +820,11 @@ std::istream& operator>>(std::istream& in, PhreeqcIO& phreeqc_io)
                 {
                     // Update amounts of kinetic reactants
                     auto const& kinetic_reactant = BaseLib::findElementOrError(
-                        kinetic_reactants.begin(), kinetic_reactants.end(),
-                        compare_by_name,
-                        "Could not find kinetic reactant '" + item_name + "'.");
+                        kinetic_reactants, compare_by_name,
+                        [&]() {
+                            OGS_FATAL("Could not find kinetic reactant '{:s}'.",
+                                      item_name);
+                        });
                     (*kinetic_reactant.molality)[chemical_system_id] =
                         accepted_items[item_id];
                     break;
@@ -828,10 +837,12 @@ std::istream& operator>>(std::istream& in, PhreeqcIO& phreeqc_io)
                     // Update values of secondary variables
                     auto const& secondary_variable =
                         BaseLib::findElementOrError(
-                            secondary_variables.begin(),
-                            secondary_variables.end(), compare_by_name,
-                            "Could not find secondary variable '" + item_name +
-                                "'.");
+                            secondary_variables, compare_by_name,
+                            [&]() {
+                                OGS_FATAL(
+                                    "Could not find secondary variable '{:s}'.",
+                                    item_name);
+                            });
                     (*secondary_variable.value)[chemical_system_id] =
                         accepted_items[item_id];
                     break;
diff --git a/MaterialLib/MPL/Medium.cpp b/MaterialLib/MPL/Medium.cpp
index 8501f576acc..de36a444bd7 100644
--- a/MaterialLib/MPL/Medium.cpp
+++ b/MaterialLib/MPL/Medium.cpp
@@ -12,8 +12,6 @@
 
 #include "Medium.h"
 
-#include <range/v3/algorithm/find_if.hpp>
-
 #include "BaseLib/Algorithm.h"
 #include "BaseLib/Error.h"
 #include "Properties/Properties.h"
@@ -39,15 +37,11 @@ Phase const& Medium::phase(std::size_t const index) const
 
 Phase const& Medium::phase(std::string const& phase_name) const
 {
-    auto it = ranges::find_if(
+    return *BaseLib::findElementOrError(
         phases_,
         [&phase_name](std::unique_ptr<MaterialPropertyLib::Phase> const& phase)
-        { return phase->name == phase_name; });
-    if (it == phases_.end())
-    {
-        OGS_FATAL("Could not find phase name '{:s}.'", phase_name);
-    }
-    return **it;
+        { return phase->name == phase_name; },
+        [&]() { OGS_FATAL("Could not find phase named '{:s}.'", phase_name); });
 }
 
 bool Medium::hasPhase(std::string const& phase_name) const
diff --git a/MaterialLib/MPL/Phase.cpp b/MaterialLib/MPL/Phase.cpp
index e2e9e56b338..8a26de94c42 100644
--- a/MaterialLib/MPL/Phase.cpp
+++ b/MaterialLib/MPL/Phase.cpp
@@ -12,8 +12,6 @@
 
 #include "Phase.h"
 
-#include <range/v3/algorithm/find_if.hpp>
-
 #include "BaseLib/Algorithm.h"
 #include "BaseLib/Error.h"
 #include "Component.h"
@@ -44,16 +42,12 @@ bool Phase::hasComponent(std::size_t const& index) const
 
 Component const& Phase::component(std::string const& name) const
 {
-    auto it = ranges::find_if(
+    return *BaseLib::findElementOrError(
         components_,
         [&name](
             std::unique_ptr<MaterialPropertyLib::Component> const& component)
-        { return component->name == name; });
-    if (it == components_.end())
-    {
-        OGS_FATAL("Could not find component name '{:s}.'", name);
-    }
-    return **it;
+        { return component->name == name; },
+        [&]() { OGS_FATAL("Could not find component named '{:s}.'", name); });
 }
 
 Property const& Phase::property(PropertyType const& p) const
diff --git a/MaterialLib/SolidModels/MFront/MFrontGeneric.h b/MaterialLib/SolidModels/MFront/MFrontGeneric.h
index b0e049d2de6..57118e3a181 100644
--- a/MaterialLib/SolidModels/MFront/MFrontGeneric.h
+++ b/MaterialLib/SolidModels/MFront/MFrontGeneric.h
@@ -427,11 +427,11 @@ public:
         {
             // find corresponding internal variable
             auto const& iv = BaseLib::findElementOrError(
-                begin(ivs),
-                end(ivs),
+                ivs,
                 [name = name](InternalVariable const& iv)
                 { return iv.name == name; },
-                fmt::format("Internal variable `{:s}' not found.", name));
+                [name = name]()
+                { OGS_FATAL("Internal variable `{:s}' not found.", name); });
 
             // evaluate parameter
             std::vector<double> values = (*parameter)(t, x);
-- 
GitLab