GCC Code Coverage Report


Directory: ./
File: src/field_can.f90
Date: 2026-10-22 05:02:02
Coverage Exec Excl Total
Lines: 80.6% 125 0 155
Functions: 69.2% 9 0 13
Branches: 72.3% 60 0 83

Line Branch Exec Source
1 module field_can_mod
2 use diag_mod, only : icounter
3 use boozer_sub, only : splint_boozer_coord
4 use magfie_sub, only : TEST, CANFLUX, BOOZER, MEISS, ALBERT, SPECTRE
5 use field, only : magnetic_field_t, vmec_field_t, create_vmec_field
6 use field_can_base, only : twopi, evaluate_base => evaluate, coordinate_transform, &
7 identity_transform, field_can_t
8 use field_can_test, only : evaluate_test
9 use field_can_flux, only : evaluate_flux, integ_to_ref_flux, ref_to_integ_flux
10 use field_can_boozer, only : evaluate_boozer, integ_to_ref_boozer, ref_to_integ_boozer, &
11 integ_to_ref_boozer_chartmap, ref_to_integ_boozer_chartmap
12 use field_can_meiss, only : init_meiss, evaluate_meiss, &
13 integ_to_ref_meiss, ref_to_integ_meiss, cleanup_meiss
14 use field_can_albert, only : evaluate_albert, init_albert, integ_to_ref_albert, &
15 ref_to_integ_albert, cleanup_albert
16 use field_can_spectre, only : construct_spectre_coordinates, evaluate_spectre_can, &
17 integ_to_ref_spectre, ref_to_integ_spectre
18 use field_boozer_chartmap, only : boozer_chartmap_field_t
19
20 implicit none
21
22 ! Define real(dp) kind parameter
23 integer, parameter :: dp = kind(1.0d0)
24
25 procedure(evaluate_base), pointer :: evaluate => null()
26
27 ! Conversion to and from reference coordinates - currently VMEC coordinates (s, th, ph)
28 procedure(coordinate_transform), pointer :: integ_to_ref => identity_transform
29 procedure(coordinate_transform), pointer :: ref_to_integ => identity_transform
30
31 !> Build the regular near-axis field representation (libneo boozer_regular,
32 !> evaluated in X = rho cos(theta), Y = rho sin(theta), phi) alongside the
33 !> polar splines when a Boozer field is initialised. Off by default: the
34 !> representation costs a second 3D spline and only the Cartesian canonical
35 !> chart evaluates it.
36 logical :: regular_axis_field = .false.
37
38 contains
39
40 6 subroutine set_regular_axis_field(enabled)
41 logical, intent(in) :: enabled
42
43 6 regular_axis_field = enabled
44 6 end subroutine set_regular_axis_field
45
46 ✗ subroutine init_field_can_boozer_chartmap_compact()
47 ✗ call field_can_from_name("boozer")
48 ✗ integ_to_ref => integ_to_ref_boozer_chartmap
49 ✗ ref_to_integ => ref_to_integ_boozer_chartmap
50 ✗ end subroutine init_field_can_boozer_chartmap_compact
51
52 95 subroutine field_can_from_name(field_name, field_noncan, n_r, n_th, n_phi, &
53 transformation_relerr)
54
55 character(*), intent(in) :: field_name
56 !> For field_can_tMeiss
57 class(magnetic_field_t), intent(in), optional :: field_noncan
58 integer, intent(in), optional :: n_r, n_th, n_phi
59 real(dp), intent(in), optional :: transformation_relerr
60
61 114 select case(trim(field_name))
62 case("test")
63 19 call cleanup_albert
64 19 call cleanup_meiss
65 19 evaluate => evaluate_test
66 case("flux")
67 6 call cleanup_albert
68 6 call cleanup_meiss
69 6 evaluate => evaluate_flux
70 6 integ_to_ref => integ_to_ref_flux
71 6 ref_to_integ => ref_to_integ_flux
72 case("boozer")
73 46 call cleanup_albert
74 46 call cleanup_meiss
75 46 evaluate => evaluate_boozer
76 46 integ_to_ref => integ_to_ref_boozer
77 46 ref_to_integ => ref_to_integ_boozer
78 case("meiss")
79 4 call cleanup_albert
80
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if (present(field_noncan)) then
81 call init_meiss(field_noncan, n_r, n_th, n_phi, &
82 4 transformation_relerr_=transformation_relerr)
83 end if
84 4 evaluate => evaluate_meiss
85 4 integ_to_ref => integ_to_ref_meiss
86 4 ref_to_integ => ref_to_integ_meiss
87 case("albert")
88
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (present(field_noncan)) then
89 call init_albert(field_noncan, n_r, n_th, n_phi, &
90 3 transformation_relerr_=transformation_relerr)
91 end if
92 3 evaluate => evaluate_albert
93 3 integ_to_ref => integ_to_ref_albert
94 3 ref_to_integ => ref_to_integ_albert
95 case("spectre")
96 17 evaluate => evaluate_spectre_can
97 17 integ_to_ref => integ_to_ref_spectre
98 17 ref_to_integ => ref_to_integ_spectre
99 case default
100 ✗ print *, "field_can_from_name: Unknown field type ", field_name
101
6/7
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 17 times.
✗ Branch 6 not taken.
95 error stop
102 end select
103 95 end subroutine field_can_from_name
104
105
106 79 subroutine field_can_from_id(field_id, field_noncan, n_r, n_th, n_phi, &
107 transformation_relerr)
108 integer, intent(in) :: field_id
109 !> For field_can_tMeiss
110 class(magnetic_field_t), intent(in), optional :: field_noncan
111 integer, intent(in), optional :: n_r, n_th, n_phi
112 real(dp), intent(in), optional :: transformation_relerr
113
114
3/4
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 71 times.
✗ Branch 3 not taken.
79 if (present(field_noncan)) then
115 call field_can_from_name(name_from_id(field_id), field_noncan, n_r, n_th, &
116 71 n_phi, transformation_relerr)
117 else
118 8 call field_can_from_name(name_from_id(field_id))
119 end if
120
121 79 end subroutine field_can_from_id
122
123
124 79 function name_from_id(field_id)
125 character(128) :: name_from_id
126 integer, intent(in) :: field_id
127
128 83 select case(field_id)
129 case(TEST)
130 4 name_from_id = "test"
131 case(CANFLUX)
132 6 name_from_id = "flux"
133 case(BOOZER)
134 46 name_from_id = "boozer"
135 case(MEISS)
136 3 name_from_id = "meiss"
137 case(ALBERT)
138 3 name_from_id = "albert"
139 case(SPECTRE)
140 17 name_from_id = "spectre"
141 case default
142 ✗ print *, "name_from_id: Unknown field id ", field_id
143
6/7
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 17 times.
✗ Branch 6 not taken.
79 error stop
144 end select
145 79 end function name_from_id
146
147
148 ✗ function id_from_name(field_name)
149 integer :: id_from_name
150 character(*), intent(in) :: field_name
151
152 ✗ select case(trim(field_name))
153 case("test")
154 id_from_name = TEST
155 case("flux")
156 id_from_name = CANFLUX
157 case("boozer")
158 id_from_name = BOOZER
159 case("meiss")
160 id_from_name = MEISS
161 case("albert")
162 id_from_name = ALBERT
163 case("spectre")
164 ✗ id_from_name = SPECTRE
165 case default
166 ✗ print *, "id_from_name: Unknown field type ", field_name
167 ✗ error stop
168 end select
169 ✗ end function id_from_name
170
171
172 79 subroutine init_field_can(field_id, field_noncan, n_r, n_th, n_phi, &
173 transformation_relerr)
174 use get_can_sub, only : get_canonical_coordinates, get_canonical_coordinates_with_field
175 use boozer_sub, only : get_boozer_coordinates
176 use boozer_chartmap, only : load_boozer_from_chartmap
177 use field_can_meiss, only : get_meiss_coordinates
178 use field_can_albert, only : get_albert_coordinates
179 use boozer_regular, only: set_boozer_regular_enabled
180
181 integer, intent(in) :: field_id
182 class(magnetic_field_t), intent(in), optional :: field_noncan
183 integer, intent(in), optional :: n_r, n_th, n_phi
184 real(dp), intent(in), optional :: transformation_relerr
185 79 type(vmec_field_t) :: vmec_field
186
187 ! The libneo Boozer builders read this switch; set it on every
188 ! initialisation so a library run that changes fields starts clean.
189
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
156 call set_boozer_regular_enabled(regular_axis_field .and. field_id == BOOZER)
190
191
3/4
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 71 times.
✗ Branch 3 not taken.
79 if (present(field_noncan)) then
192 call field_can_from_id(field_id, field_noncan, n_r, n_th, n_phi, &
193 71 transformation_relerr)
194 71 select case (field_id)
195 case (TEST)
196 5 continue
197 case (CANFLUX)
198 5 call get_canonical_coordinates_with_field(field_noncan)
199 case (BOOZER)
200 3 select type (f => field_noncan)
201 type is (boozer_chartmap_field_t)
202 8 call load_boozer_from_chartmap(f%filename)
203 ! Chartmap ref coords use rho=sqrt(s), integrator uses s
204 8 integ_to_ref => integ_to_ref_boozer_chartmap
205 8 ref_to_integ => ref_to_integ_boozer_chartmap
206 class default
207 35 call get_boozer_coordinates()
208 end select
209 case (MEISS)
210 3 call get_meiss_coordinates
211 case (ALBERT)
212 3 call get_albert_coordinates
213 case (SPECTRE)
214 17 call construct_spectre_coordinates(field_noncan)
215 case default
216 ✗ print *, "init_field_can: Unknown field id ", field_id
217
5/7
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
71 error stop
218 end select
219 return
220 end if
221
222 12 select case (field_id)
223 case (TEST)
224 4 call field_can_from_id(field_id)
225 case (CANFLUX)
226 1 call field_can_from_id(field_id)
227 1 call get_canonical_coordinates
228 case (BOOZER)
229 3 call field_can_from_id(field_id)
230 3 call get_boozer_coordinates
231 case (MEISS)
232 ✗ call create_vmec_field(vmec_field)
233 call field_can_from_id(field_id, vmec_field, n_r, n_th, n_phi, &
234 ✗ transformation_relerr)
235 ✗ call get_meiss_coordinates
236 case (ALBERT)
237 ✗ call create_vmec_field(vmec_field)
238 call field_can_from_id(field_id, vmec_field, n_r, n_th, n_phi, &
239 ✗ transformation_relerr)
240 ✗ call get_albert_coordinates
241 case default
242 ✗ print *, "init_field_can: Unknown field id ", field_id
243
3/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 error stop
244 end select
245
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
79 end subroutine init_field_can
246
247
248 19 subroutine field_can_init(f, mu, ro0, vpar)
249 type(field_can_t), intent(inout) :: f
250 real(dp), intent(in), optional :: mu, ro0, vpar
251
252
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (present(mu)) then
253 19 f%mu = mu
254 else
255 ✗ f%mu = 0d0
256 end if
257
258
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (present(ro0)) then
259 19 f%ro0 = ro0
260 else
261 ✗ f%ro0 = 0d0
262 end if
263
264
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (present(vpar)) then
265 19 f%vpar = vpar
266 else
267 ✗ f%vpar = 0d0
268 end if
269
270 79 end subroutine field_can_init
271
272
273 !ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
274 !
275 26505867 subroutine get_val(f, pphi)
276 !
277 ! computes values of H, pth and vpar at z=(r, th, ph, pphi)
278 !
279 !
280 type(field_can_t), intent(inout) :: f
281 real(dp), intent(in) :: pphi
282 !$acc routine seq
283
284 26505867 f%vpar = (pphi - f%Aph/f%ro0)/f%hph
285 26505867 f%H = f%vpar**2/2d0 + f%mu*f%Bmod + f%Phie
286 26505867 f%pth = f%hth*f%vpar + f%Ath/f%ro0
287
288 26505867 end subroutine get_val
289
290
291 !ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
292 !
293 22487957 subroutine get_derivatives(f, pphi)
294 !
295 ! computes H, pth and vpar at z=(r, th, ph, pphi) and their derivatives
296 !
297 !
298 type(field_can_t), intent(inout) :: f
299 real(dp), intent(in) :: pphi
300 !$acc routine seq
301
302 22487957 call get_val(f, pphi)
303
304
2/2
✓ Branch 0 taken 67463871 times.
✓ Branch 1 taken 22487957 times.
89951828 f%dvpar(1:3) = -(f%dAph/f%ro0 + f%dhph*f%vpar)/f%hph
305 22487957 f%dvpar(4) = 1d0/f%hph
306
307
2/2
✓ Branch 0 taken 67463871 times.
✓ Branch 1 taken 22487957 times.
89951828 f%dH(1:3) = f%vpar*f%dvpar(1:3) + f%mu*f%dBmod + f%dPhie
308 22487957 f%dH(4) = f%vpar/f%hph
309
310
2/2
✓ Branch 0 taken 67463871 times.
✓ Branch 1 taken 22487957 times.
89951828 f%dpth(1:3) = f%dvpar(1:3)*f%hth + f%vpar*f%dhth + f%dAth/f%ro0
311
312 22487957 f%dpth(4) = f%hth/f%hph
313
314 22487957 end subroutine get_derivatives
315
316 !ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
317 !
318 19936433 subroutine get_derivatives2(f, pphi)
319 !
320 ! computes H, pth and vpar at z=(r, th, ph, pphi) up to 2nd derivatives
321 ! order of second derivatives:
322 ! d2dr2, d2drdth, d2drph, d2dth2, d2dthdph, d2dph2,
323 ! d2dpphdr, d2dpphdth, d2dpphdph, d2dpph2
324 !
325 type(field_can_t), intent(inout) :: f
326 real(dp), intent(in) :: pphi
327 !$acc routine seq
328
329 19936433 call get_derivatives(f, pphi)
330
331
2/2
✓ Branch 0 taken 119618598 times.
✓ Branch 1 taken 19936433 times.
139555031 f%d2vpar(1:6) = -f%d2Aph/f%ro0 - f%d2hph*f%vpar
332 19936433 f%d2vpar(1) = f%d2vpar(1) - 2d0*f%dhph(1)*f%dvpar(1)
333 19936433 f%d2vpar(2) = f%d2vpar(2) - (f%dhph(1)*f%dvpar(2) + f%dhph(2)*f%dvpar(1))
334 19936433 f%d2vpar(3) = f%d2vpar(3) - (f%dhph(1)*f%dvpar(3) + f%dhph(3)*f%dvpar(1))
335 19936433 f%d2vpar(4) = f%d2vpar(4) - 2d0*f%dhph(2)*f%dvpar(2)
336 19936433 f%d2vpar(5) = f%d2vpar(5) - (f%dhph(2)*f%dvpar(3) + f%dhph(3)*f%dvpar(2))
337 19936433 f%d2vpar(6) = f%d2vpar(6) - 2d0*f%dhph(3)*f%dvpar(3)
338
2/2
✓ Branch 0 taken 119618598 times.
✓ Branch 1 taken 19936433 times.
139555031 f%d2vpar(1:6) = f%d2vpar(1:6)/f%hph
339
340
2/2
✓ Branch 0 taken 119618598 times.
✓ Branch 1 taken 19936433 times.
139555031 f%d2H(1:6) = f%vpar*f%d2vpar(1:6) + f%mu*f%d2Bmod + f%d2Phie
341 19936433 f%d2H(1) = f%d2H(1) + f%dvpar(1)**2
342 19936433 f%d2H(2) = f%d2H(2) + f%dvpar(1)*f%dvpar(2)
343 19936433 f%d2H(3) = f%d2H(3) + f%dvpar(1)*f%dvpar(3)
344 19936433 f%d2H(4) = f%d2H(4) + f%dvpar(2)**2
345 19936433 f%d2H(5) = f%d2H(5) + f%dvpar(2)*f%dvpar(3)
346 19936433 f%d2H(6) = f%d2H(6) + f%dvpar(3)**2
347
348
2/2
✓ Branch 0 taken 119618598 times.
✓ Branch 1 taken 19936433 times.
139555031 f%d2pth(1:6) = f%d2vpar(1:6)*f%hth + f%vpar*f%d2hth + f%d2Ath/f%ro0
349 19936433 f%d2pth(1) = f%d2pth(1) + 2d0*f%dvpar(1)*f%dhth(1)
350 19936433 f%d2pth(2) = f%d2pth(2) + f%dvpar(1)*f%dhth(2) + f%dvpar(2)*f%dhth(1)
351 19936433 f%d2pth(3) = f%d2pth(3) + f%dvpar(1)*f%dhth(3) + f%dvpar(3)*f%dhth(1)
352 19936433 f%d2pth(4) = f%d2pth(4) + 2d0*f%dvpar(2)*f%dhth(2)
353 19936433 f%d2pth(5) = f%d2pth(5) + f%dvpar(2)*f%dhth(3) + f%dvpar(3)*f%dhth(2)
354 19936433 f%d2pth(6) = f%d2pth(6) + 2d0*f%dvpar(3)*f%dhth(3)
355
356
2/2
✓ Branch 0 taken 19936433 times.
✓ Branch 1 taken 59809299 times.
79745732 f%d2vpar(7:9) = -f%dhph/f%hph**2
357
2/2
✓ Branch 0 taken 19936433 times.
✓ Branch 1 taken 59809299 times.
79745732 f%d2H(7:9) = f%dvpar(1:3)/f%hph + f%vpar*f%d2vpar(7:9)
358
2/2
✓ Branch 0 taken 59809299 times.
✓ Branch 1 taken 19936433 times.
79745732 f%d2pth(7:9) = f%dhth/f%hph + f%hth*f%d2vpar(7:9)
359
360 19936433 end subroutine get_derivatives2
361
362 ! Wrapper subroutines to expose function pointers to Python
363 ✗ subroutine integ_to_ref_wrapper(xinteg, xref)
364 real(dp), intent(in) :: xinteg(3)
365 real(dp), intent(out) :: xref(3)
366 ✗ call integ_to_ref(xinteg, xref)
367 ✗ end subroutine integ_to_ref_wrapper
368
369 ✗ subroutine ref_to_integ_wrapper(xref, xinteg)
370 real(dp), intent(in) :: xref(3)
371 real(dp), intent(out) :: xinteg(3)
372 ✗ call ref_to_integ(xref, xinteg)
373 ✗ end subroutine ref_to_integ_wrapper
374
375
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 35 times.
43 end module field_can_mod
376