GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 84.6% 258 / 0 / 305
Functions: 100.0% 18 / 0 / 18
Branches: 36.7% 176 / 0 / 480

mform/m_mform_linalg.F90
Line Branch Exec Source
1 !> The m-form linear algebra module
2 !>
3 !> This module provides an inner product, L2 projection, and L2-error computation for the m-form spaces
4 module m_mform_linalg
5 use m_common, only: wp, user_function_3d_interface
6 use m_bspline_basis, only: BSplineFun
7 use m_mform_basis, only: MFormFun, MFormSpace
8 #include "petsc.fi"
9 #include "timer.fi"
10
11 implicit none
12
13 private
14
15 public :: inner_product, l2_error, l2_norm, l2_projection
16 public :: solve_curlA_equals_B
17
18 !> @brief Compute the (L2) inner product of all elements of a m-form space with a user-defined function
19 interface inner_product
20 procedure inner_product_mform_scalar, inner_product_mform_vector
21 end interface
22
23 interface l2_projection
24 procedure l2_projection_mform_scalar, l2_projection_mform_vector
25 end interface
26
27 !> @brief Compute the L2 norm of the difference between an m-form and a user-defined function
28 interface l2_error
29 procedure l2_error_mform_scalar, l2_error_mform_vector
30 end interface
31
32 !> @brief Compute the L2 norm of an m-form
33 interface l2_norm
34 procedure l2_norm_mform
35 end interface
36
37 interface solve_curlA_equals_B
38 module procedure solve_curlA_equals_B_tensorprod
39 end interface
40
41 ! NOTE: a compiler bug in NVHPCSDK requires these variables to be in the module scope
42 ! NOTE: see also: https://forums.developer.nvidia.com/t/compiler-bug-passing-a-procedure-with-a-variable-from-a-local-scope-as-input-to-a-function-leads-to-unexpected-behavior/359876
43 type(BSplineFun) :: twoform_x_integrated, twoform_y_copy
44
45 contains
46
47 !> @brief Compute the (L2) inner product of all elements of a scalar-valued m-form with a user-defined function
48 !>
49 !> @param[inout] v The resulting m-form containing inner products values
50 !> @param[in] space The m-form space (defines the first argument of the inner product)
51 !> @param[in] userfun The second argument of the inner product is a user-defined function
52 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
53 !> @param[in] n_quad_extra _(optional)_ The extra number of quadrature points to use for the inner product on top of those needed
54 !> to exactly integrate the B-spline spaces
55 !> @param[in] user_fun_is_physical _(optional)_ Whether the user function is defined in physical coordinates (default: .false.)
56 !>
57 !> @note The resulting m-form `v` is initialized only for the response B-spline indices of the tensor product space (if needed,
58 !> call v%distribute() to distribute the data)
59 279 subroutine inner_product_mform_scalar(v, space, userfun, coord_transform, n_quad_extra, user_fun_is_physical)
60 use m_tensorprod_linalg, only: inner_product
61 use m_tensorprod_quadrature, only: DEFAULT_EXTRA_QUADRATURE_POINTS
62 use m_coord_transform, only: CoordTransformAbstract, jacobian, transform
63 implicit none
64
65 type(MFormFun), intent(inout) :: v
66 type(MFormSpace), intent(in) :: space
67 procedure(user_function_3d_interface) :: userfun
68 class(CoordTransformAbstract), intent(in), optional :: coord_transform
69 integer, intent(in), optional :: n_quad_extra
70 logical, intent(in), optional :: user_fun_is_physical
71
72 logical :: user_fun_is_physical_
73 integer :: n_quad_extra_
74
75 TIMER_DECL(t_total)
76 TIMER_INIT_START(t_total, "MFormFun::inner_product")
77
78
2/2
✓ Branch 2 → 3 taken 32 times.
✓ Branch 2 → 5 taken 247 times.
279 if (v%is_initialized()) then
79 ! NOTE: when the m-form function is already initialized, it is ASSUMED that its space matches with the input space
80 ! TODO: enforce this? we need a cheap yet robust way to check this
81 32 call v%reset()
82 else
83 247 call v%init(space)
84 end if
85
86 279 user_fun_is_physical_ = .false.
87
5/6
✓ Branch 7 → 8 taken 77 times.
✓ Branch 7 → 11 taken 202 times.
✓ Branch 8 → 9 taken 77 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 4 times.
✓ Branch 9 → 11 taken 73 times.
279 if (present(coord_transform) .and. present(user_fun_is_physical)) then
88 4 user_fun_is_physical_ = user_fun_is_physical
89 end if
90
91
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 18 taken 279 times.
279 if (space%m == 1 .or. space%m == 2) then
92 write (*, '(A,I0,A)') "ERROR in inner_product_mform_scalar: this subroutine is for scalar-valued forms. " &
93 //"The provided space is a ", space%m, "-form, which is vector-valued and requires 3 user functions. "
94 error stop 1
95 end if
96
97
2/2
✓ Branch 18 → 19 taken 38 times.
✓ Branch 18 → 20 taken 241 times.
279 if (present(n_quad_extra)) then
98 38 n_quad_extra_ = n_quad_extra
99 else
100
3/4
✓ Branch 20 → 21 taken 39 times.
✓ Branch 20 → 23 taken 202 times.
✓ Branch 21 → 22 taken 39 times.
✗ Branch 21 → 23 not taken.
241 if (present(coord_transform)) then
101 39 n_quad_extra_ = DEFAULT_EXTRA_QUADRATURE_POINTS
102 else
103 202 n_quad_extra_ = 0
104 end if
105 end if
106
107 279 call inner_product(v%tp_funs(1), space%tp_spaces(1), wrapped_userfun, n_quad_extra=n_quad_extra_)
108
109 TIMER_STOP(t_total)
110 contains
111 23438516 pure real(wp) function wrapped_userfun(xp, yp, zp) result(ans)
112 real(wp), intent(in) :: xp, yp, zp
113
114 real(wp) :: x, y, z
115
116
3/4
✓ Branch 2 → 3 taken 19606920 times.
✓ Branch 2 → 4 taken 3831596 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 19606920 times.
23438516 if (.not. present(coord_transform)) then
117 3831596 ans = userfun(xp, yp, zp)
118 else
119
2/2
✓ Branch 5 → 6 taken 506880 times.
✓ Branch 5 → 7 taken 19100040 times.
19606920 if (user_fun_is_physical_) then
120 506880 x = transform(coord_transform, 1, xp, yp, zp)
121 506880 y = transform(coord_transform, 2, xp, yp, zp)
122 506880 z = transform(coord_transform, 3, xp, yp, zp)
123 506880 ans = userfun(x, y, z)
124 else
125 19100040 ans = userfun(xp, yp, zp)
126
2/2
✓ Branch 8 → 9 taken 6942900 times.
✓ Branch 8 → 10 taken 12157140 times.
19100040 if (v%space%m == 3) then
127 ! The pushforward transformation for 3-forms yields a factor 1 / jacobian
128 6942900 ans = ans / jacobian(coord_transform, xp, yp, zp)
129 end if
130 end if
131
2/2
✓ Branch 10 → 11 taken 12479700 times.
✓ Branch 10 → 12 taken 7127220 times.
19606920 if (v%space%m == 0) then
132 12479700 ans = ans * jacobian(coord_transform, xp, yp, zp)
133 end if
134 end if
135
136 23438516 end function wrapped_userfun
137 end subroutine
138
139 !> @brief Compute the (L2) inner product of all elements of a vector-valued m-form with user-defined functions
140 !>
141 !> @param[inout] v The resulting m-form containing inner products values
142 !> @param[in] space The m-form space (defines the first argument of the inner product)
143 !> @param[in] userfun_x The x-component of the second argument of the inner product
144 !> @param[in] userfun_y The y-component of the second argument of the inner product
145 !> @param[in] userfun_z The z-component of the second argument of the inner product
146 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
147 !> @param[in] n_quad_extra _(optional)_ The extra number of quadrature points to use for the inner product on top of those needed
148 !> to exactly integrate the B-spline spaces
149 !> @param[in] user_fun_is_physical _(optional)_ Whether the user functions are defined in physical coordinates (default: .false.)
150 !>
151 !> @note The resulting m-form `v` is initialized only for the response B-spline indices of the tensor product space (if needed,
152 !> call v%distribute() to distribute the data)
153 614 subroutine inner_product_mform_vector(v, space, userfun_x, userfun_y, userfun_z, coord_transform, n_quad_extra, &
154 user_fun_is_physical)
155 use m_tensorprod_linalg, only: inner_product
156 use m_tensorprod_quadrature, only: DEFAULT_EXTRA_QUADRATURE_POINTS
157 use m_coord_transform, only: CoordTransformAbstract, jacobian, jacobian_matrix, jacobian_matrix_inv, transform, G_matrix, &
158 G_matrix_inv
159 implicit none
160
161 type(MFormFun), intent(inout) :: v
162 type(MFormSpace), intent(in) :: space
163 procedure(user_function_3d_interface) :: userfun_x, userfun_y, userfun_z
164 class(CoordTransformAbstract), intent(in), optional :: coord_transform
165 integer, intent(in), optional :: n_quad_extra
166 logical, intent(in), optional :: user_fun_is_physical
167
168 logical :: user_fun_is_physical_, is_diagonal
169 integer :: n_quad_extra_
170
171 TIMER_DECL(t_total)
172 TIMER_INIT_START(t_total, "MFormFun::inner_product")
173
174
2/2
✓ Branch 2 → 3 taken 43 times.
✓ Branch 2 → 5 taken 571 times.
614 if (v%is_initialized()) then
175 43 call v%reset()
176 else
177 571 call v%init(space)
178 end if
179
180 614 is_diagonal = .true.
181 user_fun_is_physical_ = .false.
182
3/4
✓ Branch 7 → 8 taken 464 times.
✓ Branch 7 → 12 taken 150 times.
✓ Branch 8 → 9 taken 464 times.
✗ Branch 8 → 12 not taken.
614 if (present(coord_transform)) then
183
2/2
✓ Branch 9 → 10 taken 4 times.
✓ Branch 9 → 11 taken 460 times.
464 if (present(user_fun_is_physical)) then
184 4 user_fun_is_physical_ = user_fun_is_physical
185 end if
186 464 is_diagonal = coord_transform%is_orthogonal
187 end if
188
189
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 19 taken 614 times.
614 if (space%m == 0 .or. space%m == 3) then
190 write (*, '(A,I0,A)') "ERROR in inner_product_mform_vector: this subroutine is for vector-valued forms. " &
191 //"The provided space is a ", space%m, "-form, which is scalar-valued and requires only 1 user function. "
192 error stop 1
193 end if
194
195
2/2
✓ Branch 19 → 20 taken 60 times.
✓ Branch 19 → 21 taken 554 times.
614 if (present(n_quad_extra)) then
196 60 n_quad_extra_ = n_quad_extra
197 else
198
3/4
✓ Branch 21 → 22 taken 404 times.
✓ Branch 21 → 24 taken 150 times.
✓ Branch 22 → 23 taken 404 times.
✗ Branch 22 → 24 not taken.
554 if (present(coord_transform)) then
199 404 n_quad_extra_ = DEFAULT_EXTRA_QUADRATURE_POINTS
200 else
201 150 n_quad_extra_ = 0
202 end if
203 end if
204
2/2
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 29 taken 612 times.
614 if (user_fun_is_physical_) then
205 2 call inner_product(v%tp_funs(1), space%tp_spaces(1), wrapped_physical_userfun_xx, n_quad_extra=n_quad_extra_)
206 2 call inner_product(v%tp_funs(2), space%tp_spaces(2), wrapped_physical_userfun_yy, n_quad_extra=n_quad_extra_)
207 2 call inner_product(v%tp_funs(3), space%tp_spaces(3), wrapped_physical_userfun_zz, n_quad_extra=n_quad_extra_)
208 else
209 612 call inner_product(v%tp_funs(1), space%tp_spaces(1), wrapped_userfun_xx, n_quad_extra=n_quad_extra_)
210 612 call inner_product(v%tp_funs(2), space%tp_spaces(2), wrapped_userfun_yy, n_quad_extra=n_quad_extra_)
211 612 call inner_product(v%tp_funs(3), space%tp_spaces(3), wrapped_userfun_zz, n_quad_extra=n_quad_extra_)
212 end if
213
214 TIMER_STOP(t_total)
215 contains
216 56023986 pure real(wp) function wrapped_userfun_xx(xp, yp, zp) result(ans)
217 implicit none
218
219 real(wp), intent(in) :: xp, yp, zp
220
221 56023986 ans = userfun_x(xp, yp, zp)
222
3/4
✓ Branch 3 → 4 taken 52525278 times.
✓ Branch 3 → 16 taken 3498708 times.
✓ Branch 4 → 5 taken 52525278 times.
✗ Branch 4 → 16 not taken.
56023986 if (present(coord_transform)) then
223
2/2
✓ Branch 5 → 6 taken 42099750 times.
✓ Branch 5 → 11 taken 10425528 times.
52525278 if (space%m == 1) then
224 ! The pushforward transformation for 1-forms yields
225 42099750 ans = ans * G_matrix_inv(coord_transform, 1, 1, xp, yp, zp)
226
2/2
✓ Branch 6 → 7 taken 3685536 times.
✓ Branch 6 → 10 taken 38414214 times.
42099750 if (.not. is_diagonal) then
227 3685536 ans = ans + userfun_y(xp, yp, zp) * G_matrix_inv(coord_transform, 1, 2, xp, yp, zp)
228 3685536 ans = ans + userfun_z(xp, yp, zp) * G_matrix_inv(coord_transform, 1, 3, xp, yp, zp)
229 end if
230 42099750 ans = ans * jacobian(coord_transform, xp, yp, zp)
231 else
232 ! The pushforward transformation for 2-forms yields
233 10425528 ans = ans * G_matrix(coord_transform, 1, 1, xp, yp, zp)
234
2/2
✓ Branch 11 → 12 taken 1788624 times.
✓ Branch 11 → 15 taken 8636904 times.
10425528 if (.not. is_diagonal) then
235 1788624 ans = ans + userfun_y(xp, yp, zp) * G_matrix(coord_transform, 1, 2, xp, yp, zp)
236 1788624 ans = ans + userfun_z(xp, yp, zp) * G_matrix(coord_transform, 1, 3, xp, yp, zp)
237 end if
238 10425528 ans = ans / jacobian(coord_transform, xp, yp, zp)
239 end if
240 end if
241
242 56023986 end function
243
244 489984 pure real(wp) function wrapped_physical_userfun_xx(xp, yp, zp) result(ans)
245 implicit none
246
247 real(wp), intent(in) :: xp, yp, zp
248
249 real(wp) :: x, y, z
250
251 489984 x = transform(coord_transform, 1, xp, yp, zp)
252 489984 y = transform(coord_transform, 2, xp, yp, zp)
253 489984 z = transform(coord_transform, 3, xp, yp, zp)
254
255
2/2
✓ Branch 2 → 3 taken 268800 times.
✓ Branch 2 → 7 taken 221184 times.
489984 if (space%m == 1) then
256 ! The pushforward transformation for 1-forms yields
257 268800 ans = userfun_x(x, y, z) * jacobian_matrix_inv(coord_transform, 1, 1, xp, yp, zp)
258 268800 ans = ans + userfun_y(x, y, z) * jacobian_matrix_inv(coord_transform, 1, 2, xp, yp, zp)
259 268800 ans = ans + userfun_z(x, y, z) * jacobian_matrix_inv(coord_transform, 1, 3, xp, yp, zp)
260 268800 ans = ans * jacobian(coord_transform, xp, yp, zp)
261 else
262 ! The pushforward transformation for 2-forms yields
263 221184 ans = userfun_x(x, y, z) * jacobian_matrix(coord_transform, 1, 1, xp, yp, zp)
264 221184 ans = ans + userfun_y(x, y, z) * jacobian_matrix(coord_transform, 2, 1, xp, yp, zp)
265 221184 ans = ans + userfun_z(x, y, z) * jacobian_matrix(coord_transform, 3, 1, xp, yp, zp)
266 end if
267 489984 end function
268
269 55974898 pure real(wp) function wrapped_userfun_yy(xp, yp, zp) result(ans)
270 implicit none
271
272 real(wp), intent(in) :: xp, yp, zp
273
274 55974898 ans = userfun_y(xp, yp, zp)
275
3/4
✓ Branch 3 → 4 taken 52459018 times.
✓ Branch 3 → 16 taken 3515880 times.
✓ Branch 4 → 5 taken 52459018 times.
✗ Branch 4 → 16 not taken.
55974898 if (present(coord_transform)) then
276
2/2
✓ Branch 5 → 6 taken 42060616 times.
✓ Branch 5 → 11 taken 10398402 times.
52459018 if (space%m == 1) then
277 ! The pushforward transformation for 1-forms yields
278 42060616 ans = ans * G_matrix_inv(coord_transform, 2, 2, xp, yp, zp)
279
2/2
✓ Branch 6 → 7 taken 3685536 times.
✓ Branch 6 → 10 taken 38375080 times.
42060616 if (.not. is_diagonal) then
280 3685536 ans = ans + userfun_x(xp, yp, zp) * G_matrix_inv(coord_transform, 2, 1, xp, yp, zp)
281 3685536 ans = ans + userfun_z(xp, yp, zp) * G_matrix_inv(coord_transform, 2, 3, xp, yp, zp)
282 end if
283 42060616 ans = ans * jacobian(coord_transform, xp, yp, zp)
284 else
285 ! The pushforward transformation for 2-forms yields
286 10398402 ans = ans * G_matrix(coord_transform, 2, 2, xp, yp, zp)
287
2/2
✓ Branch 11 → 12 taken 1788624 times.
✓ Branch 11 → 15 taken 8609778 times.
10398402 if (.not. is_diagonal) then
288 1788624 ans = ans + userfun_x(xp, yp, zp) * G_matrix(coord_transform, 2, 1, xp, yp, zp)
289 1788624 ans = ans + userfun_z(xp, yp, zp) * G_matrix(coord_transform, 2, 3, xp, yp, zp)
290 end if
291 10398402 ans = ans / jacobian(coord_transform, xp, yp, zp)
292 end if
293 end if
294
295 55974898 end function
296
297 491520 pure real(wp) function wrapped_physical_userfun_yy(xp, yp, zp) result(ans)
298 implicit none
299
300 real(wp), intent(in) :: xp, yp, zp
301
302 real(wp) :: x, y, z
303
304 491520 x = transform(coord_transform, 1, xp, yp, zp)
305 491520 y = transform(coord_transform, 2, xp, yp, zp)
306 491520 z = transform(coord_transform, 3, xp, yp, zp)
307
308
2/2
✓ Branch 2 → 3 taken 276480 times.
✓ Branch 2 → 7 taken 215040 times.
491520 if (space%m == 1) then
309 ! The pushforward transformation for 1-forms yields
310 276480 ans = userfun_x(x, y, z) * jacobian_matrix_inv(coord_transform, 2, 1, xp, yp, zp)
311 276480 ans = ans + userfun_y(x, y, z) * jacobian_matrix_inv(coord_transform, 2, 2, xp, yp, zp)
312 276480 ans = ans + userfun_z(x, y, z) * jacobian_matrix_inv(coord_transform, 2, 3, xp, yp, zp)
313 276480 ans = ans * jacobian(coord_transform, xp, yp, zp)
314 else
315 ! The pushforward transformation for 2-forms yields
316 215040 ans = userfun_x(x, y, z) * jacobian_matrix(coord_transform, 1, 2, xp, yp, zp)
317 215040 ans = ans + userfun_y(x, y, z) * jacobian_matrix(coord_transform, 2, 2, xp, yp, zp)
318 215040 ans = ans + userfun_z(x, y, z) * jacobian_matrix(coord_transform, 3, 2, xp, yp, zp)
319 end if
320
321 491520 end function
322
323 41607443 pure real(wp) function wrapped_userfun_zz(xp, yp, zp) result(ans)
324 implicit none
325
326 real(wp), intent(in) :: xp, yp, zp
327
328 41607443 ans = userfun_z(xp, yp, zp)
329
3/4
✓ Branch 3 → 4 taken 38121489 times.
✓ Branch 3 → 16 taken 3485954 times.
✓ Branch 4 → 5 taken 38121489 times.
✗ Branch 4 → 16 not taken.
41607443 if (present(coord_transform)) then
330
2/2
✓ Branch 5 → 6 taken 22856748 times.
✓ Branch 5 → 11 taken 15264741 times.
38121489 if (space%m == 1) then
331 ! The pushforward transformation for 1-forms yields
332 22856748 ans = ans * G_matrix_inv(coord_transform, 3, 3, xp, yp, zp)
333
2/2
✓ Branch 6 → 7 taken 3685536 times.
✓ Branch 6 → 10 taken 19171212 times.
22856748 if (.not. is_diagonal) then
334 3685536 ans = ans + userfun_x(xp, yp, zp) * G_matrix_inv(coord_transform, 3, 1, xp, yp, zp)
335 3685536 ans = ans + userfun_y(xp, yp, zp) * G_matrix_inv(coord_transform, 3, 2, xp, yp, zp)
336 end if
337 22856748 ans = ans * jacobian(coord_transform, xp, yp, zp)
338 else
339 ! The pushforward transformation for 2-forms yields
340 15264741 ans = ans * G_matrix(coord_transform, 3, 3, xp, yp, zp)
341
2/2
✓ Branch 11 → 12 taken 1788624 times.
✓ Branch 11 → 15 taken 13476117 times.
15264741 if (.not. is_diagonal) then
342 1788624 ans = ans + userfun_x(xp, yp, zp) * G_matrix(coord_transform, 3, 1, xp, yp, zp)
343 1788624 ans = ans + userfun_y(xp, yp, zp) * G_matrix(coord_transform, 3, 2, xp, yp, zp)
344 end if
345 15264741 ans = ans / jacobian(coord_transform, xp, yp, zp)
346 end if
347 end if
348
349 41607443 end function
350
351 488448 pure real(wp) function wrapped_physical_userfun_zz(xp, yp, zp) result(ans)
352 implicit none
353
354 real(wp), intent(in) :: xp, yp, zp
355
356 real(wp) :: x, y, z
357
358 488448 x = transform(coord_transform, 1, xp, yp, zp)
359 488448 y = transform(coord_transform, 2, xp, yp, zp)
360 488448 z = transform(coord_transform, 3, xp, yp, zp)
361
362
2/2
✓ Branch 2 → 3 taken 258048 times.
✓ Branch 2 → 7 taken 230400 times.
488448 if (space%m == 1) then
363 ! The pushforward transformation for 1-forms yields
364 258048 ans = userfun_x(x, y, z) * jacobian_matrix_inv(coord_transform, 3, 1, xp, yp, zp)
365 258048 ans = ans + userfun_y(x, y, z) * jacobian_matrix_inv(coord_transform, 3, 2, xp, yp, zp)
366 258048 ans = ans + userfun_z(x, y, z) * jacobian_matrix_inv(coord_transform, 3, 3, xp, yp, zp)
367 258048 ans = ans * jacobian(coord_transform, xp, yp, zp)
368 else
369 ! The pushforward transformation for 2-forms yields
370 230400 ans = userfun_x(x, y, z) * jacobian_matrix(coord_transform, 1, 3, xp, yp, zp)
371 230400 ans = ans + userfun_y(x, y, z) * jacobian_matrix(coord_transform, 2, 3, xp, yp, zp)
372 230400 ans = ans + userfun_z(x, y, z) * jacobian_matrix(coord_transform, 3, 3, xp, yp, zp)
373 end if
374
375 488448 end function
376
377 end subroutine
378
379 !> @brief Compute the L2 projection of a user-defined function onto a scalar-valued m-form space
380 !>
381 !> @param[out] c The resulting m-form containing the L2 projection
382 !> @param[in] space The m-form space onto which the projection is computed
383 !> @param[in] userfun The user-defined function to project onto the m-form space
384 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
385 !> @param[in] solver _(optional)_ The solver to use for the L2 projection, if not provided, a solver is created internally
386 !> @param[in] n_quad_extra _(optional)_ The extra number of quadrature points to use for the L2 projection on top of those needed
387 !> to exactly integrate the B-spline spaces
388 !> @param[in] user_fun_is_physical _(optional)_ Whether the user function is defined in physical coordinates (default: .false.)
389
2/2
✓ Branch 4 → 5 taken 163 times.
✓ Branch 4 → 6 taken 26 times.
189 subroutine l2_projection_mform_scalar(c, space, userfun, coord_transform, solver, n_quad_extra, user_fun_is_physical)
390 use m_tensorprod_linalg, only: l2_projection
391 use m_mform_solver, only: GenericSolver
392 use m_mform_matrix, only: MassMatrix
393 use m_coord_transform_abstract, only: CoordTransformAbstract
394 implicit none
395
396 type(MFormFun), intent(out) :: c
397 type(MFormSpace), intent(in) :: space
398 procedure(user_function_3d_interface) :: userfun
399
400 class(CoordTransformAbstract), intent(in), optional :: coord_transform
401 type(GenericSolver), intent(in), optional :: solver
402 integer, intent(in), optional :: n_quad_extra
403 logical, intent(in), optional :: user_fun_is_physical
404
405 189 type(MFormFun) :: f
406 189 type(MassMatrix) :: mass
407
2/2
✓ Branch 2 → 3 taken 173 times.
✓ Branch 2 → 4 taken 16 times.
189 type(GenericSolver) :: solver_
408
409 TIMER_DECL(t_total)
410 TIMER_INIT_START(t_total, "MFormFun::l2_projection")
411
412
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 189 times.
189 if (space%m == 1 .or. space%m == 2) then
413 error stop "l2_projection for vector-valued forms requires 3 userfunctions"
414 end if
415
416 call inner_product(f, space, userfun, n_quad_extra=n_quad_extra, user_fun_is_physical=user_fun_is_physical, &
417 189 coord_transform=coord_transform)
418
419
1/2
✓ Branch 9 → 10 taken 189 times.
✗ Branch 9 → 12 not taken.
189 if (present(solver)) then
420 189 call solver%solve(c, f)
421 else
422 call mass%init(space, n_quad_extra=n_quad_extra, coord_transform=coord_transform)
423 call solver_%init(mass, coord_transform=coord_transform)
424 call solver_%solve(c, f)
425
426 call mass%destroy()
427 call solver_%destroy()
428 end if
429
430 189 call f%destroy()
431 TIMER_STOP(t_total)
432
7/80
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 189 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 189 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 95 taken 189 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 71 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 31 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 70 not taken.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 47 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 46 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 58 not taken.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 57 not taken.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 52 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 69 not taken.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 68 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 65 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 75 → 77 not taken.
✗ Branch 77 → 78 not taken.
✗ Branch 77 → 79 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 94 not taken.
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 93 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 86 not taken.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 97 taken 189 times.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 99 taken 189 times.
✓ Branch 99 → 100 taken 189 times.
✗ Branch 99 → 101 not taken.
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 103 taken 189 times.
189 end subroutine l2_projection_mform_scalar
433
434 !> @brief Compute the L2 projection of a user-defined function onto a vector-valued m-form space
435 !>
436 !> @param[out] c The resulting m-form containing the L2 projection
437 !> @param[in] space The m-form space onto which the projection is computed
438 !> @param[in] userfun_x The x-component of the user-defined function to project onto the m-form space
439 !> @param[in] userfun_y The y-component of the user-defined function to project onto the m-form space
440 !> @param[in] userfun_z The z-component of the user-defined function to project onto the m-form space
441 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
442 !> @param[in] solver _(optional)_ The solver to use for the L2 projection, if not provided, a solver is created internally
443 !> @param[in] n_quad_extra _(optional)_ The extra number of quadrature points to use for the L2 projection on top of those needed
444 !> to exactly integrate the B-spline spaces
445 !> @param[in] user_fun_is_physical _(optional)_ Whether the user functions are defined in physical coordinates (default: .false.)
446
1/2
✓ Branch 4 → 5 taken 234 times.
✗ Branch 4 → 6 not taken.
234 subroutine l2_projection_mform_vector(c, space, userfun_x, userfun_y, userfun_z, coord_transform, solver, n_quad_extra, &
447 user_fun_is_physical)
448 use m_tensorprod_linalg, only: l2_projection
449 use m_mform_solver, only: GenericSolver
450 use m_mform_matrix, only: MassMatrix
451 use m_coord_transform_abstract, only: CoordTransformAbstract
452 implicit none
453
454 type(MFormFun), intent(out) :: c
455 type(MFormSpace), intent(in) :: space
456 procedure(user_function_3d_interface) :: userfun_x, userfun_y, userfun_z
457 class(CoordTransformAbstract), intent(in), optional :: coord_transform
458 type(GenericSolver), intent(in), optional :: solver
459 integer, intent(in), optional :: n_quad_extra
460 logical, intent(in), optional :: user_fun_is_physical
461
462 234 type(MFormFun) :: f
463 234 type(MassMatrix) :: mass
464
1/2
✓ Branch 2 → 3 taken 234 times.
✗ Branch 2 → 4 not taken.
234 type(GenericSolver) :: solver_
465
466 TIMER_DECL(t_total)
467 TIMER_INIT_START(t_total, "MFormFun::l2_projection")
468
469
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 234 times.
234 if (space%m == 0 .or. space%m == 3) then
470 error stop "l2_projection for scalar-valued forms requires 1 userfunction"
471 end if
472
473 call inner_product(f, space, userfun_x, userfun_y, userfun_z, n_quad_extra=n_quad_extra, &
474 234 & user_fun_is_physical=user_fun_is_physical, coord_transform=coord_transform)
475
476
1/2
✓ Branch 9 → 10 taken 234 times.
✗ Branch 9 → 12 not taken.
234 if (present(solver)) then
477 234 call solver%solve(c, f)
478 else
479 call mass%init(space, n_quad_extra=n_quad_extra, coord_transform=coord_transform)
480 call solver_%init(mass, coord_transform=coord_transform)
481 call solver_%solve(c, f)
482
483 call mass%destroy()
484 call solver_%destroy()
485 end if
486
487 234 call f%destroy()
488 TIMER_STOP(t_total)
489
7/80
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 234 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 234 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 95 taken 234 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 71 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 31 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 70 not taken.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 47 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 46 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 58 not taken.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 57 not taken.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 52 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 69 not taken.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 68 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 65 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 75 → 77 not taken.
✗ Branch 77 → 78 not taken.
✗ Branch 77 → 79 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 94 not taken.
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 93 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 86 not taken.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 97 taken 234 times.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 99 taken 234 times.
✓ Branch 99 → 100 taken 234 times.
✗ Branch 99 → 101 not taken.
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 103 taken 234 times.
234 end subroutine l2_projection_mform_vector
490
491 !> @brief Compute the L2 norm of the difference between a scalar-valued m-form and a user-defined function
492 !>
493 !> @param[in] v The m-form to compare with the user-defined function
494 !> @param[in] userfun The user-defined function to compare with the m-form
495 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
496 !> @param[in] n_quad_extra _(optional)_ The extra number of quadrature points to use for the L2 error on top of those needed
497 !> to exactly integrate the B-spline spaces
498 !> @param[in] user_fun_is_physical _(optional)_ Whether the user function is defined in physical coordinates (default: .false.)
499 !>
500 !> @return The L2 norm of the difference between the m-form and the user-defined function
501 343 real(wp) function l2_error_mform_scalar(v, userfun, coord_transform, n_quad_extra, user_fun_is_physical) result(ans)
502 use m_tensorprod_basis, only: evaluate
503 use m_tensorprod_linalg, only: integrate
504 use m_coord_transform, only: CoordTransformAbstract, jacobian, transform
505 implicit none
506
507 type(MFormFun), intent(in) :: v
508 procedure(user_function_3d_interface) :: userfun
509 class(CoordTransformAbstract), intent(in), optional :: coord_transform
510 integer, intent(in), optional :: n_quad_extra
511 logical, intent(in), optional :: user_fun_is_physical
512
513 logical :: user_fun_is_physical_
514
515 TIMER_DECL(t_total)
516 TIMER_INIT_START(t_total, "MFormFun::l2_error")
517
518
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 343 times.
343 if (v%space%m == 1 .or. v%space%m == 2) then
519 error stop "l2_error for vector-valued forms requires three userfunctions"
520 end if
521
522
3/4
✓ Branch 4 → 5 taken 139 times.
✓ Branch 4 → 7 taken 204 times.
✓ Branch 5 → 6 taken 139 times.
✗ Branch 5 → 7 not taken.
343 if (present(coord_transform)) then
523 139 user_fun_is_physical_ = present(user_fun_is_physical)
524 else
525 204 user_fun_is_physical_ = .false.
526 end if
527
528 343 ans = sqrt(integrate(v%space%tp_spaces(1), int_function, n_quad_extra=n_quad_extra))
529
530 TIMER_STOP(t_total)
531 contains
532 48519616 pure real(wp) function int_function(xp, yp, zp) result(val)
533 real(wp), intent(in) :: xp, yp, zp
534
535 real(wp) :: x, y, z, user_val, jac_val
536
537
3/4
✓ Branch 2 → 3 taken 34861144 times.
✓ Branch 2 → 5 taken 13658472 times.
✓ Branch 3 → 4 taken 34861144 times.
✗ Branch 3 → 5 not taken.
48519616 if (present(coord_transform)) then
538 34861144 jac_val = jacobian(coord_transform, xp, yp, zp)
539 else
540 jac_val = 1.0_wp
541 end if
542
543
1/2
✓ Branch 5 → 6 taken 48519616 times.
✗ Branch 5 → 7 not taken.
48519616 if (.not. user_fun_is_physical_) then
544 48519616 user_val = userfun(xp, yp, zp)
545 else
546 if (present(coord_transform)) then
547 x = transform(coord_transform, 1, xp, yp, zp)
548 y = transform(coord_transform, 2, xp, yp, zp)
549 z = transform(coord_transform, 3, xp, yp, zp)
550 user_val = userfun(x, y, z)
551
552 if (v%space%m == 3) then
553 ! The pullback transformation for 3-forms yields a factor jacobian
554 user_val = user_val * jac_val
555 end if
556 end if
557 end if
558 48519616 val = (user_val - evaluate(v%tp_funs(1), xp, yp, zp))**2 ! value of the logical m-form
559
560
3/4
✓ Branch 12 → 13 taken 34861144 times.
✓ Branch 12 → 17 taken 13658472 times.
✓ Branch 13 → 14 taken 34861144 times.
✗ Branch 13 → 17 not taken.
48519616 if (present(coord_transform)) then
561
2/2
✓ Branch 14 → 15 taken 6758580 times.
✓ Branch 14 → 16 taken 28102564 times.
34861144 if (v%space%m == 3) then
562 6758580 val = val / jac_val
563 else
564 28102564 val = val * jac_val
565 end if
566 end if
567 48519616 end function int_function
568 end function
569
570 !> @brief Compute the L2 norm of the difference between a vector-valued m-form and a user-defined function
571 !>
572 !> @param[in] v The m-form to compare with the user-defined function
573 !> @param[in] userfun_x The x-component of the user-defined function to compare with the m-form
574 !> @param[in] userfun_y The y-component of the user-defined function to compare with the m-form
575 !> @param[in] userfun_z The z-component of the user-defined function to compare with the m-form
576 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
577 !> @param[in] n_quad_extra _(optional)_ The extra number of quadrature points to use for the L2 error on top of those needed
578 !> to exactly integrate the B-spline spaces
579 !> @param[in] user_fun_is_physical _(optional)_ Whether the user functions are defined in physical coordinates (default: .false.)
580 !>
581 !> @return The L2 norm of the difference between the m-form and the user-defined function
582 337 real(wp) function l2_error_mform_vector(v, userfun_x, userfun_y, userfun_z, coord_transform &
583 , n_quad_extra, user_fun_is_physical) result(ans)
584 use m_mform_basis, only: MFormSpace, get_zero_form_space
585 use m_tensorprod_basis, only: TensorProdSpace, evaluate
586 use m_tensorprod_linalg, only: integrate
587 use m_coord_transform, only: CoordTransformAbstract, jacobian, transform, jacobian_matrix, jacobian_matrix_inv, G_matrix, &
588 G_matrix_inv
589 implicit none
590
591 type(MFormFun), intent(in) :: v
592 procedure(user_function_3d_interface) :: userfun_x, userfun_y, userfun_z
593 class(CoordTransformAbstract), intent(in), optional :: coord_transform
594 integer, intent(in), optional :: n_quad_extra
595 logical, intent(in), optional :: user_fun_is_physical
596
597 logical :: user_fun_is_physical_
598 type(MFormSpace) :: space0
599
600 TIMER_DECL(t_total)
601 TIMER_INIT_START(t_total, "MFormFun::l2_error")
602
603
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 337 times.
337 if (v%space%m == 0 .or. v%space%m == 3) then
604 error stop "l2_error for scalar-valued forms requires one userfunction"
605 end if
606
607
3/4
✓ Branch 4 → 5 taken 110 times.
✓ Branch 4 → 7 taken 227 times.
✓ Branch 5 → 6 taken 110 times.
✗ Branch 5 → 7 not taken.
337 if (present(coord_transform)) then
608 110 user_fun_is_physical_ = present(user_fun_is_physical)
609 else
610 227 user_fun_is_physical_ = .false.
611 end if
612
613 ! TODO: user_fun_is_physical (assumed .false. in current implementation)
614
615 337 space0 = get_zero_form_space(v%space)
616
617
1/2
✓ Branch 10 → 11 taken 337 times.
✗ Branch 10 → 12 not taken.
337 ans = sqrt(integrate(space0%tp_spaces(1), int_function, n_quad_extra=n_quad_extra))
618
619 TIMER_STOP(t_total)
620 contains
621 51742602 pure real(wp) function int_function(xp, yp, zp) result(val)
622 real(wp), intent(in) :: xp, yp, zp
623
624 real(wp) :: x, y, z, jac_val, diff_x, diff_y, diff_z
625 real(wp) :: userfun_xp_val, userfun_yp_val, userfun_zp_val, userfun_x_val, userfun_y_val, userfun_z_val
626 real(wp) :: mat(3, 3)
627
628
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 11 taken 51742602 times.
51742602 if (user_fun_is_physical_) then
629 if (present(coord_transform)) then
630 x = transform(coord_transform, 1, xp, yp, zp)
631 y = transform(coord_transform, 2, xp, yp, zp)
632 z = transform(coord_transform, 3, xp, yp, zp)
633 userfun_x_val = userfun_x(x, y, z)
634 userfun_y_val = userfun_y(x, y, z)
635 userfun_z_val = userfun_z(x, y, z)
636
637 if (v%space%m == 1) then
638 userfun_xp_val = jacobian_matrix(coord_transform, 1, 1, xp, yp, zp) * userfun_x_val + &
639 jacobian_matrix(coord_transform, 2, 1, xp, yp, zp) * userfun_y_val + &
640 jacobian_matrix(coord_transform, 3, 1, xp, yp, zp) * userfun_z_val
641 userfun_yp_val = jacobian_matrix(coord_transform, 1, 2, xp, yp, zp) * userfun_x_val + &
642 jacobian_matrix(coord_transform, 2, 2, xp, yp, zp) * userfun_y_val + &
643 jacobian_matrix(coord_transform, 3, 2, xp, yp, zp) * userfun_z_val
644 userfun_zp_val = jacobian_matrix(coord_transform, 1, 3, xp, yp, zp) * userfun_x_val + &
645 jacobian_matrix(coord_transform, 2, 3, xp, yp, zp) * userfun_y_val + &
646 jacobian_matrix(coord_transform, 3, 3, xp, yp, zp) * userfun_z_val
647 else
648 jac_val = jacobian(coord_transform, xp, yp, zp)
649 userfun_xp_val = jacobian_matrix_inv(coord_transform, 1, 1, xp, yp, zp) * userfun_x_val + &
650 jacobian_matrix_inv(coord_transform, 1, 2, xp, yp, zp) * userfun_y_val + &
651 jacobian_matrix_inv(coord_transform, 1, 3, xp, yp, zp) * userfun_z_val
652 userfun_yp_val = jacobian_matrix_inv(coord_transform, 2, 1, xp, yp, zp) * userfun_x_val + &
653 jacobian_matrix_inv(coord_transform, 2, 2, xp, yp, zp) * userfun_y_val + &
654 jacobian_matrix_inv(coord_transform, 2, 3, xp, yp, zp) * userfun_z_val
655 userfun_zp_val = jacobian_matrix_inv(coord_transform, 3, 1, xp, yp, zp) * userfun_x_val + &
656 jacobian_matrix_inv(coord_transform, 3, 2, xp, yp, zp) * userfun_y_val + &
657 jacobian_matrix_inv(coord_transform, 3, 3, xp, yp, zp) * userfun_z_val
658 userfun_xp_val = userfun_xp_val * jac_val
659 userfun_yp_val = userfun_yp_val * jac_val
660 userfun_zp_val = userfun_zp_val * jac_val
661 end if
662 end if
663 else
664 51742602 userfun_xp_val = userfun_x(xp, yp, zp)
665 51742602 userfun_yp_val = userfun_y(xp, yp, zp)
666 51742602 userfun_zp_val = userfun_z(xp, yp, zp)
667 end if
668 51742602 diff_x = userfun_xp_val - evaluate(v%tp_funs(1), xp, yp, zp)
669 51742602 diff_y = userfun_yp_val - evaluate(v%tp_funs(2), xp, yp, zp)
670 51742602 diff_z = userfun_zp_val - evaluate(v%tp_funs(3), xp, yp, zp)
671
672
3/4
✓ Branch 14 → 15 taken 26832552 times.
✓ Branch 14 → 16 taken 24910050 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 26832552 times.
51742602 if (.not. present(coord_transform)) then
673 24910050 val = diff_x**2 + diff_y**2 + diff_z**2
674 else
675 26832552 jac_val = jacobian(coord_transform, xp, yp, zp)
676
2/2
✓ Branch 17 → 18 taken 11834580 times.
✓ Branch 17 → 20 taken 14997972 times.
26832552 if (v%space%m == 1) then
677 11834580 mat(1, 1) = G_matrix_inv(coord_transform, 1, 1, xp, yp, zp) * jac_val
678 11834580 mat(2, 2) = G_matrix_inv(coord_transform, 2, 2, xp, yp, zp) * jac_val
679 11834580 mat(3, 3) = G_matrix_inv(coord_transform, 3, 3, xp, yp, zp) * jac_val
680
2/2
✓ Branch 18 → 19 taken 2644416 times.
✓ Branch 18 → 22 taken 9190164 times.
11834580 if (.not. coord_transform%is_orthogonal) then
681 2644416 mat(1, 2) = G_matrix_inv(coord_transform, 1, 2, xp, yp, zp) * jac_val
682 2644416 mat(1, 3) = G_matrix_inv(coord_transform, 1, 3, xp, yp, zp) * jac_val
683 2644416 mat(2, 3) = G_matrix_inv(coord_transform, 2, 3, xp, yp, zp) * jac_val
684 end if
685 else ! if (v%space%m == 2) then
686 14997972 mat(1, 1) = G_matrix(coord_transform, 1, 1, xp, yp, zp) / jac_val
687 14997972 mat(2, 2) = G_matrix(coord_transform, 2, 2, xp, yp, zp) / jac_val
688 14997972 mat(3, 3) = G_matrix(coord_transform, 3, 3, xp, yp, zp) / jac_val
689
2/2
✓ Branch 20 → 21 taken 2644416 times.
✓ Branch 20 → 22 taken 12353556 times.
14997972 if (.not. coord_transform%is_orthogonal) then
690 2644416 mat(1, 2) = G_matrix(coord_transform, 1, 2, xp, yp, zp) / jac_val
691 2644416 mat(1, 3) = G_matrix(coord_transform, 1, 3, xp, yp, zp) / jac_val
692 2644416 mat(2, 3) = G_matrix(coord_transform, 2, 3, xp, yp, zp) / jac_val
693 end if
694 end if
695 26832552 val = diff_x**2 * mat(1, 1) + diff_y**2 * mat(2, 2) + diff_z**2 * mat(3, 3)
696
2/2
✓ Branch 22 → 23 taken 5288832 times.
✓ Branch 22 → 24 taken 21543720 times.
26832552 if (.not. coord_transform%is_orthogonal) then
697 5288832 val = val + 2.0_wp * (diff_x * diff_y * mat(1, 2) + diff_x * diff_z * mat(1, 3) + diff_y * diff_z * mat(2, 3))
698 end if
699 end if
700
701 51742602 end function int_function
702
703 end function
704
705 !> @brief Compute the L2 norm of an m-form
706 !>
707 !> @param[in] v The m-form
708 !> @param[in] coord_transform _(optional)_ The coordinate transformation associated with the m-form space
709 !>
710 !> @return The L2 norm of the m-form
711 39 real(wp) function l2_norm_mform(v, coord_transform) result(ans)
712 use m_common, only: zero_function
713 use m_coord_transform, only: CoordTransformAbstract
714 implicit none
715
716 type(MFormFun), intent(in) :: v
717 class(CoordTransformAbstract), intent(in), optional :: coord_transform
718
719 TIMER_DECL(t_total)
720 TIMER_INIT_START(t_total, "MFormFun::l2_norm")
721
722
2/2
✓ Branch 2 → 3 taken 37 times.
✓ Branch 2 → 4 taken 2 times.
39 if (v%space%m == 1 .or. v%space%m == 2) then
723 37 ans = l2_error(v, zero_function, zero_function, zero_function, coord_transform=coord_transform)
724 else
725 2 ans = l2_error(v, zero_function, coord_transform=coord_transform)
726 end if
727 TIMER_STOP(t_total)
728 39 end function
729
730 !> @brief Find the weakly divergence-free one-form whose curl is the given two-form (in 2D, assuming homogeneous BC)
731 !>
732 !> @param[out] oneform The resulting one-form whose curl is the given two-form
733 !> @param[in] twoform_x The x-component of the two-form whose curl 'inverse' is to be computed
734 !> @param[in] twoform_y The y-component of the two-form whose curl 'inverse' is to be computed
735 !> @param[in] twoform_space The space of the provided two-form components
736 !> @param[in] cylinder_transform The coordinate transformation associated with the m-form space
737 !> @param[inout] zeroform_solver _(optional)_ The divgrad solver, if not provided, a solver is created internally
738 !> @param[in] constraint _(optional)_ The constraint to be applied to both solvers (besides the boundary conditions)
739
2/2
✓ Branch 4 → 5 taken 175 times.
✓ Branch 4 → 6 taken 74 times.
249 subroutine solve_curlA_equals_B_tensorprod(oneform, twoform_x, twoform_y, twoform_space, cylinder_transform, zeroform_solver, &
740 constraint)
741 use m_common, only: zero_function, user_function_3d_interface, cumsum
742 use m_bspline_basis, only: BSplineFun
743 use m_mform_solver, only: GenericSolver
744 use m_mform_matrix, only: MassMatrix, DiffDiffMatrix
745 use m_mform_basis, only: MFormFun, gradient, gradient_adjoint, curl_adjoint, previous
746 use m_mform_constraint_abstract, only: MFormConstraintLocal
747 use m_mform_constraint_boundary, only: MFormDirichlet, MFormNeumann
748 use m_coord_transform, only: CylinderTransform
749 implicit none
750
751 type(MFormFun), intent(out) :: oneform
752 type(BSplineFun), intent(in) :: twoform_x, twoform_y
753 type(MFormSpace), intent(in) :: twoform_space
754 type(GenericSolver), intent(inout), target, optional :: zeroform_solver
755 class(MFormConstraintLocal), intent(in), optional :: constraint
756 type(CylinderTransform), intent(in), optional :: cylinder_transform
757 249 type(GenericSolver), target :: zeroform_solver_
758 type(GenericSolver), pointer :: zeroform_solver_p
759 249 type(DiffDiffMatrix) :: divgrad
760
2/2
✓ Branch 2 → 3 taken 178 times.
✓ Branch 2 → 4 taken 71 times.
249 type(MFormFun) :: f, df, zeroform, grad, oneform_tmp
761 type(MFormSpace) :: oneform_space
762 class(MFormConstraintLocal), allocatable :: constraint_local
763 integer :: twoform_m, i, j, k
764 character(len=100) :: err_msg
765
766 TIMER_DECL(t_total)
767 TIMER_INIT_START(t_total, "MFormFun::solve_curlA_equals_B")
768
769 ! TODO calling previous() multiple times (for each twoform) is not efficient
770 249 oneform_space = previous(twoform_space)
771 249 twoform_m = twoform_space%m
772
773
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 14 taken 249 times.
249 if (twoform_m /= 2) then
774 write (err_msg, '(A,I0,A)') "ERROR in solve_curlA_equals_B_ori: the provided space is a ", twoform_m, &
775 "-form, but a 2-form is required."
776 error stop err_msg
777 end if
778
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 249 times.
249 if (twoform_space%dimensionality == 3) then
779 error stop "ERROR in solve_curlA_equals_B_ori: the provided space is not 2D, but a 2D space is required."
780 end if
781
782
2/2
✓ Branch 16 → 17 taken 35 times.
✓ Branch 16 → 60 taken 214 times.
249 if (present(zeroform_solver)) then
783 zeroform_solver_p => zeroform_solver
784 else
785
1/2
✓ Branch 19 → 20 taken 35 times.
✗ Branch 19 → 21 not taken.
35 call divgrad%init(previous(oneform_space), coord_transform=cylinder_transform)
786
4/34
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 35 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 37 taken 35 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 36 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 48 taken 35 times.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 47 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 59 taken 35 times.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 58 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
35 call zeroform_solver_%init(divgrad, constraint1=MFormNeumann(), constraint2=constraint, coord_transform=cylinder_transform)
787 zeroform_solver_p => zeroform_solver_
788 end if
789
790 ! Step one: find a one-form such that curl oneform = twoform
791 ! This can be done analytically since the two-form is given as a product of 1D B-splines
792 249 call twoform_x_integrated%init(oneform_space%tp_spaces(2)%spaces(1))
793 249 call cumsum(twoform_x_integrated%data, twoform_x%data)
794 249 call twoform_y_copy%init(twoform_y%bspline)
795
4/10
✓ Branch 63 → 64 taken 249 times.
✗ Branch 63 → 65 not taken.
✗ Branch 64 → 65 not taken.
✓ Branch 64 → 71 taken 249 times.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 67 → 69 not taken.
✓ Branch 72 → 73 taken 7607 times.
✓ Branch 72 → 74 taken 249 times.
8105 twoform_y_copy%data = twoform_y%data
796 249 call oneform_tmp%init(oneform_space)
797
2/2
✓ Branch 76 → 77 taken 249 times.
✓ Branch 76 → 86 taken 249 times.
498 do k = oneform_space%tp_spaces(2)%rank_resp_bspline%k0, oneform_space%tp_spaces(2)%rank_resp_bspline%k1
798
2/2
✓ Branch 78 → 79 taken 6876 times.
✓ Branch 78 → 84 taken 249 times.
7374 do j = oneform_space%tp_spaces(2)%rank_resp_bspline%j0, oneform_space%tp_spaces(2)%rank_resp_bspline%j1
799
2/2
✓ Branch 80 → 81 taken 163388 times.
✓ Branch 80 → 82 taken 6876 times.
170513 do i = oneform_space%tp_spaces(2)%rank_resp_bspline%i0, oneform_space%tp_spaces(2)%rank_resp_bspline%i1
800 170264 oneform_tmp%tp_funs(2)%data(i, j, k) = twoform_x_integrated%data(i) * twoform_y%data(j)
801 end do
802 end do
803 end do
804
805 249 call oneform_tmp%distribute()
806
807 ! Step two: project the one-form onto the weakly divergence-free space
808 249 call inner_product(f, oneform_space, zero_function, wrapped_oneform_y, zero_function, cylinder_transform)
809
810 249 call gradient_adjoint(df, f)
811 249 call zeroform_solver_p%solve(zeroform, df)
812 249 call gradient(grad, zeroform)
813 249 call oneform_tmp%axpy(-1.0_wp, grad)
814
815
3/4
✓ Branch 93 → 94 taken 35 times.
✓ Branch 93 → 107 taken 214 times.
✓ Branch 94 → 95 taken 35 times.
✗ Branch 94 → 107 not taken.
249 if (present(constraint)) then
816
1/2
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 97 taken 35 times.
35 allocate (constraint_local, source=constraint)
817 35 call constraint_local%init(oneform_space, cylinder_transform)
818 35 call constraint_local%apply(oneform, oneform_tmp)
819 35 call constraint_local%destroy()
820
2/4
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 103 taken 35 times.
✓ Branch 103 → 104 taken 35 times.
✗ Branch 103 → 106 not taken.
70 deallocate (constraint_local)
821 else
822 ! NOTE: this is a deep copy using the overloaded assignment(=) operator
823 214 oneform = oneform_tmp
824 end if
825
826 249 call f%destroy()
827 249 call df%destroy()
828 249 call grad%destroy()
829 249 call zeroform%destroy()
830 249 call oneform_tmp%destroy()
831 249 call twoform_x_integrated%destroy()
832
833
21/98
✓ Branch 115 → 116 taken 35 times.
✓ Branch 115 → 119 taken 214 times.
✓ Branch 119 → 120 taken 35 times.
✓ Branch 119 → 121 taken 214 times.
✓ Branch 121 → 122 taken 35 times.
✓ Branch 121 → 123 taken 214 times.
✗ Branch 123 → 124 not taken.
✓ Branch 123 → 195 taken 249 times.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 171 not taken.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 131 not taken.
✗ Branch 129 → 130 not taken.
✗ Branch 129 → 131 not taken.
✗ Branch 131 → 132 not taken.
✗ Branch 131 → 170 not taken.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 134 → 135 not taken.
✗ Branch 134 → 147 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 146 not taken.
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
✗ Branch 139 → 140 not taken.
✗ Branch 139 → 141 not taken.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
✗ Branch 143 → 144 not taken.
✗ Branch 143 → 145 not taken.
✗ Branch 147 → 148 not taken.
✗ Branch 147 → 158 not taken.
✗ Branch 149 → 150 not taken.
✗ Branch 149 → 157 not taken.
✗ Branch 150 → 151 not taken.
✗ Branch 150 → 152 not taken.
✗ Branch 152 → 153 not taken.
✗ Branch 152 → 154 not taken.
✗ Branch 154 → 155 not taken.
✗ Branch 154 → 156 not taken.
✗ Branch 158 → 159 not taken.
✗ Branch 158 → 169 not taken.
✗ Branch 160 → 161 not taken.
✗ Branch 160 → 168 not taken.
✗ Branch 161 → 162 not taken.
✗ Branch 161 → 163 not taken.
✗ Branch 163 → 164 not taken.
✗ Branch 163 → 165 not taken.
✗ Branch 165 → 166 not taken.
✗ Branch 165 → 167 not taken.
✗ Branch 171 → 172 not taken.
✗ Branch 171 → 173 not taken.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
✗ Branch 175 → 176 not taken.
✗ Branch 175 → 177 not taken.
✗ Branch 177 → 178 not taken.
✗ Branch 177 → 179 not taken.
✗ Branch 179 → 180 not taken.
✗ Branch 179 → 181 not taken.
✗ Branch 181 → 182 not taken.
✗ Branch 181 → 194 not taken.
✗ Branch 183 → 184 not taken.
✗ Branch 183 → 193 not taken.
✗ Branch 184 → 185 not taken.
✗ Branch 184 → 186 not taken.
✗ Branch 186 → 187 not taken.
✗ Branch 186 → 188 not taken.
✗ Branch 188 → 189 not taken.
✗ Branch 188 → 190 not taken.
✗ Branch 190 → 191 not taken.
✗ Branch 190 → 192 not taken.
✓ Branch 195 → 196 taken 249 times.
✗ Branch 195 → 197 not taken.
✗ Branch 197 → 198 not taken.
✓ Branch 197 → 199 taken 249 times.
✓ Branch 199 → 200 taken 249 times.
✗ Branch 199 → 201 not taken.
✗ Branch 201 → 202 not taken.
✓ Branch 201 → 203 taken 249 times.
✓ Branch 203 → 204 taken 249 times.
✗ Branch 203 → 205 not taken.
✗ Branch 205 → 206 not taken.
✓ Branch 205 → 207 taken 249 times.
✓ Branch 207 → 208 taken 249 times.
✗ Branch 207 → 209 not taken.
✗ Branch 209 → 210 not taken.
✓ Branch 209 → 211 taken 249 times.
✓ Branch 211 → 212 taken 35 times.
✓ Branch 211 → 213 taken 214 times.
✓ Branch 213 → 214 taken 35 times.
✓ Branch 213 → 215 taken 214 times.
✓ Branch 215 → 216 taken 249 times.
✗ Branch 215 → 217 not taken.
✗ Branch 217 → 218 not taken.
✓ Branch 217 → 219 taken 249 times.
284 if (.not. present(zeroform_solver)) then
834 35 call divgrad%destroy()
835 35 call zeroform_solver_%destroy()
836 end if
837 TIMER_STOP(t_total)
838 contains
839 ! pure real(wp) function wrapped_oneform_x(xp, yp, zp) result(ans)
840 ! implicit none
841
842 ! real(wp), intent(in) :: xp, yp, zp
843
844 ! ans = evaluate(oneform_tmp, 1, xp, yp, zp, eval_logical_form=.true.)
845 ! end function wrapped_oneform_x
846
847 17569824 pure real(wp) function wrapped_oneform_y(xp, yp, zp) result(ans)
848 use m_bspline_basis, only: evaluate
849 implicit none
850
851 real(wp), intent(in) :: xp, yp, zp
852
853 ! ans = evaluate(oneform_tmp, 2, xp, yp, zp, eval_logical_form=.true.)
854 17569824 ans = evaluate(twoform_x_integrated, xp) * evaluate(twoform_y_copy, yp)
855 17569824 end function wrapped_oneform_y
856 end subroutine solve_curlA_equals_B_tensorprod
857
858 end module m_mform_linalg
859