DUNE PDELab (2.8)

checklopinterface.hh
1// -*- tab-width: 2; indent-tabs-mode: nil -*-
2// vi: set et ts=2 sw=2 sts=2:
3
4#ifndef DUNE_PDELAB_BACKEND_ISTL_MATRIXFREE_CHECKLOPINTERFACE_HH
5#define DUNE_PDELAB_BACKEND_ISTL_MATRIXFREE_CHECKLOPINTERFACE_HH
6
7
8namespace Dune {
9 namespace PDELab {
10 namespace impl{
11 // Some of the matrix free solvers require that the local operator describing
12 // the model uses the new interface for nonlinear Jacobian apply methods where
13 // the current solution and the linearization point may have different
14 // type. This is best explained by an example:
15 //
16 //
17 // old: void jacobian_apply_volume (const EG& eg, const LFSU& lfsu, const X& x, const X& z, const LFSV& lfsv, Y& y) const {}
18 // new: void jacobian_apply_volume (const EG& eg, const LFSU& lfsu, const X& x, const Z& z, const LFSV& lfsv, Y& y) const {}
19 //
20 // In the new interface the coefficient z has type Z which may be different
21 // than X.
22 //
23 // The purpose of this file is to provide a helper function to check whether a
24 // local operator complies to the new interface. I'm all in favor of removing
25 // this code as soon as possible.
26 //
27 // The current implementation is based on C++-14 SFINAE. It is entirely
28 // possible that this could be replaced with features from the current
29 // concepts implementation in dune-common.
30
31 // SFINAE Infrastructure
32 template <typename T>
33 struct Result
34 {
35 private:
36 template <typename A> constexpr auto test(int)
37 -> decltype(std::declval<T>()(std::declval<A>()), std::true_type())
38 {
39 return std::true_type();
40 }
41 template <typename A> constexpr std::false_type test(...)
42 {
43 return std::false_type();
44 }
45
46 public:
47 template <typename A> constexpr auto operator()(const A& p)
48 {
49 return test<A>(int());
50 }
51 };
52
53 template <typename T> constexpr auto lambdaToTemplate(const T& t)
54 {
55 return Result<T>();
56 }
57
58 // The actual tests if the new method exists
59 //
60 // Note: The types in the declval don't really matter. The important part
61 // is that they are all different. This way we check that we have the new
62 // interface since it would not be valid to pass this to the old one.
63 //
64 // Note: Don't forget the & for the last argument. This is necessary
65 // since the last argument of jacobian_apply_volume is passed by
66 // reference (without being const).
67 auto hasNewJacobianApplyVolume =
68 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_volume(std::declval<bool>(),
69 std::declval<int>(),
70 std::declval<double>(),
71 std::declval<short int>(),
72 std::declval<long int>(),
73 std::declval<long long int&>()
74 )) {});
75 auto hasNewJacobianApplyVolumePostSkeleton =
76 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_volume_post_skeleton(std::declval<bool>(),
77 std::declval<int>(),
78 std::declval<double>(),
79 std::declval<short int>(),
80 std::declval<long int>(),
81 std::declval<long long int&>()
82 )) {});
83 auto hasNewJacobianApplySkeleton =
84 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_skeleton(std::declval<bool>(),
85 std::declval<int>(),
86 std::declval<double>(),
87 std::declval<short int>(),
88 std::declval<long int>(),
89 std::declval<int>(),
90 std::declval<double>(),
91 std::declval<short int>(),
92 std::declval<long int>(),
93 std::declval<long long int&>(),
94 std::declval<long long int&>()
95 )) {});
96 auto hasNewJacobianApplyBoundary =
97 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_boundary(std::declval<bool>(),
98 std::declval<int>(),
99 std::declval<double>(),
100 std::declval<short int>(),
101 std::declval<long int>(),
102 std::declval<long long int&>()
103 )) {});
104
105 // Similar tests that show if the old or the new methods exist
106 //
107 // Note: Since the calls to the old interface are also valid in the new
108 // interface we only get the information if the method exists in one of
109 // the two implementations.
110 auto hasOldOrNewJacobianApplyVolume =
111 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_volume(std::declval<bool>(),
112 std::declval<int>(),
113 std::declval<double>(),
114 std::declval<double>(),
115 std::declval<long int>(),
116 std::declval<long long int&>()
117 )) {});
118 auto hasOldOrNewJacobianApplyVolumePostSkeleton =
119 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_volume_post_skeleton(std::declval<bool>(),
120 std::declval<int>(),
121 std::declval<double>(),
122 std::declval<double>(),
123 std::declval<long int>(),
124 std::declval<long long int&>()
125 )) {});
126 auto hasOldOrNewJacobianApplySkeleton =
127 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_skeleton(std::declval<bool>(),
128 std::declval<int>(),
129 std::declval<double>(),
130 std::declval<double>(),
131 std::declval<long int>(),
132 std::declval<int>(),
133 std::declval<double>(),
134 std::declval<double>(),
135 std::declval<long int>(),
136 std::declval<long long int&>(),
137 std::declval<long long int&>()
138 )) {});
139 auto hasOldOrNewJacobianApplyBoundary =
140 lambdaToTemplate([](auto&& lop) -> decltype(lop.jacobian_apply_boundary(std::declval<bool>(),
141 std::declval<int>(),
142 std::declval<double>(),
143 std::declval<double>(),
144 std::declval<long int>(),
145 std::declval<long long int&>()
146 )) {});
147
148 // Now comes the test that should actually be called. If a localoperator
149 // implements the OldOrNew interface for one of the nonlinear jacobian
150 // apply methods but not the new interface we can conlude that only the
151 // old one is implemented.
152 template <typename T>
153 constexpr auto hasOldLOPInterface(T& t) -> typename std::enable_if<
154 (decltype(hasOldOrNewJacobianApplyVolume(t))::value && !decltype(hasNewJacobianApplyVolume(t))::value)
155 || (decltype(hasOldOrNewJacobianApplyVolumePostSkeleton(t))::value && !decltype(hasNewJacobianApplyVolumePostSkeleton(t))::value)
156 || (decltype(hasOldOrNewJacobianApplySkeleton(t))::value && !decltype(hasNewJacobianApplySkeleton(t))::value)
157 || (decltype(hasOldOrNewJacobianApplyBoundary(t))::value && !decltype(hasNewJacobianApplyBoundary(t))::value),
158 std::true_type>::type
159 {
160 return std::true_type();
161 }
162 template <typename T>
163 constexpr auto hasOldLOPInterface(T& t) -> typename std::enable_if<
164 !((decltype(hasOldOrNewJacobianApplyVolume(t))::value && !decltype(hasNewJacobianApplyVolume(t))::value)
165 || (decltype(hasOldOrNewJacobianApplyVolumePostSkeleton(t))::value && !decltype(hasNewJacobianApplyVolumePostSkeleton(t))::value)
166 || (decltype(hasOldOrNewJacobianApplySkeleton(t))::value && !decltype(hasNewJacobianApplySkeleton(t))::value)
167 || (decltype(hasOldOrNewJacobianApplyBoundary(t))::value && !decltype(hasNewJacobianApplyBoundary(t))::value)),
168 std::false_type>::type
169 {
170 return std::false_type();
171 }
172 }
173 }
174}
175
176
177#endif
Dune namespace.
Definition: alignedallocator.hh:11
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Dec 22, 23:30, 2024)