GCC Code Coverage Report


Directory: ./
File: src/classification.f90
Date: 2026-10-22 05:02:02
Coverage Exec Excl Total
Lines: 52.1% 163 0 313
Functions: 55.6% 5 0 9
Branches: 33.0% 148 0 448

Line Branch Exec Source
1 module classification
2 use, intrinsic :: iso_fortran_env, only: dp => real64
3 use finite_check, only: is_finite, quiet_nan
4 !$ use omp_lib
5 use params, only: zstart, zend, times_lost, trap_par, perp_inv, iclass, &
6 ntimstep, confpart_trap, confpart_pass, notrace_passing, contr_pp, &
7 class_plot, ntcut, nturns, fast_class, n_tip_vars, nplagr, nder, npl_half, &
8 class_scores, &
9 nfp, fper, zerolam, num_surf, bmax, bmin, dtaumin, v0, cut_in_per, &
10 integmode, relerr, ntau, should_skip, orbit_exit_code, unresolved_orbits, &
11 max_consecutive_warning_holds, &
12 ORBIT_EXIT_COMPLETED, ORBIT_EXIT_LCFS, ORBIT_EXIT_SKIPPED, &
13 ORBIT_EXIT_NUMERICAL_DOMAIN, ORBIT_EXIT_NUMERICAL_MAXITER, &
14 ORBIT_EXIT_NUMERICAL_LINEAR, ORBIT_EXIT_NUMERICAL_EVENT
15 use util, only: twopi, sqrt2
16 use velo_mod, only : isw_field_type
17 use orbit_symplectic, only : advance_symplectic_with_retry, &
18 orbit_timestep_sympl, get_val
19 use orbit_symplectic_base, only: SYMPLECTIC_STEP_BOUNDARY, &
20 SYMPLECTIC_STEP_OUTSIDE_DOMAIN, SYMPLECTIC_STEP_MAXITER, &
21 SYMPLECTIC_STEP_LINEAR_SOLVE, symplectic_newton_warning_mode
22 use simple, only : init_sympl, tracer_t
23 use cut_detector, only : fract_dimension
24 use diag_mod, only : icounter
25 use get_can_sub, only : vmec_to_can, can_to_vmec
26 use boozer_sub, only : vmec_to_boozer, boozer_to_vmec
27 use magfie_sub, only : CANFLUX, BOOZER
28 use check_orbit_type_sub, only : check_orbit_type
29 use detect_oneline_mod, only: jpar_variation_rate, rotation_number_drift, &
30 precession_turns, jpar_sample_count, &
31 rotation_half_count, tip_count, &
32 jpar_spread, jpar_ref, topology_margin, &
33 score_status
34 use diag_counters, only: count_event, EVT_WARNING_STEP_SKIP
35
36 implicit none
37
38 ! Classification result type - separates data from I/O
39 ! Note: 0=unclassified means the classification was not computed
40 ! This depends on orbit type (trapped/passing) and class_plot flag
41 type :: classification_result_t
42 logical :: passing ! Trapped (false) or passing (true)
43 logical :: lost ! Orbit lost (true) or confined (false)
44 integer :: fractal ! Fractal: 0=unclassified, 1=regular, 2=chaotic
45 integer :: jpar ! J_parallel: 0=unclassified, 1=regular, 2=stochastic
46 integer :: topology ! Topology: 0=unclassified, 1=ideal, 2=non-ideal
47 integer :: exit_code ! Physical or numerical orbit termination
48 end type classification_result_t
49
50 ! output files:
51 ! iaaa_bou - trapped-passing boundary
52 ! iaaa_pnt - forced regular passing
53 ! iaaa_prp - lossed passing
54 ! iaaa_prt - lossed trapped
55 ! iaaa_rep - regular passing
56 ! iaaa_ret - regular trapped
57 ! iaaa_stp - stochastic passing
58 ! iaaa_stt - stochastic trapped
59 integer, parameter :: iaaa_bou=20000, iaaa_pnt=10000, iaaa_prp=10001, iaaa_prt=10002, &
60 iaaa_rep=10011, iaaa_ret=10012, iaaa_stp=10021, iaaa_stt=10022
61
62 ! output files:
63 ! iaaa_jre - regular trapped by J_parallel
64 ! iaaa_jst - stochastic trapped by J_parallel
65 ! iaaa_jer - non-classified trapped by J_parallel
66 ! iaaa_ire - ideal trapped by recurrences and monotonicity
67 ! iaaa_ist - non-ideal trapped by recurrences and monotonicity
68 ! iaaa_ier - non-classified trapped by recurrences and monotonicity
69 integer, parameter :: iaaa_jre=40012, iaaa_jst=40022, iaaa_jer=40032, &
70 iaaa_ire=50012, iaaa_ist=50022, iaaa_ier=50032
71
72 contains
73
74 48 subroutine trace_orbit_with_classifiers(anorb, ipart, class_result)
75 use find_bminmax_sub, only : get_bminmax
76 use magfie_sub, only : magfie
77 use plag_coeff_sub, only : plag_coeff
78 use alpha_lifetime_sub, only : orbit_timestep_axis
79
80 type(tracer_t), intent(inout) :: anorb
81 integer, intent(in) :: ipart
82 type(classification_result_t), intent(out) :: class_result
83 integer :: ierr
84 real(dp), dimension(5) :: z, z_step_start
85 real(dp) :: bmod,sqrtg
86 real(dp) :: surf_bmin, surf_bmax
87 real(dp), dimension(3) :: bder, hcovar, hctrvr, hcurl
88 integer :: first_unresolved_it, hold_streak, it, ktau, it_f
89 integer(8) :: kt
90 logical :: passing
91
92 integer :: ifp_tip,ifp_per
93 48 integer, dimension(:), allocatable :: ipoi
94 48 real(dp), dimension(:), allocatable :: xp
95 48 real(dp), dimension(:,:), allocatable :: coef,orb_sten
96 48 real(dp), dimension(:,:), allocatable :: zpoipl_tip,zpoipl_per,dummy2d
97 real(dp), dimension(n_tip_vars) :: var_tip
98 real(dp) :: phiper, alam_prev, par_inv
99 integer :: iper, itip, kper, nfp_tip, nfp_per
100
101 real(dp) :: fraction
102 real(dp) :: r,theta_vmec,varphi_vmec
103 logical :: regular
104
105 ! Variables and settings for classification by J_parallel and ideal orbit condition:
106 integer, parameter :: nfp_dim=3
107 integer :: nfp_cot,ideal,ijpar,ierr_cot,iangvar
108 real(dp), dimension(nfp_dim) :: fpr_in
109
110
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 48 times.
288 zend(:,ipart) = 0d0
111 !
112 48 iangvar=2
113 ! End variables and settings for classification by J_parallel and ideal orbit condition
114 !
115
116 ! Initialize classification result - all unclassified
117 48 class_result%passing = .false.
118 48 class_result%lost = .false.
119 48 class_result%fractal = 0
120 48 class_result%jpar = 0
121 48 class_result%topology = 0
122 48 class_result%exit_code = ORBIT_EXIT_COMPLETED
123
124 ! open(unit=10000+ipart, iostat=stat, status='old')
125 ! if (stat == 0) close(10000+ipart, status='delete')
126 ! open(unit=20000+ipart, iostat=stat, status='old')
127 ! if (stat == 0) close(20000+ipart, status='delete')
128
129 ! Write out trapped-passing boundary at the classification cut:
130
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 8 times.
48 if(class_plot) then
131
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 33 times.
40 if(ipart.eq.1) then
132 7 z(1)=zstart(1,ipart)
133 7 z(3)=cut_in_per*fper
134
2/2
✓ Branch 0 taken 7007 times.
✓ Branch 1 taken 7 times.
7014 do kt=0,1000
135 7007 z(2)=1d-3*twopi*dble(kt)
136 7007 call magfie(z(1:3),bmod,sqrtg,bder,hcovar,hctrvr,hcurl)
137 7014 write(iaaa_bou,*) z(2),sqrt(1.d0-bmod/bmax)
138 enddo
139 endif
140 endif
141 ! End write out trapped-passing boundary at the classification cut
142 !
143
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 48 times.
288 z = zstart(:, ipart)
144 48 r=z(1)
145 48 theta_vmec=z(2)
146 48 varphi_vmec=z(3)
147 !
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if(isw_field_type .eq. CANFLUX) then
149 ✗ call vmec_to_can(r,theta_vmec,varphi_vmec,z(2),z(3))
150
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 elseif(isw_field_type .eq. BOOZER) then
151 48 call vmec_to_boozer(r,theta_vmec,varphi_vmec,z(2),z(3))
152 endif
153
154 ! In case of classification plot all starting points are moved to the classification cut:
155
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 8 times.
48 if(class_plot) then
156 40 z(3)=cut_in_per*fper
157
2/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
40 zstart(2,ipart)=modulo(zstart(2,ipart),twopi)
158 endif
159 ! End moving starting points to the classification cut
160
161
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 if (integmode>0) call init_sympl(anorb%si, anorb%f, z, dtaumin, dtaumin, relerr, integmode)
162
163 48 call magfie(z(1:3),bmod,sqrtg,bder,hcovar,hctrvr,hcurl)
164
165 ! Keep surface extrema per particle; the boundary plot reads shared bmax.
166 96 !$omp critical
167
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 14 times.
48 if(num_surf > 1) then
168 34 call get_bminmax(z(1), surf_bmin, surf_bmax)
169 else
170 14 surf_bmin = bmin
171 14 surf_bmax = bmax
172 endif
173 48 passing = z(5)**2 .gt. 1.d0 - bmod/surf_bmax
174 trap_par(ipart) = ((1.d0 - z(5)**2)*surf_bmax/bmod - 1.d0)*surf_bmin &
175 48 /(surf_bmax - surf_bmin)
176 48 perp_inv(ipart) = z(4)**2*(1.d0-z(5)**2)/bmod
177
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 48 times.
192 iclass(:,ipart) = 0
178 !$omp end critical
179
180 ! Store passing status in result
181 48 class_result%passing = passing
182
183 ! Forced classification of passing as regular:
184
3/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
48 if(passing .and. should_skip(ipart)) then
185 72 !$omp critical
186
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 36 times.
132 confpart_pass=confpart_pass+1.d0
187
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 36 times.
216 zend(:, ipart) = zstart(:, ipart)
188 36 times_lost(ipart) = -1.d0
189 !$omp end critical
190
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 8 times.
36 if(class_plot) then
191 28 !$omp critical
192 28 write (iaaa_pnt,*) zstart(2,ipart),zstart(5,ipart),trap_par(ipart)
193 !$omp end critical
194 endif
195
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 36 times.
144 iclass(:,ipart) = 1
196 ! Mark as regular passing (fractal=1) and not lost
197 36 class_result%fractal = 1
198 36 class_result%lost = .false.
199 36 class_result%exit_code = ORBIT_EXIT_SKIPPED
200 36 orbit_exit_code(ipart) = ORBIT_EXIT_SKIPPED
201 36 return
202 endif
203 ! End forced classification of passing as regular
204
205 24 !$omp critical
206
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (.not. allocated(ipoi)) &
207
27/54
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 12 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 12 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 12 times.
✓ Branch 19 taken 12 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✓ Branch 22 taken 12 times.
✓ Branch 23 taken 12 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 12 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 12 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 12 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 12 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 12 times.
✓ Branch 37 taken 12 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 12 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 12 times.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✓ Branch 44 taken 12 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 12 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 12 times.
✓ Branch 51 taken 12 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 12 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 12 times.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 58 taken 12 times.
✗ Branch 60 not taken.
✓ Branch 61 taken 12 times.
120 allocate(ipoi(nplagr),coef(0:nder,nplagr),orb_sten(6,nplagr),xp(nplagr))
208 !$omp end critical
209
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 12 times.
60 do it=1,nplagr
210 60 ipoi(it)=it
211 enddo
212
213 12 nfp_tip=nfp !<= initial array dimension for tips
214 12 nfp_per=nfp !<= initial array dimension for periods
215 24 !$omp critical
216 if (.not. allocated(zpoipl_tip)) &
217
10/20
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 12 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 12 times.
✓ Branch 16 taken 12 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 12 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 12 times.
48 allocate(zpoipl_tip(2,nfp_tip),zpoipl_per(2,nfp_per))
218 !$omp end critical
219
220 ! open(unit=10000+ipart, recl=1024, position='append')
221 ! open(unit=20000+ipart, recl=1024, position='append')
222
223 12 ifp_tip=0 !<= initialize footprint counter on tips
224 12 ifp_per=0 !<= initialize footprint counter on periods
225
226 12 icounter=0
227 12 phiper=0.0d0
228
229
230 12 kt = 0
231 12 first_unresolved_it = 0
232 12 hold_streak = 0
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (passing) then
234 ✗ !$omp atomic
235 confpart_pass(1)=confpart_pass(1)+1.d0
236 else
237 12 !$omp atomic
238 confpart_trap(1)=confpart_trap(1)+1.d0
239 end if
240
241 !--------------------------------
242 ! Initialize tip detector
243
244 12 itip=npl_half+1
245 12 alam_prev=z(5)
246
247 ! End initialize tip detector
248 !--------------------------------
249 ! Initialize period crossing detector
250
251 12 iper=npl_half+1
252 12 kper=int(z(3)/fper)
253
254 ! End initialize period crossing detector
255 !--------------------------------
256 !
257 ! Initialize classification by J_parallel and ideal orbit condition:
258 12 nfp_cot=0
259 ! End Initialize classification by J_parallel and ideal orbit condition
260 !
261 12 par_inv = 0d0
262 12 regular = .False.
263
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 do it=2,ntimstep
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (regular) then ! regular orbit, will not be lost
265 ✗ if(passing) then
266 ✗ !$omp atomic
267 confpart_pass(it)=confpart_pass(it)+1.d0
268 else
269 ✗ !$omp atomic
270 confpart_trap(it)=confpart_trap(it)+1.d0
271 endif
272 ✗ kt = kt+ntau
273 ✗ cycle
274 endif
275
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 780 times.
792 do ktau=1,ntau
276 780 z_step_start = z
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
780 if (integmode <= 0) then
278 ✗ call orbit_timestep_axis(z, dtaumin, dtaumin, relerr, ierr)
279 ✗ if (ierr /= 0 .and. ierr /= 1 .and. &
280 symplectic_newton_warning_mode) then
281 ✗ z = z_step_start
282 ✗ if (hold_streak < max_consecutive_warning_holds) then
283 ✗ call count_event(EVT_WARNING_STEP_SKIP)
284 ✗ hold_streak = hold_streak + 1
285 ✗ ierr = 0
286 end if
287 ✗ else if (ierr == 0) then
288 780 hold_streak = 0
289 end if
290 else
291 call advance_symplectic_with_retry(anorb%si, anorb%f, &
292 780 orbit_timestep_sympl, ierr)
293
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
780 if (ierr /= 0 .and. ierr /= SYMPLECTIC_STEP_BOUNDARY .and. &
294 symplectic_newton_warning_mode) then
295 ✗ z = z_step_start
296 ✗ if (hold_streak < max_consecutive_warning_holds) then
297 ✗ call count_event(EVT_WARNING_STEP_SKIP)
298 ✗ hold_streak = hold_streak + 1
299 ✗ ierr = 0
300 end if
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
780 else if (ierr == 0) then
302 3120 hold_streak = 0
303
2/2
✓ Branch 0 taken 2340 times.
✓ Branch 1 taken 780 times.
3120 z(1:3) = anorb%si%z(1:3)
304 z(4) = dsqrt(anorb%f%mu*anorb%f%Bmod + &
305 780 0.5d0*anorb%f%vpar**2)
306 780 z(5) = anorb%f%vpar/(z(4)*sqrt2)
307 end if
308 endif
309
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
780 if(ierr.ne.0) then
311 call classify_classifier_exit(ierr, integmode, &
312 ✗ class_result%lost, class_result%exit_code)
313 ✗ if(class_plot .and. class_result%lost) then
314 ✗ call output_lost_orbit_starting_data(ipart, passing)
315 endif
316 exit
317 endif
318 780 kt = kt+1
319
320 780 par_inv = par_inv+z(5)**2*dtaumin ! parallel adiabatic invariant
321
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 732 times.
780 if(kt.le.nplagr) then !<=first nplagr points to initialize stencil
322
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 48 times.
288 orb_sten(1:5,kt)=z
323 48 orb_sten(6,kt)=par_inv
324 else !<=normal case, shift stencil
325
2/2
✓ Branch 0 taken 3660 times.
✓ Branch 1 taken 732 times.
4392 orb_sten(1:5,ipoi(1))=z
326 732 orb_sten(6,ipoi(1))=par_inv
327
6/10
✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2928 times.
✓ Branch 4 taken 732 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 732 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 2928 times.
✓ Branch 10 taken 732 times.
7320 ipoi=cshift(ipoi,1)
328 endif
329
330 ! Tip detection and interpolation
331
3/4
✓ Branch 0 taken 520 times.
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 260 times.
✗ Branch 3 not taken.
780 if(alam_prev.lt.0.d0.and.z(5).gt.0.d0) itip=0 !<=tip has been passed
332 780 itip=itip+1
333 780 alam_prev=z(5)
334
2/2
✓ Branch 0 taken 732 times.
✓ Branch 1 taken 48 times.
780 if(kt.gt.nplagr) then !<=use only initialized stencil
335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 732 times.
732 if(itip.eq.npl_half) then !<=stencil around tip is complete, interpolate
336 ✗ xp=orb_sten(5,ipoi)
337
338 ✗ call plag_coeff(nplagr,nder,zerolam,xp,coef)
339
340 ✗ var_tip=matmul(orb_sten(:,ipoi),coef(0,:))
341 ✗ if (.not. all(is_finite(var_tip))) then
342 ! A failed numerical step must not become a raw
343 ! NaN/Inf classifier score. Preserve the event as
344 ! an explicitly unresolved particle; consumers
345 ! can then exclude it using the zero sample counts.
346 ✗ class_scores(:, ipart) = 0.0_dp
347 ✗ iclass(:, ipart) = 0
348 ✗ class_result%fractal = 0
349 ✗ class_result%jpar = 0
350 ✗ class_result%topology = 0
351 ✗ class_result%exit_code = ORBIT_EXIT_NUMERICAL_EVENT
352 ✗ ierr = 1
353 exit
354 end if
355 ✗ var_tip(2)=modulo(var_tip(2),twopi)
356 ✗ var_tip(3)=modulo(var_tip(3),twopi)
357
358 ! write(10000+ipart,*) var_tip
359
360 ✗ ifp_tip=ifp_tip+1
361 ✗ if(ifp_tip.gt.nfp_tip) then !<=increase the buffer for banana tips
362 ✗ !$omp critical
363 ✗ allocate(dummy2d(2,ifp_tip-1))
364 !$omp end critical
365 ✗ dummy2d=zpoipl_tip(:,1:ifp_tip-1)
366 ✗ !$omp critical
367 ✗ deallocate(zpoipl_tip)
368 !$omp end critical
369 ✗ nfp_tip=nfp_tip+nfp
370 ✗ !$omp critical
371 ✗ allocate(zpoipl_tip(2,nfp_tip))
372 !$omp end critical
373 ✗ zpoipl_tip(:,1:ifp_tip-1)=dummy2d
374 ✗ !$omp critical
375 ✗ deallocate(dummy2d)
376 !$omp end critical
377 endif
378 ✗ zpoipl_tip(:,ifp_tip)=var_tip(1:2)
379 ✗ par_inv = par_inv - var_tip(6)
380 !
381 ! Classification by J_parallel and ideal orbit conditions:
382 ✗ fpr_in(1)=var_tip(1)
383 ✗ fpr_in(2)=var_tip(iangvar)
384 ✗ fpr_in(3)=var_tip(6)
385 !
386 ✗ call check_orbit_type(nturns,nfp_cot,fpr_in,ideal,ijpar,ierr_cot)
387 !
388 ✗ class_scores(1, ipart) = jpar_variation_rate
389 ✗ class_scores(2, ipart) = rotation_number_drift
390 ✗ class_scores(3, ipart) = precession_turns
391 ✗ class_scores(4, ipart) = dble(jpar_sample_count)
392 ✗ class_scores(5, ipart) = dble(rotation_half_count)
393 ✗ class_scores(6, ipart) = dble(tip_count)
394 ✗ class_scores(7, ipart) = jpar_spread
395 ✗ class_scores(8, ipart) = jpar_ref
396 ✗ class_scores(9, ipart) = topology_margin
397 ✗ class_scores(10, ipart) = dble(score_status)
398 ✗ iclass(1,ipart) = ijpar
399 ✗ iclass(2,ipart) = ideal
400 ! Store in classification result
401 ✗ class_result%jpar = ijpar
402 ✗ class_result%topology = ideal
403 ! fast_class without class_plot: stop tracing regular
404 ! orbits early (they are confined). With class_plot,
405 ! continue to ntcut so classification output is written.
406 if(fast_class .and. .not. class_plot &
407 ✗ .and. ierr_cot /= 0 .and. ijpar == 1) then
408 ✗ regular = .True.
409 endif
410 !
411 ! End classification by J_parallel and ideal orbit conditions
412 endif
413 endif
414 ! End tip detection and interpolation
415
416 ! Periodic boundary footprint detection and interpolation
417
1/2
✓ Branch 0 taken 780 times.
✗ Branch 1 not taken.
780 if(z(3).gt.dble(kper+1)*fper) then
418 iper=0 !<=periodic boundary has been passed
419 phiper=dble(kper+1)*fper
420 kper=kper+1
421
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 776 times.
780 elseif(z(3).lt.dble(kper)*fper) then
422 4 iper=0 !<=periodic boundary has been passed
423 4 phiper=dble(kper)*fper
424 4 kper=kper-1
425 endif
426 780 iper=iper+1
427
2/2
✓ Branch 0 taken 732 times.
✓ Branch 1 taken 48 times.
780 if(kt.gt.nplagr) then !<=use only initialized stencil
428
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 732 times.
732 if(iper.eq.npl_half) then !<=stencil around periodic boundary is complete, interpolate
429 ✗ xp=orb_sten(3,ipoi)-phiper
430
431 ✗ call plag_coeff(nplagr,nder,zerolam,xp,coef)
432
433 ✗ var_tip=matmul(orb_sten(:,ipoi),coef(0,:))
434 ✗ var_tip(2)=modulo(var_tip(2),twopi)
435 ✗ var_tip(3)=modulo(var_tip(3),twopi)
436 ! write(20000+ipart,*) var_tip
437 ✗ ifp_per=ifp_per+1
438 ✗ if(ifp_per.gt.nfp_per) then !<=increase the buffer for periodic boundary footprints
439 ✗ !$omp critical
440 ✗ allocate(dummy2d(2,ifp_per-1))
441 !$omp end critical
442 ✗ dummy2d=zpoipl_per(:,1:ifp_per-1)
443 ✗ !$omp critical
444 ✗ deallocate(zpoipl_per)
445 !$omp end critical
446 ✗ nfp_per=nfp_per+nfp
447 ✗ !$omp critical
448 ✗ allocate(zpoipl_per(2,nfp_per))
449 !$omp end critical
450 ✗ zpoipl_per(:,1:ifp_per-1)=dummy2d
451 ✗ !$omp critical
452 ✗ deallocate(dummy2d)
453 !$omp end critical
454 endif
455 ✗ zpoipl_per(:,ifp_per)=var_tip(1:2)
456 endif
457 endif
458 ! End periodic boundary footprint detection and interpolation
459
460 ! Cut classification into regular or chaotic
461
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
780 if (kt == ntcut) then
462 ✗ regular = .True.
463
464 ✗ if(ifp_per > 0) then
465
466 ✗ call fract_dimension(ifp_per,zpoipl_per(:,1:ifp_per),fraction)
467
468 ✗ if(fraction.gt.0.2d0) then
469 ✗ print *, ipart, ' chaotic per ', ifp_per
470 ✗ regular = .False.
471 else
472 ✗ print *, ipart, ' regular per', ifp_per
473 endif
474 endif
475
476 ✗ if(ifp_tip > 0) then
477
478 ✗ call fract_dimension(ifp_tip,zpoipl_tip(:,1:ifp_tip),fraction)
479
480 ✗ if(fraction.gt.0.2d0) then
481 ✗ print *, ipart, ' chaotic tip ', ifp_tip
482 ✗ regular = .False.
483 ✗ iclass(3,ipart) = 2
484 else
485 ✗ print *, ipart, ' regular tip ', ifp_tip
486 ✗ iclass(3,ipart) = 1
487 endif
488 endif
489
490 ! Store fractal classification in result
491 ✗ if(regular) then
492 ✗ class_result%fractal = 1
493 else
494 ✗ class_result%fractal = 2
495 endif
496
497 ✗ if(class_plot) then
498 ✗ call output_minkowsky_class(ipart, regular, passing)
499 ✗ ierr=1
500 endif
501 endif
502 !
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
792 if(ierr.ne.0) then
504 ✗ if(class_plot .and. .not. passing) then
505 ✗ call output_jpar_class(ipart, ijpar)
506 ✗ call output_topological_class(ipart, ideal)
507 ✗ exit
508 endif
509 endif
510 ! write(999, *) kt*dtaumin/v0, z
511 enddo
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(ierr.ne.0) exit
513
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
24 if(passing) then
514 ✗ !$omp atomic
515 confpart_pass(it)=confpart_pass(it)+1.d0
516 else
517 12 !$omp atomic
518 confpart_trap(it)=confpart_trap(it)+1.d0
519 endif
520 enddo
521
522 24 !$omp critical
523
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 12 times.
72 zend(:,ipart) = z
524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(isw_field_type .eq. CANFLUX) then
525 ✗ call can_to_vmec(z(1),z(2),z(3),zend(2,ipart),zend(3,ipart))
526
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 elseif(isw_field_type .eq. BOOZER) then
527 12 call boozer_to_vmec(z(1),z(2),z(3),zend(2,ipart),zend(3,ipart))
528 endif
529 12 orbit_exit_code(ipart) = class_result%exit_code
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (class_result%exit_code >= ORBIT_EXIT_NUMERICAL_DOMAIN) then
531 ✗ times_lost(ipart) = quiet_nan()
532 else
533 12 times_lost(ipart) = kt*dtaumin/v0
534 end if
535
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
12 deallocate(zpoipl_tip, zpoipl_per)
536 !$omp end critical
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (class_result%exit_code >= ORBIT_EXIT_NUMERICAL_DOMAIN) then
538 ✗ if (first_unresolved_it == 0) first_unresolved_it = min(it, ntimstep)
539 ✗ do it_f = first_unresolved_it, ntimstep
540 ✗ !$omp atomic update
541 unresolved_orbits(it_f) = unresolved_orbits(it_f) + 1
542 end do
543 end if
544 ! close(unit=10000+ipart)
545 ! close(unit=10000+ipart)
546
10/10
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 36 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 36 times.
✓ Branch 9 taken 12 times.
✓ Branch 10 taken 36 times.
48 end subroutine trace_orbit_with_classifiers
547
548 3 pure subroutine classify_classifier_exit(ierr, mode, lost, exit_code)
549 integer, intent(in) :: ierr, mode
550 logical, intent(out) :: lost
551 integer, intent(out) :: exit_code
552
553 3 lost = .false.
554
5/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 2 times.
3 if ((mode <= 0 .and. ierr == 1) .or. &
555 ierr == SYMPLECTIC_STEP_BOUNDARY) then
556 1 lost = .true.
557 1 exit_code = ORBIT_EXIT_LCFS
558 1 return
559 end if
560
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (mode <= 0) then
561 1 exit_code = ORBIT_EXIT_NUMERICAL_EVENT
562 1 return
563 end if
564
565 ✗ select case (ierr)
566 case (SYMPLECTIC_STEP_OUTSIDE_DOMAIN)
567 ✗ exit_code = ORBIT_EXIT_NUMERICAL_DOMAIN
568 case (SYMPLECTIC_STEP_MAXITER)
569 1 exit_code = ORBIT_EXIT_NUMERICAL_MAXITER
570 case (SYMPLECTIC_STEP_LINEAR_SOLVE)
571 ✗ exit_code = ORBIT_EXIT_NUMERICAL_LINEAR
572 case default
573
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 exit_code = ORBIT_EXIT_NUMERICAL_EVENT
574 end select
575 48 end subroutine classify_classifier_exit
576
577
578 ✗ subroutine output_lost_orbit_starting_data(ipart, passing)
579 integer, intent(in) :: ipart
580 logical, intent(in) :: passing
581
582 ✗ if(passing) then
583 ✗ call write_output_line(iaaa_prp, ipart)
584 else
585 ✗ call write_output_line(iaaa_prt, ipart)
586 endif
587 ✗ end subroutine output_lost_orbit_starting_data
588
589
590 28 subroutine output_minkowsky_class(ipart, regular, passing)
591 integer, intent(in) :: ipart
592 logical, intent(in) :: regular, passing
593
594
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if(regular) then
595
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if(passing) then
596 28 call write_output_line(iaaa_rep, ipart)
597 else
598 ✗ call write_output_line(iaaa_ret, ipart)
599 endif
600 else
601 ✗ if(passing) then
602 ✗ call write_output_line(iaaa_stp, ipart)
603 else
604 ✗ call write_output_line(iaaa_stt, ipart)
605 endif
606 endif
607 28 end subroutine output_minkowsky_class
608
609
610 ✗ subroutine output_jpar_class(ipart, ijpar)
611 integer, intent(in) :: ipart, ijpar
612
613 ✗ select case(ijpar)
614 case(0)
615 ✗ call write_output_line(iaaa_jer, ipart)
616 case(1)
617 ✗ call write_output_line(iaaa_jre, ipart)
618 case(2)
619 ✗ call write_output_line(iaaa_jst, ipart)
620 end select
621 ✗ end subroutine output_jpar_class
622
623
624 ✗ subroutine output_topological_class(ipart, ideal)
625 integer, intent(in) :: ipart, ideal
626
627 ✗ select case(ideal)
628 case(0)
629 ✗ call write_output_line(iaaa_ier, ipart)
630 case(1)
631 ✗ call write_output_line(iaaa_ire, ipart)
632 case(2)
633 ✗ call write_output_line(iaaa_ist, ipart)
634 end select
635 ✗ end subroutine output_topological_class
636
637
638 28 subroutine write_output_line(iunit, ipart)
639 integer, intent(in) :: iunit, ipart
640
641 56 !$omp critical
642 28 write (iunit, *) zstart(2,ipart), zstart(5,ipart), trap_par(ipart)
643 !$omp end critical
644 28 end subroutine write_output_line
645
646
647 ! Write classification results to fort.* files
648 ! This subroutine centralizes all classification file I/O
649 ! Note: Only writes classifications that were computed (non-zero values)
650 40 subroutine write_classification_results(ipart, class_result)
651 integer, intent(in) :: ipart
652 type(classification_result_t), intent(in) :: class_result
653 logical :: regular
654
655
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 if (class_result%exit_code >= ORBIT_EXIT_NUMERICAL_DOMAIN) return
656
657 ! Write lost orbits
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
40 if(class_result%lost) then
659 ✗ call output_lost_orbit_starting_data(ipart, class_result%passing)
660 ✗ return
661 endif
662
663 ! Write fractal classification if computed
664
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 12 times.
40 if(class_result%fractal /= 0) then
665 28 regular = (class_result%fractal == 1)
666 28 call output_minkowsky_class(ipart, regular, class_result%passing)
667 endif
668
669 ! Write J_parallel and topological classification if computed
670 ! These are only done for trapped orbits
671
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 28 times.
40 if(.not. class_result%passing) then
672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(class_result%jpar /= 0) then
673 ✗ call output_jpar_class(ipart, class_result%jpar)
674 endif
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(class_result%topology /= 0) then
676 ✗ call output_topological_class(ipart, class_result%topology)
677 endif
678 endif
679
680 end subroutine write_classification_results
681
682 ✗ end module classification
683