-
Notifications
You must be signed in to change notification settings - Fork 109
WIP: Lazy stencil expressions for kernel fusion #3401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
c9955c3
acb30ab
4fd34af
1238f4b
19502b4
6c15db4
38fb3c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,17 +65,9 @@ int main(int argc, char** argv) { | |
| // Nested loops over block data | ||
| ITERATOR_TEST_BLOCK("DDZ Default", result = DDZ(a);); | ||
|
|
||
| ITERATOR_TEST_BLOCK("DDZ C2", result = DDZ(a, CELL_DEFAULT, "DIFF_C2");); | ||
| ITERATOR_TEST_BLOCK("DDZ C2", result = DDZ(a, CELL_DEFAULT, DIFF_C2);); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "DIFF_C2" is directly included [misc-include-cleaner] ITERATOR_TEST_BLOCK("DDZ C2", result = DDZ(a, CELL_DEFAULT, DIFF_C2););
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: variable 'start' of type 'SteadyClock' (aka 'time_pointstd::chrono::steady_clock') can be declared 'const' [misc-const-correctness] ITERATOR_TEST_BLOCK("DDZ C2", result = DDZ(a, CELL_DEFAULT, DIFF_C2););
^Additional contextexamples/performance/ddz/ddz.cxx:27: expanded from macro 'ITERATOR_TEST_BLOCK' SteadyClock start = steady_clock::now(); \
^ |
||
|
|
||
| ITERATOR_TEST_BLOCK("DDZ C4", result = DDZ(a, CELL_DEFAULT, "DIFF_C4");); | ||
|
|
||
| ITERATOR_TEST_BLOCK("DDZ S2", result = DDZ(a, CELL_DEFAULT, "DIFF_S2");); | ||
|
|
||
| ITERATOR_TEST_BLOCK("DDZ W2", result = DDZ(a, CELL_DEFAULT, "DIFF_W2");); | ||
|
|
||
| ITERATOR_TEST_BLOCK("DDZ W3", result = DDZ(a, CELL_DEFAULT, "DIFF_W3");); | ||
|
|
||
| ITERATOR_TEST_BLOCK("DDZ FFT", result = DDZ(a, CELL_DEFAULT, "DIFF_FFT");); | ||
| ITERATOR_TEST_BLOCK("DDZ C4", result = DDZ(a, CELL_DEFAULT, DIFF_C4);); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "DIFF_C4" is directly included [misc-include-cleaner] ITERATOR_TEST_BLOCK("DDZ C4", result = DDZ(a, CELL_DEFAULT, DIFF_C4););
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: variable 'start' of type 'SteadyClock' (aka 'time_pointstd::chrono::steady_clock') can be declared 'const' [misc-const-correctness] ITERATOR_TEST_BLOCK("DDZ C4", result = DDZ(a, CELL_DEFAULT, DIFF_C4););
^Additional contextexamples/performance/ddz/ddz.cxx:27: expanded from macro 'ITERATOR_TEST_BLOCK' SteadyClock start = steady_clock::now(); \
^ |
||
|
|
||
| if (profileMode) { | ||
| int nthreads = 0; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -31,6 +31,7 @@ | |||
|
|
||||
| #include "bout/field2d.hxx" | ||||
| #include "bout/field3d.hxx" | ||||
| #include "bout/stencil_expr.hxx" | ||||
| #include "bout/vector2d.hxx" | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: included header stencil_expr.hxx is not used directly [misc-include-cleaner]
Suggested change
|
||||
| #include "bout/vector3d.hxx" | ||||
|
|
||||
|
|
@@ -102,22 +103,6 @@ Coordinates::FieldMetric DDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, | |||
| const std::string& method = "DEFAULT", | ||||
| const std::string& region = "RGN_NOBNDRY"); | ||||
|
|
||||
| /// Calculate first partial derivative in Z | ||||
| /// | ||||
| /// \f$\partial / \partial z\f$ | ||||
| /// | ||||
| /// @param[in] f The field to be differentiated | ||||
| /// @param[in] outloc The cell location where the result is desired. If | ||||
| /// staggered grids is not enabled then this has no effect | ||||
| /// If not given, defaults to CELL_DEFAULT | ||||
| /// @param[in] method Differencing method to use. This overrides the default | ||||
| /// If not given, defaults to DIFF_DEFAULT | ||||
| /// @param[in] region What region is expected to be calculated | ||||
| /// If not given, defaults to RGN_NOBNDRY | ||||
| Field3D DDZ(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, | ||||
| const std::string& method = "DEFAULT", | ||||
| const std::string& region = "RGN_NOBNDRY"); | ||||
|
|
||||
| /// Calculate first partial derivative in Z | ||||
| /// | ||||
| /// \f$\partial / \partial z\f$ | ||||
|
|
@@ -147,7 +132,11 @@ Coordinates::FieldMetric DDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, | |||
| /// @param[in] region What region is expected to be calculated | ||||
| /// If not given, defaults to RGN_NOBNDRY | ||||
| Vector3D DDZ(const Vector3D& f, CELL_LOC outloc = CELL_DEFAULT, | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: function 'DDZ' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name] ^Additional contextsrc/sys/derivs.cxx:96: the definition seen here Vector3D DDZ(const Vector3D& v, CELL_LOC outloc, DIFF_METHOD method,
^include/bout/derivs.hxx:133: differing parameters are named here: ('f'), in definition: ('v') ^ |
||||
| const std::string& method = "DEFAULT", | ||||
| DIFF_METHOD method = DIFF_DEFAULT, | ||||
| const std::string& region = "RGN_NOBNDRY"); | ||||
|
|
||||
| /// Compatibility overload for string-based callers. | ||||
| Vector3D DDZ(const Vector3D& f, CELL_LOC outloc, const std::string& method, | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: function 'DDZ' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name] ^Additional contextsrc/sys/derivs.cxx:127: the definition seen here Vector3D DDZ(const Vector3D& v, CELL_LOC outloc, const std::string& method,
^include/bout/derivs.hxx:138: differing parameters are named here: ('f'), in definition: ('v') ^ |
||||
| const std::string& region = "RGN_NOBNDRY"); | ||||
|
|
||||
| /// Calculate first partial derivative in Z | ||||
|
|
@@ -163,7 +152,11 @@ Vector3D DDZ(const Vector3D& f, CELL_LOC outloc = CELL_DEFAULT, | |||
| /// @param[in] region What region is expected to be calculated | ||||
| /// If not given, defaults to RGN_NOBNDRY | ||||
| Vector2D DDZ(const Vector2D& f, CELL_LOC outloc = CELL_DEFAULT, | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: function 'DDZ' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name] ^Additional contextsrc/sys/derivs.cxx:156: the definition seen here Vector2D DDZ(const Vector2D& v, CELL_LOC UNUSED(outloc), DIFF_METHOD UNUSED(method),
^include/bout/derivs.hxx:153: differing parameters are named here: ('f'), in definition: ('v') ^ |
||||
| const std::string& method = "DEFAULT", | ||||
| DIFF_METHOD method = DIFF_DEFAULT, | ||||
| const std::string& region = "RGN_NOBNDRY"); | ||||
|
|
||||
| /// Compatibility overload for string-based callers. | ||||
| Vector2D DDZ(const Vector2D& f, CELL_LOC outloc, const std::string& method, | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: function 'DDZ' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name] ^Additional contextsrc/sys/derivs.cxx:172: the definition seen here Vector2D DDZ(const Vector2D& v, CELL_LOC outloc, const std::string& method,
^include/bout/derivs.hxx:158: differing parameters are named here: ('f'), in definition: ('v') ^ |
||||
| const std::string& region = "RGN_NOBNDRY"); | ||||
|
|
||||
| ////////// SECOND DERIVATIVES ////////// | ||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -539,53 +539,127 @@ inline BoutReal mean(const BinaryExpr<ResT, L, R, Func>& f, bool allpe = false, | |
| return bout::reduce::Mean::finalize(state); | ||
| } | ||
|
|
||
| /// Exponent: pow(lhs, lhs) is \p lhs raised to the power of \p rhs | ||
| /// | ||
| /// This loops over the entire domain, including guard/boundary cells by | ||
| /// default (can be changed using the \p rgn argument) | ||
| /// If CHECK >= 3 then the result will be checked for non-finite numbers | ||
| template <typename T, typename = bout::utils::EnableIfField<T>> | ||
| T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { | ||
|
|
||
| ASSERT1(areFieldsCompatible(lhs, rhs)); | ||
| class Field3DParallel; | ||
| class FieldPerp; | ||
|
|
||
| T result{emptyFrom(lhs)}; | ||
| namespace bout::detail { | ||
| template <typename T> | ||
| struct expression_result { | ||
| using type = std::decay_t<T>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::decay_t" is directly included [misc-include-cleaner] include/bout/field.hxx:26: - class Field;
+ #include <type_traits>
+ class Field; |
||
| }; | ||
|
|
||
| BOUT_FOR(i, result.getRegion(rgn)) { result[i] = ::pow(lhs[i], rhs[i]); } | ||
| template <typename ResT, typename L, typename R, typename Func> | ||
| struct expression_result<BinaryExpr<ResT, L, R, Func>> { | ||
| using type = ResT; | ||
| }; | ||
|
Comment on lines
+551
to
+554
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be clearer to consistently just use |
||
|
|
||
| checkData(result); | ||
| return result; | ||
| } | ||
| template <typename T> | ||
| using expression_result_t = typename expression_result<std::decay_t<T>>::type; | ||
|
|
||
| template <typename T, typename = bout::utils::EnableIfField<T>> | ||
| T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { | ||
| template <typename T> | ||
| inline constexpr bool is_expression_field_v = | ||
| is_expr_field2d_v<T> || is_expr_field3d_v<T> || is_expr_fieldperp_v<T>; | ||
|
|
||
| // Check if the inputs are allocated | ||
| checkData(lhs); | ||
| checkData(rhs); | ||
| template <typename L, typename R> | ||
| inline constexpr bool is_same_expression_rank_v = | ||
| (is_expr_field2d_v<L> && is_expr_field2d_v<R>) | ||
| || (is_expr_field3d_v<L> && is_expr_field3d_v<R>) | ||
| || (is_expr_fieldperp_v<L> && is_expr_fieldperp_v<R>); | ||
|
|
||
| T result{emptyFrom(lhs)}; | ||
| template <typename T> | ||
| std::optional<int> getPerpYIndex(const T& value) { | ||
| if constexpr (std::is_same_v<std::decay_t<T>, ::FieldPerp>) { | ||
| return value.getIndex(); | ||
| } else { | ||
| return std::nullopt; | ||
| } | ||
| } | ||
|
|
||
| BOUT_FOR(i, result.getRegion(rgn)) { result[i] = ::pow(lhs[i], rhs); } | ||
| template <typename ResT, typename L, typename R, typename Func> | ||
| std::optional<int> getPerpYIndex(const BinaryExpr<ResT, L, R, Func>& expr) { | ||
| if constexpr (std::is_same_v<ResT, ::FieldPerp>) { | ||
| return expr.getIndex(); | ||
| } else { | ||
| return std::nullopt; | ||
| } | ||
| } | ||
|
|
||
| checkData(result); | ||
| return result; | ||
| template <typename Expr> | ||
| std::optional<size_t> getExpressionRegionID(const Expr& expr, | ||
| const std::string& region_name) { | ||
| std::optional<size_t> region_id{}; | ||
| if (expr.getMesh()->hasRegion3D(region_name)) { | ||
| region_id = expr.getMesh()->getRegionID(region_name); | ||
| } | ||
| return expr.getMesh()->getCommonRegion(region_id, expr.getRegionID()); | ||
| } | ||
|
|
||
| template <typename T, typename = bout::utils::EnableIfField<T>> | ||
| T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { | ||
| template <typename L, typename R> | ||
| std::optional<size_t> getExpressionRegionID(const L& lhs, const R& rhs, | ||
| const std::string& region_name) { | ||
| return lhs.getMesh()->getCommonRegion(getExpressionRegionID(lhs, region_name), | ||
| rhs.getRegionID()); | ||
| } | ||
| } // namespace bout::detail | ||
|
|
||
| // Check if the inputs are allocated | ||
| checkData(lhs); | ||
| checkData(rhs); | ||
| /// Exponent: pow(lhs, lhs) is \p lhs raised to the power of \p rhs | ||
| /// | ||
| /// This loops over the entire domain, including guard/boundary cells by | ||
| /// default (can be changed using the \p rgn argument) | ||
| /// If CHECK >= 3 then the result will be checked for non-finite numbers | ||
| template <typename L, typename R, typename ResT = bout::detail::expression_result_t<L>, | ||
| typename = std::enable_if_t<bout::detail::is_same_expression_rank_v<L, R>>> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "std::enable_if_t" is directly included [misc-include-cleaner] ,
^ |
||
| auto pow(const L& lhs, const R& rhs, const std::string& rgn = "RGN_ALL") { | ||
|
|
||
| // Define and allocate the output result | ||
| T result{emptyFrom(rhs)}; | ||
| if constexpr (bout::utils::is_Field_v<std::decay_t<L>> | ||
| && bout::utils::is_Field_v<std::decay_t<R>>) { | ||
| ASSERT1(areFieldsCompatible(lhs, rhs)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "ASSERT1" is directly included [misc-include-cleaner] {
^ |
||
| } else { | ||
| ASSERT1_EXPR_COMPATIBLE(lhs, rhs); | ||
| } | ||
|
|
||
| return BinaryExpr<ResT, L, R, bout::op::Pow>{ | ||
| static_cast<typename L::View>(lhs), | ||
| static_cast<typename R::View>(rhs), | ||
| bout::op::Pow{}, | ||
| lhs.getMesh(), | ||
| lhs.getLocation(), | ||
| lhs.getDirections(), | ||
| bout::detail::getExpressionRegionID(lhs, rhs, rgn), | ||
| lhs.getMesh()->template getRegion<ResT>(rgn), | ||
| bout::detail::getPerpYIndex(lhs)}; | ||
| } | ||
|
|
||
| BOUT_FOR(i, result.getRegion(rgn)) { result[i] = ::pow(lhs, rhs[i]); } | ||
| template <typename T, typename ResT = bout::detail::expression_result_t<T>, | ||
| typename = std::enable_if_t<bout::detail::is_expression_field_v<T>>> | ||
| auto pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { | ||
|
|
||
| return BinaryExpr<ResT, T, Constant<BoutReal>, bout::op::Pow>{ | ||
| static_cast<typename T::View>(lhs), | ||
| static_cast<typename Constant<BoutReal>::View>(rhs), | ||
| bout::op::Pow{}, | ||
| lhs.getMesh(), | ||
| lhs.getLocation(), | ||
| lhs.getDirections(), | ||
| bout::detail::getExpressionRegionID(lhs, rgn), | ||
| lhs.getMesh()->template getRegion<ResT>(rgn), | ||
| bout::detail::getPerpYIndex(lhs)}; | ||
| } | ||
|
|
||
| checkData(result); | ||
| return result; | ||
| template <typename T, typename ResT = bout::detail::expression_result_t<T>, | ||
| typename = std::enable_if_t<bout::detail::is_expression_field_v<T>>> | ||
| auto pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { | ||
|
|
||
| return BinaryExpr<ResT, Constant<BoutReal>, T, bout::op::Pow>{ | ||
| static_cast<typename Constant<BoutReal>::View>(lhs), | ||
| static_cast<typename T::View>(rhs), | ||
| bout::op::Pow{}, | ||
| rhs.getMesh(), | ||
| rhs.getLocation(), | ||
| rhs.getDirections(), | ||
| bout::detail::getExpressionRegionID(rhs, rgn), | ||
| rhs.getMesh()->template getRegion<ResT>(rgn), | ||
| bout::detail::getPerpYIndex(rhs)}; | ||
| } | ||
|
|
||
| /*! | ||
|
|
@@ -604,29 +678,6 @@ T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { | |
| * result for non-finite numbers | ||
| * | ||
| */ | ||
| class Field3DParallel; | ||
| class FieldPerp; | ||
|
|
||
| namespace bout::detail { | ||
| template <typename T> | ||
| std::optional<int> getPerpYIndex(const T& value) { | ||
| if constexpr (std::is_same_v<std::decay_t<T>, ::FieldPerp>) { | ||
| return value.getIndex(); | ||
| } else { | ||
| return std::nullopt; | ||
| } | ||
| } | ||
|
|
||
| template <typename ResT, typename L, typename R, typename Func> | ||
| std::optional<int> getPerpYIndex(const BinaryExpr<ResT, L, R, Func>& expr) { | ||
| if constexpr (std::is_same_v<ResT, ::FieldPerp>) { | ||
| return expr.getIndex(); | ||
| } else { | ||
| return std::nullopt; | ||
| } | ||
| } | ||
| } // namespace bout::detail | ||
|
|
||
| #ifdef FIELD_FUNC | ||
| #error This macro has already been defined | ||
| #else | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -900,7 +900,24 @@ inline auto operator-(const Field3D& f) { | |
| /// This loops over the entire domain, including guard/boundary cells by | ||
| /// default (can be changed using the \p rgn argument). | ||
| /// If CHECK >= 3 then the result will be checked for non-finite numbers | ||
| Field3D pow(const Field3D& lhs, const Field2D& rhs, const std::string& rgn = "RGN_ALL"); | ||
| template <typename L, typename R> | ||
| std::enable_if_t<is_expr_field3d_v<L> && is_expr_field2d_v<R>, | ||
| BinaryExpr<Field3D, L, R, bout::op::Pow>> | ||
| pow(const L& lhs, const R& rhs, const std::string& rgn = "RGN_ALL") { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "pow" is directly included [misc-include-cleaner] include/bout/field3d.hxx:23: - #include <cstddef>
+ #include <cmath>
+ #include <cstddef> |
||
| ASSERT1_EXPR_COMPATIBLE(lhs, rhs); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "lhs" is directly included [misc-include-cleaner] ASSERT1_EXPR_COMPATIBLE(lhs, rhs);
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "rhs" is directly included [misc-include-cleaner] ASSERT1_EXPR_COMPATIBLE(lhs, rhs);
^ |
||
| auto regionID = bout::detail::getExpressionRegionID(lhs, rhs, rgn); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "regionID" is directly included [misc-include-cleaner] auto regionID = bout::detail::getExpressionRegionID(lhs, rhs, rgn);
^
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "rgn" is directly included [misc-include-cleaner] auto regionID = bout::detail::getExpressionRegionID(lhs, rhs, rgn);
^ |
||
| int mesh_nz = lhs.getMesh()->LocalNz; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "mesh_nz" is directly included [misc-include-cleaner] int mesh_nz = lhs.getMesh()->LocalNz;
^ |
||
| return BinaryExpr<Field3D, L, R, bout::op::Pow>{ | ||
| static_cast<typename L::View>(lhs), | ||
| static_cast<typename R::View>(rhs).setScale(1, mesh_nz), | ||
| bout::op::Pow{}, | ||
| lhs.getMesh(), | ||
| lhs.getLocation(), | ||
| lhs.getDirections(), | ||
| regionID, | ||
| (regionID.has_value() ? lhs.getMesh()->getRegion(regionID.value()) | ||
| : lhs.getMesh()->getRegion(rgn))}; | ||
| } | ||
| FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, | ||
| const std::string& rgn = "RGN_ALL"); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,6 +118,16 @@ struct Div { | |
| return a / b; | ||
| } | ||
| }; | ||
| struct Pow { | ||
| template <typename LView, typename RView> | ||
| BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(int idx, const LView& L, | ||
| const RView& R) const { | ||
| return ::pow(L(idx), R(idx)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warning: no header providing "pow" is directly included [misc-include-cleaner] include/bout/fieldops.hxx:1: - #ifndef BOUT_FIELDOPS_HXX
+ #include <cmath>
+ #ifndef BOUT_FIELDOPS_HXX |
||
| } | ||
| BOUT_HOST_DEVICE BOUT_FORCEINLINE BoutReal operator()(BoutReal a, BoutReal b) const { | ||
| return ::pow(a, b); | ||
| } | ||
| }; | ||
| struct IfElse { | ||
| bool condition; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning: no header providing "CELL_DEFAULT" is directly included [misc-include-cleaner]