MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_trace_field.t
Go to the documentation of this file.
2 use, intrinsic :: ieee_arithmetic, only: ieee_value,ieee_quiet_nan, &
3 ieee_is_finite
5 use mod_geometry, only: geo_coordinate => coordinate, &
6 geo_cartesian => cartesian, geo_spherical => spherical, &
7 geo_cartesian_stretched => cartesian_stretched, &
11 implicit none
12
13 integer, parameter, public :: trace_status_active = 0
14 integer, parameter, public :: trace_status_boundary = 1
15 integer, parameter, public :: trace_status_weak_field = 2
16 integer, parameter, public :: trace_status_max_steps = 3
17 integer, parameter, public :: trace_status_seed_outside = 4
18 integer, parameter, public :: trace_status_out_of_domain = 5
19 integer, parameter, public :: trace_status_invalid_input = 6
20 integer, parameter, public :: trace_status_unsupported_geometry = 7
21 integer, parameter, public :: trace_status_trac_stop = 8
22 integer, parameter, public :: trace_status_mpi_unsupported = 9
23 integer, parameter, public :: trace_status_bad_curl_stencil = 10
24 integer, parameter, public :: trace_status_bad_grad_stencil = 11
25 integer, parameter, public :: trace_status_bad_face_limit_sample= 12
26 integer, parameter, public :: trace_status_singular_q = 13
27 integer, parameter, public :: trace_status_bad_q_bound = 14
28
29 ! Both standard and perpendicular squashing factors have the analytic
30 ! lower bound Q >= 2. Cartesian tangent transport can undershoot this
31 ! bound slightly because of integration error, so valid outputs are floored
32 ! before taking the logarithm.
33 double precision, parameter, private :: trace_q_min=2.d0
34
35 integer, parameter, public :: trace_face_none = 0
36 integer, parameter, public :: trace_face_xmin = 1
37 integer, parameter, public :: trace_face_xmax = 2
38 integer, parameter, public :: trace_face_ymin = 3
39 integer, parameter, public :: trace_face_ymax = 4
40 integer, parameter, public :: trace_face_zmin = 5
41 integer, parameter, public :: trace_face_zmax = 6
42 integer, parameter, public :: trace_face_ambiguous = 7
43
58 public :: trace_set_integrator
60 public :: trace_rk2_stats_reset
78 private :: trace_summary_seed,trace_summary_multi
79 private :: trace_summary_twist_seed,trace_summary_twist_multi
80 private :: trace_summary_mapping_seed,trace_summary_mapping_multi
81 private :: trace_summary_topology_multi
82 private :: trace_summary_validate,trace_summary_init_result
83 private :: trace_summary_init_twist_result
84 private :: trace_summary_init_mapping_result
85 private :: trace_summary_init_topology_result
86 private :: trace_summary_locate_seed,trace_summary_trace_seed
87 private :: trace_summary_trace_twist_seed
88 private :: trace_summary_init_state,trace_summary_advance_state
89 private :: trace_summary_trace_state_to_end,trace_summary_trace_states_grouped
90 private :: trace_summary_advance_state_in_grid
91 private :: trace_summary_rk2_step,trace_summary_rk45_cartesian_step
92 private :: trace_summary_rk45_spherical_step
93 private :: trace_intersect_domain
94 private :: trace_summary_accumulate_twist,trace_twist_density_at_point
95 private :: trace_tangent_accumulate_twist,trace_tangent_fill_twist_result
96 private :: sample_b_curlb_at_point
97 private :: sample_b_curlb_spherical_at_point
98 private :: sample_b_at_point,trace_summary_fill_mapping
99 private :: trace_total_b_at_cell,trace_bhat_at_cell
100 private :: sample_bhat_gradbhat_at_point
101 private :: sample_bhat_gradbhat_cellfd_at_point
102 private :: sample_bhat_gradbhat_xeps_at_point
103 private :: sample_bhat_gradbhat_interpderiv_at_point
104 private :: trace_debug_locate_point,trace_locate_point_with_hint
105 private :: trace_tangent_rhs
106 private :: trace_advance_tangent_state_rk2,trace_project_to_perp_bhat
107 private :: trace_tangent_rk2_trial,trace_endpoint_b_bhat
108 private :: trace_tangent_rk2_trial_from_rhs
109 private :: trace_tangent_state_advance_in_grid_rk45_cartesian
110 private :: trace_tangent_rk45_cartesian_step
111 private :: trace_tangent_rhs_cartesian_located
112 private :: trace_tangent_rk45_cartesian_finish_boundary
113 private :: trace_tangent_trace_states_grouped_rk45_spherical
114 private :: trace_tangent_state_advance_in_grid_rk45_spherical
115 private :: trace_tangent_rk45_spherical_step
116 private :: trace_tangent_rk45_spherical_finish_boundary
117 private :: trace_q0_finalize_from_states
118 private :: trace_sample_b_on_domain_face_limit
119 private :: trace_locate_face_limit_grid
120 private :: trace_face_normal,trace_project_to_boundary_face
121 private :: trace_make_perp_basis,trace_debug_nan
122 private :: trace_face_from_dim_side,trace_face_bn,trace_face_is_boundary
123 private :: trace_boundary_face_at_point
124 private :: trace_is_supported_summary_geometry
125 private :: trace_effective_step,trace_spherical_metric_ok
126 private :: trace_segment_length
127 private :: trace_summary_twist_status_from_states
128 private :: trace_init_qperp_result,trace_qperp_single_core
129 private :: trace_qperp_compute_q0_scalars
130 private :: trace_qperp_project_to_boundary_face
131 private :: trace_tangent_state_init
132 private :: trace_tangent_trace_states_grouped
133 private :: trace_tangent_state_advance_in_grid
134 private :: trace_tangent_state_finalize_boundary
135 private :: trace_qperp_prepare_seed_result
136 private :: trace_qperp_compute_scalars
137 private :: trace_qperp_finalize_from_states
138 private :: trace_spherical_rmin_q_prepare_seed_result
139 private :: trace_spherical_rmin_q_finalize_from_states
140 private :: trace_spherical_radial_q_finalize_from_states
141 private :: trace_spherical_radial_q_face_pair_admitted
142 private :: trace_spherical_radial_endpoint_matrix
143 private :: trace_spherical_radial_project_to_surface
144 private :: trace_spherical_qperp_prepare_seed_result
145 private :: trace_spherical_qperp_finalize_from_states
146 private :: trace_spherical_qperp_compute_from_states
147 private :: trace_spherical_q_from_endpoint_matrices
148 private :: trace_spherical_basis,trace_spherical_coord_to_cart
149 private :: trace_cart_to_spherical_coord
150 private :: trace_spherical_sample_b_bhat_cart
151 private :: trace_spherical_sample_bhat_gradbhat_covariant
152 private :: trace_spherical_sample_bhat_gradbhat_cartfd
153 private :: trace_spherical_bhat_cart_to_rhs
154 private :: trace_spherical_endpoint_b_bhat
155 private :: trace_spherical_face_probe_coord
156 private :: trace_spherical_physical_cell_scale
157 private :: trace_spherical_cell_scale_from_widths
158 private :: trace_spherical_interp_ctx_build
159 private :: trace_spherical_interp_ctx_build_cached
160 private :: trace_spherical_effective_step_ctx
161 private :: trace_spherical_sample_bsph_ctx
162 private :: trace_spherical_sample_b_bhat_cart_ctx
163 private :: trace_spherical_sample_cached_curlb_ctx
164 private :: trace_spherical_sample_bhat_gradbhat_covariant_ctx
165 private :: get_k_spherical_ctx
166 private :: trace_spherical_interp_ctx_fill
167 private :: trace_spherical_interp_ctx_load_bcorners
168 private :: trace_interp_weights_block,trace_interp_index_3d
169 private :: trace_interp_weights_block_near,trace_interp_index_3d_near
170 private :: trace_spherical_local_cell_widths
171 private :: trace_spherical_profile_add_count
172 private :: trace_spherical_profile_add_time
173 private :: trace_spherical_profile_note_trace_steps
174 private :: trace_spherical_profile_time
175 private :: trace_cartesian_like_geometry,trace_rk45_position_integrator
176 private :: trace_cartesian_rhs_bhat
177 private :: trace_cartesian_local_cell_size
178 private :: trace_rk45_try_boundary_finish
179 private :: trace_rk2_stats_note_direction
180 private :: trace_rk2_stats_note_step
181 private :: trace_rk2_stats_note_completion
182 private :: trace_rk2_stats_note_step_limit
183 private :: trace_rk2_stats_note_group_iteration
184 private :: trace_rk2_stats_note_grid_group
185 private :: trace_rk2_stats_note_rhs
186 private :: trace_rk2_stats_add_grouped_time
187 private :: trace_rk45_stats_note_direction
188 private :: trace_rk45_stats_note_attempt
189 private :: trace_rk45_stats_note_tangent_error
190
191 type, private :: trace_sph_interp_ctx
192 logical :: valid=.false.
193 integer :: igrid=-1
194 integer :: ixbl(3)=0
195 double precision :: x(3)=zero
196 double precision :: xd(3)=zero
197 double precision :: dxc(3)=zero
198 double precision :: w(0:1,3)=zero
199 double precision :: dxloc(3)=zero
200 double precision :: r=zero,theta=zero,phi=zero
201 double precision :: sin_theta=zero,cos_theta=zero
202 double precision :: sin_phi=zero,cos_phi=zero
203 double precision :: rsin_theta=zero
204 double precision :: h_local=zero
205 logical :: bcorner_valid=.false.
206 double precision :: bcorner(0:1,0:1,0:1,3)=zero
207 end type trace_sph_interp_ctx
208
209 type, private :: trace_spherical_curl_cache_block
210 double precision, dimension(:^D&,:), allocatable :: current
211 logical :: ready=.false.
212 end type trace_spherical_curl_cache_block
213
214 type(trace_spherical_curl_cache_block), allocatable, private :: &
215 trace_spherical_curl_cache(:)
216 logical, private :: trace_spherical_curl_cache_ready=.false.
217
218 integer, parameter, private :: trace_step_control_fixed=0
219 integer, parameter, private :: trace_step_control_cell_fraction=1
220 integer, private :: trace_step_control_mode=trace_step_control_fixed
221 double precision, private :: trace_step_fraction=0.1d0
222 double precision, private :: trace_step_min=0.d0
223
224 integer, parameter, private :: trace_integrator_rk2=0
225 integer, parameter, private :: trace_integrator_rk45_cartesian=1
226 integer, parameter, private :: trace_integrator_rk45_spherical=2
227 integer, parameter, private :: trace_tangent_group_rk2_general=1
228 integer, parameter, private :: trace_tangent_group_rk2_short_boundary=2
229 integer, parameter, private :: trace_tangent_group_rk45_cartesian=3
230 integer, parameter, private :: trace_rk45_reject_error=1
231 integer, parameter, private :: trace_rk45_reject_stage_failure=2
232 integer, parameter, private :: trace_rk45_reject_stage_outside=3
233 integer, parameter, private :: trace_rk45_reject_boundary=4
234 integer, parameter, private :: trace_rk45_reject_tangent_error=5
235 integer, private :: trace_integrator_mode=trace_integrator_rk2
236 double precision, private :: trace_rk45_atol=1.d-8
237 double precision, private :: trace_rk45_rtol=1.d-6
238 double precision, private :: trace_rk45_safety=0.9d0
239 double precision, private :: trace_rk45_min_shrink=0.2d0
240 double precision, private :: trace_rk45_max_grow=5.d0
241 double precision, private :: trace_rk45_tangent_floor=1.d0
242 double precision, private :: trace_rk45_tangent_rtol=2.d-5
243 logical, private :: trace_rk2_stats_enabled=.false.
244 integer(kind=8), private :: trace_rk2_directions=0_8
245 integer(kind=8), private :: trace_rk2_completed_directions=0_8
246 integer(kind=8), private :: trace_rk2_total_steps=0_8
247 integer(kind=8), private :: trace_rk2_boundary_completions=0_8
248 integer(kind=8), private :: trace_rk2_max_steps_failures=0_8
249 integer(kind=8), private :: trace_rk2_domain_limited_steps=0_8
250 integer(kind=8), private :: trace_rk2_grid_transition_steps=0_8
251 integer(kind=8), private :: trace_rk2_cell_fraction_limited=0_8
252 integer(kind=8), private :: trace_rk2_dl_limited=0_8
253 integer(kind=8), private :: trace_rk2_dl_min_limited=0_8
254 integer(kind=8), private :: trace_rk2_group_iterations=0_8
255 integer(kind=8), private :: trace_rk2_grid_groups=0_8
256 integer(kind=8), private :: trace_rk2_group_state_sum=0_8
257 integer(kind=8), private :: trace_rk2_group_state_max=0_8
258 integer(kind=8), private :: trace_rk2_rhs_calls=0_8
259 double precision, private :: trace_rk2_sum_step_length=0.d0
260 double precision, private :: trace_rk2_min_step_length=huge(one)
261 double precision, private :: trace_rk2_max_step_length=0.d0
262 double precision, private :: trace_rk2_grouped_time=0.d0
263 integer(kind=8), private :: trace_rk2_max_steps_per_direction=0_8
264 logical, private :: trace_rk45_stats_enabled=.false.
265 integer(kind=8), private :: trace_rk45_directions=0_8
266 integer(kind=8), private :: trace_rk45_attempts=0_8
267 integer(kind=8), private :: trace_rk45_accepted=0_8
268 integer(kind=8), private :: trace_rk45_rejected=0_8
269 integer(kind=8), private :: trace_rk45_rejected_error=0_8
270 integer(kind=8), private :: trace_rk45_rejected_position_error=0_8
271 integer(kind=8), private :: trace_rk45_rejected_tangent_error=0_8
272 integer(kind=8), private :: trace_rk45_rejected_stage_failure=0_8
273 integer(kind=8), private :: trace_rk45_rejected_stage_outside=0_8
274 integer(kind=8), private :: trace_rk45_rejected_boundary=0_8
275 integer(kind=8), private :: trace_rk45_boundary_limited=0_8
276 double precision, private :: trace_rk45_sum_accepted_h=0.d0
277 double precision, private :: trace_rk45_min_accepted_h=huge(one)
278 double precision, private :: trace_rk45_max_accepted_h=0.d0
279 integer(kind=8), private :: trace_rk45_tangent_error_samples=0_8
280 integer(kind=8), private :: trace_rk45_tangent_would_reject=0_8
281 double precision, private :: trace_rk45_sum_tangent_error_ratio=0.d0
282 double precision, private :: trace_rk45_max_tangent_error_ratio=0.d0
283 double precision, private :: trace_rk45_max_tangent_u_error_ratio=0.d0
284 double precision, private :: trace_rk45_max_tangent_v_error_ratio=0.d0
285 double precision, private :: trace_rk45_max_position_error_ratio=0.d0
286
287 logical, private :: trace_spherical_profile_enabled=.false.
288 logical, parameter, private :: trace_spherical_profile_timers=.false.
289 integer(kind=8), private :: trace_profile_seeds=0_8
290 integer(kind=8), private :: trace_profile_directions=0_8
291 integer(kind=8), private :: trace_profile_steps=0_8
292 integer(kind=8), private :: trace_profile_boundary_events=0_8
293 integer(kind=8), private :: trace_profile_endpoint_finalizations=0_8
294 integer(kind=8), private :: trace_profile_context_requests=0_8
295 integer(kind=8), private :: trace_profile_same_cell_hits=0_8
296 integer(kind=8), private :: trace_profile_same_grid_hits=0_8
297 integer(kind=8), private :: trace_profile_full_context_builds=0_8
298 integer(kind=8), private :: trace_profile_context_failures=0_8
299 integer(kind=8), private :: trace_profile_cache_invalidations=0_8
300 integer(kind=8), private :: trace_profile_b_samples=0_8
301 integer(kind=8), private :: trace_profile_curl_samples=0_8
302 integer(kind=8), private :: trace_profile_grad_samples=0_8
303 integer(kind=8), private :: trace_profile_grad_success=0_8
304 integer(kind=8), private :: trace_profile_grad_fallbacks=0_8
305 integer(kind=8), private :: trace_profile_b_corner_loads=0_8
306 integer(kind=8), private :: trace_profile_b_corner_hits=0_8
307 integer(kind=8), private :: trace_profile_hlocal_evals=0_8
308 integer(kind=8), private :: trace_profile_face_probes=0_8
309 integer(kind=8), private :: trace_profile_tangent_systems=0_8
310 integer(kind=8), private :: trace_profile_q_tangent_systems=0_8
311 integer(kind=8), private :: trace_profile_qperp_tangent_systems=0_8
312 integer(kind=8), private :: trace_profile_combined_tangent_systems=0_8
313 integer(kind=8), private :: trace_profile_max_steps_per_trace=0_8
314 double precision, private :: trace_profile_time_context=0.d0
315 double precision, private :: trace_profile_time_b_interp=0.d0
316 double precision, private :: trace_profile_time_curl_interp=0.d0
317 double precision, private :: trace_profile_time_grad_bhat=0.d0
318 double precision, private :: trace_profile_time_step=0.d0
319 double precision, private :: trace_profile_time_tangent=0.d0
320
321 type, private :: trace_summary_state
322 double precision :: x(ndim)
323 double precision :: footpoint(ndim)
324 double precision :: length
325 double precision :: twist
326 integer :: nstep
327 integer :: status
328 integer :: twist_status
329 integer :: igrid
330 integer :: seed_id
331 integer :: face
332 logical :: forward
333 logical :: active
334 logical :: accumulate_twist
335 double precision :: rk45_h
336 type(trace_sph_interp_ctx) :: sph_cache
337 end type trace_summary_state
338
339 type, private :: trace_tangent_state
340 integer :: seed_id
341 integer :: direction
342 logical :: active
343 logical :: complete
344 logical :: has_extra
345 double precision :: x(ndim)
346 double precision :: u(ndim)
347 double precision :: v(ndim)
348 double precision :: p(ndim)
349 double precision :: q(ndim)
350 double precision :: length
351 double precision :: twist
352 integer :: nstep
353 integer :: igrid
354 integer :: status
355 integer :: twist_status
356 integer :: face_id
357 double precision :: endpoint(ndim)
358 double precision :: endpoint_b(ndim)
359 double precision :: endpoint_bhat(ndim)
360 double precision :: u_perp(ndim)
361 double precision :: v_perp(ndim)
362 double precision :: p_perp(ndim)
363 double precision :: q_perp(ndim)
364 double precision :: rk45_h
365 logical :: accumulate_twist
366 type(trace_sph_interp_ctx) :: sph_cache
367 end type trace_tangent_state
368
369 type, public :: trace_length_result
370 double precision :: seed(ndim)
371 double precision :: forward_footpoint(ndim)
372 double precision :: backward_footpoint(ndim)
373 double precision :: forward_length
374 double precision :: backward_length
375 double precision :: total_length
376 integer :: forward_nstep
377 integer :: backward_nstep
378 integer :: forward_status
379 integer :: backward_status
380 end type trace_length_result
381
382 type, public :: trace_twist_result
384 double precision :: forward_twist
385 double precision :: backward_twist
386 double precision :: total_twist
387 logical :: valid_twist
388 integer :: status_twist
389 end type trace_twist_result
390
391 type, public :: trace_mapping_result
392 double precision :: seed(ndim)
393 double precision :: source_b(3)
394 double precision :: forward_footpoint(ndim)
395 double precision :: backward_footpoint(ndim)
396 double precision :: forward_b(3)
397 double precision :: backward_b(3)
398 double precision :: forward_length
399 double precision :: backward_length
400 double precision :: source_bn
401 double precision :: forward_bn
402 double precision :: backward_bn
403 integer :: forward_face
404 integer :: backward_face
405 integer :: forward_status
406 integer :: backward_status
407 logical :: valid
408 end type trace_mapping_result
409
410 type, public :: trace_qperp_result
411 double precision :: seed(ndim)
412 double precision :: qperp
413 double precision :: logqperp
414 double precision :: n2
415 double precision :: bfactor
416 double precision :: q0
417 double precision :: logq0
418 double precision :: qperp0
419 double precision :: logqperp0
420 double precision :: n2_qperp0
421 double precision :: bfactor_qperp0
422 double precision :: forward_bn_q0
423 double precision :: backward_bn_q0
424 double precision :: b_seed(ndim)
425 double precision :: bhat_seed(ndim)
426 double precision :: forward_endpoint(ndim)
427 double precision :: backward_endpoint(ndim)
428 double precision :: forward_b(ndim)
429 double precision :: backward_b(ndim)
430 double precision :: forward_bhat(ndim)
431 double precision :: backward_bhat(ndim)
432 double precision :: forward_length
433 double precision :: backward_length
434 integer :: forward_nstep
435 integer :: backward_nstep
436 double precision :: u0(ndim)
437 double precision :: v0(ndim)
438 double precision :: u_forward_perp(ndim)
439 double precision :: v_forward_perp(ndim)
440 double precision :: u_backward_perp(ndim)
441 double precision :: v_backward_perp(ndim)
442 integer :: forward_face
443 integer :: backward_face
444 integer :: forward_status
445 integer :: backward_status
446 integer :: status_q0
447 integer :: status_qperp0
448 integer :: status
449 logical :: valid
450 logical :: valid_q0
451 logical :: valid_qperp0
452 end type trace_qperp_result
453
454 type, public :: trace_topology_result
455 double precision :: seed(ndim)
456 double precision :: length_forward
457 double precision :: length_backward
458 double precision :: length_total
459 double precision :: twist_forward
460 double precision :: twist_backward
461 double precision :: twist_total
462 double precision :: forward_endpoint(ndim)
463 double precision :: backward_endpoint(ndim)
464 integer :: forward_nstep
465 integer :: backward_nstep
466 integer :: forward_face
467 integer :: backward_face
468 integer :: forward_status
469 integer :: backward_status
470 double precision :: map_forward_endpoint(ndim)
471 double precision :: map_backward_endpoint(ndim)
472 double precision :: map_forward_length
473 double precision :: map_backward_length
474 integer :: map_forward_face
475 integer :: map_backward_face
476 integer :: map_forward_status
477 integer :: map_backward_status
478 double precision :: source_b(3)
479 double precision :: forward_b(3)
480 double precision :: backward_b(3)
481 double precision :: source_bn
482 double precision :: forward_bn
483 double precision :: backward_bn
484 logical :: has_twist
485 logical :: has_mapping
486 logical :: valid_twist
487 logical :: valid
488 integer :: status_twist
489 integer :: status
490 end type trace_topology_result
491
492contains
493
494 subroutine trace_set_step_control(mode,step_fraction,step_min)
495 character(len=*), intent(in) :: mode
496 double precision, intent(in) :: step_fraction,step_min
497
498 character(len=len(mode)) :: mode_lc
499 integer :: i,code
500
501 mode_lc=mode
502 do i=1,len(mode_lc)
503 code=iachar(mode_lc(i:i))
504 if (code>=iachar('A') .and. code<=iachar('Z')) then
505 mode_lc(i:i)=achar(code+iachar('a')-iachar('A'))
506 endif
507 enddo
508
509 select case (trim(mode_lc))
510 case ('cell_fraction')
511 trace_step_control_mode=trace_step_control_cell_fraction
512 case default
513 trace_step_control_mode=trace_step_control_fixed
514 end select
515 trace_step_fraction=step_fraction
516 trace_step_min=max(zero,step_min)
517 end subroutine trace_set_step_control
518
519 subroutine trace_set_integrator(mode,atol,rtol,safety,min_shrink, &
520 max_grow,tangent_floor,tangent_rtol)
521 character(len=*), intent(in) :: mode
522 double precision, intent(in) :: atol,rtol,safety,min_shrink,max_grow
523 double precision, intent(in) :: tangent_floor,tangent_rtol
524
525 character(len=len(mode)) :: mode_lc
526 integer :: i,code
527
528 mode_lc=mode
529 do i=1,len(mode_lc)
530 code=iachar(mode_lc(i:i))
531 if (code>=iachar('A') .and. code<=iachar('Z')) then
532 mode_lc(i:i)=achar(code+iachar('a')-iachar('A'))
533 endif
534 enddo
535
536 select case (trim(mode_lc))
537 case ('rk45_cartesian')
538 trace_integrator_mode=trace_integrator_rk45_cartesian
539 case ('rk45_spherical')
540 trace_integrator_mode=trace_integrator_rk45_spherical
541 case default
542 trace_integrator_mode=trace_integrator_rk2
543 end select
544 trace_rk45_atol=max(atol,zero)
545 trace_rk45_rtol=max(rtol,zero)
546 trace_rk45_safety=max(0.1d0,min(safety,one))
547 trace_rk45_min_shrink=max(0.01d0,min(min_shrink,one))
548 trace_rk45_max_grow=max(one,max_grow)
549 trace_rk45_tangent_floor=max(tangent_floor,smalldouble)
550 trace_rk45_tangent_rtol=max(tangent_rtol,smalldouble)
551 end subroutine trace_set_integrator
552
553 subroutine trace_rk2_stats_set_enabled(enabled)
554 logical, intent(in) :: enabled
555
556 trace_rk2_stats_enabled=enabled
557 end subroutine trace_rk2_stats_set_enabled
558
560 trace_rk2_directions=0_8
561 trace_rk2_completed_directions=0_8
562 trace_rk2_total_steps=0_8
563 trace_rk2_boundary_completions=0_8
564 trace_rk2_max_steps_failures=0_8
565 trace_rk2_domain_limited_steps=0_8
566 trace_rk2_grid_transition_steps=0_8
567 trace_rk2_cell_fraction_limited=0_8
568 trace_rk2_dl_limited=0_8
569 trace_rk2_dl_min_limited=0_8
570 trace_rk2_group_iterations=0_8
571 trace_rk2_grid_groups=0_8
572 trace_rk2_group_state_sum=0_8
573 trace_rk2_group_state_max=0_8
574 trace_rk2_rhs_calls=0_8
575 trace_rk2_sum_step_length=0.d0
576 trace_rk2_min_step_length=huge(one)
577 trace_rk2_max_step_length=0.d0
578 trace_rk2_grouped_time=0.d0
579 trace_rk2_max_steps_per_direction=0_8
580 end subroutine trace_rk2_stats_reset
581
582 subroutine trace_rk2_stats_report(label)
583 character(len=*), intent(in) :: label
584
585 double precision :: mean_group_size,mean_h,mean_steps_per_direction
586
587 if (.not.trace_rk2_stats_enabled) return
588
589 mean_h=zero
590 if (trace_rk2_total_steps>0_8) then
591 mean_h=trace_rk2_sum_step_length/dble(trace_rk2_total_steps)
592 endif
593 mean_steps_per_direction=zero
594 if (trace_rk2_completed_directions>0_8) then
595 mean_steps_per_direction=dble(trace_rk2_total_steps)/ &
596 dble(trace_rk2_completed_directions)
597 endif
598 mean_group_size=zero
599 if (trace_rk2_grid_groups>0_8) then
600 mean_group_size=dble(trace_rk2_group_state_sum)/ &
601 dble(trace_rk2_grid_groups)
602 endif
603
604 write(*,'(a)') '[mt_rk2] Cartesian field-line RK2 summary: '// &
605 trim(label)
606 write(*,'(a,i0)') ' traced_directions: ',trace_rk2_directions
607 write(*,'(a,i0)') ' completed_directions: ', &
608 trace_rk2_completed_directions
609 write(*,'(a,i0)') ' total_steps: ',trace_rk2_total_steps
610 write(*,'(a,es12.4)') ' mean_step_length: ',mean_h
611 if (trace_rk2_total_steps>0_8) then
612 write(*,'(a,es12.4)') ' min_step_length: ', &
613 trace_rk2_min_step_length
614 write(*,'(a,es12.4)') ' max_step_length: ', &
615 trace_rk2_max_step_length
616 endif
617 write(*,'(a,es12.4)') ' mean_steps_per_direction: ', &
618 mean_steps_per_direction
619 write(*,'(a,i0)') ' max_steps_per_direction: ', &
620 trace_rk2_max_steps_per_direction
621 write(*,'(a,i0)') ' boundary_completions: ', &
622 trace_rk2_boundary_completions
623 write(*,'(a,i0)') ' max_steps_failures: ', &
624 trace_rk2_max_steps_failures
625 write(*,'(a,i0)') ' domain_boundary_limited_steps: ', &
626 trace_rk2_domain_limited_steps
627 write(*,'(a,i0)') ' block_grid_clipped_steps: 0'
628 write(*,'(a,i0)') ' grid_transition_steps: ', &
629 trace_rk2_grid_transition_steps
630 write(*,'(a,i0)') ' cell_fraction_limited_evals: ', &
631 trace_rk2_cell_fraction_limited
632 write(*,'(a,i0)') ' dL_cap_limited_evals: ',trace_rk2_dl_limited
633 write(*,'(a,i0)') ' dL_min_limited_evals: ',trace_rk2_dl_min_limited
634 write(*,'(a,i0)') ' grouped_driver_outer_iterations: ', &
635 trace_rk2_group_iterations
636 write(*,'(a,i0)') ' grouped_driver_parallel_batches: ', &
637 trace_rk2_grid_groups
638 write(*,'(a,es12.4)') ' grouped_driver_mean_batch_size: ', &
639 mean_group_size
640 write(*,'(a,i0)') ' grouped_driver_max_batch_size: ', &
641 trace_rk2_group_state_max
642 write(*,'(a,es12.4)') ' grouped_driver_cpu_time: ', &
643 trace_rk2_grouped_time
644 write(*,'(a,i0)') ' trace_tangent_rhs_calls: ',trace_rk2_rhs_calls
645 write(*,'(a)') ' note: Cartesian RK2 grouped tracing does not clip '// &
646 'steps to AMR block/grid boundaries; grid changes are counted after '// &
647 'accepted full steps.'
648 end subroutine trace_rk2_stats_report
649
650 subroutine trace_rk2_stats_note_direction()
651 if (.not.trace_rk2_stats_enabled) return
652 !$OMP CRITICAL(trace_rk2_stats)
653 trace_rk2_directions=trace_rk2_directions+1_8
654 !$OMP END CRITICAL(trace_rk2_stats)
655 end subroutine trace_rk2_stats_note_direction
656
657 subroutine trace_rk2_stats_note_step(h,domain_limited,grid_transition)
658 double precision, intent(in) :: h
659 logical, intent(in) :: domain_limited,grid_transition
660
661 if (.not.trace_rk2_stats_enabled) return
662 !$OMP CRITICAL(trace_rk2_stats)
663 trace_rk2_total_steps=trace_rk2_total_steps+1_8
664 trace_rk2_sum_step_length=trace_rk2_sum_step_length+abs(h)
665 trace_rk2_min_step_length=min(trace_rk2_min_step_length,abs(h))
666 trace_rk2_max_step_length=max(trace_rk2_max_step_length,abs(h))
667 if (domain_limited) trace_rk2_domain_limited_steps= &
668 trace_rk2_domain_limited_steps+1_8
669 if (grid_transition) trace_rk2_grid_transition_steps= &
670 trace_rk2_grid_transition_steps+1_8
671 !$OMP END CRITICAL(trace_rk2_stats)
672 end subroutine trace_rk2_stats_note_step
673
674 subroutine trace_rk2_stats_note_completion(status,nstep)
675 integer, intent(in) :: status,nstep
676
677 if (.not.trace_rk2_stats_enabled) return
678 !$OMP CRITICAL(trace_rk2_stats)
679 trace_rk2_completed_directions=trace_rk2_completed_directions+1_8
680 trace_rk2_max_steps_per_direction=max( &
681 trace_rk2_max_steps_per_direction,int(nstep,kind=8))
682 select case (status)
684 trace_rk2_boundary_completions=trace_rk2_boundary_completions+1_8
686 trace_rk2_max_steps_failures=trace_rk2_max_steps_failures+1_8
687 end select
688 !$OMP END CRITICAL(trace_rk2_stats)
689 end subroutine trace_rk2_stats_note_completion
690
691 subroutine trace_rk2_stats_note_step_limit(step_cap,cell_step,actual_step)
692 double precision, intent(in) :: step_cap,cell_step,actual_step
693
694 double precision :: tol
695
696 if (.not.trace_rk2_stats_enabled) return
697 tol=100.d0*epsilon(one)*max(one,step_cap,cell_step,actual_step)
698 !$OMP CRITICAL(trace_rk2_stats)
699 if (actual_step>=step_cap-tol) then
700 trace_rk2_dl_limited=trace_rk2_dl_limited+1_8
701 else if (trace_step_min>zero .and. actual_step>=trace_step_min-tol .and. &
702 actual_step>cell_step+tol) then
703 trace_rk2_dl_min_limited=trace_rk2_dl_min_limited+1_8
704 else
705 trace_rk2_cell_fraction_limited=trace_rk2_cell_fraction_limited+1_8
706 endif
707 !$OMP END CRITICAL(trace_rk2_stats)
708 end subroutine trace_rk2_stats_note_step_limit
709
710 subroutine trace_rk2_stats_note_group_iteration()
711 if (.not.trace_rk2_stats_enabled) return
712 !$OMP CRITICAL(trace_rk2_stats)
713 trace_rk2_group_iterations=trace_rk2_group_iterations+1_8
714 !$OMP END CRITICAL(trace_rk2_stats)
715 end subroutine trace_rk2_stats_note_group_iteration
716
717 subroutine trace_rk2_stats_note_grid_group(group_size)
718 integer, intent(in) :: group_size
719
720 if (.not.trace_rk2_stats_enabled) return
721 !$OMP CRITICAL(trace_rk2_stats)
722 trace_rk2_grid_groups=trace_rk2_grid_groups+1_8
723 trace_rk2_group_state_sum=trace_rk2_group_state_sum+ &
724 int(group_size,kind=8)
725 trace_rk2_group_state_max=max(trace_rk2_group_state_max, &
726 int(group_size,kind=8))
727 !$OMP END CRITICAL(trace_rk2_stats)
728 end subroutine trace_rk2_stats_note_grid_group
729
730 subroutine trace_rk2_stats_note_rhs()
731 if (.not.trace_rk2_stats_enabled) return
732 !$OMP ATOMIC
733 trace_rk2_rhs_calls=trace_rk2_rhs_calls+1_8
734 end subroutine trace_rk2_stats_note_rhs
735
736 subroutine trace_rk2_stats_add_grouped_time(dt)
737 double precision, intent(in) :: dt
738
739 if (.not.trace_rk2_stats_enabled) return
740 !$OMP CRITICAL(trace_rk2_stats)
741 trace_rk2_grouped_time=trace_rk2_grouped_time+dt
742 !$OMP END CRITICAL(trace_rk2_stats)
743 end subroutine trace_rk2_stats_add_grouped_time
744
746 logical, intent(in) :: enabled
747
748 trace_rk45_stats_enabled=enabled
749 end subroutine trace_rk45_stats_set_enabled
750
752 trace_rk45_directions=0_8
753 trace_rk45_attempts=0_8
754 trace_rk45_accepted=0_8
755 trace_rk45_rejected=0_8
756 trace_rk45_rejected_error=0_8
757 trace_rk45_rejected_position_error=0_8
758 trace_rk45_rejected_tangent_error=0_8
759 trace_rk45_rejected_stage_failure=0_8
760 trace_rk45_rejected_stage_outside=0_8
761 trace_rk45_rejected_boundary=0_8
762 trace_rk45_boundary_limited=0_8
763 trace_rk45_sum_accepted_h=0.d0
764 trace_rk45_min_accepted_h=huge(one)
765 trace_rk45_max_accepted_h=0.d0
766 trace_rk45_tangent_error_samples=0_8
767 trace_rk45_tangent_would_reject=0_8
768 trace_rk45_sum_tangent_error_ratio=0.d0
769 trace_rk45_max_tangent_error_ratio=0.d0
770 trace_rk45_max_tangent_u_error_ratio=0.d0
771 trace_rk45_max_tangent_v_error_ratio=0.d0
772 trace_rk45_max_position_error_ratio=0.d0
773 end subroutine trace_rk45_stats_reset
774
775 subroutine trace_rk45_stats_report(label)
776 character(len=*), intent(in) :: label
777
778 double precision :: mean_h,mean_reject_per_trace,mean_tangent_error
779
780 if (.not.trace_rk45_stats_enabled) return
781 if (trace_integrator_mode/=trace_integrator_rk45_cartesian .and. &
782 trace_integrator_mode/=trace_integrator_rk45_spherical) return
783
784 mean_h=zero
785 if (trace_rk45_accepted>0_8) then
786 mean_h=trace_rk45_sum_accepted_h/dble(trace_rk45_accepted)
787 endif
788 mean_reject_per_trace=zero
789 if (trace_rk45_directions>0_8) then
790 mean_reject_per_trace=dble(trace_rk45_rejected)/dble(trace_rk45_directions)
791 endif
792
793 select case (trace_integrator_mode)
794 case (trace_integrator_rk45_cartesian)
795 write(*,'(a)') '[mt_rk45] Cartesian field-line RK45 summary: '// &
796 trim(label)
797 case (trace_integrator_rk45_spherical)
798 write(*,'(a)') '[mt_rk45] Spherical field-line RK45 summary: '// &
799 trim(label)
800 end select
801 write(*,'(a,i0)') ' traced_directions: ',trace_rk45_directions
802 write(*,'(a,i0)') ' attempts: ',trace_rk45_attempts
803 write(*,'(a,i0)') ' accepted_steps: ',trace_rk45_accepted
804 write(*,'(a,i0)') ' rejected_steps: ',trace_rk45_rejected
805 write(*,'(a,i0)') ' rejected_error_steps: ', &
806 trace_rk45_rejected_error
807 write(*,'(a,i0)') ' rejected_position_error_steps: ', &
808 trace_rk45_rejected_position_error
809 write(*,'(a,i0)') ' rejected_tangent_error_steps: ', &
810 trace_rk45_rejected_tangent_error
811 write(*,'(a,i0)') ' rejected_stage_failure_steps: ', &
812 trace_rk45_rejected_stage_failure
813 write(*,'(a,i0)') ' rejected_stage_outside_steps: ', &
814 trace_rk45_rejected_stage_outside
815 write(*,'(a,i0)') ' rejected_boundary_steps: ', &
816 trace_rk45_rejected_boundary
817 write(*,'(a,es12.4)') ' rejected_steps_per_direction: ', &
818 mean_reject_per_trace
819 write(*,'(a,i0)') ' boundary_limited_steps: ', &
820 trace_rk45_boundary_limited
821 write(*,'(a,es12.4)') ' mean_accepted_step: ',mean_h
822 if (trace_rk45_accepted>0_8) then
823 write(*,'(a,es12.4)') ' min_accepted_step: ', &
824 trace_rk45_min_accepted_h
825 write(*,'(a,es12.4)') ' max_accepted_step: ', &
826 trace_rk45_max_accepted_h
827 endif
828 if (trace_rk45_tangent_error_samples>0_8) then
829 write(*,'(a)') ' error_control_mode: position_tangent'
830 mean_tangent_error=trace_rk45_sum_tangent_error_ratio/ &
831 dble(trace_rk45_tangent_error_samples)
832 write(*,'(a,i0)') ' tangent_error_samples: ', &
833 trace_rk45_tangent_error_samples
834 write(*,'(a,i0)') ' tangent_would_reject_steps: ', &
835 trace_rk45_tangent_would_reject
836 write(*,'(a,es12.4)') ' max_position_error_ratio: ', &
837 trace_rk45_max_position_error_ratio
838 write(*,'(a,es12.4)') ' mean_tangent_error_ratio: ', &
839 mean_tangent_error
840 write(*,'(a,es12.4)') ' max_tangent_error_ratio: ', &
841 trace_rk45_max_tangent_error_ratio
842 write(*,'(a,es12.4)') ' max_tangent_u_error_ratio: ', &
843 trace_rk45_max_tangent_u_error_ratio
844 write(*,'(a,es12.4)') ' max_tangent_v_error_ratio: ', &
845 trace_rk45_max_tangent_v_error_ratio
846 write(*,'(a,es12.4)') ' tangent_error_rtol: ', &
847 trace_rk45_tangent_rtol
848 write(*,'(a,es12.4)') ' tangent_error_floor: ', &
849 trace_rk45_tangent_floor
850 write(*,'(a)') ' tangent_error_note: standard-logQ tangent '// &
851 'traces use position+tangent RK45 accept/reject.'
852 else
853 write(*,'(a)') ' error_control_mode: position'
854 endif
855 write(*,'(a)') ' note: this experimental RK45 mode traces '// &
856 'position/length/twist and tangent-transport Q products where enabled.'
857 end subroutine trace_rk45_stats_report
858
859 subroutine trace_rk45_stats_note_direction()
860 if (.not.trace_rk45_stats_enabled) return
861 if (trace_integrator_mode/=trace_integrator_rk45_cartesian .and. &
862 trace_integrator_mode/=trace_integrator_rk45_spherical) return
863 !$OMP CRITICAL(trace_rk45_stats)
864 trace_rk45_directions=trace_rk45_directions+1_8
865 !$OMP END CRITICAL(trace_rk45_stats)
866 end subroutine trace_rk45_stats_note_direction
867
868 subroutine trace_rk45_stats_note_attempt(accepted,boundary_limited,h, &
869 reject_reason)
870 logical, intent(in) :: accepted,boundary_limited
871 double precision, intent(in) :: h
872 integer, intent(in), optional :: reject_reason
873
874 if (.not.trace_rk45_stats_enabled) return
875 if (trace_integrator_mode/=trace_integrator_rk45_cartesian .and. &
876 trace_integrator_mode/=trace_integrator_rk45_spherical) return
877 !$OMP CRITICAL(trace_rk45_stats)
878 trace_rk45_attempts=trace_rk45_attempts+1_8
879 if (accepted) then
880 trace_rk45_accepted=trace_rk45_accepted+1_8
881 trace_rk45_sum_accepted_h=trace_rk45_sum_accepted_h+abs(h)
882 trace_rk45_min_accepted_h=min(trace_rk45_min_accepted_h,abs(h))
883 trace_rk45_max_accepted_h=max(trace_rk45_max_accepted_h,abs(h))
884 if (boundary_limited) trace_rk45_boundary_limited= &
885 trace_rk45_boundary_limited+1_8
886 else
887 trace_rk45_rejected=trace_rk45_rejected+1_8
888 if (present(reject_reason)) then
889 select case (reject_reason)
890 case (trace_rk45_reject_error)
891 trace_rk45_rejected_error=trace_rk45_rejected_error+1_8
892 trace_rk45_rejected_position_error= &
893 trace_rk45_rejected_position_error+1_8
894 case (trace_rk45_reject_tangent_error)
895 trace_rk45_rejected_error=trace_rk45_rejected_error+1_8
896 trace_rk45_rejected_tangent_error= &
897 trace_rk45_rejected_tangent_error+1_8
898 case (trace_rk45_reject_stage_failure)
899 trace_rk45_rejected_stage_failure= &
900 trace_rk45_rejected_stage_failure+1_8
901 case (trace_rk45_reject_stage_outside)
902 trace_rk45_rejected_stage_outside= &
903 trace_rk45_rejected_stage_outside+1_8
904 case (trace_rk45_reject_boundary)
905 trace_rk45_rejected_boundary=trace_rk45_rejected_boundary+1_8
906 end select
907 endif
908 endif
909 !$OMP END CRITICAL(trace_rk45_stats)
910 end subroutine trace_rk45_stats_note_attempt
911
912 subroutine trace_rk45_stats_note_tangent_error(position_error_ratio, &
913 u_error_ratio,v_error_ratio)
914 double precision, intent(in) :: position_error_ratio
915 double precision, intent(in) :: u_error_ratio,v_error_ratio
916
917 double precision :: tangent_error_ratio
918
919 if (.not.trace_rk45_stats_enabled) return
920 if (trace_integrator_mode/=trace_integrator_rk45_cartesian .and. &
921 trace_integrator_mode/=trace_integrator_rk45_spherical) return
922 tangent_error_ratio=max(u_error_ratio,v_error_ratio)
923 !$OMP CRITICAL(trace_rk45_stats)
924 trace_rk45_tangent_error_samples= &
925 trace_rk45_tangent_error_samples+1_8
926 trace_rk45_sum_tangent_error_ratio= &
927 trace_rk45_sum_tangent_error_ratio+tangent_error_ratio
928 trace_rk45_max_tangent_error_ratio= &
929 max(trace_rk45_max_tangent_error_ratio,tangent_error_ratio)
930 trace_rk45_max_tangent_u_error_ratio= &
931 max(trace_rk45_max_tangent_u_error_ratio,u_error_ratio)
932 trace_rk45_max_tangent_v_error_ratio= &
933 max(trace_rk45_max_tangent_v_error_ratio,v_error_ratio)
934 trace_rk45_max_position_error_ratio= &
935 max(trace_rk45_max_position_error_ratio,position_error_ratio)
936 if (tangent_error_ratio>trace_rk45_tangent_rtol) &
937 trace_rk45_tangent_would_reject= &
938 trace_rk45_tangent_would_reject+1_8
939 !$OMP END CRITICAL(trace_rk45_stats)
940 end subroutine trace_rk45_stats_note_tangent_error
941
942 subroutine trace_spherical_profile_set(enabled)
943 logical, intent(in) :: enabled
944
945 trace_spherical_profile_enabled=enabled
946 end subroutine trace_spherical_profile_set
947
949 trace_profile_seeds=0_8
950 trace_profile_directions=0_8
951 trace_profile_steps=0_8
952 trace_profile_boundary_events=0_8
953 trace_profile_endpoint_finalizations=0_8
954 trace_profile_context_requests=0_8
955 trace_profile_same_cell_hits=0_8
956 trace_profile_same_grid_hits=0_8
957 trace_profile_full_context_builds=0_8
958 trace_profile_context_failures=0_8
959 trace_profile_cache_invalidations=0_8
960 trace_profile_b_samples=0_8
961 trace_profile_curl_samples=0_8
962 trace_profile_grad_samples=0_8
963 trace_profile_grad_success=0_8
964 trace_profile_grad_fallbacks=0_8
965 trace_profile_b_corner_loads=0_8
966 trace_profile_b_corner_hits=0_8
967 trace_profile_hlocal_evals=0_8
968 trace_profile_face_probes=0_8
969 trace_profile_tangent_systems=0_8
970 trace_profile_q_tangent_systems=0_8
971 trace_profile_qperp_tangent_systems=0_8
972 trace_profile_combined_tangent_systems=0_8
973 trace_profile_max_steps_per_trace=0_8
974 trace_profile_time_context=0.d0
975 trace_profile_time_b_interp=0.d0
976 trace_profile_time_curl_interp=0.d0
977 trace_profile_time_grad_bhat=0.d0
978 trace_profile_time_step=0.d0
979 trace_profile_time_tangent=0.d0
980 end subroutine trace_spherical_profile_reset
981
983 integer, intent(in) :: nseed
984
985 if (.not.trace_spherical_profile_enabled) return
986 call trace_spherical_profile_add_count(trace_profile_seeds, &
987 int(max(nseed,0),kind=8))
989
991 character(len=*), intent(in) :: label
992
993 double precision :: avg_steps,denom,hit_denom
994
995 if (.not.trace_spherical_profile_enabled) return
996 if (mype/=0) return
997
998 denom=max(one,dble(trace_profile_directions))
999 avg_steps=dble(trace_profile_steps)/denom
1000 hit_denom=max(one,dble(trace_profile_context_requests))
1001
1002 write(*,'(a)') '[mt_profile] spherical topology profiling summary '// &
1003 trim(label)
1004 write(*,'(a,i0)') ' seeds: ',trace_profile_seeds
1005 write(*,'(a,i0)') ' traced_directions: ',trace_profile_directions
1006 write(*,'(a,i0)') ' accepted_steps_all_integrators: ', &
1007 trace_profile_steps
1008 write(*,'(a,es12.4)') ' avg_steps_per_counted_trace: ',avg_steps
1009 write(*,'(a,i0)') ' max_steps_per_trace: ', &
1010 trace_profile_max_steps_per_trace
1011 write(*,'(a)') ' note: accepted steps include counted spherical '// &
1012 'summary and tangent integrations; shared Q/Qperp tangent '// &
1013 'transport is counted once.'
1014 write(*,'(a)') ' note: profiling counters are diagnostic and may '// &
1015 'significantly slow hot loops; do not use profiling-on wall time '// &
1016 'as production timing.'
1017 write(*,'(a,i0)') ' boundary_events: ',trace_profile_boundary_events
1018 write(*,'(a,i0)') ' endpoint_finalizations: ', &
1019 trace_profile_endpoint_finalizations
1020 write(*,'(a,i0)') ' context_requests: ',trace_profile_context_requests
1021 write(*,'(a,i0,a,f6.2,a)') ' same_cell_hits: ', &
1022 trace_profile_same_cell_hits,' (', &
1023 100.d0*dble(trace_profile_same_cell_hits)/hit_denom,'%)'
1024 write(*,'(a,i0,a,f6.2,a)') ' same_grid_hits: ', &
1025 trace_profile_same_grid_hits,' (', &
1026 100.d0*dble(trace_profile_same_grid_hits)/hit_denom,'%)'
1027 write(*,'(a,i0,a,f6.2,a)') ' full_context_builds: ', &
1028 trace_profile_full_context_builds,' (', &
1029 100.d0*dble(trace_profile_full_context_builds)/hit_denom,'%)'
1030 write(*,'(a,i0)') ' context_failures: ',trace_profile_context_failures
1031 write(*,'(a,i0)') ' cache_invalidations: ', &
1032 trace_profile_cache_invalidations
1033 write(*,'(a,i0)') ' B_samples: ',trace_profile_b_samples
1034 write(*,'(a,i0)') ' curl_samples: ',trace_profile_curl_samples
1035 write(*,'(a,i0)') ' grad_bhat_samples: ',trace_profile_grad_samples
1036 write(*,'(a,i0)') ' grad_bhat_success: ',trace_profile_grad_success
1037 write(*,'(a,i0)') ' grad_bhat_fallbacks: ', &
1038 trace_profile_grad_fallbacks
1039 write(*,'(a,i0)') ' B_corner_loads: ',trace_profile_b_corner_loads
1040 write(*,'(a,i0)') ' B_corner_cache_hits: ', &
1041 trace_profile_b_corner_hits
1042 write(*,'(a,i0)') ' h_local_evals: ',trace_profile_hlocal_evals
1043 write(*,'(a,i0)') ' boundary_face_probes: ', &
1044 trace_profile_face_probes
1045 write(*,'(a,i0)') ' tangent_systems: ', &
1046 trace_profile_tangent_systems
1047 write(*,'(a,i0)') ' q_tangent_systems: ', &
1048 trace_profile_q_tangent_systems
1049 write(*,'(a,i0)') ' qperp_tangent_systems: ', &
1050 trace_profile_qperp_tangent_systems
1051 write(*,'(a,i0)') ' combined_q_qperp_tangent_systems: ', &
1052 trace_profile_combined_tangent_systems
1053 write(*,'(a)') ' timing_cpu_seconds:'
1054 if (.not.trace_spherical_profile_timers) then
1055 write(*,'(a)') ' disabled (hot-loop timers distort profiling runs)'
1056 return
1057 endif
1058 write(*,'(a,es12.4)') ' context: ',trace_profile_time_context
1059 write(*,'(a,es12.4)') ' B_interp: ',trace_profile_time_b_interp
1060 write(*,'(a,es12.4)') ' curl_interp: ', &
1061 trace_profile_time_curl_interp
1062 write(*,'(a,es12.4)') ' grad_bhat: ',trace_profile_time_grad_bhat
1063 write(*,'(a,es12.4)') ' step_size: ',trace_profile_time_step
1064 write(*,'(a,es12.4)') ' tangent_transport: ', &
1065 trace_profile_time_tangent
1066 end subroutine trace_spherical_profile_report
1067
1068 subroutine trace_spherical_profile_add_count(counter,delta)
1069 integer(kind=8), intent(inout) :: counter
1070 integer(kind=8), intent(in) :: delta
1071
1072 if (.not.trace_spherical_profile_enabled) return
1073 !$OMP ATOMIC UPDATE
1074 counter=counter+delta
1075 end subroutine trace_spherical_profile_add_count
1076
1077 subroutine trace_spherical_profile_add_time(timer,dt)
1078 double precision, intent(inout) :: timer
1079 double precision, intent(in) :: dt
1080
1081 if (.not.trace_spherical_profile_enabled) return
1082 if (.not.trace_spherical_profile_timers) return
1083 !$OMP ATOMIC UPDATE
1084 timer=timer+dt
1085 end subroutine trace_spherical_profile_add_time
1086
1087 subroutine trace_spherical_profile_note_trace_steps(nstep)
1088 integer, intent(in) :: nstep
1089
1090 if (.not.trace_spherical_profile_enabled) return
1091 !$OMP CRITICAL(trace_spherical_profile_max_steps)
1092 trace_profile_max_steps_per_trace=max(trace_profile_max_steps_per_trace, &
1093 int(nstep,kind=8))
1094 !$OMP END CRITICAL(trace_spherical_profile_max_steps)
1095 end subroutine trace_spherical_profile_note_trace_steps
1096
1097 double precision function trace_spherical_profile_time() result(tnow)
1098 if (.not.trace_spherical_profile_timers) then
1099 tnow=0.d0
1100 return
1101 endif
1102 call cpu_time(tnow)
1103 end function trace_spherical_profile_time
1104
1106 integer, intent(out) :: status
1107
1108 double precision :: bvec(ixg^t,1:3)
1109 integer :: iigrid,igrid,idirmin
1110 integer :: ixi^l,ixo^l,ixa^l
1111
1112 status=trace_status_active
1114
1115 if (ndim/=3 .or. geo_coordinate/=geo_spherical) then
1117 return
1118 endif
1119 if (.not.b0field .and. .not.allocated(iw_mag)) then
1121 return
1122 endif
1123
1124 {^ifthreed
1125 allocate(trace_spherical_curl_cache(max_blocks))
1126 ixi^l=ixg^ll;
1127 ixo^l=ixm^ll^ladd1;
1128 ixa^l=ixo^l^ladd1;
1129 if (iximin1>ixamin1 .or. iximax1<ixamax1 .or. &
1130 iximin2>ixamin2 .or. iximax2<ixamax2 .or. &
1131 iximin3>ixamin3 .or. iximax3<ixamax3) then
1134 return
1135 endif
1136
1137 do iigrid=1,igridstail
1138 igrid=igrids(iigrid)
1139 allocate(trace_spherical_curl_cache(igrid)%current( &
1140 lbound(ps(igrid)%w,1):ubound(ps(igrid)%w,1), &
1141 lbound(ps(igrid)%w,2):ubound(ps(igrid)%w,2), &
1142 lbound(ps(igrid)%w,3):ubound(ps(igrid)%w,3),1:3))
1143 trace_spherical_curl_cache(igrid)%current=zero
1144 bvec=zero
1145 if (allocated(iw_mag)) then
1146 bvec(:,:,:,1)=ps(igrid)%w(:,:,:,iw_mag(1))
1147 bvec(:,:,:,2)=ps(igrid)%w(:,:,:,iw_mag(2))
1148 bvec(:,:,:,3)=ps(igrid)%w(:,:,:,iw_mag(3))
1149 endif
1150
1151 block=>ps(igrid)
1152 call curlvector(bvec,ixi^l,ixo^l, &
1153 trace_spherical_curl_cache(igrid)%current,idirmin,1,3)
1154 if (b0field) then
1155 trace_spherical_curl_cache(igrid)%current(ixo^s,1:3)= &
1156 trace_spherical_curl_cache(igrid)%current(ixo^s,1:3) &
1157 +ps(igrid)%J0(ixo^s,1:3)
1158 endif
1159 trace_spherical_curl_cache(igrid)%ready=.true.
1160 enddo
1161 trace_spherical_curl_cache_ready=.true.
1162 }
1164
1166 integer :: igrid
1167
1168 if (allocated(trace_spherical_curl_cache)) then
1169 do igrid=1,size(trace_spherical_curl_cache)
1170 if (allocated(trace_spherical_curl_cache(igrid)%current)) then
1171 deallocate(trace_spherical_curl_cache(igrid)%current)
1172 endif
1173 trace_spherical_curl_cache(igrid)%ready=.false.
1174 enddo
1175 deallocate(trace_spherical_curl_cache)
1176 endif
1177 trace_spherical_curl_cache_ready=.false.
1179
1180 subroutine trace_field_length_single(seed,dL,max_steps,result,b_min)
1181 ! Trace one magnetic field line in both directions without storing its path.
1182 ! This first implementation supports uniform Cartesian grids and npe=1.
1183 double precision, intent(in) :: seed(ndim),dl
1184 integer, intent(in) :: max_steps
1185 type(trace_length_result), intent(out) :: result
1186 double precision, intent(in), optional :: b_min
1187
1188 if (present(b_min)) then
1189 call trace_summary_seed(seed,dl,max_steps,result,b_min)
1190 else
1191 call trace_summary_seed(seed,dl,max_steps,result)
1192 endif
1193 end subroutine trace_field_length_single
1194
1195 subroutine trace_field_twist_single(seed,dL,max_steps,result,b_min)
1196 ! Trace one magnetic field line and integrate twist without storing its path.
1197 double precision, intent(in) :: seed(ndim),dl
1198 integer, intent(in) :: max_steps
1199 type(trace_twist_result), intent(out) :: result
1200 double precision, intent(in), optional :: b_min
1201
1202 if (present(b_min)) then
1203 call trace_summary_twist_seed(seed,dl,max_steps,result,b_min)
1204 else
1205 call trace_summary_twist_seed(seed,dl,max_steps,result)
1206 endif
1207 end subroutine trace_field_twist_single
1208
1209 subroutine trace_field_mapping_single(seed,dL,max_steps,result,b_min, &
1210 source_normal)
1211 ! Trace one seed and return endpoint metadata for future mapping/Q work.
1212 double precision, intent(in) :: seed(ndim),dl
1213 integer, intent(in) :: max_steps
1214 type(trace_mapping_result), intent(out) :: result
1215 double precision, intent(in), optional :: b_min
1216 double precision, intent(in), optional :: source_normal(3)
1217
1218 if (present(b_min)) then
1219 if (present(source_normal)) then
1220 call trace_summary_mapping_seed(seed,dl,max_steps,result,b_min, &
1221 source_normal)
1222 else
1223 call trace_summary_mapping_seed(seed,dl,max_steps,result,b_min)
1224 endif
1225 else
1226 if (present(source_normal)) then
1227 call trace_summary_mapping_seed(seed,dl,max_steps,result, &
1228 source_normal=source_normal)
1229 else
1230 call trace_summary_mapping_seed(seed,dl,max_steps,result)
1231 endif
1232 endif
1233 end subroutine trace_field_mapping_single
1234
1235 subroutine trace_field_qperp_single(seed,dL,max_steps,result,b_min)
1236 ! Trace one seed with Method-II tangent transport and return Q_perp.
1237 double precision, intent(in) :: seed(ndim),dl
1238 integer, intent(in) :: max_steps
1239 type(trace_qperp_result), intent(out) :: result
1240 double precision, intent(in), optional :: b_min
1241
1242 if (present(b_min)) then
1243 call trace_qperp_single_core(seed,dl,max_steps,result,b_min)
1244 else
1245 call trace_qperp_single_core(seed,dl,max_steps,result)
1246 endif
1247 end subroutine trace_field_qperp_single
1248
1249 subroutine trace_debug_sample_bhat_gradbhat(seed,bhat,grad_bhat,status,b_min)
1250 ! Debug/validation hook for the future Method-II sampler.
1251 double precision, intent(in) :: seed(ndim)
1252 double precision, intent(out) :: bhat(3),grad_bhat(3,3)
1253 integer, intent(out) :: status
1254 double precision, intent(in), optional :: b_min
1255
1256 type(trace_length_result) :: located
1257 double precision :: field_min
1258 integer :: igrid
1259
1260 bhat=zero
1261 grad_bhat=zero
1262 status=trace_status_active
1263 if (npe/=1) then
1265 return
1266 else if (ndim/=3 .or. .not.slab_uniform) then
1268 return
1269 endif
1270
1271 field_min=smalldouble
1272 if (present(b_min)) field_min=max(b_min,zero)
1273
1274 call trace_summary_locate_seed(seed,located,igrid)
1275 status=located%forward_status
1276 if (status/=trace_status_active) return
1277
1278 call sample_bhat_gradbhat_at_point(seed,igrid,field_min,bhat, &
1279 grad_bhat,status)
1281
1282 subroutine trace_debug_sample_endpoint_b_face_limit(xhit,face_id,B,bhat, &
1283 status,b_min)
1284 ! Debug/validation hook for the Qperp endpoint face-limit B sampler.
1285 double precision, intent(in) :: xhit(ndim)
1286 integer, intent(in) :: face_id
1287 double precision, intent(out) :: b(ndim),bhat(ndim)
1288 integer, intent(out) :: status
1289 double precision, intent(in), optional :: b_min
1290
1291 double precision :: field_min,xprobe(ndim),b3(3),bhat3(3)
1292 double precision :: xface,span
1293 integer :: normal_dim,side,igrid
1294
1295 b=zero
1296 bhat=zero
1297 status=trace_status_active
1298 if (npe/=1) then
1300 return
1301 else if (ndim/=3 .or. .not.slab_uniform) then
1303 return
1304 endif
1305 if (.not.trace_face_is_boundary(face_id)) then
1307 return
1308 endif
1309
1310 field_min=smalldouble
1311 if (present(b_min)) field_min=max(b_min,zero)
1312
1313 {^ifthreed
1314 select case(face_id)
1315 case(trace_face_xmin)
1316 normal_dim=1
1317 side=-1
1318 xface=xprobmin1
1319 case(trace_face_xmax)
1320 normal_dim=1
1321 side=1
1322 xface=xprobmax1
1323 case(trace_face_ymin)
1324 normal_dim=2
1325 side=-1
1326 xface=xprobmin2
1327 case(trace_face_ymax)
1328 normal_dim=2
1329 side=1
1330 xface=xprobmax2
1331 case(trace_face_zmin)
1332 normal_dim=3
1333 side=-1
1334 xface=xprobmin3
1335 case(trace_face_zmax)
1336 normal_dim=3
1337 side=1
1338 xface=xprobmax3
1339 case default
1341 return
1342 end select
1343
1344 span=max(abs(xprobmax1-xprobmin1), &
1345 max(abs(xprobmax2-xprobmin2),abs(xprobmax3-xprobmin3)))
1346 xprobe=xhit
1347 xprobe(normal_dim)=xface-side*1.d-6*max(one,span)
1348 call trace_debug_locate_point(xprobe,igrid,status)
1349 if (status/=trace_status_active) return
1350
1351 call trace_sample_b_on_domain_face_limit(xhit,face_id,igrid,field_min, &
1352 b3,bhat3,status)
1353 if (status/=trace_status_active) return
1354 b=b3(1:ndim)
1355 bhat=bhat3(1:ndim)
1357 }
1359
1360 subroutine trace_debug_compare_gradbhat(seed,eps,bhat_current, &
1361 grad_current,status_current,bhat_xeps,grad_xeps,status_xeps,b_min)
1362 ! Debug/validation hook comparing production and x+-eps grad(bhat).
1363 double precision, intent(in) :: seed(ndim),eps
1364 double precision, intent(out) :: bhat_current(3),grad_current(3,3)
1365 double precision, intent(out) :: bhat_xeps(3),grad_xeps(3,3)
1366 integer, intent(out) :: status_current,status_xeps
1367 double precision, intent(in), optional :: b_min
1368
1369 type(trace_length_result) :: located
1370 double precision :: field_min
1371 integer :: igrid
1372
1373 bhat_current=zero
1374 grad_current=zero
1375 bhat_xeps=zero
1376 grad_xeps=zero
1377 status_current=trace_status_active
1378 status_xeps=trace_status_active
1379 if (npe/=1) then
1380 status_current=trace_status_mpi_unsupported
1382 return
1383 else if (ndim/=3 .or. .not.slab_uniform) then
1386 return
1387 endif
1388
1389 field_min=smalldouble
1390 if (present(b_min)) field_min=max(b_min,zero)
1391
1392 call trace_summary_locate_seed(seed,located,igrid)
1393 status_current=located%forward_status
1394 if (status_current==trace_status_active) then
1395 call sample_bhat_gradbhat_at_point(seed,igrid,field_min, &
1396 bhat_current,grad_current,status_current)
1397 endif
1398
1399 call sample_bhat_gradbhat_xeps_at_point(seed,eps,field_min,bhat_xeps, &
1400 grad_xeps,status_xeps)
1401 end subroutine trace_debug_compare_gradbhat
1402
1403 subroutine trace_debug_compare_gradbhat_methods(seed,eps,bhat_current, &
1404 grad_current,status_current,bhat_xeps,grad_xeps,status_xeps, &
1405 bhat_interp,grad_interp,status_interp,b_min)
1406 ! Debug/validation hook comparing all grad(bhat) sampler candidates.
1407 double precision, intent(in) :: seed(ndim),eps
1408 double precision, intent(out) :: bhat_current(3),grad_current(3,3)
1409 double precision, intent(out) :: bhat_xeps(3),grad_xeps(3,3)
1410 double precision, intent(out) :: bhat_interp(3),grad_interp(3,3)
1411 integer, intent(out) :: status_current,status_xeps,status_interp
1412 double precision, intent(in), optional :: b_min
1413
1414 type(trace_length_result) :: located
1415 double precision :: field_min
1416 integer :: igrid
1417
1418 bhat_current=zero
1419 grad_current=zero
1420 bhat_xeps=zero
1421 grad_xeps=zero
1422 bhat_interp=zero
1423 grad_interp=zero
1424 status_current=trace_status_active
1425 status_xeps=trace_status_active
1426 status_interp=trace_status_active
1427 if (npe/=1) then
1428 status_current=trace_status_mpi_unsupported
1430 status_interp=trace_status_mpi_unsupported
1431 return
1432 else if (ndim/=3 .or. .not.slab_uniform) then
1436 return
1437 endif
1438
1439 field_min=smalldouble
1440 if (present(b_min)) field_min=max(b_min,zero)
1441
1442 call trace_summary_locate_seed(seed,located,igrid)
1443 status_current=located%forward_status
1444 status_interp=located%forward_status
1445 if (status_current==trace_status_active) then
1446 call sample_bhat_gradbhat_cellfd_at_point(seed,igrid,field_min, &
1447 bhat_current,grad_current,status_current)
1448 endif
1449 if (status_interp==trace_status_active) then
1450 call sample_bhat_gradbhat_interpderiv_at_point(seed,igrid,field_min, &
1451 bhat_interp,grad_interp,status_interp)
1452 endif
1453
1454 call sample_bhat_gradbhat_xeps_at_point(seed,eps,field_min,bhat_xeps, &
1455 grad_xeps,status_xeps)
1457
1458 subroutine trace_debug_transport_tangents(seed,u0,v0,dL,nstep,forward, &
1459 x_end,u_end,v_end,bhat_end,u_perp_end,v_perp_end,status,b_min)
1460 ! Debug/validation hook for future Method-II tangent transport.
1461 double precision, intent(in) :: seed(ndim),u0(ndim),v0(ndim),dl
1462 integer, intent(in) :: nstep
1463 logical, intent(in) :: forward
1464 double precision, intent(out) :: x_end(ndim),u_end(ndim),v_end(ndim)
1465 double precision, intent(out) :: bhat_end(ndim)
1466 double precision, intent(out) :: u_perp_end(ndim),v_perp_end(ndim)
1467 integer, intent(out) :: status
1468 double precision, intent(in), optional :: b_min
1469
1470 double precision :: field_min,h,bhat3(3),grad_end(3,3)
1471 integer :: igrid,istep
1472
1473 x_end=seed
1474 u_end=u0
1475 v_end=v0
1476 bhat_end=zero
1477 u_perp_end=zero
1478 v_perp_end=zero
1479
1480 status=trace_status_active
1481 if (npe/=1) then
1483 return
1484 else if (ndim/=3 .or. .not.slab_uniform) then
1486 return
1487 else if (dl<=zero .or. nstep<0) then
1489 return
1490 endif
1491
1492 field_min=smalldouble
1493 if (present(b_min)) field_min=max(b_min,zero)
1494 h=dl
1495 if (.not.forward) h=-dl
1496
1497 call trace_debug_locate_point(x_end,igrid,status)
1498 if (status/=trace_status_active) return
1499
1500 do istep=1,nstep
1501 call trace_advance_tangent_state_rk2(x_end,u_end,v_end,igrid,h, &
1502 field_min,status)
1503 if (status/=trace_status_active) exit
1504 enddo
1505
1506 if (status==trace_status_active) then
1507 {^ifthreed
1508 call sample_bhat_gradbhat_at_point(x_end,igrid,field_min,bhat3, &
1509 grad_end,status)
1510 if (status==trace_status_active) bhat_end=bhat3(1:ndim)
1511 }
1512 endif
1513 if (status/=trace_status_active) return
1514
1515 call trace_project_to_perp_bhat(u_end,bhat_end,u_perp_end)
1516 call trace_project_to_perp_bhat(v_end,bhat_end,v_perp_end)
1517 end subroutine trace_debug_transport_tangents
1518
1520 max_steps,forward,x_end,u_end,v_end,bhat_end,b_end,u_perp_end, &
1521 v_perp_end,u_face_end,v_face_end,length,face_id,status,b_min)
1522 ! Debug/validation hook for Method-II style tangent transport to boundary.
1523 double precision, intent(in) :: seed(ndim),u0(ndim),v0(ndim),dl
1524 integer, intent(in) :: max_steps
1525 logical, intent(in) :: forward
1526 double precision, intent(out) :: x_end(ndim),u_end(ndim),v_end(ndim)
1527 double precision, intent(out) :: bhat_end(ndim),b_end(ndim)
1528 double precision, intent(out) :: u_perp_end(ndim),v_perp_end(ndim)
1529 double precision, intent(out) :: u_face_end(ndim),v_face_end(ndim)
1530 double precision, intent(out) :: length
1531 integer, intent(out) :: face_id,status
1532 double precision, intent(in), optional :: b_min
1533
1534 double precision :: field_min,h,h_partial,partial_length
1535 double precision :: xprobe(ndim),xtrial(ndim),utrial(ndim),vtrial(ndim)
1536 double precision :: xhit(ndim),xold(ndim),uold(ndim),vold(ndim)
1537 double precision :: kx1(ndim),ku1(ndim),kv1(ndim)
1538 integer :: igrid,igrid_trial,istep,point_domain
1539 logical :: hit_ok
1540
1541 x_end=seed
1542 u_end=u0
1543 v_end=v0
1544 bhat_end=zero
1545 b_end=zero
1546 u_perp_end=zero
1547 v_perp_end=zero
1548 u_face_end=zero
1549 v_face_end=zero
1550 length=zero
1551 face_id=trace_face_none
1552
1553 status=trace_status_active
1554 if (npe/=1) then
1556 return
1557 else if (ndim/=3 .or. .not.slab_uniform) then
1559 return
1560 else if (dl<=zero .or. max_steps<0) then
1562 return
1563 endif
1564
1565 field_min=smalldouble
1566 if (present(b_min)) field_min=max(b_min,zero)
1567 h=dl
1568 if (.not.forward) h=-dl
1569
1570 call trace_debug_locate_point(x_end,igrid,status)
1571 if (status/=trace_status_active) return
1572
1573 do istep=1,max_steps
1574 xold=x_end
1575 uold=u_end
1576 vold=v_end
1577
1578 call trace_tangent_rhs(xold,uold,vold,igrid,field_min,kx1,ku1,kv1, &
1579 status)
1580 if (status/=trace_status_active) return
1581 xprobe=xold+h*kx1
1582
1583 point_domain=0
1584 {if (xprobe(^db)>=xprobmin^db .and. xprobe(^db)<xprobmax^db) point_domain=point_domain+1\}
1585 if (point_domain/=ndim) then
1586 call trace_intersect_domain(xold,xprobe,xhit,hit_ok,face_id)
1587 if (.not.hit_ok) then
1588 call trace_boundary_face_at_point(xold,face_id,hit_ok)
1589 if (.not.hit_ok) then
1591 x_end=xold
1592 return
1593 endif
1594 x_end=xold
1595 u_end=uold
1596 v_end=vold
1598 exit
1599 endif
1600 partial_length=dsqrt(sum((xhit-xold)**2))
1601 if (partial_length<=100.d0*epsilon(one)*max(one,abs(h))) then
1602 x_end=xhit
1603 u_end=uold
1604 v_end=vold
1606 exit
1607 endif
1608 h_partial=sign(partial_length,h)
1609 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,igrid, &
1610 h_partial,field_min,kx1,ku1,kv1,xtrial,utrial,vtrial,status)
1611 if (status/=trace_status_active) return
1612 x_end=xhit
1613 u_end=utrial
1614 v_end=vtrial
1615 length=length+partial_length
1617 exit
1618 endif
1619
1620 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,igrid,h, &
1621 field_min,kx1,ku1,kv1,xtrial,utrial,vtrial,status)
1622 if (status/=trace_status_active) return
1623
1624 point_domain=0
1625 {if (xtrial(^db)>=xprobmin^db .and. xtrial(^db)<xprobmax^db) point_domain=point_domain+1\}
1626 if (point_domain==ndim) then
1627 call trace_locate_point_with_hint(xtrial,igrid,igrid_trial,status)
1628 if (status/=trace_status_active) return
1629 x_end=xtrial
1630 u_end=utrial
1631 v_end=vtrial
1632 igrid=igrid_trial
1633 length=length+abs(h)
1634 else
1635 call trace_intersect_domain(xold,xtrial,xhit,hit_ok,face_id)
1636 if (.not.hit_ok) then
1637 call trace_boundary_face_at_point(xold,face_id,hit_ok)
1638 if (.not.hit_ok) then
1640 x_end=xold
1641 return
1642 endif
1643 x_end=xold
1644 u_end=uold
1645 v_end=vold
1647 exit
1648 endif
1649 partial_length=dsqrt(sum((xhit-xold)**2))
1650 if (partial_length<=100.d0*epsilon(one)*max(one,abs(h))) then
1651 x_end=xhit
1652 u_end=uold
1653 v_end=vold
1655 exit
1656 endif
1657 h_partial=sign(partial_length,h)
1658 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,igrid, &
1659 h_partial,field_min,kx1,ku1,kv1,xtrial,utrial,vtrial,status)
1660 if (status/=trace_status_active) return
1661 x_end=xhit
1662 u_end=utrial
1663 v_end=vtrial
1664 length=length+partial_length
1666 exit
1667 endif
1668 enddo
1669
1670 if (status==trace_status_active) then
1672 return
1673 endif
1674 if (status/=trace_status_boundary) return
1675
1676 call trace_endpoint_b_bhat(x_end,face_id,igrid,field_min,b_end, &
1677 bhat_end,status)
1678 if (status/=trace_status_boundary) return
1679
1680 call trace_project_to_perp_bhat(u_end,bhat_end,u_perp_end)
1681 call trace_project_to_perp_bhat(v_end,bhat_end,v_perp_end)
1682 call trace_project_to_boundary_face(u_end,bhat_end,face_id,u_face_end)
1683 call trace_project_to_boundary_face(v_end,bhat_end,face_id,v_face_end)
1685
1686 subroutine trace_debug_qperp_single(seed,dL,max_steps,qperp,logqperp, &
1687 x_f,x_b,u_f_perp,v_f_perp,u_b_perp,v_b_perp,b_seed,b_f,b_b, &
1688 length_f,length_b,face_f,face_b,status,N2,bfactor,b_min)
1689 ! Debug/validation hook for single-seed Method-II Q_perp diagnostics.
1690 double precision, intent(in) :: seed(ndim),dl
1691 integer, intent(in) :: max_steps
1692 double precision, intent(out) :: qperp,logqperp,n2,bfactor
1693 double precision, intent(out) :: x_f(ndim),x_b(ndim)
1694 double precision, intent(out) :: u_f_perp(ndim),v_f_perp(ndim)
1695 double precision, intent(out) :: u_b_perp(ndim),v_b_perp(ndim)
1696 double precision, intent(out) :: b_seed(ndim),b_f(ndim),b_b(ndim)
1697 double precision, intent(out) :: length_f,length_b
1698 integer, intent(out) :: face_f,face_b,status
1699 double precision, intent(in), optional :: b_min
1700
1701 type(trace_qperp_result) :: result
1702
1703 if (present(b_min)) then
1704 call trace_field_qperp_single(seed,dl,max_steps,result,b_min)
1705 else
1706 call trace_field_qperp_single(seed,dl,max_steps,result)
1707 endif
1708
1709 qperp=result%qperp
1710 logqperp=result%logqperp
1711 n2=result%N2
1712 bfactor=result%bfactor
1713 x_f=result%forward_endpoint
1714 x_b=result%backward_endpoint
1715 u_f_perp=result%u_forward_perp
1716 v_f_perp=result%v_forward_perp
1717 u_b_perp=result%u_backward_perp
1718 v_b_perp=result%v_backward_perp
1719 b_seed=result%B_seed
1720 b_f=result%forward_B
1721 b_b=result%backward_B
1722 length_f=result%forward_length
1723 length_b=result%backward_length
1724 face_f=result%forward_face
1725 face_b=result%backward_face
1726 status=result%status
1727 end subroutine trace_debug_qperp_single
1728
1729 subroutine trace_init_qperp_result(seed,result)
1730 double precision, intent(in) :: seed(ndim)
1731 type(trace_qperp_result), intent(out) :: result
1732
1733 result%seed=seed
1734 result%qperp=trace_debug_nan()
1735 result%logqperp=trace_debug_nan()
1736 result%N2=trace_debug_nan()
1737 result%bfactor=trace_debug_nan()
1738 result%q0=trace_debug_nan()
1739 result%logq0=trace_debug_nan()
1740 result%qperp0=trace_debug_nan()
1741 result%logqperp0=trace_debug_nan()
1742 result%N2_qperp0=trace_debug_nan()
1743 result%bfactor_qperp0=trace_debug_nan()
1744 result%forward_Bn_q0=trace_debug_nan()
1745 result%backward_Bn_q0=trace_debug_nan()
1746 result%B_seed=zero
1747 result%bhat_seed=zero
1748 result%forward_endpoint=seed
1749 result%backward_endpoint=seed
1750 result%forward_B=zero
1751 result%backward_B=zero
1752 result%forward_bhat=zero
1753 result%backward_bhat=zero
1754 result%forward_length=zero
1755 result%backward_length=zero
1756 result%forward_nstep=0
1757 result%backward_nstep=0
1758 result%u0=zero
1759 result%v0=zero
1760 result%u_forward_perp=zero
1761 result%v_forward_perp=zero
1762 result%u_backward_perp=zero
1763 result%v_backward_perp=zero
1764 result%forward_face=trace_face_none
1765 result%backward_face=trace_face_none
1766 result%forward_status=trace_status_active
1767 result%backward_status=trace_status_active
1768 result%status_q0=trace_status_active
1769 result%status_qperp0=trace_status_active
1770 result%status=trace_status_active
1771 result%valid=.false.
1772 result%valid_q0=.false.
1773 result%valid_qperp0=.false.
1774 end subroutine trace_init_qperp_result
1775
1776 subroutine trace_qperp_single_core(seed,dL,max_steps,result,b_min)
1777 double precision, intent(in) :: seed(ndim),dl
1778 integer, intent(in) :: max_steps
1779 type(trace_qperp_result), intent(out) :: result
1780 double precision, intent(in), optional :: b_min
1781
1782 double precision :: field_min,b3(3),bseed_norm
1783 double precision :: u_end(ndim),v_end(ndim),u_face(ndim),v_face(ndim)
1784 integer :: igrid,status,indomain
1785
1786 call trace_init_qperp_result(seed,result)
1787
1788 if (npe/=1) then
1789 result%status=trace_status_mpi_unsupported
1790 return
1791 else if (ndim/=3 .or. .not.trace_cartesian_like_geometry()) then
1793 return
1794 else if (dl<=zero .or. max_steps<=0) then
1795 result%status=trace_status_invalid_input
1796 return
1797 endif
1798
1799 indomain=0
1800 {if (seed(^db)>=xprobmin^db .and. seed(^db)<xprobmax^db) indomain=indomain+1\}
1801 if (indomain/=ndim) then
1802 result%status=trace_status_seed_outside
1803 return
1804 endif
1805
1806 field_min=smalldouble
1807 if (present(b_min)) field_min=max(b_min,zero)
1808
1809 call trace_debug_locate_point(seed,igrid,status)
1810 if (status/=trace_status_active) then
1811 result%status=status
1812 return
1813 endif
1814 call sample_b_at_point(seed,igrid,field_min,b3,status)
1815 if (status/=trace_status_active) then
1816 result%status=status
1817 return
1818 endif
1819 result%B_seed=b3(1:ndim)
1820 bseed_norm=dsqrt(sum(result%B_seed**2))
1821 if (bseed_norm<=zero .or. bseed_norm<field_min) then
1822 result%status=trace_status_weak_field
1823 return
1824 endif
1825 result%bhat_seed=result%B_seed/bseed_norm
1826 call trace_make_perp_basis(b3/bseed_norm,result%u0,result%v0,status)
1827 if (status/=trace_status_active) then
1828 result%status=status
1829 return
1830 endif
1831
1832 call trace_debug_transport_tangents_to_boundary(seed,result%u0, &
1833 result%v0,dl,max_steps,.true.,result%forward_endpoint,u_end, &
1834 v_end,result%forward_bhat,result%forward_B,result%u_forward_perp, &
1835 result%v_forward_perp,u_face,v_face,result%forward_length, &
1836 result%forward_face,status,b_min=field_min)
1837 result%forward_status=status
1838 if (status/=trace_status_boundary) then
1839 result%status=status
1840 return
1841 endif
1842
1843 call trace_debug_transport_tangents_to_boundary(seed,result%u0, &
1844 result%v0,dl,max_steps,.false.,result%backward_endpoint,u_end, &
1845 v_end,result%backward_bhat,result%backward_B,result%u_backward_perp, &
1846 result%v_backward_perp,u_face,v_face,result%backward_length, &
1847 result%backward_face,status,b_min=field_min)
1848 result%backward_status=status
1849 if (status/=trace_status_boundary) then
1850 result%status=status
1851 return
1852 endif
1853
1854 call trace_qperp_compute_scalars(result,field_min)
1855 end subroutine trace_qperp_single_core
1856
1857 subroutine trace_tangent_state_init(seed,u0,v0,igrid,seed_id,direction, &
1858 state,accumulate_twist)
1859 double precision, intent(in) :: seed(ndim),u0(ndim),v0(ndim)
1860 integer, intent(in) :: igrid,seed_id,direction
1861 type(trace_tangent_state), intent(out) :: state
1862 logical, intent(in), optional :: accumulate_twist
1863
1864 state%seed_id=seed_id
1865 state%direction=direction
1866 state%active=.true.
1867 state%complete=.false.
1868 state%has_extra=.false.
1869 state%x=seed
1870 state%u=u0
1871 state%v=v0
1872 state%p=zero
1873 state%q=zero
1874 state%length=zero
1875 state%twist=zero
1876 state%nstep=0
1877 state%igrid=igrid
1878 state%status=trace_status_active
1879 state%twist_status=trace_status_active
1880 state%face_id=trace_face_none
1881 state%endpoint=seed
1882 state%endpoint_B=zero
1883 state%endpoint_bhat=zero
1884 state%u_perp=zero
1885 state%v_perp=zero
1886 state%p_perp=zero
1887 state%q_perp=zero
1888 state%rk45_h=zero
1889 state%accumulate_twist=.false.
1890 if (present(accumulate_twist)) state%accumulate_twist=accumulate_twist
1891 state%sph_cache%valid=.false.
1892 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) then
1893 call trace_spherical_profile_add_count(trace_profile_directions,1_8)
1894 call trace_spherical_profile_add_count(trace_profile_tangent_systems,1_8)
1895 endif
1896 end subroutine trace_tangent_state_init
1897
1898 subroutine trace_tangent_trace_states_grouped(states,nstate,dL,max_steps, &
1899 threshold,trace_mode)
1900 ! One grouped-state/OpenMP driver for RK2, RK2 short-boundary, and RK45
1901 ! Cartesian tangent tracing. The mode selects only the single-state
1902 ! advance kernel, preserving the distinct numerical semantics.
1903 integer, intent(in) :: nstate,max_steps
1904 integer, intent(in), optional :: trace_mode
1905 type(trace_tangent_state), intent(inout) :: states(nstate)
1906 double precision, intent(in) :: dl,threshold
1907
1908 logical, allocatable :: processed(:)
1909 double precision :: t0,tgroup0,tgroup1
1910 integer :: active_count,group_size,istate,jstate,target_grid,group_mode
1911 logical :: any_active
1912 logical :: profile_tangent
1913
1914 group_mode=trace_tangent_group_rk2_general
1915 if (present(trace_mode)) group_mode=trace_mode
1916 profile_tangent=(group_mode/=trace_tangent_group_rk45_cartesian .and. &
1917 trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical)
1918 if (profile_tangent) t0=trace_spherical_profile_time()
1919
1920 if (trace_cartesian_like_geometry()) then
1921 ! Cartesian interpolation/finalization is thread-safe across grids, and
1922 ! each state advance stops when that state leaves its current grid. A
1923 ! single active-state parallel loop avoids thousands of tiny grid-group
1924 ! scans/parallel-region launches on AMR products.
1925 if (trace_rk2_stats_enabled) call cpu_time(tgroup0)
1926 do
1927 any_active=.false.
1928 active_count=0
1929 do istate=1,nstate
1930 if (states(istate)%active) then
1931 any_active=.true.
1932 active_count=active_count+1
1933 endif
1934 enddo
1935 if (.not.any_active) exit
1936 call trace_rk2_stats_note_group_iteration()
1937 call trace_rk2_stats_note_grid_group(active_count)
1938 !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(jstate) SCHEDULE(DYNAMIC,16)
1939 do jstate=1,nstate
1940 if (.not.states(jstate)%active) cycle
1941 select case (group_mode)
1942 case (trace_tangent_group_rk2_general)
1943 call trace_tangent_state_advance_in_grid(states(jstate), &
1944 states(jstate)%igrid,dl,max_steps,threshold)
1945 case (trace_tangent_group_rk2_short_boundary)
1946 call trace_tangent_state_advance_in_grid(states(jstate), &
1947 states(jstate)%igrid,dl,max_steps,threshold, &
1948 short_boundary=.true.)
1949 case (trace_tangent_group_rk45_cartesian)
1950 call trace_tangent_state_advance_in_grid_rk45_cartesian( &
1951 states(jstate),states(jstate)%igrid,dl,max_steps,threshold)
1952 case default
1953 states(jstate)%status=trace_status_invalid_input
1954 states(jstate)%active=.false.
1955 states(jstate)%complete=.true.
1956 end select
1957 enddo
1958 !$OMP END PARALLEL DO
1959 enddo
1960 if (trace_rk2_stats_enabled) then
1961 call cpu_time(tgroup1)
1962 call trace_rk2_stats_add_grouped_time(tgroup1-tgroup0)
1963 endif
1964 return
1965 endif
1966
1967 if (trace_rk2_stats_enabled) call cpu_time(tgroup0)
1968 allocate(processed(nstate))
1969 do
1970 any_active=.false.
1971 do istate=1,nstate
1972 if (states(istate)%active) then
1973 any_active=.true.
1974 exit
1975 endif
1976 enddo
1977 if (.not.any_active) exit
1978 call trace_rk2_stats_note_group_iteration()
1979
1980 processed=.false.
1981 do istate=1,nstate
1982 if (.not.states(istate)%active .or. processed(istate)) cycle
1983 target_grid=states(istate)%igrid
1984 if (trace_rk2_stats_enabled) then
1985 group_size=0
1986 do jstate=1,nstate
1987 if (states(jstate)%active .and. .not.processed(jstate) .and. &
1988 states(jstate)%igrid==target_grid) group_size=group_size+1
1989 enddo
1990 call trace_rk2_stats_note_grid_group(group_size)
1991 endif
1992 !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(jstate) SCHEDULE(DYNAMIC,16)
1993 do jstate=1,nstate
1994 if (.not.states(jstate)%active .or. processed(jstate)) cycle
1995 if (states(jstate)%igrid/=target_grid) cycle
1996 select case (group_mode)
1997 case (trace_tangent_group_rk2_general)
1998 call trace_tangent_state_advance_in_grid(states(jstate), &
1999 target_grid,dl,max_steps,threshold)
2000 case (trace_tangent_group_rk2_short_boundary)
2001 call trace_tangent_state_advance_in_grid(states(jstate), &
2002 target_grid,dl,max_steps,threshold,short_boundary=.true.)
2003 case (trace_tangent_group_rk45_cartesian)
2004 call trace_tangent_state_advance_in_grid_rk45_cartesian( &
2005 states(jstate),target_grid,dl,max_steps,threshold)
2006 case default
2007 states(jstate)%status=trace_status_invalid_input
2008 states(jstate)%active=.false.
2009 states(jstate)%complete=.true.
2010 end select
2011 processed(jstate)=.true.
2012 enddo
2013 !$OMP END PARALLEL DO
2014 enddo
2015 enddo
2016 deallocate(processed)
2017 if (trace_rk2_stats_enabled) then
2018 call cpu_time(tgroup1)
2019 call trace_rk2_stats_add_grouped_time(tgroup1-tgroup0)
2020 endif
2021 if (profile_tangent) call trace_spherical_profile_add_time( &
2022 trace_profile_time_tangent,trace_spherical_profile_time()-t0)
2023 end subroutine trace_tangent_trace_states_grouped
2024
2025 subroutine trace_tangent_state_advance_in_grid_rk45_cartesian(state, &
2026 igrid,dL,max_steps,threshold)
2027 type(trace_tangent_state), intent(inout) :: state
2028 integer, intent(in) :: igrid,max_steps
2029 double precision, intent(in) :: dl,threshold
2030
2031 integer :: status
2032
2033 if (.not.state%active) return
2034
2035 do while(state%active .and. state%igrid==igrid .and. &
2036 state%nstep<max_steps)
2037 call trace_tangent_rk45_cartesian_step(state,dl,threshold,status)
2038 if (status/=trace_status_active) then
2039 state%status=status
2040 state%active=.false.
2041 state%complete=.true.
2042 return
2043 endif
2044 enddo
2045
2046 if (state%active .and. state%nstep>=max_steps) then
2047 state%status=trace_status_max_steps
2048 state%active=.false.
2049 state%complete=.true.
2050 endif
2051 end subroutine trace_tangent_state_advance_in_grid_rk45_cartesian
2052
2053 subroutine trace_tangent_rk45_cartesian_step(state,dL,threshold,status)
2054 type(trace_tangent_state), intent(inout) :: state
2055 double precision, intent(in) :: dl,threshold
2056 integer, intent(out) :: status
2057
2058 double precision, parameter :: b21=1.d0/5.d0
2059 double precision, parameter :: b31=3.d0/40.d0,b32=9.d0/40.d0
2060 double precision, parameter :: b41=3.d0/10.d0,b42=-9.d0/10.d0, &
2061 b43=6.d0/5.d0
2062 double precision, parameter :: b51=-11.d0/54.d0,b52=5.d0/2.d0, &
2063 b53=-70.d0/27.d0,b54=35.d0/27.d0
2064 double precision, parameter :: b61=1631.d0/55296.d0, &
2065 b62=175.d0/512.d0,b63=575.d0/13824.d0, &
2066 b64=44275.d0/110592.d0,b65=253.d0/4096.d0
2067 double precision, parameter :: c1=37.d0/378.d0,c3=250.d0/621.d0, &
2068 c4=125.d0/594.d0,c6=512.d0/1771.d0
2069 double precision, parameter :: cs1=2825.d0/27648.d0, &
2070 cs3=18575.d0/48384.d0,cs4=13525.d0/55296.d0, &
2071 cs5=277.d0/14336.d0,cs6=one/4.d0
2072
2073 double precision :: xold(ndim),uold(ndim),vold(ndim)
2074 double precision :: kx1(ndim),kx2(ndim),kx3(ndim),kx4(ndim)
2075 double precision :: kx5(ndim),kx6(ndim)
2076 double precision :: ku1(ndim),ku2(ndim),ku3(ndim),ku4(ndim)
2077 double precision :: ku5(ndim),ku6(ndim)
2078 double precision :: kv1(ndim),kv2(ndim),kv3(ndim),kv4(ndim)
2079 double precision :: kv5(ndim),kv6(ndim)
2080 double precision :: x2(ndim),x3(ndim),x4(ndim),x5s(ndim),x6(ndim)
2081 double precision :: u2(ndim),u3(ndim),u4(ndim),u5s(ndim),u6(ndim)
2082 double precision :: v2(ndim),v3(ndim),v4(ndim),v5s(ndim),v6(ndim)
2083 double precision :: x5sol(ndim),x4err(ndim)
2084 double precision :: u5sol(ndim),v5sol(ndim)
2085 double precision :: u4err(ndim),v4err(ndim)
2086 double precision :: h,hmax,hfloor,hcell,tol,err,grow,hsigned
2087 double precision :: pos_err_ratio,u_err_ratio,v_err_ratio
2088 double precision :: tan_err_ratio,control_ratio
2089 double precision :: tw1,tw3,tw4,tw6,twist_increment
2090 double precision :: dxb^d
2091 integer :: igrid1,igrid2,igrid3,igrid4,igrid5,igrid6
2092 integer :: iter,point_domain,reject_reason,twist_status
2093 logical :: boundary_finished,pos_ok,tan_ok
2094
2095 status=trace_status_active
2096 if (.not.trace_cartesian_like_geometry()) then
2098 return
2099 endif
2100 if (dl<=zero) then
2102 return
2103 endif
2104
2105 xold=state%x
2106 uold=state%u
2107 vold=state%v
2108 ^d&dxb^d=rnode(rpdx^d_,state%igrid);
2109 hmax=abs(trace_effective_step(xold,dl,state%igrid,dxb^d))
2110 if (hmax<=zero) then
2112 return
2113 endif
2114 h=state%rk45_h
2115 if (h<=zero) h=hmax
2116 h=min(h,hmax)
2117 hfloor=max(trace_step_min,100.d0*epsilon(one)*max(one,hmax))
2118 h=max(h,hfloor)
2119
2120 do iter=1,100
2121 h=max(hfloor,min(h,hmax))
2122 hsigned=h
2123 if (state%direction<0) hsigned=-h
2124
2125 call trace_tangent_rhs_cartesian_located(xold,uold,vold, &
2126 state%igrid,threshold,kx1,ku1,kv1,igrid1,status)
2127 if (status/=trace_status_active) return
2128
2129 x2=xold+hsigned*b21*kx1
2130 u2=uold+hsigned*b21*ku1
2131 v2=vold+hsigned*b21*kv1
2132 call trace_tangent_rhs_cartesian_located(x2,u2,v2,igrid1, &
2133 threshold,kx2,ku2,kv2,igrid2,status)
2134 if (status/=trace_status_active) then
2135 if (status==trace_status_weak_field) return
2136 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2137 vold,x2,hsigned,threshold,kx1,ku1,kv1,status, &
2138 boundary_finished)
2139 if (boundary_finished) return
2140 reject_reason=trace_rk45_reject_stage_failure
2141 if (status==trace_status_boundary .or. &
2143 reject_reason=trace_rk45_reject_stage_outside
2144 if (trace_rk45_stats_enabled) &
2145 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2146 reject_reason)
2147 if (h<=hfloor*(one+epsilon(one))) exit
2148 h=max(hfloor,half*h)
2149 cycle
2150 endif
2151
2152 x3=xold+hsigned*(b31*kx1+b32*kx2)
2153 u3=uold+hsigned*(b31*ku1+b32*ku2)
2154 v3=vold+hsigned*(b31*kv1+b32*kv2)
2155 call trace_tangent_rhs_cartesian_located(x3,u3,v3,igrid2, &
2156 threshold,kx3,ku3,kv3,igrid3,status)
2157 if (status/=trace_status_active) then
2158 if (status==trace_status_weak_field) return
2159 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2160 vold,x3,hsigned,threshold,kx1,ku1,kv1,status, &
2161 boundary_finished)
2162 if (boundary_finished) return
2163 reject_reason=trace_rk45_reject_stage_failure
2164 if (status==trace_status_boundary .or. &
2166 reject_reason=trace_rk45_reject_stage_outside
2167 if (trace_rk45_stats_enabled) &
2168 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2169 reject_reason)
2170 if (h<=hfloor*(one+epsilon(one))) exit
2171 h=max(hfloor,half*h)
2172 cycle
2173 endif
2174
2175 x4=xold+hsigned*(b41*kx1+b42*kx2+b43*kx3)
2176 u4=uold+hsigned*(b41*ku1+b42*ku2+b43*ku3)
2177 v4=vold+hsigned*(b41*kv1+b42*kv2+b43*kv3)
2178 call trace_tangent_rhs_cartesian_located(x4,u4,v4,igrid3, &
2179 threshold,kx4,ku4,kv4,igrid4,status)
2180 if (status/=trace_status_active) then
2181 if (status==trace_status_weak_field) return
2182 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2183 vold,x4,hsigned,threshold,kx1,ku1,kv1,status, &
2184 boundary_finished)
2185 if (boundary_finished) return
2186 reject_reason=trace_rk45_reject_stage_failure
2187 if (status==trace_status_boundary .or. &
2189 reject_reason=trace_rk45_reject_stage_outside
2190 if (trace_rk45_stats_enabled) &
2191 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2192 reject_reason)
2193 if (h<=hfloor*(one+epsilon(one))) exit
2194 h=max(hfloor,half*h)
2195 cycle
2196 endif
2197
2198 x5s=xold+hsigned*(b51*kx1+b52*kx2+b53*kx3+b54*kx4)
2199 u5s=uold+hsigned*(b51*ku1+b52*ku2+b53*ku3+b54*ku4)
2200 v5s=vold+hsigned*(b51*kv1+b52*kv2+b53*kv3+b54*kv4)
2201 call trace_tangent_rhs_cartesian_located(x5s,u5s,v5s,igrid4, &
2202 threshold,kx5,ku5,kv5,igrid5,status)
2203 if (status/=trace_status_active) then
2204 if (status==trace_status_weak_field) return
2205 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2206 vold,x5s,hsigned,threshold,kx1,ku1,kv1,status, &
2207 boundary_finished)
2208 if (boundary_finished) return
2209 reject_reason=trace_rk45_reject_stage_failure
2210 if (status==trace_status_boundary .or. &
2212 reject_reason=trace_rk45_reject_stage_outside
2213 if (trace_rk45_stats_enabled) &
2214 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2215 reject_reason)
2216 if (h<=hfloor*(one+epsilon(one))) exit
2217 h=max(hfloor,half*h)
2218 cycle
2219 endif
2220
2221 x6=xold+hsigned*(b61*kx1+b62*kx2+b63*kx3+b64*kx4+b65*kx5)
2222 u6=uold+hsigned*(b61*ku1+b62*ku2+b63*ku3+b64*ku4+b65*ku5)
2223 v6=vold+hsigned*(b61*kv1+b62*kv2+b63*kv3+b64*kv4+b65*kv5)
2224 call trace_tangent_rhs_cartesian_located(x6,u6,v6,igrid5, &
2225 threshold,kx6,ku6,kv6,igrid6,status)
2226 if (status/=trace_status_active) then
2227 if (status==trace_status_weak_field) return
2228 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2229 vold,x6,hsigned,threshold,kx1,ku1,kv1,status, &
2230 boundary_finished)
2231 if (boundary_finished) return
2232 reject_reason=trace_rk45_reject_stage_failure
2233 if (status==trace_status_boundary .or. &
2235 reject_reason=trace_rk45_reject_stage_outside
2236 if (trace_rk45_stats_enabled) &
2237 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2238 reject_reason)
2239 if (h<=hfloor*(one+epsilon(one))) exit
2240 h=max(hfloor,half*h)
2241 cycle
2242 endif
2243
2244 x5sol=xold+hsigned*(c1*kx1+c3*kx3+c4*kx4+c6*kx6)
2245 u5sol=uold+hsigned*(c1*ku1+c3*ku3+c4*ku4+c6*ku6)
2246 v5sol=vold+hsigned*(c1*kv1+c3*kv3+c4*kv4+c6*kv6)
2247 x4err=xold+hsigned*(cs1*kx1+cs3*kx3+cs4*kx4+ &
2248 cs5*kx5+cs6*kx6)
2249 u4err=uold+hsigned*(cs1*ku1+cs3*ku3+cs4*ku4+ &
2250 cs5*ku5+cs6*ku6)
2251 v4err=vold+hsigned*(cs1*kv1+cs3*kv3+cs4*kv4+ &
2252 cs5*kv5+cs6*kv6)
2253
2254 point_domain=0
2255 {if (x5sol(^db)>=xprobmin^db .and. x5sol(^db)<xprobmax^db) point_domain=point_domain+1\}
2256 if (point_domain/=ndim) then
2257 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2258 vold,x5sol,hsigned,threshold,kx1,ku1,kv1,status, &
2259 boundary_finished)
2260 if (boundary_finished) return
2261 if (trace_rk45_stats_enabled) &
2262 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2263 trace_rk45_reject_boundary)
2265 return
2266 endif
2267
2268 call trace_cartesian_local_cell_size(xold,igrid1,hcell,status)
2269 if (status/=trace_status_active .or. hcell<=zero) hcell=hmax
2270 err=dsqrt(sum((x5sol-x4err)**2))
2271 tol=trace_rk45_atol+trace_rk45_rtol*max(h,hcell)
2272 if (tol>zero) then
2273 pos_err_ratio=err/tol
2274 else
2275 pos_err_ratio=zero
2276 endif
2277 u_err_ratio=dsqrt(sum((u5sol-u4err)**2))/ &
2278 max(dsqrt(sum(u5sol**2)),trace_rk45_tangent_floor)
2279 v_err_ratio=dsqrt(sum((v5sol-v4err)**2))/ &
2280 max(dsqrt(sum(v5sol**2)),trace_rk45_tangent_floor)
2281 tan_err_ratio=max(u_err_ratio,v_err_ratio)
2282 if (trace_rk45_stats_enabled) &
2283 call trace_rk45_stats_note_tangent_error(pos_err_ratio, &
2284 u_err_ratio,v_err_ratio)
2285 pos_ok=(err<=tol)
2286 tan_ok=(tan_err_ratio<=trace_rk45_tangent_rtol)
2287 if ((pos_ok .and. tan_ok) .or. h<=hfloor*(one+epsilon(one))) then
2288 if (state%accumulate_twist .and. &
2289 state%twist_status==trace_status_active) then
2290 twist_status=trace_status_active
2291 call trace_twist_density_at_point(xold,igrid1,threshold,tw1, &
2292 twist_status)
2293 if (twist_status==trace_status_active) &
2294 call trace_twist_density_at_point(x3,igrid3,threshold,tw3, &
2295 twist_status)
2296 if (twist_status==trace_status_active) &
2297 call trace_twist_density_at_point(x4,igrid4,threshold,tw4, &
2298 twist_status)
2299 if (twist_status==trace_status_active) &
2300 call trace_twist_density_at_point(x6,igrid6,threshold,tw6, &
2301 twist_status)
2302 if (twist_status==trace_status_active) then
2303 twist_increment=h*(c1*tw1+c3*tw3+c4*tw4+c6*tw6)
2304 state%twist=state%twist+twist_increment
2305 else
2306 state%twist_status=twist_status
2307 endif
2308 endif
2309 state%x=x5sol
2310 state%u=u5sol
2311 state%v=v5sol
2312 state%length=state%length+h
2313 state%nstep=state%nstep+1
2314 call trace_locate_point_with_hint(state%x,igrid6,state%igrid,status)
2315 if (status/=trace_status_active) return
2316 control_ratio=max(pos_err_ratio, &
2317 tan_err_ratio/trace_rk45_tangent_rtol)
2318 if (control_ratio>zero) then
2319 grow=trace_rk45_safety*(one/control_ratio)**0.2d0
2320 grow=max(trace_rk45_min_shrink,min(trace_rk45_max_grow,grow))
2321 else
2322 grow=trace_rk45_max_grow
2323 endif
2324 state%rk45_h=min(hmax,max(hfloor,h*grow))
2325 if (trace_rk45_stats_enabled) &
2326 call trace_rk45_stats_note_attempt(.true.,.false.,h)
2327 status=trace_status_active
2328 return
2329 endif
2330
2331 if (.not.pos_ok) then
2332 grow=trace_rk45_safety*(tol/max(err,smalldouble))**0.25d0
2333 if (trace_rk45_stats_enabled) &
2334 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2335 trace_rk45_reject_error)
2336 else
2337 grow=trace_rk45_safety*(trace_rk45_tangent_rtol/ &
2338 max(tan_err_ratio,smalldouble))**0.25d0
2339 if (trace_rk45_stats_enabled) &
2340 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2341 trace_rk45_reject_tangent_error)
2342 endif
2343 grow=max(trace_rk45_min_shrink,min(one,grow))
2344 h=max(hfloor,h*grow)
2345 enddo
2346
2347 call trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2348 vold,xold+hsigned*kx1,hsigned,threshold,kx1,ku1,kv1,status, &
2349 boundary_finished)
2350 if (.not.boundary_finished) status=trace_status_out_of_domain
2351 end subroutine trace_tangent_rk45_cartesian_step
2352
2353 subroutine trace_tangent_rhs_cartesian_located(x,u,v,igrid_hint, &
2354 threshold,kx,ku,kv,igrid,status)
2355 double precision, intent(in) :: x(ndim),u(ndim),v(ndim),threshold
2356 integer, intent(in) :: igrid_hint
2357 double precision, intent(out) :: kx(ndim),ku(ndim),kv(ndim)
2358 integer, intent(out) :: igrid,status
2359
2360 kx=zero
2361 ku=zero
2362 kv=zero
2363 igrid=-1
2365 if (.not.trace_cartesian_like_geometry()) return
2366
2367 call trace_locate_point_with_hint(x,igrid_hint,igrid,status)
2368 if (status/=trace_status_active) return
2369 call trace_tangent_rhs(x,u,v,igrid,threshold,kx,ku,kv,status)
2370 end subroutine trace_tangent_rhs_cartesian_located
2371
2372 subroutine trace_tangent_rk45_cartesian_finish_boundary(state,xold,uold, &
2373 vold,xstage,hsigned,threshold,kx1,ku1,kv1,status,finished)
2374 type(trace_tangent_state), intent(inout) :: state
2375 double precision, intent(in) :: xold(ndim),uold(ndim),vold(ndim)
2376 double precision, intent(in) :: xstage(ndim),hsigned,threshold
2377 double precision, intent(in) :: kx1(ndim),ku1(ndim),kv1(ndim)
2378 integer, intent(out) :: status
2379 logical, intent(out) :: finished
2380
2381 double precision :: xhit(ndim),utrial(ndim),vtrial(ndim)
2382 double precision :: xtrial(ndim),partial_length,h_partial
2383 logical :: hit_ok
2384
2385 finished=.false.
2386 call trace_intersect_domain(xold,xstage,xhit,hit_ok,state%face_id)
2387 if (.not.hit_ok) return
2388
2389 partial_length=dsqrt(sum((xhit-xold)**2))
2390 if (partial_length>100.d0*epsilon(one)*max(one,abs(hsigned))) then
2391 h_partial=sign(partial_length,hsigned)
2392 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
2393 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,status)
2394 if (status/=trace_status_active) return
2395 call trace_tangent_accumulate_twist(state,xhit,partial_length, &
2396 threshold)
2397 state%u=utrial
2398 state%v=vtrial
2399 state%length=state%length+partial_length
2400 state%nstep=state%nstep+1
2401 else
2402 state%u=uold
2403 state%v=vold
2404 endif
2405 state%x=xhit
2406 state%endpoint=xhit
2407 state%status=trace_status_boundary
2408 state%active=.false.
2409 state%complete=.true.
2410 state%rk45_h=zero
2411 call trace_tangent_state_finalize_boundary(state,threshold)
2412 call trace_rk45_stats_note_attempt(.true.,.true.,partial_length)
2413 status=trace_status_active
2414 finished=.true.
2415 end subroutine trace_tangent_rk45_cartesian_finish_boundary
2416
2417 subroutine trace_tangent_trace_states_grouped_rk45_spherical(states, &
2418 nstate,dL,max_steps,threshold)
2419 integer, intent(in) :: nstate,max_steps
2420 type(trace_tangent_state), intent(inout) :: states(nstate)
2421 double precision, intent(in) :: dl,threshold
2422
2423 logical, allocatable :: processed(:)
2424 integer :: istate,jstate,target_grid
2425 logical :: any_active
2426
2427 allocate(processed(nstate))
2428 do
2429 any_active=.false.
2430 do istate=1,nstate
2431 if (states(istate)%active) then
2432 any_active=.true.
2433 exit
2434 endif
2435 enddo
2436 if (.not.any_active) exit
2437
2438 processed=.false.
2439 do istate=1,nstate
2440 if (.not.states(istate)%active .or. processed(istate)) cycle
2441 target_grid=states(istate)%igrid
2442 !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(jstate) SCHEDULE(DYNAMIC,16)
2443 do jstate=1,nstate
2444 if (.not.states(jstate)%active .or. processed(jstate)) cycle
2445 if (states(jstate)%igrid/=target_grid) cycle
2446 call trace_tangent_state_advance_in_grid_rk45_spherical( &
2447 states(jstate),target_grid,dl,max_steps,threshold)
2448 processed(jstate)=.true.
2449 enddo
2450 !$OMP END PARALLEL DO
2451 enddo
2452 enddo
2453 deallocate(processed)
2454 end subroutine trace_tangent_trace_states_grouped_rk45_spherical
2455
2456 subroutine trace_tangent_state_advance_in_grid_rk45_spherical(state, &
2457 igrid,dL,max_steps,threshold)
2458 type(trace_tangent_state), intent(inout) :: state
2459 integer, intent(in) :: igrid,max_steps
2460 double precision, intent(in) :: dl,threshold
2461
2462 integer :: status
2463
2464 if (.not.state%active) return
2465
2466 do while(state%active .and. state%igrid==igrid .and. &
2467 state%nstep<max_steps)
2468 call trace_tangent_rk45_spherical_step(state,dl,threshold,status)
2469 if (status/=trace_status_active) then
2470 state%status=status
2471 state%active=.false.
2472 state%complete=.true.
2473 return
2474 endif
2475 enddo
2476
2477 if (state%active .and. state%nstep>=max_steps) then
2478 state%status=trace_status_max_steps
2479 state%active=.false.
2480 state%complete=.true.
2481 endif
2482 if ((.not.state%active) .and. trace_spherical_profile_enabled .and. &
2483 geo_coordinate==geo_spherical) then
2484 call trace_spherical_profile_note_trace_steps(state%nstep)
2485 endif
2486 end subroutine trace_tangent_state_advance_in_grid_rk45_spherical
2487
2488 subroutine trace_tangent_rk45_spherical_step(state,dL,threshold,status)
2489 type(trace_tangent_state), intent(inout) :: state
2490 double precision, intent(in) :: dl,threshold
2491 integer, intent(out) :: status
2492
2493 double precision, parameter :: b21=1.d0/5.d0
2494 double precision, parameter :: b31=3.d0/40.d0,b32=9.d0/40.d0
2495 double precision, parameter :: b41=3.d0/10.d0,b42=-9.d0/10.d0, &
2496 b43=6.d0/5.d0
2497 double precision, parameter :: b51=-11.d0/54.d0,b52=5.d0/2.d0, &
2498 b53=-70.d0/27.d0,b54=35.d0/27.d0
2499 double precision, parameter :: b61=1631.d0/55296.d0, &
2500 b62=175.d0/512.d0,b63=575.d0/13824.d0, &
2501 b64=44275.d0/110592.d0,b65=253.d0/4096.d0
2502 double precision, parameter :: c1=37.d0/378.d0,c3=250.d0/621.d0, &
2503 c4=125.d0/594.d0,c6=512.d0/1771.d0
2504 double precision, parameter :: cs1=2825.d0/27648.d0, &
2505 cs3=18575.d0/48384.d0,cs4=13525.d0/55296.d0, &
2506 cs5=277.d0/14336.d0,cs6=one/4.d0
2507
2508 double precision :: xold(ndim),uold(ndim),vold(ndim)
2509 double precision :: pold(ndim),qold(ndim)
2510 double precision :: kx1(ndim),kx2(ndim),kx3(ndim),kx4(ndim)
2511 double precision :: kx5(ndim),kx6(ndim)
2512 double precision :: ku1(ndim),ku2(ndim),ku3(ndim),ku4(ndim)
2513 double precision :: ku5(ndim),ku6(ndim)
2514 double precision :: kv1(ndim),kv2(ndim),kv3(ndim),kv4(ndim)
2515 double precision :: kv5(ndim),kv6(ndim)
2516 double precision :: kp1(ndim),kp2(ndim),kp3(ndim),kp4(ndim)
2517 double precision :: kp5(ndim),kp6(ndim)
2518 double precision :: kq1(ndim),kq2(ndim),kq3(ndim),kq4(ndim)
2519 double precision :: kq5(ndim),kq6(ndim)
2520 double precision :: x2(ndim),x3(ndim),x4(ndim),x5s(ndim),x6(ndim)
2521 double precision :: u2(ndim),u3(ndim),u4(ndim),u5s(ndim),u6(ndim)
2522 double precision :: v2(ndim),v3(ndim),v4(ndim),v5s(ndim),v6(ndim)
2523 double precision :: p2(ndim),p3(ndim),p4(ndim),p5s(ndim),p6(ndim)
2524 double precision :: q2(ndim),q3(ndim),q4(ndim),q5s(ndim),q6(ndim)
2525 double precision :: x5sol(ndim),x4err(ndim)
2526 double precision :: u5sol(ndim),v5sol(ndim)
2527 double precision :: u4err(ndim),v4err(ndim)
2528 double precision :: p5sol(ndim),q5sol(ndim)
2529 double precision :: p4err(ndim),q4err(ndim)
2530 double precision :: h,hmax,hfloor,hcell,tol,err,grow,hsigned
2531 double precision :: pos_err_ratio,u_err_ratio,v_err_ratio
2532 double precision :: p_err_ratio,q_err_ratio
2533 double precision :: tan_err_ratio,control_ratio
2534 double precision :: tw1,tw3,tw4,tw6,twist_increment
2535 double precision :: r_metric,sin_theta_metric
2536 double precision :: domain_min(ndim),domain_max(ndim)
2537 type(trace_sph_interp_ctx) :: ctx1,ctx2,ctx3,ctx4,ctx5,ctx6
2538 integer :: igrid1,igrid2,igrid3,igrid4,igrid5,igrid6
2539 integer :: iter,point_domain,ctx_status,reject_reason,twist_status
2540 logical :: boundary_finished,pos_ok,tan_ok
2541
2542 status=trace_status_active
2543 if (geo_coordinate/=geo_spherical) then
2545 return
2546 endif
2547 if (dl<=zero) then
2549 return
2550 endif
2551
2552 xold=state%x
2553 uold=state%u
2554 vold=state%v
2555 pold=state%p
2556 qold=state%q
2557 call trace_spherical_interp_ctx_build_cached(xold,state%igrid, &
2558 state%sph_cache,ctx1,ctx_status)
2559 if (ctx_status/=trace_status_active) then
2560 status=ctx_status
2561 return
2562 endif
2563 hmax=abs(trace_spherical_effective_step_ctx(dl,ctx1))
2564 hcell=ctx1%h_local
2565 if (hmax<=zero) then
2567 return
2568 endif
2569 h=state%rk45_h
2570 if (h<=zero) h=hmax
2571 h=min(h,hmax)
2572 hfloor=max(trace_step_min,100.d0*epsilon(one)*max(one,hmax))
2573 h=max(h,hfloor)
2574 ^d&domain_min(^d)=xprobmin^d;
2575 ^d&domain_max(^d)=xprobmax^d;
2576
2577 do iter=1,100
2578 h=max(hfloor,min(h,hmax))
2579 hsigned=h
2580 if (state%direction<0) hsigned=-h
2581
2582 if (state%has_extra) then
2583 call trace_tangent_rhs(xold,uold,vold,ctx1%igrid,threshold,kx1, &
2584 ku1,kv1,status,pold,qold,kp1,kq1,sph_ctx=ctx1)
2585 else
2586 call trace_tangent_rhs(xold,uold,vold,ctx1%igrid,threshold,kx1, &
2587 ku1,kv1,status,sph_ctx=ctx1)
2588 endif
2589 if (status/=trace_status_active) return
2590 igrid1=ctx1%igrid
2591
2592 x2=xold+hsigned*b21*kx1
2593 u2=uold+hsigned*b21*ku1
2594 v2=vold+hsigned*b21*kv1
2595 if (state%has_extra) then
2596 p2=pold+hsigned*b21*kp1
2597 q2=qold+hsigned*b21*kq1
2598 endif
2599 call trace_spherical_interp_ctx_build_cached(x2,igrid1, &
2600 state%sph_cache,ctx2,ctx_status)
2601 if (ctx_status/=trace_status_active) then
2602 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2603 vold,x2,hsigned,threshold,kx1,ku1,kv1,status, &
2604 boundary_finished,pold,qold,kp1,kq1)
2605 if (boundary_finished) return
2606 reject_reason=trace_rk45_reject_stage_failure
2607 if (ctx_status==trace_status_boundary .or. &
2608 ctx_status==trace_status_out_of_domain) &
2609 reject_reason=trace_rk45_reject_stage_outside
2610 call trace_rk45_stats_note_attempt(.false.,.false.,h,reject_reason)
2611 if (h<=hfloor*(one+epsilon(one))) exit
2612 h=max(hfloor,half*h)
2613 cycle
2614 endif
2615 if (state%has_extra) then
2616 call trace_tangent_rhs(x2,u2,v2,ctx2%igrid,threshold,kx2,ku2, &
2617 kv2,status,p2,q2,kp2,kq2,sph_ctx=ctx2)
2618 else
2619 call trace_tangent_rhs(x2,u2,v2,ctx2%igrid,threshold,kx2,ku2, &
2620 kv2,status,sph_ctx=ctx2)
2621 endif
2622 if (status/=trace_status_active) return
2623 igrid2=ctx2%igrid
2624
2625 x3=xold+hsigned*(b31*kx1+b32*kx2)
2626 u3=uold+hsigned*(b31*ku1+b32*ku2)
2627 v3=vold+hsigned*(b31*kv1+b32*kv2)
2628 if (state%has_extra) then
2629 p3=pold+hsigned*(b31*kp1+b32*kp2)
2630 q3=qold+hsigned*(b31*kq1+b32*kq2)
2631 endif
2632 call trace_spherical_interp_ctx_build_cached(x3,igrid2, &
2633 state%sph_cache,ctx3,ctx_status)
2634 if (ctx_status/=trace_status_active) then
2635 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2636 vold,x3,hsigned,threshold,kx1,ku1,kv1,status, &
2637 boundary_finished,pold,qold,kp1,kq1)
2638 if (boundary_finished) return
2639 reject_reason=trace_rk45_reject_stage_failure
2640 if (ctx_status==trace_status_boundary .or. &
2641 ctx_status==trace_status_out_of_domain) &
2642 reject_reason=trace_rk45_reject_stage_outside
2643 call trace_rk45_stats_note_attempt(.false.,.false.,h,reject_reason)
2644 if (h<=hfloor*(one+epsilon(one))) exit
2645 h=max(hfloor,half*h)
2646 cycle
2647 endif
2648 if (state%has_extra) then
2649 call trace_tangent_rhs(x3,u3,v3,ctx3%igrid,threshold,kx3,ku3, &
2650 kv3,status,p3,q3,kp3,kq3,sph_ctx=ctx3)
2651 else
2652 call trace_tangent_rhs(x3,u3,v3,ctx3%igrid,threshold,kx3,ku3, &
2653 kv3,status,sph_ctx=ctx3)
2654 endif
2655 if (status/=trace_status_active) return
2656 igrid3=ctx3%igrid
2657
2658 x4=xold+hsigned*(b41*kx1+b42*kx2+b43*kx3)
2659 u4=uold+hsigned*(b41*ku1+b42*ku2+b43*ku3)
2660 v4=vold+hsigned*(b41*kv1+b42*kv2+b43*kv3)
2661 if (state%has_extra) then
2662 p4=pold+hsigned*(b41*kp1+b42*kp2+b43*kp3)
2663 q4=qold+hsigned*(b41*kq1+b42*kq2+b43*kq3)
2664 endif
2665 call trace_spherical_interp_ctx_build_cached(x4,igrid3, &
2666 state%sph_cache,ctx4,ctx_status)
2667 if (ctx_status/=trace_status_active) then
2668 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2669 vold,x4,hsigned,threshold,kx1,ku1,kv1,status, &
2670 boundary_finished,pold,qold,kp1,kq1)
2671 if (boundary_finished) return
2672 reject_reason=trace_rk45_reject_stage_failure
2673 if (ctx_status==trace_status_boundary .or. &
2674 ctx_status==trace_status_out_of_domain) &
2675 reject_reason=trace_rk45_reject_stage_outside
2676 call trace_rk45_stats_note_attempt(.false.,.false.,h,reject_reason)
2677 if (h<=hfloor*(one+epsilon(one))) exit
2678 h=max(hfloor,half*h)
2679 cycle
2680 endif
2681 if (state%has_extra) then
2682 call trace_tangent_rhs(x4,u4,v4,ctx4%igrid,threshold,kx4,ku4, &
2683 kv4,status,p4,q4,kp4,kq4,sph_ctx=ctx4)
2684 else
2685 call trace_tangent_rhs(x4,u4,v4,ctx4%igrid,threshold,kx4,ku4, &
2686 kv4,status,sph_ctx=ctx4)
2687 endif
2688 if (status/=trace_status_active) return
2689 igrid4=ctx4%igrid
2690
2691 x5s=xold+hsigned*(b51*kx1+b52*kx2+b53*kx3+b54*kx4)
2692 u5s=uold+hsigned*(b51*ku1+b52*ku2+b53*ku3+b54*ku4)
2693 v5s=vold+hsigned*(b51*kv1+b52*kv2+b53*kv3+b54*kv4)
2694 if (state%has_extra) then
2695 p5s=pold+hsigned*(b51*kp1+b52*kp2+b53*kp3+b54*kp4)
2696 q5s=qold+hsigned*(b51*kq1+b52*kq2+b53*kq3+b54*kq4)
2697 endif
2698 call trace_spherical_interp_ctx_build_cached(x5s,igrid4, &
2699 state%sph_cache,ctx5,ctx_status)
2700 if (ctx_status/=trace_status_active) then
2701 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2702 vold,x5s,hsigned,threshold,kx1,ku1,kv1,status, &
2703 boundary_finished,pold,qold,kp1,kq1)
2704 if (boundary_finished) return
2705 reject_reason=trace_rk45_reject_stage_failure
2706 if (ctx_status==trace_status_boundary .or. &
2707 ctx_status==trace_status_out_of_domain) &
2708 reject_reason=trace_rk45_reject_stage_outside
2709 call trace_rk45_stats_note_attempt(.false.,.false.,h,reject_reason)
2710 if (h<=hfloor*(one+epsilon(one))) exit
2711 h=max(hfloor,half*h)
2712 cycle
2713 endif
2714 if (state%has_extra) then
2715 call trace_tangent_rhs(x5s,u5s,v5s,ctx5%igrid,threshold,kx5,ku5, &
2716 kv5,status,p5s,q5s,kp5,kq5,sph_ctx=ctx5)
2717 else
2718 call trace_tangent_rhs(x5s,u5s,v5s,ctx5%igrid,threshold,kx5,ku5, &
2719 kv5,status,sph_ctx=ctx5)
2720 endif
2721 if (status/=trace_status_active) return
2722 igrid5=ctx5%igrid
2723
2724 x6=xold+hsigned*(b61*kx1+b62*kx2+b63*kx3+b64*kx4+b65*kx5)
2725 u6=uold+hsigned*(b61*ku1+b62*ku2+b63*ku3+b64*ku4+b65*ku5)
2726 v6=vold+hsigned*(b61*kv1+b62*kv2+b63*kv3+b64*kv4+b65*kv5)
2727 if (state%has_extra) then
2728 p6=pold+hsigned*(b61*kp1+b62*kp2+b63*kp3+b64*kp4+b65*kp5)
2729 q6=qold+hsigned*(b61*kq1+b62*kq2+b63*kq3+b64*kq4+b65*kq5)
2730 endif
2731 call trace_spherical_interp_ctx_build_cached(x6,igrid5, &
2732 state%sph_cache,ctx6,ctx_status)
2733 if (ctx_status/=trace_status_active) then
2734 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2735 vold,x6,hsigned,threshold,kx1,ku1,kv1,status, &
2736 boundary_finished,pold,qold,kp1,kq1)
2737 if (boundary_finished) return
2738 reject_reason=trace_rk45_reject_stage_failure
2739 if (ctx_status==trace_status_boundary .or. &
2740 ctx_status==trace_status_out_of_domain) &
2741 reject_reason=trace_rk45_reject_stage_outside
2742 call trace_rk45_stats_note_attempt(.false.,.false.,h,reject_reason)
2743 if (h<=hfloor*(one+epsilon(one))) exit
2744 h=max(hfloor,half*h)
2745 cycle
2746 endif
2747 if (state%has_extra) then
2748 call trace_tangent_rhs(x6,u6,v6,ctx6%igrid,threshold,kx6,ku6, &
2749 kv6,status,p6,q6,kp6,kq6,sph_ctx=ctx6)
2750 else
2751 call trace_tangent_rhs(x6,u6,v6,ctx6%igrid,threshold,kx6,ku6, &
2752 kv6,status,sph_ctx=ctx6)
2753 endif
2754 if (status/=trace_status_active) return
2755 igrid6=ctx6%igrid
2756
2757 x5sol=xold+hsigned*(c1*kx1+c3*kx3+c4*kx4+c6*kx6)
2758 u5sol=uold+hsigned*(c1*ku1+c3*ku3+c4*ku4+c6*ku6)
2759 v5sol=vold+hsigned*(c1*kv1+c3*kv3+c4*kv4+c6*kv6)
2760 x4err=xold+hsigned*(cs1*kx1+cs3*kx3+cs4*kx4+ &
2761 cs5*kx5+cs6*kx6)
2762 u4err=uold+hsigned*(cs1*ku1+cs3*ku3+cs4*ku4+ &
2763 cs5*ku5+cs6*ku6)
2764 v4err=vold+hsigned*(cs1*kv1+cs3*kv3+cs4*kv4+ &
2765 cs5*kv5+cs6*kv6)
2766 if (state%has_extra) then
2767 p5sol=pold+hsigned*(c1*kp1+c3*kp3+c4*kp4+c6*kp6)
2768 q5sol=qold+hsigned*(c1*kq1+c3*kq3+c4*kq4+c6*kq6)
2769 p4err=pold+hsigned*(cs1*kp1+cs3*kp3+cs4*kp4+ &
2770 cs5*kp5+cs6*kp6)
2771 q4err=qold+hsigned*(cs1*kq1+cs3*kq3+cs4*kq4+ &
2772 cs5*kq5+cs6*kq6)
2773 endif
2774
2775 point_domain=0
2776 {if (x5sol(^db)>=domain_min(^db) .and. x5sol(^db)<domain_max(^db)) point_domain=point_domain+1\}
2777 if (point_domain/=ndim) then
2778 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2779 vold,x5sol,hsigned,threshold,kx1,ku1,kv1,status, &
2780 boundary_finished,pold,qold,kp1,kq1)
2781 if (boundary_finished) return
2782 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2783 trace_rk45_reject_boundary)
2785 return
2786 endif
2787
2788 err=zero
2789 {^ifthreed
2790 r_metric=max(abs(xold(1)),smalldouble)
2791 sin_theta_metric=max(abs(dsin(xold(2))),smalldouble)
2792 err=dsqrt((x5sol(1)-x4err(1))**2+ &
2793 (r_metric*(x5sol(2)-x4err(2)))**2+ &
2794 (r_metric*sin_theta_metric*(x5sol(3)-x4err(3)))**2)
2795 }
2796 if (hcell<=zero) hcell=hmax
2797 tol=trace_rk45_atol+trace_rk45_rtol*max(h,hcell)
2798 if (tol>zero) then
2799 pos_err_ratio=err/tol
2800 else
2801 pos_err_ratio=zero
2802 endif
2803 u_err_ratio=dsqrt(sum((u5sol-u4err)**2))/ &
2804 max(dsqrt(sum(u5sol**2)),trace_rk45_tangent_floor)
2805 v_err_ratio=dsqrt(sum((v5sol-v4err)**2))/ &
2806 max(dsqrt(sum(v5sol**2)),trace_rk45_tangent_floor)
2807 if (state%has_extra) then
2808 p_err_ratio=dsqrt(sum((p5sol-p4err)**2))/ &
2809 max(dsqrt(sum(p5sol**2)),trace_rk45_tangent_floor)
2810 q_err_ratio=dsqrt(sum((q5sol-q4err)**2))/ &
2811 max(dsqrt(sum(q5sol**2)),trace_rk45_tangent_floor)
2812 u_err_ratio=max(u_err_ratio,p_err_ratio)
2813 v_err_ratio=max(v_err_ratio,q_err_ratio)
2814 endif
2815 tan_err_ratio=max(u_err_ratio,v_err_ratio)
2816 call trace_rk45_stats_note_tangent_error(pos_err_ratio, &
2817 u_err_ratio,v_err_ratio)
2818 pos_ok=(err<=tol)
2819 tan_ok=(tan_err_ratio<=trace_rk45_tangent_rtol)
2820 if ((pos_ok .and. tan_ok) .or. h<=hfloor*(one+epsilon(one))) then
2821 if (state%accumulate_twist .and. &
2822 state%twist_status==trace_status_active) then
2823 twist_status=trace_status_active
2824 call trace_twist_density_at_point(xold,ctx1%igrid,threshold,tw1, &
2825 twist_status,sph_cache=ctx1)
2826 if (twist_status==trace_status_active) &
2827 call trace_twist_density_at_point(x3,ctx3%igrid,threshold, &
2828 tw3,twist_status,sph_cache=ctx3)
2829 if (twist_status==trace_status_active) &
2830 call trace_twist_density_at_point(x4,ctx4%igrid,threshold, &
2831 tw4,twist_status,sph_cache=ctx4)
2832 if (twist_status==trace_status_active) &
2833 call trace_twist_density_at_point(x6,ctx6%igrid,threshold, &
2834 tw6,twist_status,sph_cache=ctx6)
2835 if (twist_status==trace_status_active) then
2836 twist_increment=h*(c1*tw1+c3*tw3+c4*tw4+c6*tw6)
2837 state%twist=state%twist+twist_increment
2838 else
2839 state%twist_status=twist_status
2840 endif
2841 endif
2842 state%x=x5sol
2843 state%u=u5sol
2844 state%v=v5sol
2845 if (state%has_extra) then
2846 state%p=p5sol
2847 state%q=q5sol
2848 endif
2849 state%length=state%length+h
2850 state%nstep=state%nstep+1
2851 call trace_locate_point_with_hint(state%x,igrid6,state%igrid,status)
2852 if (status/=trace_status_active) return
2853 state%sph_cache%valid=.false.
2854 if (trace_spherical_profile_enabled) &
2855 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
2856 control_ratio=max(pos_err_ratio, &
2857 tan_err_ratio/trace_rk45_tangent_rtol)
2858 if (control_ratio>zero) then
2859 grow=trace_rk45_safety*(one/control_ratio)**0.2d0
2860 grow=max(trace_rk45_min_shrink,min(trace_rk45_max_grow,grow))
2861 else
2862 grow=trace_rk45_max_grow
2863 endif
2864 state%rk45_h=min(hmax,max(hfloor,h*grow))
2865 call trace_rk45_stats_note_attempt(.true.,.false.,h)
2866 status=trace_status_active
2867 return
2868 endif
2869
2870 if (.not.pos_ok) then
2871 grow=trace_rk45_safety*(tol/max(err,smalldouble))**0.25d0
2872 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2873 trace_rk45_reject_error)
2874 else
2875 grow=trace_rk45_safety*(trace_rk45_tangent_rtol/ &
2876 max(tan_err_ratio,smalldouble))**0.25d0
2877 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
2878 trace_rk45_reject_tangent_error)
2879 endif
2880 grow=max(trace_rk45_min_shrink,min(one,grow))
2881 h=max(hfloor,h*grow)
2882 enddo
2883
2884 call trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2885 vold,xold+hsigned*kx1,hsigned,threshold,kx1,ku1,kv1,status, &
2886 boundary_finished,pold,qold,kp1,kq1)
2887 if (.not.boundary_finished) status=trace_status_out_of_domain
2888 end subroutine trace_tangent_rk45_spherical_step
2889
2890 subroutine trace_tangent_rk45_spherical_finish_boundary(state,xold,uold, &
2891 vold,xstage,hsigned,threshold,kx1,ku1,kv1,status,finished, &
2892 pold,qold,kp1,kq1)
2893 type(trace_tangent_state), intent(inout) :: state
2894 double precision, intent(in) :: xold(ndim),uold(ndim),vold(ndim)
2895 double precision, intent(in) :: xstage(ndim),hsigned,threshold
2896 double precision, intent(in) :: kx1(ndim),ku1(ndim),kv1(ndim)
2897 integer, intent(out) :: status
2898 logical, intent(out) :: finished
2899 double precision, intent(in), optional :: pold(ndim),qold(ndim)
2900 double precision, intent(in), optional :: kp1(ndim),kq1(ndim)
2901
2902 double precision :: xhit(ndim),utrial(ndim),vtrial(ndim)
2903 double precision :: ptrial(ndim),qtrial(ndim)
2904 double precision :: xtrial(ndim),partial_length,h_partial,alpha_hit
2905 logical :: hit_ok
2906
2907 finished=.false.
2908 call trace_intersect_domain(xold,xstage,xhit,hit_ok,state%face_id, &
2909 alpha_hit)
2910 if (.not.hit_ok) return
2911
2912 partial_length=trace_segment_length(xold,xhit,hsigned,alpha_hit)
2913 if (partial_length>100.d0*epsilon(one)*max(one,abs(hsigned))) then
2914 h_partial=sign(partial_length,hsigned)
2915 if (state%has_extra) then
2916 if (.not.(present(pold) .and. present(qold) .and. present(kp1) &
2917 .and. present(kq1))) then
2919 return
2920 endif
2921 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
2922 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,status, &
2923 pold,qold,kp1,kq1,ptrial,qtrial,sph_cache=state%sph_cache)
2924 else
2925 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
2926 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,status, &
2927 sph_cache=state%sph_cache)
2928 endif
2929 if (status/=trace_status_active) return
2930 call trace_tangent_accumulate_twist(state,xhit,partial_length, &
2931 threshold)
2932 state%u=utrial
2933 state%v=vtrial
2934 if (state%has_extra) then
2935 state%p=ptrial
2936 state%q=qtrial
2937 endif
2938 state%length=state%length+partial_length
2939 state%nstep=state%nstep+1
2940 if (trace_spherical_profile_enabled) &
2941 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
2942 else
2943 state%u=uold
2944 state%v=vold
2945 if (state%has_extra) then
2946 if (.not.(present(pold) .and. present(qold))) then
2948 return
2949 endif
2950 state%p=pold
2951 state%q=qold
2952 endif
2953 endif
2954 state%x=xhit
2955 state%endpoint=xhit
2956 state%status=trace_status_boundary
2957 state%active=.false.
2958 state%complete=.true.
2959 state%rk45_h=zero
2960 state%sph_cache%valid=.false.
2961 if (trace_spherical_profile_enabled) then
2962 call trace_spherical_profile_add_count(trace_profile_boundary_events,1_8)
2963 endif
2964 call trace_tangent_state_finalize_boundary(state,threshold)
2965 call trace_rk45_stats_note_attempt(.true.,.true.,partial_length)
2966 status=trace_status_active
2967 finished=.true.
2968 end subroutine trace_tangent_rk45_spherical_finish_boundary
2969
2970 subroutine trace_tangent_state_advance_in_grid(state,igrid,dL,max_steps, &
2971 threshold,short_boundary)
2972 type(trace_tangent_state), intent(inout) :: state
2973 integer, intent(in) :: igrid,max_steps
2974 double precision, intent(in) :: dl,threshold
2975 logical, intent(in), optional :: short_boundary
2976
2977 integer, parameter :: max_bisect=50
2978 double precision :: h,h_partial,partial_length,alpha_hit
2979 double precision :: h_abs_low,h_abs_high,h_abs_mid,h_abs_final,hsign
2980 double precision :: xprobe(ndim),xtrial(ndim),utrial(ndim),vtrial(ndim)
2981 double precision :: ptrial(ndim),qtrial(ndim)
2982 double precision :: xhit(ndim),xold(ndim),uold(ndim),vold(ndim)
2983 double precision :: pold(ndim),qold(ndim)
2984 double precision :: xlow(ndim),ulow(ndim),vlow(ndim)
2985 double precision :: plow(ndim),qlow(ndim),xhigh(ndim)
2986 double precision :: kx1(ndim),ku1(ndim),kv1(ndim),kp1(ndim),kq1(ndim)
2987 double precision :: dxb^d
2988 type(trace_sph_interp_ctx) :: ctx_old
2989 integer :: igrid_old,igrid_trial,point_domain,iter
2990 integer :: ctx_status,trial_status
2991 logical :: hit_ok,use_short_boundary
2992
2993 if (.not.state%active) return
2994 use_short_boundary=.false.
2995 if (present(short_boundary)) use_short_boundary=short_boundary
2996
2997 do while(state%active .and. state%igrid==igrid .and. &
2998 state%nstep<max_steps)
2999 xold=state%x
3000 uold=state%u
3001 vold=state%v
3002 pold=state%p
3003 qold=state%q
3004 ^d&dxb^d=rnode(rpdx^d_,state%igrid);
3006 if (geo_coordinate==geo_spherical) then
3007 call trace_spherical_interp_ctx_build_cached(xold,state%igrid, &
3008 state%sph_cache,ctx_old,ctx_status)
3009 endif
3010 if (ctx_status==trace_status_active) then
3011 h=trace_spherical_effective_step_ctx(dl,ctx_old)
3012 else
3013 h=trace_effective_step(xold,dl,state%igrid,dxb^d)
3014 endif
3015 if (state%direction<0) h=-h
3016
3017 if (state%has_extra) then
3018 if (ctx_status==trace_status_active) then
3019 call trace_tangent_rhs(xold,uold,vold,state%igrid,threshold,kx1, &
3020 ku1,kv1,state%status,pold,qold,kp1,kq1,sph_ctx=ctx_old)
3021 else
3022 call trace_tangent_rhs(xold,uold,vold,state%igrid,threshold,kx1, &
3023 ku1,kv1,state%status,pold,qold,kp1,kq1)
3024 endif
3025 else
3026 if (ctx_status==trace_status_active) then
3027 call trace_tangent_rhs(xold,uold,vold,state%igrid,threshold,kx1, &
3028 ku1,kv1,state%status,sph_ctx=ctx_old)
3029 else
3030 call trace_tangent_rhs(xold,uold,vold,state%igrid,threshold,kx1, &
3031 ku1,kv1,state%status)
3032 endif
3033 endif
3034 if (state%status/=trace_status_active) then
3035 state%active=.false.
3036 state%complete=.true.
3037 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3038 return
3039 endif
3040 if (use_short_boundary) then
3041 ! The q0/logQ short-boundary path keeps the same RK2 RHS but clamps
3042 ! the trial stage and bisects the final step to the domain boundary.
3043 hsign=sign(one,h)
3044 if (state%has_extra) then
3045 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
3046 h,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,state%status, &
3047 pold,qold,kp1,kq1,ptrial,qtrial,sph_cache=state%sph_cache, &
3048 clamp_stage=.true.)
3049 else
3050 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
3051 h,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,state%status, &
3052 sph_cache=state%sph_cache,clamp_stage=.true.)
3053 endif
3054 if (state%status/=trace_status_active) then
3055 state%active=.false.
3056 state%complete=.true.
3057 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3058 return
3059 endif
3060
3061 point_domain=0
3062 {if (xtrial(^db)>=xprobmin^db .and. xtrial(^db)<xprobmax^db) point_domain=point_domain+1\}
3063 if (point_domain==ndim) then
3064 igrid_old=state%igrid
3065 call trace_locate_point_with_hint(xtrial,state%igrid,igrid_trial, &
3066 state%status)
3067 if (state%status/=trace_status_active) then
3068 state%active=.false.
3069 state%complete=.true.
3070 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3071 return
3072 endif
3073 call trace_tangent_accumulate_twist(state,xtrial,abs(h),threshold)
3074 state%x=xtrial
3075 state%u=utrial
3076 state%v=vtrial
3077 if (state%has_extra) then
3078 state%p=ptrial
3079 state%q=qtrial
3080 endif
3081 state%igrid=igrid_trial
3082 state%length=state%length+abs(h)
3083 state%nstep=state%nstep+1
3084 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_step(abs(h),.false., &
3085 igrid_trial/=igrid_old)
3086 if (trace_spherical_profile_enabled .and. &
3087 geo_coordinate==geo_spherical) then
3088 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
3089 endif
3090 else
3091 h_abs_low=zero
3092 h_abs_high=abs(h)
3093 xlow=xold
3094 ulow=uold
3095 vlow=vold
3096 plow=pold
3097 qlow=qold
3098 xhigh=xtrial
3099 do iter=1,max_bisect
3100 h_abs_mid=half*(h_abs_low+h_abs_high)
3101 if (h_abs_high-h_abs_low<=100.d0*epsilon(one)*max(one,abs(h))) &
3102 exit
3103 trial_status=trace_status_active
3104 if (state%has_extra) then
3105 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold, &
3106 state%igrid,hsign*h_abs_mid,threshold,kx1,ku1,kv1, &
3107 xtrial,utrial,vtrial,trial_status,pold,qold,kp1,kq1, &
3108 ptrial,qtrial,sph_cache=state%sph_cache, &
3109 clamp_stage=.true.)
3110 else
3111 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold, &
3112 state%igrid,hsign*h_abs_mid,threshold,kx1,ku1,kv1, &
3113 xtrial,utrial,vtrial,trial_status, &
3114 sph_cache=state%sph_cache,clamp_stage=.true.)
3115 endif
3116 if (trial_status/=trace_status_active) then
3117 h_abs_high=h_abs_mid
3118 cycle
3119 endif
3120 point_domain=0
3121 {if (xtrial(^db)>=xprobmin^db .and. xtrial(^db)<xprobmax^db) point_domain=point_domain+1\}
3122 if (point_domain==ndim) then
3123 h_abs_low=h_abs_mid
3124 xlow=xtrial
3125 ulow=utrial
3126 vlow=vtrial
3127 if (state%has_extra) then
3128 plow=ptrial
3129 qlow=qtrial
3130 endif
3131 else
3132 h_abs_high=h_abs_mid
3133 xhigh=xtrial
3134 endif
3135 enddo
3136
3137 call trace_intersect_domain(xlow,xhigh,xhit,hit_ok,state%face_id, &
3138 alpha_hit)
3139 if (hit_ok) then
3140 h_abs_final=h_abs_low+max(zero,min(one,alpha_hit))* &
3141 (h_abs_high-h_abs_low)
3142 else
3143 xhit=xlow
3144 h_abs_final=h_abs_low
3145 call trace_boundary_face_at_point(xhit,state%face_id,hit_ok)
3146 endif
3147 if (.not.hit_ok) then
3148 state%status=trace_status_out_of_domain
3149 state%active=.false.
3150 state%complete=.true.
3151 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3152 return
3153 endif
3154
3155 partial_length=h_abs_final
3156 if (partial_length>100.d0*epsilon(one)*max(one,abs(h))) then
3157 trial_status=trace_status_active
3158 if (state%has_extra) then
3159 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold, &
3160 state%igrid,hsign*h_abs_final,threshold,kx1,ku1,kv1, &
3161 xtrial,utrial,vtrial,trial_status,pold,qold,kp1,kq1, &
3162 ptrial,qtrial,sph_cache=state%sph_cache, &
3163 clamp_stage=.true.)
3164 else
3165 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold, &
3166 state%igrid,hsign*h_abs_final,threshold,kx1,ku1,kv1, &
3167 xtrial,utrial,vtrial,trial_status, &
3168 sph_cache=state%sph_cache,clamp_stage=.true.)
3169 endif
3170 if (trial_status/=trace_status_active) then
3171 state%status=trial_status
3172 state%active=.false.
3173 state%complete=.true.
3174 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3175 return
3176 endif
3177 call trace_tangent_accumulate_twist(state,xhit,partial_length, &
3178 threshold)
3179 state%u=utrial
3180 state%v=vtrial
3181 if (state%has_extra) then
3182 state%p=ptrial
3183 state%q=qtrial
3184 endif
3185 state%length=state%length+partial_length
3186 state%nstep=state%nstep+1
3187 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_step(partial_length,.true.,.false.)
3188 if (trace_spherical_profile_enabled .and. &
3189 geo_coordinate==geo_spherical) then
3190 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
3191 endif
3192 else
3193 state%u=ulow
3194 state%v=vlow
3195 if (state%has_extra) then
3196 state%p=plow
3197 state%q=qlow
3198 endif
3199 endif
3200 state%x=xhit
3201 state%endpoint=xhit
3202 state%status=trace_status_boundary
3203 state%active=.false.
3204 if (trace_spherical_profile_enabled .and. &
3205 geo_coordinate==geo_spherical) then
3206 call trace_spherical_profile_add_count( &
3207 trace_profile_boundary_events,1_8)
3208 endif
3209 call trace_tangent_state_finalize_boundary(state,threshold)
3210 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3211 return
3212 endif
3213 cycle
3214 endif
3215 xprobe=xold+h*kx1
3216
3217 point_domain=0
3218 {if (xprobe(^db)>=xprobmin^db .and. xprobe(^db)<xprobmax^db) point_domain=point_domain+1\}
3219 if (point_domain/=ndim) then
3220 call trace_intersect_domain(xold,xprobe,xhit,hit_ok,state%face_id, &
3221 alpha_hit)
3222 if (.not.hit_ok) then
3223 call trace_boundary_face_at_point(xold,state%face_id,hit_ok)
3224 state%status=trace_status_boundary
3225 state%active=.false.
3226 state%x=xold
3227 state%u=uold
3228 state%v=vold
3229 if (state%has_extra) then
3230 state%p=pold
3231 state%q=qold
3232 endif
3233 state%endpoint=xold
3234 if (hit_ok) call trace_tangent_state_finalize_boundary(state, &
3235 threshold)
3236 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3237 return
3238 endif
3239 partial_length=trace_segment_length(xold,xhit,h,alpha_hit)
3240 if (partial_length<=100.d0*epsilon(one)*max(one,abs(h))) then
3241 state%x=xhit
3242 state%u=uold
3243 state%v=vold
3244 if (state%has_extra) then
3245 state%p=pold
3246 state%q=qold
3247 endif
3248 state%endpoint=xhit
3249 state%status=trace_status_boundary
3250 state%active=.false.
3251 call trace_tangent_state_finalize_boundary(state,threshold)
3252 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3253 return
3254 endif
3255 h_partial=sign(partial_length,h)
3256 if (state%has_extra) then
3257 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
3258 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial, &
3259 state%status,pold,qold,kp1,kq1,ptrial,qtrial, &
3260 sph_cache=state%sph_cache)
3261 else
3262 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
3263 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial, &
3264 state%status,sph_cache=state%sph_cache)
3265 endif
3266 if (state%status/=trace_status_active) then
3267 state%active=.false.
3268 state%complete=.true.
3269 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3270 return
3271 endif
3272 call trace_tangent_accumulate_twist(state,xhit,partial_length, &
3273 threshold)
3274 state%x=xhit
3275 state%u=utrial
3276 state%v=vtrial
3277 if (state%has_extra) then
3278 state%p=ptrial
3279 state%q=qtrial
3280 endif
3281 state%length=state%length+partial_length
3282 state%endpoint=xhit
3283 state%status=trace_status_boundary
3284 state%active=.false.
3285 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_step(partial_length,.true.,.false.)
3286 call trace_tangent_state_finalize_boundary(state,threshold)
3287 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3288 return
3289 endif
3290
3291 if (state%has_extra) then
3292 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid,h, &
3293 threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,state%status, &
3294 pold,qold,kp1,kq1,ptrial,qtrial,sph_cache=state%sph_cache)
3295 else
3296 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid,h, &
3297 threshold,kx1,ku1,kv1,xtrial,utrial,vtrial,state%status, &
3298 sph_cache=state%sph_cache)
3299 endif
3300 if (state%status/=trace_status_active) then
3301 state%active=.false.
3302 state%complete=.true.
3303 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3304 return
3305 endif
3306
3307 point_domain=0
3308 {if (xtrial(^db)>=xprobmin^db .and. xtrial(^db)<xprobmax^db) point_domain=point_domain+1\}
3309 if (point_domain==ndim) then
3310 igrid_old=state%igrid
3311 call trace_locate_point_with_hint(xtrial,state%igrid,igrid_trial, &
3312 state%status)
3313 if (state%status/=trace_status_active) then
3314 state%active=.false.
3315 state%complete=.true.
3316 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3317 return
3318 endif
3319 call trace_tangent_accumulate_twist(state,xtrial,abs(h),threshold)
3320 state%x=xtrial
3321 state%u=utrial
3322 state%v=vtrial
3323 if (state%has_extra) then
3324 state%p=ptrial
3325 state%q=qtrial
3326 endif
3327 state%igrid=igrid_trial
3328 state%length=state%length+abs(h)
3329 state%nstep=state%nstep+1
3330 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_step(abs(h),.false., &
3331 igrid_trial/=igrid_old)
3332 if (trace_spherical_profile_enabled .and. &
3333 geo_coordinate==geo_spherical) then
3334 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
3335 endif
3336 else
3337 call trace_intersect_domain(xold,xtrial,xhit,hit_ok,state%face_id, &
3338 alpha_hit)
3339 if (.not.hit_ok) then
3340 call trace_boundary_face_at_point(xold,state%face_id,hit_ok)
3341 state%status=trace_status_boundary
3342 state%active=.false.
3343 state%x=xold
3344 state%u=uold
3345 state%v=vold
3346 if (state%has_extra) then
3347 state%p=pold
3348 state%q=qold
3349 endif
3350 state%endpoint=xold
3351 if (hit_ok) call trace_tangent_state_finalize_boundary(state, &
3352 threshold)
3353 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3354 return
3355 endif
3356 partial_length=trace_segment_length(xold,xhit,h,alpha_hit)
3357 if (partial_length<=100.d0*epsilon(one)*max(one,abs(h))) then
3358 state%x=xhit
3359 state%u=uold
3360 state%v=vold
3361 if (state%has_extra) then
3362 state%p=pold
3363 state%q=qold
3364 endif
3365 state%endpoint=xhit
3366 state%status=trace_status_boundary
3367 state%active=.false.
3368 call trace_tangent_state_finalize_boundary(state,threshold)
3369 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3370 return
3371 endif
3372 h_partial=sign(partial_length,h)
3373 if (state%has_extra) then
3374 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
3375 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial, &
3376 state%status,pold,qold,kp1,kq1,ptrial,qtrial, &
3377 sph_cache=state%sph_cache)
3378 else
3379 call trace_tangent_rk2_trial_from_rhs(xold,uold,vold,state%igrid, &
3380 h_partial,threshold,kx1,ku1,kv1,xtrial,utrial,vtrial, &
3381 state%status,sph_cache=state%sph_cache)
3382 endif
3383 if (state%status/=trace_status_active) then
3384 state%active=.false.
3385 state%complete=.true.
3386 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3387 return
3388 endif
3389 call trace_tangent_accumulate_twist(state,xhit,partial_length, &
3390 threshold)
3391 state%x=xhit
3392 state%u=utrial
3393 state%v=vtrial
3394 if (state%has_extra) then
3395 state%p=ptrial
3396 state%q=qtrial
3397 endif
3398 state%length=state%length+partial_length
3399 state%endpoint=xhit
3400 state%status=trace_status_boundary
3401 state%active=.false.
3402 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_step(partial_length,.true.,.false.)
3403 if (trace_spherical_profile_enabled .and. &
3404 geo_coordinate==geo_spherical) then
3405 call trace_spherical_profile_add_count(trace_profile_boundary_events, &
3406 1_8)
3407 endif
3408 call trace_tangent_state_finalize_boundary(state,threshold)
3409 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3410 return
3411 endif
3412 enddo
3413
3414 if (state%active .and. state%nstep>=max_steps) then
3415 state%status=trace_status_max_steps
3416 state%active=.false.
3417 state%complete=.true.
3418 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_completion(state%status,state%nstep)
3419 endif
3420 if ((.not.state%active) .and. trace_spherical_profile_enabled .and. &
3421 geo_coordinate==geo_spherical) then
3422 call trace_spherical_profile_note_trace_steps(state%nstep)
3423 endif
3424 end subroutine trace_tangent_state_advance_in_grid
3425
3426 subroutine trace_tangent_state_finalize_boundary(state,threshold)
3427 type(trace_tangent_state), intent(inout) :: state
3428 double precision, intent(in) :: threshold
3429
3430 if (state%status/=trace_status_boundary) return
3431 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
3432 call trace_spherical_profile_add_count( &
3433 trace_profile_endpoint_finalizations,1_8)
3434
3435 call trace_endpoint_b_bhat(state%endpoint,state%face_id,state%igrid, &
3436 threshold,state%endpoint_B,state%endpoint_bhat,state%status)
3437 if (state%status/=trace_status_boundary) return
3438
3439 call trace_project_to_perp_bhat(state%u,state%endpoint_bhat, &
3440 state%u_perp)
3441 call trace_project_to_perp_bhat(state%v,state%endpoint_bhat, &
3442 state%v_perp)
3443 if (state%has_extra) then
3444 call trace_project_to_perp_bhat(state%p,state%endpoint_bhat, &
3445 state%p_perp)
3446 call trace_project_to_perp_bhat(state%q,state%endpoint_bhat, &
3447 state%q_perp)
3448 endif
3449 state%complete=.true.
3450 end subroutine trace_tangent_state_finalize_boundary
3451
3452 subroutine trace_qperp_prepare_seed_result(seed,field_min,result,igrid, &
3453 status)
3454 double precision, intent(in) :: seed(ndim),field_min
3455 type(trace_qperp_result), intent(out) :: result
3456 integer, intent(out) :: igrid,status
3457
3458 double precision :: b3(3),bseed_norm
3459 integer :: indomain
3460
3461 call trace_init_qperp_result(seed,result)
3462 igrid=-1
3463 status=trace_status_active
3464
3465 indomain=0
3466 {if (seed(^db)>=xprobmin^db .and. seed(^db)<xprobmax^db) indomain=indomain+1\}
3467 if (indomain/=ndim) then
3469 result%status=status
3470 return
3471 endif
3472
3473 call trace_debug_locate_point(seed,igrid,status)
3474 if (status/=trace_status_active) then
3475 result%status=status
3476 return
3477 endif
3478
3479 call sample_b_at_point(seed,igrid,field_min,b3,status)
3480 if (status/=trace_status_active) then
3481 result%status=status
3482 return
3483 endif
3484 result%B_seed=b3(1:ndim)
3485 bseed_norm=dsqrt(sum(result%B_seed**2))
3486 if (bseed_norm<=zero .or. bseed_norm<field_min) then
3488 result%status=status
3489 return
3490 endif
3491
3492 result%bhat_seed=result%B_seed/bseed_norm
3493 call trace_make_perp_basis(b3/bseed_norm,result%u0,result%v0,status)
3494 if (status/=trace_status_active) result%status=status
3495 end subroutine trace_qperp_prepare_seed_result
3496
3497 subroutine trace_qperp_compute_scalars(result,field_min)
3498 type(trace_qperp_result), intent(inout) :: result
3499 double precision, intent(in) :: field_min
3500
3501 double precision :: bseed_norm,bf_norm,bb_norm,dot_f,dot_b,qtmp
3502
3503 bseed_norm=dsqrt(sum(result%B_seed**2))
3504 bf_norm=dsqrt(sum(result%forward_B**2))
3505 bb_norm=dsqrt(sum(result%backward_B**2))
3506 if (bseed_norm<=zero .or. bf_norm<=zero .or. bb_norm<=zero .or. &
3507 bseed_norm<field_min .or. bf_norm<field_min .or. &
3508 bb_norm<field_min) then
3509 result%status=trace_status_weak_field
3510 return
3511 endif
3512
3513 dot_f=sum(result%u_forward_perp*result%v_forward_perp)
3514 dot_b=sum(result%u_backward_perp*result%v_backward_perp)
3515 result%N2=sum(result%u_forward_perp**2)* &
3516 sum(result%v_backward_perp**2)+ &
3517 sum(result%u_backward_perp**2)* &
3518 sum(result%v_forward_perp**2)-2.d0*dot_f*dot_b
3519 result%bfactor=abs(bf_norm*bb_norm)/(bseed_norm**2)
3520 qtmp=result%N2*result%bfactor
3521 if (qtmp>zero .and. ieee_is_finite(qtmp)) then
3522 qtmp=max(qtmp,trace_q_min)
3523 result%qperp=qtmp
3524 result%logqperp=dlog10(qtmp)
3525 result%valid=.true.
3526 result%status=trace_status_boundary
3527 result%qperp0=qtmp
3528 result%logqperp0=result%logqperp
3529 result%valid_qperp0=.true.
3530 result%status_qperp0=trace_status_boundary
3531 else
3532 result%N2=trace_debug_nan()
3533 result%bfactor=trace_debug_nan()
3534 result%qperp=trace_debug_nan()
3535 result%logqperp=trace_debug_nan()
3536 result%status=trace_status_invalid_input
3537 result%valid=.false.
3538 result%qperp0=trace_debug_nan()
3539 result%logqperp0=trace_debug_nan()
3540 result%valid_qperp0=.false.
3541 result%status_qperp0=trace_status_invalid_input
3542 endif
3543 call trace_qperp_compute_q0_scalars(result,field_min)
3544 end subroutine trace_qperp_compute_scalars
3545
3546 subroutine trace_qperp_compute_q0_scalars(result,field_min)
3547 type(trace_qperp_result), intent(inout) :: result
3548 double precision, intent(in) :: field_min
3549
3550 double precision :: bseed_norm,bnf,bnb,n2bnd,qtmp
3551 double precision :: uf_face(ndim),vf_face(ndim)
3552 double precision :: ub_face(ndim),vb_face(ndim)
3553 integer :: status_f,status_b
3554
3555 result%q0=trace_debug_nan()
3556 result%logq0=trace_debug_nan()
3557 result%N2_qperp0=trace_debug_nan()
3558 result%bfactor_qperp0=trace_debug_nan()
3559 result%forward_Bn_q0=trace_debug_nan()
3560 result%backward_Bn_q0=trace_debug_nan()
3561 result%valid_q0=.false.
3562
3563 if (result%forward_status/=trace_status_boundary) then
3564 result%status_q0=result%forward_status
3565 return
3566 endif
3567 if (result%backward_status/=trace_status_boundary) then
3568 result%status_q0=result%backward_status
3569 return
3570 endif
3571
3572 bseed_norm=dsqrt(sum(result%B_seed**2))
3573 if (bseed_norm<=zero .or. bseed_norm<field_min) then
3574 result%status_q0=trace_status_weak_field
3575 return
3576 endif
3577
3578 call trace_qperp_project_to_boundary_face(result%u_forward_perp, &
3579 result%forward_B,result%forward_face,uf_face,bnf,status_f)
3580 call trace_qperp_project_to_boundary_face(result%v_forward_perp, &
3581 result%forward_B,result%forward_face,vf_face,bnf,status_f)
3582 call trace_qperp_project_to_boundary_face(result%u_backward_perp, &
3583 result%backward_B,result%backward_face,ub_face,bnb,status_b)
3584 call trace_qperp_project_to_boundary_face(result%v_backward_perp, &
3585 result%backward_B,result%backward_face,vb_face,bnb,status_b)
3586 if (status_f/=trace_status_active) then
3587 result%status_q0=status_f
3588 return
3589 endif
3590 if (status_b/=trace_status_active) then
3591 result%status_q0=status_b
3592 return
3593 endif
3594 if (abs(bnf)<field_min .or. abs(bnb)<field_min) then
3595 result%status_q0=trace_status_weak_field
3596 return
3597 endif
3598
3599 n2bnd=sum(uf_face**2)*sum(vb_face**2)+ &
3600 sum(ub_face**2)*sum(vf_face**2)- &
3601 2.d0*sum(uf_face*vf_face)*sum(ub_face*vb_face)
3602 result%N2_qperp0=abs(n2bnd)
3603 result%bfactor_qperp0=abs(bnf*bnb)/(bseed_norm**2)
3604 result%forward_Bn_q0=bnf
3605 result%backward_Bn_q0=bnb
3606 qtmp=result%N2_qperp0*result%bfactor_qperp0
3607 if (qtmp>zero .and. ieee_is_finite(qtmp)) then
3608 qtmp=max(qtmp,trace_q_min)
3609 result%q0=qtmp
3610 result%logq0=dlog10(qtmp)
3611 result%valid_q0=.true.
3612 result%status_q0=trace_status_boundary
3613 else
3614 result%q0=trace_debug_nan()
3615 result%logq0=trace_debug_nan()
3616 result%valid_q0=.false.
3617 result%status_q0=trace_status_invalid_input
3618 endif
3619 end subroutine trace_qperp_compute_q0_scalars
3620
3621 subroutine trace_qperp_project_to_boundary_face(vec,B,face_id,vec_face, &
3622 Bn,status)
3623 double precision, intent(in) :: vec(ndim),b(ndim)
3624 integer, intent(in) :: face_id
3625 double precision, intent(out) :: vec_face(ndim),bn
3626 integer, intent(out) :: status
3627
3628 double precision :: normal(ndim),vecn
3629 logical :: ok
3630
3631 vec_face=zero
3632 bn=zero
3634 call trace_face_normal(face_id,normal,ok)
3635 if (.not.ok) return
3636 bn=sum(b*normal)
3637 if (bn==zero) then
3639 return
3640 endif
3641 vecn=sum(vec*normal)
3642 vec_face=vec-vecn/bn*b
3643 status=trace_status_active
3644 end subroutine trace_qperp_project_to_boundary_face
3645
3646 subroutine trace_qperp_finalize_from_states(forward_state,backward_state, &
3647 result,field_min)
3648 type(trace_tangent_state), intent(in) :: forward_state,backward_state
3649 type(trace_qperp_result), intent(inout) :: result
3650 double precision, intent(in) :: field_min
3651
3652 result%forward_endpoint=forward_state%endpoint
3653 result%forward_B=forward_state%endpoint_B
3654 result%forward_bhat=forward_state%endpoint_bhat
3655 result%forward_length=forward_state%length
3656 result%forward_nstep=forward_state%nstep
3657 result%forward_face=forward_state%face_id
3658 result%forward_status=forward_state%status
3659 result%u_forward_perp=forward_state%u_perp
3660 result%v_forward_perp=forward_state%v_perp
3661
3662 result%backward_endpoint=backward_state%endpoint
3663 result%backward_B=backward_state%endpoint_B
3664 result%backward_bhat=backward_state%endpoint_bhat
3665 result%backward_length=backward_state%length
3666 result%backward_nstep=backward_state%nstep
3667 result%backward_face=backward_state%face_id
3668 result%backward_status=backward_state%status
3669 result%u_backward_perp=backward_state%u_perp
3670 result%v_backward_perp=backward_state%v_perp
3671
3672 if (forward_state%status/=trace_status_boundary) then
3673 result%status=forward_state%status
3674 return
3675 endif
3676 if (backward_state%status/=trace_status_boundary) then
3677 result%status=backward_state%status
3678 return
3679 endif
3680
3681 call trace_qperp_compute_scalars(result,field_min)
3682 end subroutine trace_qperp_finalize_from_states
3683
3684 subroutine trace_q0_finalize_from_states(forward_state,backward_state, &
3685 result,field_min)
3686 type(trace_tangent_state), intent(in) :: forward_state,backward_state
3687 type(trace_qperp_result), intent(inout) :: result
3688 double precision, intent(in) :: field_min
3689
3690 result%forward_endpoint=forward_state%endpoint
3691 result%forward_B=forward_state%endpoint_B
3692 result%forward_bhat=forward_state%endpoint_bhat
3693 result%forward_length=forward_state%length
3694 result%forward_nstep=forward_state%nstep
3695 result%forward_face=forward_state%face_id
3696 result%forward_status=forward_state%status
3697 result%u_forward_perp=forward_state%u_perp
3698 result%v_forward_perp=forward_state%v_perp
3699
3700 result%backward_endpoint=backward_state%endpoint
3701 result%backward_B=backward_state%endpoint_B
3702 result%backward_bhat=backward_state%endpoint_bhat
3703 result%backward_length=backward_state%length
3704 result%backward_nstep=backward_state%nstep
3705 result%backward_face=backward_state%face_id
3706 result%backward_status=backward_state%status
3707 result%u_backward_perp=backward_state%u_perp
3708 result%v_backward_perp=backward_state%v_perp
3709
3710 if (forward_state%status/=trace_status_boundary) then
3711 result%status=forward_state%status
3712 result%status_q0=forward_state%status
3713 return
3714 endif
3715 if (backward_state%status/=trace_status_boundary) then
3716 result%status=backward_state%status
3717 result%status_q0=backward_state%status
3718 return
3719 endif
3720
3721 result%status=trace_status_boundary
3722 call trace_qperp_compute_q0_scalars(result,field_min)
3723 end subroutine trace_q0_finalize_from_states
3724
3725 subroutine trace_spherical_rmin_q_prepare_seed_result(seed,field_min, &
3726 result,igrid,status)
3727 double precision, intent(in) :: seed(ndim),field_min
3728 type(trace_qperp_result), intent(out) :: result
3729 integer, intent(out) :: igrid,status
3730
3731 double precision :: bcart(3),bhat_cart(3),er(3),etheta(3),ephi(3)
3732 double precision :: radial_tol
3733 integer :: indomain
3734
3735 call trace_init_qperp_result(seed,result)
3736 igrid=-1
3737 status=trace_status_active
3738
3739 if (ndim/=3 .or. geo_coordinate/=geo_spherical) then
3741 result%status=status
3742 result%status_q0=status
3743 return
3744 endif
3745
3746 indomain=0
3747 {if (seed(^db)>=xprobmin^db .and. seed(^db)<xprobmax^db) indomain=indomain+1\}
3748 if (indomain/=ndim) then
3750 result%status=status
3751 result%status_q0=status
3752 return
3753 endif
3754
3755 call trace_debug_locate_point(seed,igrid,status)
3756 if (status/=trace_status_active) then
3757 result%status=status
3758 result%status_q0=status
3759 return
3760 endif
3761
3762 {^ifthreed
3763 call trace_spherical_sample_b_bhat_cart(seed,igrid,field_min,bcart, &
3764 bhat_cart,status)
3765 if (status/=trace_status_active) then
3766 result%status=status
3767 result%status_q0=status
3768 return
3769 endif
3770 call trace_spherical_basis(seed,er,etheta,ephi,status)
3771 if (status/=trace_status_active) then
3772 result%status=status
3773 result%status_q0=status
3774 return
3775 endif
3776 result%B_seed=bcart(1:ndim)
3777 result%bhat_seed=bhat_cart(1:ndim)
3778 radial_tol=100.d0*epsilon(one)*max(one,abs(xprobmin1),abs(xprobmax1))
3779 ! Boundary seeds use the radial-surface basis; interior sampled Q uses a
3780 ! B-transverse basis to avoid a nearly singular initial tangent plane.
3781 if (abs(seed(1)-xprobmin1)<=radial_tol .or. &
3782 abs(seed(1)-xprobmax1)<=radial_tol) then
3783 result%u0=etheta(1:ndim)
3784 result%v0=ephi(1:ndim)
3785 else
3786 call trace_make_perp_basis(bhat_cart,result%u0,result%v0,status)
3787 if (status/=trace_status_active) then
3788 result%status=status
3789 result%status_q0=status
3790 return
3791 endif
3792 endif
3793 }
3794 end subroutine trace_spherical_rmin_q_prepare_seed_result
3795
3796 subroutine trace_spherical_rmin_q_finalize_from_states(forward_state, &
3797 backward_state,result,field_min)
3798 type(trace_tangent_state), intent(in) :: forward_state,backward_state
3799 type(trace_qperp_result), intent(inout) :: result
3800 double precision, intent(in) :: field_min
3801
3802 call trace_spherical_radial_q_finalize_from_states(forward_state, &
3803 backward_state,result,field_min)
3804
3805 end subroutine trace_spherical_rmin_q_finalize_from_states
3806
3807 subroutine trace_spherical_radial_q_finalize_from_states(forward_state, &
3808 backward_state,result,field_min)
3809 type(trace_tangent_state), intent(in) :: forward_state,backward_state
3810 type(trace_qperp_result), intent(inout) :: result
3811 double precision, intent(in) :: field_min
3812
3813 double precision :: bnf,bnb,frob2,qtmp,logqtmp,bfactor
3814 double precision :: af(2,2),ab(2,2)
3815 integer :: status_f,status_b,status_q
3816 logical :: valid_q
3817
3818 result%forward_endpoint=forward_state%endpoint
3819 result%forward_B=forward_state%endpoint_B
3820 result%forward_bhat=forward_state%endpoint_bhat
3821 result%forward_length=forward_state%length
3822 result%forward_nstep=forward_state%nstep
3823 result%forward_face=forward_state%face_id
3824 result%forward_status=forward_state%status
3825 result%u_forward_perp=forward_state%u
3826 result%v_forward_perp=forward_state%v
3827
3828 result%backward_endpoint=backward_state%endpoint
3829 result%backward_B=backward_state%endpoint_B
3830 result%backward_bhat=backward_state%endpoint_bhat
3831 result%backward_length=backward_state%length
3832 result%backward_nstep=backward_state%nstep
3833 result%backward_face=backward_state%face_id
3834 result%backward_status=backward_state%status
3835 result%u_backward_perp=backward_state%u
3836 result%v_backward_perp=backward_state%v
3837
3838 result%q0=trace_debug_nan()
3839 result%logq0=trace_debug_nan()
3840 result%N2_qperp0=trace_debug_nan()
3841 result%bfactor_qperp0=trace_debug_nan()
3842 result%forward_Bn_q0=trace_debug_nan()
3843 result%backward_Bn_q0=trace_debug_nan()
3844 result%valid_q0=.false.
3845
3846 if (forward_state%status/=trace_status_boundary) then
3847 result%status_q0=forward_state%status
3848 result%status=forward_state%status
3849 return
3850 endif
3851 if (backward_state%status/=trace_status_boundary) then
3852 result%status_q0=backward_state%status
3853 result%status=backward_state%status
3854 return
3855 endif
3856 result%status=trace_status_boundary
3857
3858 if (.not.trace_spherical_radial_q_face_pair_admitted( &
3859 forward_state%face_id,backward_state%face_id)) then
3860 result%status_q0=trace_status_invalid_input
3861 return
3862 endif
3863
3864 if (dsqrt(sum(result%B_seed**2))<=zero .or. &
3865 dsqrt(sum(result%B_seed**2))<field_min) then
3866 result%status_q0=trace_status_weak_field
3867 return
3868 endif
3869
3870 call trace_spherical_radial_endpoint_matrix(forward_state,af,bnf,status_f)
3871 result%forward_Bn_q0=bnf
3872 if (status_f/=trace_status_active) then
3873 result%status_q0=status_f
3874 return
3875 endif
3876
3877 call trace_spherical_radial_endpoint_matrix(backward_state,ab,bnb,status_b)
3878 result%backward_Bn_q0=bnb
3879 if (status_b/=trace_status_active) then
3880 result%status_q0=status_b
3881 return
3882 endif
3883
3884 if (abs(bnf)<1.d-10 .or. abs(bnb)<1.d-10) then
3885 result%status_q0=trace_status_weak_field
3886 return
3887 endif
3888
3889 call trace_spherical_q_from_endpoint_matrices(af,ab,qtmp,logqtmp, &
3890 frob2,bfactor,valid_q,status_q)
3891 result%N2_qperp0=frob2
3892 result%bfactor_qperp0=bfactor
3893 if (.not.valid_q) then
3894 result%status_q0=status_q
3895 return
3896 endif
3897
3898 result%q0=qtmp
3899 result%logq0=logqtmp
3900 result%valid_q0=.true.
3901 result%status_q0=trace_status_boundary
3902 end subroutine trace_spherical_radial_q_finalize_from_states
3903
3904 logical function trace_spherical_radial_q_face_pair_admitted(face_forward, &
3905 face_backward) result(is_admitted)
3906 integer, intent(in) :: face_forward,face_backward
3907
3908 is_admitted=.false.
3909 select case (face_forward)
3910 case (trace_face_xmin)
3911 is_admitted=(face_backward==trace_face_xmin .or. &
3912 face_backward==trace_face_xmax)
3913 case (trace_face_xmax)
3914 is_admitted=(face_backward==trace_face_xmin)
3915 end select
3916
3917 end function trace_spherical_radial_q_face_pair_admitted
3918
3919 subroutine trace_spherical_radial_endpoint_matrix(state,Amat,Bn,status)
3920 type(trace_tangent_state), intent(in) :: state
3921 double precision, intent(out) :: amat(2,2),bn
3922 integer, intent(out) :: status
3923
3924 double precision :: u_face(ndim),v_face(ndim)
3925 double precision :: er(3),etheta(3),ephi(3)
3926 double precision :: bn_u,bn_v
3927 integer :: status_u,status_v,basis_status
3928
3929 amat=zero
3930 bn=zero
3931 status=trace_status_active
3932
3933 call trace_spherical_radial_project_to_surface(state%u,state%endpoint_B, &
3934 state%endpoint,state%face_id,u_face,bn_u,status_u)
3935 if (status_u/=trace_status_active) then
3936 status=status_u
3937 return
3938 endif
3939
3940 call trace_spherical_radial_project_to_surface(state%v,state%endpoint_B, &
3941 state%endpoint,state%face_id,v_face,bn_v,status_v)
3942 if (status_v/=trace_status_active) then
3943 status=status_v
3944 return
3945 endif
3946
3947 bn=bn_u
3948
3949 {^ifthreed
3950 call trace_spherical_basis(state%endpoint,er,etheta,ephi,basis_status)
3951 if (basis_status/=trace_status_active) then
3952 status=basis_status
3953 return
3954 endif
3955 amat(1,1)=sum(u_face*etheta(1:ndim))
3956 amat(2,1)=sum(u_face*ephi(1:ndim))
3957 amat(1,2)=sum(v_face*etheta(1:ndim))
3958 amat(2,2)=sum(v_face*ephi(1:ndim))
3959 }
3960
3961 end subroutine trace_spherical_radial_endpoint_matrix
3962
3963 subroutine trace_spherical_radial_project_to_surface(vec,B,x,face_id, &
3964 vec_face,Bn,status)
3965 double precision, intent(in) :: vec(ndim),b(ndim),x(ndim)
3966 integer, intent(in) :: face_id
3967 double precision, intent(out) :: vec_face(ndim),bn
3968 integer, intent(out) :: status
3969
3970 double precision :: er(3),etheta(3),ephi(3),normal(ndim),vecn
3971 double precision :: bhat(ndim),bnorm
3972
3973 vec_face=zero
3974 bn=zero
3976 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
3977
3978 {^ifthreed
3979 call trace_spherical_basis(x,er,etheta,ephi,status)
3980 if (status/=trace_status_active) return
3981 select case (face_id)
3982 case (trace_face_xmin)
3983 normal=-er(1:ndim)
3984 case (trace_face_xmax)
3985 normal=er(1:ndim)
3986 case default
3988 return
3989 end select
3990 bnorm=dsqrt(sum(b**2))
3991 if (bnorm<=smalldouble) then
3993 return
3994 endif
3995 bhat=b/bnorm
3996 bn=sum(bhat*normal)
3997 if (abs(bn)<=smalldouble) then
3999 return
4000 endif
4001 vecn=sum(vec*normal)
4002 vec_face=vec-vecn/bn*bhat
4003 status=trace_status_active
4004 }
4005 end subroutine trace_spherical_radial_project_to_surface
4006
4007 subroutine trace_spherical_qperp_prepare_seed_result(seed,field_min, &
4008 result,igrid,status)
4009 double precision, intent(in) :: seed(ndim),field_min
4010 type(trace_qperp_result), intent(out) :: result
4011 integer, intent(out) :: igrid,status
4012
4013 double precision :: bcart(3),bhat_cart(3)
4014 integer :: indomain
4015
4016 call trace_init_qperp_result(seed,result)
4017 igrid=-1
4018 status=trace_status_active
4019
4020 if (ndim/=3 .or. geo_coordinate/=geo_spherical) then
4022 result%status=status
4023 return
4024 endif
4025
4026 indomain=0
4027 {if (seed(^db)>=xprobmin^db .and. seed(^db)<xprobmax^db) indomain=indomain+1\}
4028 if (indomain/=ndim) then
4030 result%status=status
4031 return
4032 endif
4033
4034 call trace_debug_locate_point(seed,igrid,status)
4035 if (status/=trace_status_active) then
4036 result%status=status
4037 return
4038 endif
4039
4040 {^ifthreed
4041 call trace_spherical_sample_b_bhat_cart(seed,igrid,field_min,bcart, &
4042 bhat_cart,status)
4043 if (status/=trace_status_active) then
4044 result%status=status
4045 return
4046 endif
4047 result%B_seed=bcart(1:ndim)
4048 result%bhat_seed=bhat_cart(1:ndim)
4049 call trace_make_perp_basis(bhat_cart,result%u0,result%v0,status)
4050 if (status/=trace_status_active) result%status=status
4051 }
4052 end subroutine trace_spherical_qperp_prepare_seed_result
4053
4054 subroutine trace_spherical_qperp_finalize_from_states(forward_state, &
4055 backward_state,result,field_min)
4056 type(trace_tangent_state), intent(in) :: forward_state,backward_state
4057 type(trace_qperp_result), intent(inout) :: result
4058 double precision, intent(in) :: field_min
4059
4060 result%forward_endpoint=forward_state%endpoint
4061 result%forward_B=forward_state%endpoint_B
4062 result%forward_bhat=forward_state%endpoint_bhat
4063 result%forward_length=forward_state%length
4064 result%forward_nstep=forward_state%nstep
4065 result%forward_face=forward_state%face_id
4066 result%forward_status=forward_state%status
4067 result%u_forward_perp=forward_state%u_perp
4068 result%v_forward_perp=forward_state%v_perp
4069
4070 result%backward_endpoint=backward_state%endpoint
4071 result%backward_B=backward_state%endpoint_B
4072 result%backward_bhat=backward_state%endpoint_bhat
4073 result%backward_length=backward_state%length
4074 result%backward_nstep=backward_state%nstep
4075 result%backward_face=backward_state%face_id
4076 result%backward_status=backward_state%status
4077 result%u_backward_perp=backward_state%u_perp
4078 result%v_backward_perp=backward_state%v_perp
4079
4080 if (forward_state%status/=trace_status_boundary) then
4081 result%status=forward_state%status
4082 result%status_qperp0=forward_state%status
4083 return
4084 endif
4085 if (backward_state%status/=trace_status_boundary) then
4086 result%status=backward_state%status
4087 result%status_qperp0=backward_state%status
4088 return
4089 endif
4090
4091 call trace_spherical_qperp_compute_from_states(result,field_min)
4092 end subroutine trace_spherical_qperp_finalize_from_states
4093
4094 subroutine trace_spherical_qperp_compute_from_states(result,field_min)
4095 type(trace_qperp_result), intent(inout) :: result
4096 double precision, intent(in) :: field_min
4097
4098 double precision :: f1(ndim),f2(ndim),b1(ndim),b2(ndim)
4099 double precision :: forward_bhat3(3),backward_bhat3(3)
4100 double precision :: af(2,2),ab(2,2)
4101 double precision :: bseed_norm,bf_norm,bb_norm,frob2,qtmp,logqtmp,bfactor
4102 integer :: status_f,status_b,status_q
4103 logical :: valid_q
4104
4105 result%qperp=trace_debug_nan()
4106 result%logqperp=trace_debug_nan()
4107 result%N2=trace_debug_nan()
4108 result%bfactor=trace_debug_nan()
4109 result%qperp0=trace_debug_nan()
4110 result%logqperp0=trace_debug_nan()
4111 result%N2_qperp0=trace_debug_nan()
4112 result%bfactor_qperp0=trace_debug_nan()
4113 result%valid=.false.
4114 result%valid_qperp0=.false.
4115
4116 if (ndim/=3) then
4118 result%status_qperp0=trace_status_unsupported_geometry
4119 return
4120 endif
4121
4122 bseed_norm=dsqrt(sum(result%B_seed**2))
4123 bf_norm=dsqrt(sum(result%forward_B**2))
4124 bb_norm=dsqrt(sum(result%backward_B**2))
4125 if (bseed_norm<=zero .or. bf_norm<=zero .or. bb_norm<=zero .or. &
4126 bseed_norm<field_min .or. bf_norm<field_min .or. &
4127 bb_norm<field_min) then
4128 result%status=trace_status_weak_field
4129 result%status_qperp0=trace_status_weak_field
4130 return
4131 endif
4132
4133 forward_bhat3=zero
4134 backward_bhat3=zero
4135 forward_bhat3(1:ndim)=result%forward_bhat
4136 backward_bhat3(1:ndim)=result%backward_bhat
4137 call trace_make_perp_basis(forward_bhat3,f1,f2,status_f)
4138 call trace_make_perp_basis(backward_bhat3,b1,b2,status_b)
4139 if (status_f/=trace_status_active) then
4140 result%status=status_f
4141 result%status_qperp0=status_f
4142 return
4143 endif
4144 if (status_b/=trace_status_active) then
4145 result%status=status_b
4146 result%status_qperp0=status_b
4147 return
4148 endif
4149
4150 af(1,1)=sum(result%u_forward_perp*f1)
4151 af(2,1)=sum(result%u_forward_perp*f2)
4152 af(1,2)=sum(result%v_forward_perp*f1)
4153 af(2,2)=sum(result%v_forward_perp*f2)
4154 ab(1,1)=sum(result%u_backward_perp*b1)
4155 ab(2,1)=sum(result%u_backward_perp*b2)
4156 ab(1,2)=sum(result%v_backward_perp*b1)
4157 ab(2,2)=sum(result%v_backward_perp*b2)
4158
4159 call trace_spherical_q_from_endpoint_matrices(af,ab,qtmp,logqtmp, &
4160 frob2,bfactor,valid_q,status_q)
4161 result%N2=frob2
4162 result%bfactor=bfactor
4163 result%N2_qperp0=frob2
4164 result%bfactor_qperp0=bfactor
4165 if (.not.valid_q) then
4166 result%status=status_q
4167 result%status_qperp0=status_q
4168 return
4169 endif
4170
4171 result%qperp=qtmp
4172 result%logqperp=logqtmp
4173 result%valid=.true.
4174 result%status=trace_status_boundary
4175 result%qperp0=qtmp
4176 result%logqperp0=result%logqperp
4177 result%valid_qperp0=.true.
4178 result%status_qperp0=trace_status_boundary
4179 end subroutine trace_spherical_qperp_compute_from_states
4180
4181 subroutine trace_spherical_q_from_endpoint_matrices(Af,Ab,qval,logq, &
4182 frob2,bfactor,valid,status)
4183 double precision, intent(in) :: af(2,2),ab(2,2)
4184 double precision, intent(out) :: qval,logq,frob2,bfactor
4185 logical, intent(out) :: valid
4186 integer, intent(out) :: status
4187
4188 double precision :: dmat(2,2),detab,detd,q_tol,det_tol
4189
4190 qval=trace_debug_nan()
4191 logq=trace_debug_nan()
4192 frob2=trace_debug_nan()
4193 bfactor=trace_debug_nan()
4194 valid=.false.
4196
4197 detab=ab(1,1)*ab(2,2)-ab(1,2)*ab(2,1)
4198 det_tol=1.d-12
4199 if (.not.ieee_is_finite(detab) .or. abs(detab)<=det_tol) then
4201 return
4202 endif
4203
4204 dmat(1,1)=(af(1,1)*ab(2,2)-af(1,2)*ab(2,1))/detab
4205 dmat(1,2)=(-af(1,1)*ab(1,2)+af(1,2)*ab(1,1))/detab
4206 dmat(2,1)=(af(2,1)*ab(2,2)-af(2,2)*ab(2,1))/detab
4207 dmat(2,2)=(-af(2,1)*ab(1,2)+af(2,2)*ab(1,1))/detab
4208 detd=dmat(1,1)*dmat(2,2)-dmat(1,2)*dmat(2,1)
4209 frob2=sum(dmat**2)
4210 bfactor=abs(detd)
4211 if (.not.ieee_is_finite(detd) .or. abs(detd)<=det_tol) then
4213 return
4214 endif
4215
4216 qval=frob2/abs(detd)
4217 q_tol=1.d-8
4218 if (qval>zero .and. ieee_is_finite(qval)) then
4219 if (qval<trace_q_min-q_tol) then
4221 return
4222 endif
4223 qval=max(qval,trace_q_min)
4224 logq=dlog10(qval)
4225 valid=.true.
4227 else
4228 qval=trace_debug_nan()
4229 logq=trace_debug_nan()
4230 valid=.false.
4232 endif
4233
4234 end subroutine trace_spherical_q_from_endpoint_matrices
4235
4236 subroutine trace_spherical_basis(x,er,etheta,ephi,status)
4237 double precision, intent(in) :: x(ndim)
4238 double precision, intent(out) :: er(3),etheta(3),ephi(3)
4239 integer, intent(out) :: status
4240
4241 double precision :: theta,phi,sint,cost,sinp,cosp
4242
4243 er=zero
4244 etheta=zero
4245 ephi=zero
4247 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
4248
4249 {^ifthreed
4250 if (.not.trace_spherical_metric_ok(x)) then
4252 return
4253 endif
4254 theta=x(2)
4255 phi=x(3)
4256 sint=dsin(theta)
4257 cost=dcos(theta)
4258 sinp=dsin(phi)
4259 cosp=dcos(phi)
4260 er=(/ sint*cosp,sint*sinp,cost /)
4261 etheta=(/ cost*cosp,cost*sinp,-sint /)
4262 ephi=(/ -sinp,cosp,zero /)
4263 status=trace_status_active
4264 }
4265 end subroutine trace_spherical_basis
4266
4267 subroutine trace_spherical_coord_to_cart(x,xcart,status)
4268 double precision, intent(in) :: x(ndim)
4269 double precision, intent(out) :: xcart(3)
4270 integer, intent(out) :: status
4271
4272 double precision :: r,theta,phi,sint
4273
4274 xcart=zero
4276 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
4277
4278 {^ifthreed
4279 if (.not.trace_spherical_metric_ok(x)) then
4281 return
4282 endif
4283 r=x(1)
4284 theta=x(2)
4285 phi=x(3)
4286 sint=dsin(theta)
4287 xcart(1)=r*sint*dcos(phi)
4288 xcart(2)=r*sint*dsin(phi)
4289 xcart(3)=r*dcos(theta)
4290 status=trace_status_active
4291 }
4292 end subroutine trace_spherical_coord_to_cart
4293
4294 subroutine trace_cart_to_spherical_coord(xcart,x,status)
4295 double precision, intent(in) :: xcart(3)
4296 double precision, intent(out) :: x(ndim)
4297 integer, intent(out) :: status
4298
4299 double precision :: r,rho,phi,twopi
4300
4301 x=zero
4303 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
4304
4305 {^ifthreed
4306 r=dsqrt(sum(xcart**2))
4307 if (r<=100.d0*epsilon(one)) then
4309 return
4310 endif
4311 rho=dsqrt(xcart(1)**2+xcart(2)**2)
4312 phi=datan2(xcart(2),xcart(1))
4313 twopi=2.d0*dacos(-one)
4314 if (phi<xprobmin3 .and. phi+twopi<xprobmax3) phi=phi+twopi
4315 if (phi>=xprobmax3 .and. phi-twopi>=xprobmin3) phi=phi-twopi
4316 x(1)=r
4317 x(2)=datan2(rho,xcart(3))
4318 x(3)=phi
4319 status=trace_status_active
4320 }
4321 end subroutine trace_cart_to_spherical_coord
4322
4323 subroutine trace_spherical_interp_ctx_build(x,igrid,ctx,status)
4324 double precision, intent(in) :: x(ndim)
4325 integer, intent(in) :: igrid
4326 type(trace_sph_interp_ctx), intent(out) :: ctx
4327 integer, intent(out) :: status
4328
4329 double precision :: xd^d,dxc^d
4330 double precision :: t0
4331 integer :: ixi^l,ixbl^d
4332
4333 if (trace_spherical_profile_enabled) then
4334 call trace_spherical_profile_add_count(trace_profile_context_requests, &
4335 1_8)
4336 call trace_spherical_profile_add_count( &
4337 trace_profile_full_context_builds,1_8)
4338 t0=trace_spherical_profile_time()
4339 endif
4340
4341 ctx%valid=.false.
4342 ctx%igrid=-1
4343 ctx%ixbl=0
4344 ctx%x=zero
4345 ctx%xd=zero
4346 ctx%dxc=zero
4347 ctx%w=zero
4348 ctx%dxloc=zero
4349 ctx%r=zero
4350 ctx%theta=zero
4351 ctx%phi=zero
4352 ctx%sin_theta=zero
4353 ctx%cos_theta=zero
4354 ctx%sin_phi=zero
4355 ctx%cos_phi=zero
4356 ctx%rsin_theta=zero
4357 ctx%h_local=zero
4358 ctx%bcorner_valid=.false.
4359 ctx%bcorner=zero
4361 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) then
4362 if (trace_spherical_profile_enabled) then
4363 call trace_spherical_profile_add_count(trace_profile_context_failures, &
4364 1_8)
4365 call trace_spherical_profile_add_time(trace_profile_time_context, &
4366 trace_spherical_profile_time()-t0)
4367 endif
4368 return
4369 endif
4370
4371 {^ifthreed
4373 if (.not.trace_spherical_metric_ok(x)) then
4375 if (trace_spherical_profile_enabled) then
4376 call trace_spherical_profile_add_count(trace_profile_context_failures, &
4377 1_8)
4378 call trace_spherical_profile_add_time(trace_profile_time_context, &
4379 trace_spherical_profile_time()-t0)
4380 endif
4381 return
4382 endif
4383
4384 ixi^l=ixg^ll;
4385 call trace_interp_weights_block(x,igrid,ixi^l,ixbl^d,xd^d,dxc^d,status)
4386 if (status/=trace_status_active) then
4387 if (trace_spherical_profile_enabled) then
4388 call trace_spherical_profile_add_count(trace_profile_context_failures, &
4389 1_8)
4390 call trace_spherical_profile_add_time(trace_profile_time_context, &
4391 trace_spherical_profile_time()-t0)
4392 endif
4393 return
4394 endif
4395
4396 call trace_spherical_interp_ctx_fill(x,igrid,ixbl^d,xd^d,dxc^d,ctx, &
4397 status)
4398 if (status==trace_status_active) then
4399 call trace_spherical_interp_ctx_load_bcorners(ctx,status)
4400 endif
4401 if (trace_spherical_profile_enabled) then
4402 if (status/=trace_status_active) then
4403 call trace_spherical_profile_add_count(trace_profile_context_failures, &
4404 1_8)
4405 endif
4406 call trace_spherical_profile_add_time(trace_profile_time_context, &
4407 trace_spherical_profile_time()-t0)
4408 endif
4409 }
4410 end subroutine trace_spherical_interp_ctx_build
4411
4412 subroutine trace_spherical_interp_ctx_build_cached(x,igrid,cache,ctx,status)
4413 double precision, intent(in) :: x(ndim)
4414 integer, intent(in) :: igrid
4415 type(trace_sph_interp_ctx), intent(inout) :: cache
4416 type(trace_sph_interp_ctx), intent(out) :: ctx
4417 integer, intent(out) :: status
4418
4419 double precision :: xd^d,dxc^d
4420 double precision :: t0
4421 integer :: ixi^l,ixbl^d
4422 logical :: same_cell
4423
4424 if (trace_spherical_profile_enabled) then
4425 call trace_spherical_profile_add_count(trace_profile_context_requests, &
4426 1_8)
4427 t0=trace_spherical_profile_time()
4428 endif
4429
4431 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) then
4432 if (trace_spherical_profile_enabled) then
4433 call trace_spherical_profile_add_count(trace_profile_context_failures, &
4434 1_8)
4435 call trace_spherical_profile_add_time(trace_profile_time_context, &
4436 trace_spherical_profile_time()-t0)
4437 endif
4438 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
4439 return
4440 endif
4441
4442 {^ifthreed
4443 if (cache%valid .and. cache%igrid==igrid) then
4444 ixi^l=ixg^ll;
4445 call trace_interp_weights_block_near(x,igrid,ixi^l,cache%ixbl, &
4446 ixbl^d,xd^d,dxc^d,status)
4447 if (status==trace_status_active) then
4448 call trace_spherical_interp_ctx_fill(x,igrid,ixbl^d,xd^d,dxc^d, &
4449 ctx,status)
4450 if (status==trace_status_active) then
4451 same_cell=(ixbl1==cache%ixbl(1) .and. ixbl2==cache%ixbl(2) .and. &
4452 ixbl3==cache%ixbl(3))
4453 if (same_cell .and. cache%bcorner_valid) then
4454 ctx%bcorner_valid=.true.
4455 ctx%bcorner=cache%bcorner
4456 if (trace_spherical_profile_enabled) then
4457 call trace_spherical_profile_add_count( &
4458 trace_profile_b_corner_hits,1_8)
4459 endif
4460 else
4461 call trace_spherical_interp_ctx_load_bcorners(ctx,status)
4462 endif
4463 endif
4464 if (status==trace_status_active) then
4465 cache=ctx
4466 if (trace_spherical_profile_enabled) then
4467 if (same_cell) then
4468 call trace_spherical_profile_add_count( &
4469 trace_profile_same_cell_hits,1_8)
4470 else
4471 call trace_spherical_profile_add_count( &
4472 trace_profile_same_grid_hits,1_8)
4473 endif
4474 call trace_spherical_profile_add_time(trace_profile_time_context, &
4475 trace_spherical_profile_time()-t0)
4476 endif
4477 else if (trace_spherical_profile_enabled) then
4478 call trace_spherical_profile_add_count(trace_profile_context_failures, &
4479 1_8)
4480 call trace_spherical_profile_add_time(trace_profile_time_context, &
4481 trace_spherical_profile_time()-t0)
4482 endif
4483 return
4484 endif
4485 else if (cache%valid .and. trace_spherical_profile_enabled) then
4486 call trace_spherical_profile_add_count(trace_profile_cache_invalidations, &
4487 1_8)
4488 endif
4489
4490 if (trace_spherical_profile_enabled) then
4491 call trace_spherical_profile_add_time(trace_profile_time_context, &
4492 trace_spherical_profile_time()-t0)
4493 endif
4494 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
4495 if (status==trace_status_active) cache=ctx
4496 }
4497 end subroutine trace_spherical_interp_ctx_build_cached
4498
4499 subroutine trace_spherical_interp_ctx_fill(x,igrid,ixbl^D,xd^D,dxc^D,ctx, &
4500 status)
4501 double precision, intent(in) :: x(ndim)
4502 integer, intent(in) :: igrid,ixbl^d
4503 double precision, intent(in) :: xd^d,dxc^d
4504 type(trace_sph_interp_ctx), intent(out) :: ctx
4505 integer, intent(out) :: status
4506
4507 if (trace_spherical_profile_enabled) then
4508 call trace_spherical_profile_add_count(trace_profile_hlocal_evals,1_8)
4509 endif
4510
4511 ctx%valid=.false.
4512 ctx%igrid=-1
4513 ctx%ixbl=0
4514 ctx%x=zero
4515 ctx%xd=zero
4516 ctx%dxc=zero
4517 ctx%w=zero
4518 ctx%dxloc=zero
4519 ctx%r=zero
4520 ctx%theta=zero
4521 ctx%phi=zero
4522 ctx%sin_theta=zero
4523 ctx%cos_theta=zero
4524 ctx%sin_phi=zero
4525 ctx%cos_phi=zero
4526 ctx%rsin_theta=zero
4527 ctx%h_local=zero
4528 ctx%bcorner_valid=.false.
4529 ctx%bcorner=zero
4531 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) return
4532
4533 {^ifthreed
4534 ctx%igrid=igrid
4535 ctx%x(1:3)=x(1:3)
4536 ctx%ixbl=(/ixbl1,ixbl2,ixbl3/)
4537 ctx%xd=(/xd1,xd2,xd3/)
4538 ctx%dxc=(/dxc1,dxc2,dxc3/)
4539 ctx%w(0,1)=one-xd1
4540 ctx%w(1,1)=xd1
4541 ctx%w(0,2)=one-xd2
4542 ctx%w(1,2)=xd2
4543 ctx%w(0,3)=one-xd3
4544 ctx%w(1,3)=xd3
4545
4546 ctx%dxloc(1)=min(abs(ps(igrid)%dx(ixbl1,ixbl2,ixbl3,1)), &
4547 abs(ps(igrid)%dx(ixbl1+1,ixbl2,ixbl3,1)))
4548 ctx%dxloc(2)=min(abs(ps(igrid)%dx(ixbl1,ixbl2,ixbl3,2)), &
4549 abs(ps(igrid)%dx(ixbl1,ixbl2+1,ixbl3,2)))
4550 ctx%dxloc(3)=min(abs(ps(igrid)%dx(ixbl1,ixbl2,ixbl3,3)), &
4551 abs(ps(igrid)%dx(ixbl1,ixbl2,ixbl3+1,3)))
4552 ctx%dxloc=max(ctx%dxloc,smalldouble)
4553
4554 ctx%r=x(1)
4555 ctx%theta=x(2)
4556 ctx%phi=x(3)
4557 ctx%sin_theta=dsin(ctx%theta)
4558 ctx%cos_theta=dcos(ctx%theta)
4559 ctx%sin_phi=dsin(ctx%phi)
4560 ctx%cos_phi=dcos(ctx%phi)
4561 ctx%rsin_theta=ctx%r*ctx%sin_theta
4562 if (ctx%r<=smalldouble .or. abs(ctx%sin_theta)<=smalldouble) then
4564 return
4565 endif
4566
4567 ctx%h_local=trace_spherical_cell_scale_from_widths(ctx%r, &
4568 ctx%sin_theta,ctx%dxloc)
4569 ctx%h_local=max(ctx%h_local,smalldouble)
4570 ctx%valid=.true.
4571 status=trace_status_active
4572 }
4573 end subroutine trace_spherical_interp_ctx_fill
4574
4575 subroutine trace_spherical_interp_ctx_load_bcorners(ctx,status)
4576 type(trace_sph_interp_ctx), intent(inout) :: ctx
4577 integer, intent(out) :: status
4578
4579 integer :: i1,i2,i3,igrid,ix1,ix2,ix3
4580
4582 if (.not.ctx%valid) return
4583 if (ctx%bcorner_valid) then
4584 if (trace_spherical_profile_enabled) then
4585 call trace_spherical_profile_add_count(trace_profile_b_corner_hits, &
4586 1_8)
4587 endif
4588 status=trace_status_active
4589 return
4590 endif
4591 if (.not.b0field .and. .not.allocated(iw_mag)) return
4592
4593 {^ifthreed
4594 igrid=ctx%igrid
4595 ctx%bcorner=zero
4596 do i3=0,1
4597 do i2=0,1
4598 do i1=0,1
4599 ix1=ctx%ixbl(1)+i1
4600 ix2=ctx%ixbl(2)+i2
4601 ix3=ctx%ixbl(3)+i3
4602 if (allocated(iw_mag)) then
4603 ctx%bcorner(i1,i2,i3,1:3)= &
4604 ps(igrid)%w(ix1,ix2,ix3,iw_mag(1:3))
4605 endif
4606 if (b0field) then
4607 ctx%bcorner(i1,i2,i3,1:3)= &
4608 ctx%bcorner(i1,i2,i3,1:3) &
4609 +ps(igrid)%B0(ix1,ix2,ix3,1:3,0)
4610 endif
4611 enddo
4612 enddo
4613 enddo
4614 ctx%bcorner_valid=.true.
4615 if (trace_spherical_profile_enabled) then
4616 call trace_spherical_profile_add_count(trace_profile_b_corner_loads, &
4617 1_8)
4618 endif
4619 status=trace_status_active
4620 }
4621 end subroutine trace_spherical_interp_ctx_load_bcorners
4622
4623 double precision function trace_spherical_effective_step_ctx(ds,ctx) &
4624 result(ds_eff)
4625 double precision, intent(in) :: ds
4626 type(trace_sph_interp_ctx), intent(in) :: ctx
4627
4628 double precision :: step_cap,t0
4629
4630 if (trace_spherical_profile_enabled) t0=trace_spherical_profile_time()
4631
4632 ds_eff=ds
4633 if (.not.ctx%valid) then
4634 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4635 trace_profile_time_step,trace_spherical_profile_time()-t0)
4636 return
4637 endif
4638 step_cap=abs(ds)
4639 if (ctx%h_local<=zero) then
4640 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4641 trace_profile_time_step,trace_spherical_profile_time()-t0)
4642 return
4643 endif
4644 select case (trace_step_control_mode)
4645 case (trace_step_control_cell_fraction)
4646 ds_eff=min(step_cap,trace_step_fraction*ctx%h_local)
4647 if (trace_step_min>zero) ds_eff=max(ds_eff,min(trace_step_min,step_cap))
4648 case default
4649 ds_eff=min(step_cap,ctx%h_local)
4650 end select
4651 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4652 trace_profile_time_step,trace_spherical_profile_time()-t0)
4653 end function trace_spherical_effective_step_ctx
4654
4655 subroutine trace_spherical_sample_bsph_ctx(ctx,threshold,Bsph,status)
4656 type(trace_sph_interp_ctx), intent(in) :: ctx
4657 double precision, intent(in) :: threshold
4658 double precision, intent(out) :: bsph(3)
4659 integer, intent(out) :: status
4660
4661 double precision :: b2,weight
4662 double precision :: t0
4663 integer :: i1,i2,i3,j
4664
4665 if (trace_spherical_profile_enabled) then
4666 call trace_spherical_profile_add_count(trace_profile_b_samples,1_8)
4667 t0=trace_spherical_profile_time()
4668 endif
4669
4670 bsph=zero
4672 if (.not.ctx%valid) then
4673 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4674 trace_profile_time_b_interp,trace_spherical_profile_time()-t0)
4675 return
4676 endif
4677 if (.not.ctx%bcorner_valid) then
4678 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4679 trace_profile_time_b_interp,trace_spherical_profile_time()-t0)
4680 return
4681 endif
4682
4683 {^ifthreed
4684 do i3=0,1
4685 do i2=0,1
4686 do i1=0,1
4687 weight=ctx%w(i1,1)*ctx%w(i2,2)*ctx%w(i3,3)
4688 do j=1,3
4689 bsph(j)=bsph(j)+ctx%bcorner(i1,i2,i3,j)*weight
4690 enddo
4691 enddo
4692 enddo
4693 enddo
4694 b2=sum(bsph**2)
4695 if (b2<=zero .or. dsqrt(b2)<threshold) then
4697 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4698 trace_profile_time_b_interp,trace_spherical_profile_time()-t0)
4699 return
4700 endif
4701 status=trace_status_active
4702 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4703 trace_profile_time_b_interp,trace_spherical_profile_time()-t0)
4704 }
4705 end subroutine trace_spherical_sample_bsph_ctx
4706
4707 subroutine trace_spherical_sample_b_bhat_cart_ctx(ctx,threshold,Bcart, &
4708 bhat_cart,status)
4709 type(trace_sph_interp_ctx), intent(in) :: ctx
4710 double precision, intent(in) :: threshold
4711 double precision, intent(out) :: bcart(3),bhat_cart(3)
4712 integer, intent(out) :: status
4713
4714 double precision :: bsph(3),bnorm
4715
4716 bcart=zero
4717 bhat_cart=zero
4718 call trace_spherical_sample_bsph_ctx(ctx,threshold,bsph,status)
4719 if (status/=trace_status_active) return
4720
4721 bcart(1)=bsph(1)*ctx%sin_theta*ctx%cos_phi &
4722 +bsph(2)*ctx%cos_theta*ctx%cos_phi &
4723 -bsph(3)*ctx%sin_phi
4724 bcart(2)=bsph(1)*ctx%sin_theta*ctx%sin_phi &
4725 +bsph(2)*ctx%cos_theta*ctx%sin_phi &
4726 +bsph(3)*ctx%cos_phi
4727 bcart(3)=bsph(1)*ctx%cos_theta-bsph(2)*ctx%sin_theta
4728 bnorm=dsqrt(sum(bcart**2))
4729 if (bnorm<=zero .or. bnorm<threshold) then
4731 return
4732 endif
4733 bhat_cart=bcart/bnorm
4734 status=trace_status_active
4735 end subroutine trace_spherical_sample_b_bhat_cart_ctx
4736
4737 subroutine trace_spherical_sample_cached_curlb_ctx(ctx,curlB,status)
4738 type(trace_sph_interp_ctx), intent(in) :: ctx
4739 double precision, intent(out) :: curlb(3)
4740 integer, intent(out) :: status
4741
4742 double precision :: weight
4743 double precision :: t0
4744 integer :: i1,i2,i3,j,igrid,ix1,ix2,ix3
4745
4746 if (trace_spherical_profile_enabled) then
4747 call trace_spherical_profile_add_count(trace_profile_curl_samples,1_8)
4748 t0=trace_spherical_profile_time()
4749 endif
4750
4751 curlb=zero
4753 if (.not.ctx%valid) then
4754 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4755 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4756 return
4757 endif
4758 if (.not.trace_spherical_curl_cache_ready) then
4759 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4760 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4761 return
4762 endif
4763 if (.not.allocated(trace_spherical_curl_cache)) then
4764 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4765 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4766 return
4767 endif
4768 igrid=ctx%igrid
4769 if (igrid>size(trace_spherical_curl_cache)) then
4770 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4771 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4772 return
4773 endif
4774 if (.not.trace_spherical_curl_cache(igrid)%ready) then
4775 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4776 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4777 return
4778 endif
4779 if (.not.allocated(trace_spherical_curl_cache(igrid)%current)) then
4780 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4781 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4782 return
4783 endif
4784
4785 {^ifthreed
4786 do i3=0,1
4787 do i2=0,1
4788 do i1=0,1
4789 ix1=ctx%ixbl(1)+i1
4790 ix2=ctx%ixbl(2)+i2
4791 ix3=ctx%ixbl(3)+i3
4792 weight=ctx%w(i1,1)*ctx%w(i2,2)*ctx%w(i3,3)
4793 do j=1,3
4794 curlb(j)=curlb(j)+ &
4795 trace_spherical_curl_cache(igrid)%current(ix1,ix2,ix3,j) &
4796 *weight
4797 enddo
4798 enddo
4799 enddo
4800 enddo
4801 status=trace_status_active
4802 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4803 trace_profile_time_curl_interp,trace_spherical_profile_time()-t0)
4804 }
4805 end subroutine trace_spherical_sample_cached_curlb_ctx
4806
4807 subroutine trace_spherical_sample_bhat_gradbhat_covariant_ctx(ctx, &
4808 threshold,bhat_cart,grad_bhat_cart,status)
4809 type(trace_sph_interp_ctx), intent(in) :: ctx
4810 double precision, intent(in) :: threshold
4811 double precision, intent(out) :: bhat_cart(3),grad_bhat_cart(3,3)
4812 integer, intent(out) :: status
4813
4814 double precision :: dw(0:1,3),weight,bnorm,projected
4815 double precision :: bsph(3),bhat_sph(3),dbdq(3,3),dbhatdq(3,3)
4816 double precision :: a_local(3,3),e(3,3)
4817 double precision :: t0
4818 integer :: i1,i2,i3,j
4819
4820 if (trace_spherical_profile_enabled) then
4821 call trace_spherical_profile_add_count(trace_profile_grad_samples,1_8)
4822 t0=trace_spherical_profile_time()
4823 endif
4824
4825 bhat_cart=zero
4826 grad_bhat_cart=zero
4828 if (.not.ctx%valid) then
4829 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4830 trace_profile_time_grad_bhat,trace_spherical_profile_time()-t0)
4831 return
4832 endif
4833 if (.not.ctx%bcorner_valid) then
4835 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4836 trace_profile_time_grad_bhat,trace_spherical_profile_time()-t0)
4837 return
4838 endif
4839
4840 {^ifthreed
4841 dw(0,1)=-one/ctx%dxc(1)
4842 dw(1,1)= one/ctx%dxc(1)
4843 dw(0,2)=-one/ctx%dxc(2)
4844 dw(1,2)= one/ctx%dxc(2)
4845 dw(0,3)=-one/ctx%dxc(3)
4846 dw(1,3)= one/ctx%dxc(3)
4847
4848 bsph=zero
4849 dbdq=zero
4850 do i3=0,1
4851 do i2=0,1
4852 do i1=0,1
4853 weight=ctx%w(i1,1)*ctx%w(i2,2)*ctx%w(i3,3)
4854 bsph(1:3)=bsph(1:3)+ctx%bcorner(i1,i2,i3,1:3)*weight
4855 dbdq(1:3,1)=dbdq(1:3,1)+ctx%bcorner(i1,i2,i3,1:3)* &
4856 dw(i1,1)*ctx%w(i2,2)*ctx%w(i3,3)
4857 dbdq(1:3,2)=dbdq(1:3,2)+ctx%bcorner(i1,i2,i3,1:3)* &
4858 ctx%w(i1,1)*dw(i2,2)*ctx%w(i3,3)
4859 dbdq(1:3,3)=dbdq(1:3,3)+ctx%bcorner(i1,i2,i3,1:3)* &
4860 ctx%w(i1,1)*ctx%w(i2,2)*dw(i3,3)
4861 enddo
4862 enddo
4863 enddo
4864
4865 bnorm=dsqrt(sum(bsph**2))
4866 if (bnorm<=zero .or. bnorm<threshold) then
4868 if (trace_spherical_profile_enabled) call trace_spherical_profile_add_time( &
4869 trace_profile_time_grad_bhat,trace_spherical_profile_time()-t0)
4870 return
4871 endif
4872
4873 bhat_sph=bsph/bnorm
4874 do j=1,3
4875 projected=sum(bhat_sph*dbdq(1:3,j))
4876 dbhatdq(1:3,j)=(dbdq(1:3,j)-bhat_sph(1:3)*projected)/bnorm
4877 enddo
4878
4879 e(1:3,1)=(/ctx%sin_theta*ctx%cos_phi, &
4880 ctx%sin_theta*ctx%sin_phi,ctx%cos_theta/)
4881 e(1:3,2)=(/ctx%cos_theta*ctx%cos_phi, &
4882 ctx%cos_theta*ctx%sin_phi,-ctx%sin_theta/)
4883 e(1:3,3)=(/-ctx%sin_phi,ctx%cos_phi,zero/)
4884 bhat_cart=bhat_sph(1)*e(1:3,1)+bhat_sph(2)*e(1:3,2) &
4885 +bhat_sph(3)*e(1:3,3)
4886
4887 a_local(1:3,1)=dbhatdq(1:3,1)
4888 a_local(1,2)=(dbhatdq(1,2)-bhat_sph(2))/ctx%r
4889 a_local(2,2)=(dbhatdq(2,2)+bhat_sph(1))/ctx%r
4890 a_local(3,2)= dbhatdq(3,2)/ctx%r
4891 a_local(1,3)=(dbhatdq(1,3)-ctx%sin_theta*bhat_sph(3))/ctx%rsin_theta
4892 a_local(2,3)=(dbhatdq(2,3)-ctx%cos_theta*bhat_sph(3))/ctx%rsin_theta
4893 a_local(3,3)=(dbhatdq(3,3)+ctx%sin_theta*bhat_sph(1) &
4894 +ctx%cos_theta*bhat_sph(2))/ctx%rsin_theta
4895
4896 grad_bhat_cart=matmul(e,matmul(a_local,transpose(e)))
4897 status=trace_status_active
4898 if (trace_spherical_profile_enabled) then
4899 call trace_spherical_profile_add_count(trace_profile_grad_success,1_8)
4900 call trace_spherical_profile_add_time(trace_profile_time_grad_bhat, &
4901 trace_spherical_profile_time()-t0)
4902 endif
4903 }
4904 end subroutine trace_spherical_sample_bhat_gradbhat_covariant_ctx
4905
4906 subroutine get_k_spherical_ctx(ctx,K,ftype,b_min,field_ok)
4907 type(trace_sph_interp_ctx), intent(in) :: ctx
4908 double precision, intent(out) :: k(ndim)
4909 character(len=std_len), intent(in) :: ftype
4910 double precision, intent(in), optional :: b_min
4911 logical, intent(out), optional :: field_ok
4912
4913 double precision :: b(3),ftotal,field_min
4914 logical :: valid_field
4915 integer :: status
4916
4917 k=zero
4918 valid_field=.false.
4919 if (ftype/='Bfield') then
4920 if (present(field_ok)) field_ok=.false.
4921 return
4922 endif
4923 call trace_spherical_sample_bsph_ctx(ctx,zero,b,status)
4924 if (status/=trace_status_active) then
4925 if (present(field_ok)) field_ok=.false.
4926 return
4927 endif
4928 ftotal=dsqrt(sum(b**2))
4929 field_min=smalldouble
4930 if (present(b_min)) field_min=max(b_min,zero)
4931 valid_field=ftotal>zero .and. ftotal>=field_min
4932 if (present(field_ok)) field_ok=valid_field
4933 if (.not.valid_field) return
4934
4935 {^ifthreed
4936 k(1)=b(1)/ftotal
4937 k(2)=b(2)/(ctx%r*ftotal)
4938 k(3)=b(3)/(ctx%rsin_theta*ftotal)
4939 }
4940 end subroutine get_k_spherical_ctx
4941
4942 subroutine trace_spherical_sample_b_bhat_cart(x,igrid,threshold,Bcart, &
4943 bhat_cart,status)
4944 double precision, intent(in) :: x(ndim),threshold
4945 integer, intent(in) :: igrid
4946 double precision, intent(out) :: bcart(3),bhat_cart(3)
4947 integer, intent(out) :: status
4948
4949 type(trace_sph_interp_ctx) :: ctx
4950
4951 bcart=zero
4952 bhat_cart=zero
4954 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
4955
4956 {^ifthreed
4957 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
4958 if (status/=trace_status_active) return
4959 call trace_spherical_sample_b_bhat_cart_ctx(ctx,threshold,bcart, &
4960 bhat_cart,status)
4961 }
4962 end subroutine trace_spherical_sample_b_bhat_cart
4963
4964 subroutine trace_spherical_sample_bhat_gradbhat_covariant(x,igrid, &
4965 threshold,bhat_cart,grad_bhat_cart,status)
4966 double precision, intent(in) :: x(ndim),threshold
4967 integer, intent(in) :: igrid
4968 double precision, intent(out) :: bhat_cart(3),grad_bhat_cart(3,3)
4969 integer, intent(out) :: status
4970
4971 type(trace_sph_interp_ctx) :: ctx
4972
4973 bhat_cart=zero
4974 grad_bhat_cart=zero
4976 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
4977
4978 {^ifthreed
4979 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
4980 if (status/=trace_status_active) return
4981 call trace_spherical_sample_bhat_gradbhat_covariant_ctx(ctx,threshold, &
4982 bhat_cart,grad_bhat_cart,status)
4983 }
4984 end subroutine trace_spherical_sample_bhat_gradbhat_covariant
4985
4986 subroutine trace_spherical_sample_bhat_gradbhat_cartfd(x,igrid,threshold, &
4987 bhat,grad_bhat,status)
4988 double precision, intent(in) :: x(ndim),threshold
4989 integer, intent(in) :: igrid
4990 double precision, intent(out) :: bhat(3),grad_bhat(3,3)
4991 integer, intent(out) :: status
4992
4993 double precision :: xcart(3),xplus_cart(3),xminus_cart(3)
4994 double precision :: xplus(ndim),xminus(ndim),bplus(3),bminus(3)
4995 double precision :: btmp(3),eps
4996 integer :: j,igrid_plus,igrid_minus,status_plus,status_minus
4997
4998 bhat=zero
4999 grad_bhat=zero
5001 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
5002
5003 {^ifthreed
5004 call trace_spherical_sample_b_bhat_cart(x,igrid,threshold,btmp,bhat, &
5005 status)
5006 if (status/=trace_status_active) return
5007 call trace_spherical_coord_to_cart(x,xcart,status)
5008 if (status/=trace_status_active) return
5009
5010 eps=max(1.d-8*max(one,abs(x(1))), &
5011 0.01d0*trace_spherical_physical_cell_scale(x,igrid))
5012 do j=1,3
5013 xplus_cart=xcart
5014 xminus_cart=xcart
5015 xplus_cart(j)=xplus_cart(j)+eps
5016 xminus_cart(j)=xminus_cart(j)-eps
5017
5019 call trace_cart_to_spherical_coord(xplus_cart,xplus,status_plus)
5020 if (status_plus==trace_status_active) then
5021 call trace_locate_point_with_hint(xplus,igrid,igrid_plus,status_plus)
5022 endif
5023 if (status_plus==trace_status_active) then
5024 call trace_spherical_sample_b_bhat_cart(xplus,igrid_plus,threshold, &
5025 btmp,bplus,status_plus)
5026 endif
5027
5029 call trace_cart_to_spherical_coord(xminus_cart,xminus,status_minus)
5030 if (status_minus==trace_status_active) then
5031 call trace_locate_point_with_hint(xminus,igrid,igrid_minus, &
5032 status_minus)
5033 endif
5034 if (status_minus==trace_status_active) then
5035 call trace_spherical_sample_b_bhat_cart(xminus,igrid_minus,threshold, &
5036 btmp,bminus,status_minus)
5037 endif
5038
5039 if (status_plus==trace_status_active .and. &
5040 status_minus==trace_status_active) then
5041 grad_bhat(:,j)=(bplus-bminus)/(2.d0*eps)
5042 else if (status_plus==trace_status_active) then
5043 grad_bhat(:,j)=(bplus-bhat)/eps
5044 else if (status_minus==trace_status_active) then
5045 grad_bhat(:,j)=(bhat-bminus)/eps
5046 else
5048 return
5049 endif
5050 enddo
5051 status=trace_status_active
5052 }
5053 end subroutine trace_spherical_sample_bhat_gradbhat_cartfd
5054
5055 subroutine trace_spherical_bhat_cart_to_rhs(x,bhat_cart,kx,status)
5056 double precision, intent(in) :: x(ndim),bhat_cart(3)
5057 double precision, intent(out) :: kx(ndim)
5058 integer, intent(out) :: status
5059
5060 double precision :: er(3),etheta(3),ephi(3),r,sint
5061
5062 kx=zero
5064 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
5065
5066 {^ifthreed
5067 call trace_spherical_basis(x,er,etheta,ephi,status)
5068 if (status/=trace_status_active) return
5069 r=x(1)
5070 sint=dsin(x(2))
5071 if (r<=smalldouble .or. abs(sint)<=smalldouble) then
5073 return
5074 endif
5075 kx(1)=sum(bhat_cart*er)
5076 kx(2)=sum(bhat_cart*etheta)/r
5077 kx(3)=sum(bhat_cart*ephi)/(r*sint)
5078 status=trace_status_active
5079 }
5080 end subroutine trace_spherical_bhat_cart_to_rhs
5081
5082 subroutine trace_spherical_endpoint_b_bhat(x,face_id,igrid,threshold,B, &
5083 bhat,status)
5084 double precision, intent(in) :: x(ndim),threshold
5085 integer, intent(in) :: face_id,igrid
5086 double precision, intent(out) :: b(ndim),bhat(ndim)
5087 integer, intent(out) :: status
5088
5089 double precision :: xprobe(ndim),bcart(3),bhat_cart(3)
5090 integer :: normal_dim,sample_igrid
5091
5092 b=zero
5093 bhat=zero
5095 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) then
5097 return
5098 endif
5099 if (.not.trace_face_is_boundary(face_id)) return
5100
5101 {^ifthreed
5102 select case(face_id)
5103 case(trace_face_xmin)
5104 normal_dim=1
5105 case(trace_face_xmax)
5106 normal_dim=1
5107 case(trace_face_ymin)
5108 normal_dim=2
5109 case(trace_face_ymax)
5110 normal_dim=2
5111 case(trace_face_zmin)
5112 normal_dim=3
5113 case(trace_face_zmax)
5114 normal_dim=3
5115 case default
5116 return
5117 end select
5118
5119 xprobe=x
5120 call trace_spherical_face_probe_coord(face_id,igrid, &
5121 xprobe(normal_dim),status)
5122 if (status/=trace_status_active) return
5123 xprobe(1)=max(xprobmin1,min(xprobe(1), &
5124 xprobmax1-100.d0*epsilon(one)*max(one,abs(xprobmax1))))
5125 xprobe(2)=max(xprobmin2,min(xprobe(2), &
5126 xprobmax2-100.d0*epsilon(one)*max(one,abs(xprobmax2))))
5127 xprobe(3)=max(xprobmin3,min(xprobe(3), &
5128 xprobmax3-100.d0*epsilon(one)*max(one,abs(xprobmax3))))
5129 call trace_debug_locate_point(xprobe,sample_igrid,status)
5130 if (status/=trace_status_active) return
5131 call trace_spherical_sample_b_bhat_cart(xprobe,sample_igrid,threshold, &
5132 bcart,bhat_cart,status)
5133 if (status/=trace_status_active) return
5134 b=bcart(1:ndim)
5135 bhat=bhat_cart(1:ndim)
5137 }
5138 end subroutine trace_spherical_endpoint_b_bhat
5139
5140 subroutine trace_spherical_face_probe_coord(face_id,igrid,xprobe,status)
5141 integer, intent(in) :: face_id,igrid
5142 double precision, intent(out) :: xprobe
5143 integer, intent(out) :: status
5144
5145 integer :: i1,i2,i3,normal_dim,side
5146
5147 if (trace_spherical_profile_enabled) then
5148 call trace_spherical_profile_add_count(trace_profile_face_probes,1_8)
5149 endif
5150
5151 xprobe=zero
5153 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) then
5155 return
5156 endif
5157
5158 {^ifthreed
5159 i1=ixmlo1
5160 i2=ixmlo2
5161 i3=ixmlo3
5162 select case(face_id)
5163 case(trace_face_xmin)
5164 normal_dim=1
5165 side=-1
5166 i1=ixmlo1
5167 case(trace_face_xmax)
5168 normal_dim=1
5169 side=1
5170 i1=ixmhi1
5171 case(trace_face_ymin)
5172 normal_dim=2
5173 side=-1
5174 i2=ixmlo2
5175 case(trace_face_ymax)
5176 normal_dim=2
5177 side=1
5178 i2=ixmhi2
5179 case(trace_face_zmin)
5180 normal_dim=3
5181 side=-1
5182 i3=ixmlo3
5183 case(trace_face_zmax)
5184 normal_dim=3
5185 side=1
5186 i3=ixmhi3
5187 case default
5188 return
5189 end select
5190
5191 xprobe=ps(igrid)%x(i1,i2,i3,normal_dim)
5192 if (side<0) then
5193 if (xprobe<xprobmin1 .and. normal_dim==1) return
5194 if (xprobe<xprobmin2 .and. normal_dim==2) return
5195 if (xprobe<xprobmin3 .and. normal_dim==3) return
5196 else
5197 if (xprobe>xprobmax1 .and. normal_dim==1) return
5198 if (xprobe>xprobmax2 .and. normal_dim==2) return
5199 if (xprobe>xprobmax3 .and. normal_dim==3) return
5200 endif
5201 status=trace_status_active
5202 }
5203 end subroutine trace_spherical_face_probe_coord
5204
5205 double precision function trace_spherical_physical_cell_scale(x,igrid) &
5206 result(cell_scale)
5207 double precision, intent(in) :: x(ndim)
5208 integer, intent(in) :: igrid
5209
5210 double precision :: r,sint,dxloc(ndim)
5211 integer :: status
5212
5213 cell_scale=one
5214 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) return
5215
5216 {^ifthreed
5217 call trace_spherical_local_cell_widths(x,igrid,dxloc,status)
5218 if (status/=trace_status_active) return
5219 r=max(abs(x(1)),smalldouble)
5220 sint=max(abs(dsin(x(2))),smalldouble)
5221 cell_scale=trace_spherical_cell_scale_from_widths(r,sint,dxloc)
5222 }
5223 end function trace_spherical_physical_cell_scale
5224
5225 double precision function trace_spherical_cell_scale_from_widths(r,sint, &
5226 dxloc) result(cell_scale)
5227 double precision, intent(in) :: r,sint,dxloc(ndim)
5228
5229 cell_scale=one
5230 {^ifthreed
5231 cell_scale=min(abs(dxloc(1)),abs(r)*abs(dxloc(2)))
5232 cell_scale=min(cell_scale,abs(r)*max(abs(sint),smalldouble)* &
5233 abs(dxloc(3)))
5234 cell_scale=max(cell_scale,smalldouble)
5235 }
5236 end function trace_spherical_cell_scale_from_widths
5237
5239 double precision, intent(out) :: hmin
5240 integer, intent(out) :: status
5241
5242 double precision :: dxloc(ndim),hcell,r,sint
5243 integer :: iigrid,igrid,ix1,ix2,ix3
5244
5245 hmin=huge(one)
5247 if (ndim/=3 .or. geo_coordinate/=geo_spherical) return
5248
5249 {^ifthreed
5251 do iigrid=1,igridstail
5252 igrid=igrids(iigrid)
5253 do ix3=ixmlo3,ixmhi3
5254 do ix2=ixmlo2,ixmhi2
5255 do ix1=ixmlo1,ixmhi1
5256 r=ps(igrid)%x(ix1,ix2,ix3,1)
5257 sint=dsin(ps(igrid)%x(ix1,ix2,ix3,2))
5258 if (r<=smalldouble .or. abs(sint)<=smalldouble) cycle
5259 dxloc(1)=abs(ps(igrid)%dx(ix1,ix2,ix3,1))
5260 dxloc(2)=abs(ps(igrid)%dx(ix1,ix2,ix3,2))
5261 dxloc(3)=abs(ps(igrid)%dx(ix1,ix2,ix3,3))
5262 hcell=trace_spherical_cell_scale_from_widths(r,sint,dxloc)
5263 hmin=min(hmin,hcell)
5264 enddo
5265 enddo
5266 enddo
5267 enddo
5268 if (hmin<huge(one)) status=trace_status_active
5269 }
5271
5273 double precision, intent(out) :: hmin
5274 integer, intent(out) :: status
5275
5276 double precision :: hlocal,hcell
5277 integer :: iigrid,igrid,ix1,ix2,ix3
5278
5279 hmin=huge(one)
5280 hlocal=huge(one)
5282 if (ndim/=3 .or. .not.trace_cartesian_like_geometry()) return
5283
5284 {^ifthreed
5286 do iigrid=1,igridstail
5287 igrid=igrids(iigrid)
5288 do ix3=ixmlo3,ixmhi3
5289 do ix2=ixmlo2,ixmhi2
5290 do ix1=ixmlo1,ixmhi1
5291 hcell=min(abs(ps(igrid)%dx(ix1,ix2,ix3,1)), &
5292 abs(ps(igrid)%dx(ix1,ix2,ix3,2)))
5293 hcell=min(hcell,abs(ps(igrid)%dx(ix1,ix2,ix3,3)))
5294 if (hcell>zero) hlocal=min(hlocal,hcell)
5295 enddo
5296 enddo
5297 enddo
5298 enddo
5299 if (npe>1) then
5300 call mpi_allreduce(hlocal,hmin,1,mpi_double_precision,mpi_min, &
5301 icomm,ierrmpi)
5302 else
5303 hmin=hlocal
5304 endif
5305 if (hmin<huge(one)) status=trace_status_active
5306 }
5308
5309 logical function trace_cartesian_like_geometry() result(is_cart_like)
5310 is_cart_like=.false.
5311 if (ndim/=3) return
5312 {^ifthreed
5313 select case (geo_coordinate)
5314 case (geo_cartesian,geo_cartesian_stretched)
5315 is_cart_like=.true.
5316 end select
5317 }
5318 end function trace_cartesian_like_geometry
5319
5320 logical function trace_rk45_position_integrator() result(is_rk45)
5321 is_rk45=.false.
5322 if (ndim/=3) return
5323 {^ifthreed
5324 select case (trace_integrator_mode)
5325 case (trace_integrator_rk45_cartesian)
5326 is_rk45=trace_cartesian_like_geometry()
5327 case (trace_integrator_rk45_spherical)
5328 is_rk45=geo_coordinate==geo_spherical
5329 end select
5330 }
5331 end function trace_rk45_position_integrator
5332
5333 subroutine trace_interp_weights_block(x,igrid,ixI^L,ixbl^D,xd^D,dxc^D, &
5334 status)
5335 double precision, intent(in) :: x(ndim)
5336 integer, intent(in) :: igrid,ixi^l
5337 integer, intent(out) :: ixbl^d,status
5338 double precision, intent(out) :: xd^d,dxc^d
5339
5341 ^d&ixbl^d=iximin^d;
5342 ^d&xd^d=zero;
5343 ^d&dxc^d=zero;
5344 if (igrid<0) return
5345
5346 {^ifthreed
5347 call trace_interp_index_3d(x(1),igrid,1,ixi^l,ixbl1,xd1,dxc1,status)
5348 if (status/=trace_status_active) return
5349 call trace_interp_index_3d(x(2),igrid,2,ixi^l,ixbl2,xd2,dxc2,status)
5350 if (status/=trace_status_active) return
5351 call trace_interp_index_3d(x(3),igrid,3,ixi^l,ixbl3,xd3,dxc3,status)
5352 }
5353 end subroutine trace_interp_weights_block
5354
5355 subroutine trace_interp_weights_block_near(x,igrid,ixI^L,ixstart,ixbl^D, &
5356 xd^D,dxc^D,status)
5357 double precision, intent(in) :: x(ndim)
5358 integer, intent(in) :: igrid,ixi^l
5359 integer, intent(in) :: ixstart(3)
5360 integer, intent(out) :: ixbl^d,status
5361 double precision, intent(out) :: xd^d,dxc^d
5362
5364 ^d&ixbl^d=iximin^d;
5365 ^d&xd^d=zero;
5366 ^d&dxc^d=zero;
5367 if (igrid<0) return
5368
5369 {^ifthreed
5370 call trace_interp_index_3d_near(x(1),igrid,1,ixi^l,ixstart(1), &
5371 ixbl1,xd1,dxc1,status)
5372 if (status/=trace_status_active) return
5373 call trace_interp_index_3d_near(x(2),igrid,2,ixi^l,ixstart(2), &
5374 ixbl2,xd2,dxc2,status)
5375 if (status/=trace_status_active) return
5376 call trace_interp_index_3d_near(x(3),igrid,3,ixi^l,ixstart(3), &
5377 ixbl3,xd3,dxc3,status)
5378 }
5379 end subroutine trace_interp_weights_block_near
5380
5381 subroutine trace_interp_index_3d(xval,igrid,idim,ixI^L,ixlo,frac,dxc, &
5382 status)
5383 double precision, intent(in) :: xval
5384 integer, intent(in) :: igrid,idim,ixi^l
5385 integer, intent(out) :: ixlo,status
5386 double precision, intent(out) :: frac,dxc
5387
5388 double precision :: xlo,xhi,tol
5389 integer :: i
5390
5391 ixlo=iximin1
5392 frac=zero
5393 dxc=zero
5395 if (igrid<0) return
5396
5397 {^ifthreed
5398 select case(idim)
5399 case(1)
5400 do i=iximin1,iximax1-1
5401 xlo=ps(igrid)%x(i,iximin2,iximin3,1)
5402 xhi=ps(igrid)%x(i+1,iximin2,iximin3,1)
5403 dxc=xhi-xlo
5404 tol=100.d0*epsilon(one)*max(one,max(abs(xlo),abs(xhi)))
5405 if (abs(dxc)<=smalldouble) cycle
5406 if (xval>=min(xlo,xhi)-tol .and. &
5407 xval<=max(xlo,xhi)+tol) then
5408 ixlo=i
5409 frac=max(zero,min(one,(xval-xlo)/dxc))
5410 status=trace_status_active
5411 return
5412 endif
5413 enddo
5414 case(2)
5415 do i=iximin2,iximax2-1
5416 xlo=ps(igrid)%x(iximin1,i,iximin3,2)
5417 xhi=ps(igrid)%x(iximin1,i+1,iximin3,2)
5418 dxc=xhi-xlo
5419 tol=100.d0*epsilon(one)*max(one,max(abs(xlo),abs(xhi)))
5420 if (abs(dxc)<=smalldouble) cycle
5421 if (xval>=min(xlo,xhi)-tol .and. &
5422 xval<=max(xlo,xhi)+tol) then
5423 ixlo=i
5424 frac=max(zero,min(one,(xval-xlo)/dxc))
5425 status=trace_status_active
5426 return
5427 endif
5428 enddo
5429 case(3)
5430 do i=iximin3,iximax3-1
5431 xlo=ps(igrid)%x(iximin1,iximin2,i,3)
5432 xhi=ps(igrid)%x(iximin1,iximin2,i+1,3)
5433 dxc=xhi-xlo
5434 tol=100.d0*epsilon(one)*max(one,max(abs(xlo),abs(xhi)))
5435 if (abs(dxc)<=smalldouble) cycle
5436 if (xval>=min(xlo,xhi)-tol .and. &
5437 xval<=max(xlo,xhi)+tol) then
5438 ixlo=i
5439 frac=max(zero,min(one,(xval-xlo)/dxc))
5440 status=trace_status_active
5441 return
5442 endif
5443 enddo
5444 end select
5445 }
5446 end subroutine trace_interp_index_3d
5447
5448 subroutine trace_interp_index_3d_near(xval,igrid,idim,ixI^L,ixstart, &
5449 ixlo,frac,dxc,status)
5450 double precision, intent(in) :: xval
5451 integer, intent(in) :: igrid,idim,ixi^l,ixstart
5452 integer, intent(out) :: ixlo,status
5453 double precision, intent(out) :: frac,dxc
5454
5455 double precision :: xlo,xhi,tol
5456 integer :: i,ilo,ihi,idir
5457
5458 ixlo=iximin1
5459 frac=zero
5460 dxc=zero
5462 if (igrid<0) return
5463
5464 {^ifthreed
5465 select case(idim)
5466 case(1)
5467 ilo=iximin1
5468 ihi=iximax1-1
5469 case(2)
5470 ilo=iximin2
5471 ihi=iximax2-1
5472 case(3)
5473 ilo=iximin3
5474 ihi=iximax3-1
5475 case default
5476 return
5477 end select
5478
5479 i=max(ilo,min(ixstart,ihi))
5480 do
5481 select case(idim)
5482 case(1)
5483 xlo=ps(igrid)%x(i,iximin2,iximin3,1)
5484 xhi=ps(igrid)%x(i+1,iximin2,iximin3,1)
5485 case(2)
5486 xlo=ps(igrid)%x(iximin1,i,iximin3,2)
5487 xhi=ps(igrid)%x(iximin1,i+1,iximin3,2)
5488 case(3)
5489 xlo=ps(igrid)%x(iximin1,iximin2,i,3)
5490 xhi=ps(igrid)%x(iximin1,iximin2,i+1,3)
5491 end select
5492 dxc=xhi-xlo
5493 tol=100.d0*epsilon(one)*max(one,max(abs(xlo),abs(xhi)))
5494 if (abs(dxc)>smalldouble .and. &
5495 xval>=min(xlo,xhi)-tol .and. &
5496 xval<=max(xlo,xhi)+tol) then
5497 ixlo=i
5498 frac=max(zero,min(one,(xval-xlo)/dxc))
5499 status=trace_status_active
5500 return
5501 endif
5502
5503 if (abs(dxc)<=smalldouble) then
5504 idir=1
5505 else if (xval<min(xlo,xhi)-tol) then
5506 idir=-1
5507 else
5508 idir=1
5509 endif
5510
5511 i=i+idir
5512 if (i<ilo .or. i>ihi) exit
5513 enddo
5514 }
5515 end subroutine trace_interp_index_3d_near
5516
5517 subroutine trace_spherical_local_cell_widths(x,igrid,dxloc,status)
5518 double precision, intent(in) :: x(ndim)
5519 integer, intent(in) :: igrid
5520 double precision, intent(out) :: dxloc(ndim)
5521 integer, intent(out) :: status
5522
5523 type(trace_sph_interp_ctx) :: ctx
5524
5525 dxloc=one
5527 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) return
5528
5529 {^ifthreed
5530 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
5531 if (status/=trace_status_active) return
5532 dxloc(1:3)=ctx%dxloc(1:3)
5533 }
5534 end subroutine trace_spherical_local_cell_widths
5535
5536 subroutine trace_debug_locate_point(x,igrid,status)
5538
5539 double precision, intent(in) :: x(ndim)
5540 integer, intent(out) :: igrid,status
5541
5542 double precision :: x3d(3)
5543 integer :: indomain,ipe,j
5544
5545 igrid=-1
5546 status=trace_status_active
5547 indomain=0
5548 {if (x(^db)>=xprobmin^db .and. x(^db)<xprobmax^db) indomain=indomain+1\}
5549 if (indomain/=ndim) then
5551 return
5552 endif
5553
5554 x3d=zero
5555 do j=1,ndim
5556 x3d(j)=x(j)
5557 enddo
5558 call find_particle_ipe(x3d,igrid,ipe)
5559 if (igrid<0 .or. ipe/=mype) then
5561 endif
5562 end subroutine trace_debug_locate_point
5563
5564 subroutine trace_locate_point_with_hint(x,igrid_hint,igrid,status)
5565 double precision, intent(in) :: x(ndim)
5566 integer, intent(in) :: igrid_hint
5567 integer, intent(out) :: igrid,status
5568
5569 double precision :: xbmin^d,xbmax^d
5570 integer :: inblock
5571
5572 igrid=-1
5573 status=trace_status_active
5574 if (igrid_hint>=0) then
5575 ^d&xbmin^d=rnode(rpxmin^d_,igrid_hint);
5576 ^d&xbmax^d=rnode(rpxmax^d_,igrid_hint);
5577 inblock=0
5578 {if (x(^db)>xbmin^db .and. x(^db)<xbmax^db) inblock=inblock+1\}
5579 if (inblock==ndim) then
5580 igrid=igrid_hint
5581 return
5582 endif
5583 endif
5584
5585 call trace_debug_locate_point(x,igrid,status)
5586 end subroutine trace_locate_point_with_hint
5587
5588 subroutine trace_tangent_rhs(x,u,v,igrid,threshold,kx,ku,kv,status, &
5589 p,q,kp,kq,sph_ctx)
5590 double precision, intent(in) :: x(ndim),u(ndim),v(ndim),threshold
5591 integer, intent(in) :: igrid
5592 double precision, intent(out) :: kx(ndim),ku(ndim),kv(ndim)
5593 integer, intent(out) :: status
5594 double precision, intent(in), optional :: p(ndim),q(ndim)
5595 double precision, intent(out), optional :: kp(ndim),kq(ndim)
5596 type(trace_sph_interp_ctx), intent(in), optional :: sph_ctx
5597
5598 double precision :: bhat(3),grad_bhat(3,3)
5599 double precision :: u3(3),v3(3),p3(3),q3(3)
5600 double precision :: ku3(3),kv3(3),kp3(3),kq3(3)
5601 logical :: use_ctx
5602
5603 kx=zero
5604 ku=zero
5605 kv=zero
5606 if (present(kp)) kp=zero
5607 if (present(kq)) kq=zero
5609 if (ndim/=3) return
5610 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_rhs()
5611
5612 {^ifthreed
5613 if (geo_coordinate==geo_spherical) then
5614 use_ctx=.false.
5615 if (present(sph_ctx)) then
5616 use_ctx=sph_ctx%valid .and. sph_ctx%igrid==igrid .and. &
5617 maxval(abs(sph_ctx%x(1:3)-x(1:3))) <= &
5618 100.d0*epsilon(one)*max(one,maxval(abs(x(1:3))))
5619 endif
5620 if (use_ctx) then
5621 call trace_spherical_sample_bhat_gradbhat_covariant_ctx(sph_ctx, &
5622 threshold,bhat,grad_bhat,status)
5623 else
5624 call trace_spherical_sample_bhat_gradbhat_covariant(x,igrid, &
5625 threshold,bhat,grad_bhat,status)
5626 endif
5627 if (status==trace_status_bad_grad_stencil) then
5628 if (trace_spherical_profile_enabled) then
5629 call trace_spherical_profile_add_count(trace_profile_grad_fallbacks, &
5630 1_8)
5631 endif
5632 call trace_spherical_sample_bhat_gradbhat_cartfd(x,igrid,threshold, &
5633 bhat,grad_bhat,status)
5634 endif
5635 if (status/=trace_status_active) return
5636 call trace_spherical_bhat_cart_to_rhs(x,bhat,kx,status)
5637 if (status/=trace_status_active) return
5638 else
5639 call sample_bhat_gradbhat_at_point(x,igrid,threshold,bhat,grad_bhat,status)
5640 if (status/=trace_status_active) return
5641 kx=bhat(1:ndim)
5642 endif
5643 if (status/=trace_status_active) return
5644
5645 u3=zero
5646 v3=zero
5647 u3(1:ndim)=u
5648 v3(1:ndim)=v
5649 ku3=matmul(grad_bhat,u3)
5650 kv3=matmul(grad_bhat,v3)
5651 ku=ku3(1:ndim)
5652 kv=kv3(1:ndim)
5653 if (present(p) .and. present(q) .and. present(kp) .and. &
5654 present(kq)) then
5655 p3=zero
5656 q3=zero
5657 p3(1:ndim)=p
5658 q3(1:ndim)=q
5659 kp3=matmul(grad_bhat,p3)
5660 kq3=matmul(grad_bhat,q3)
5661 kp=kp3(1:ndim)
5662 kq=kq3(1:ndim)
5663 endif
5664 }
5665 end subroutine trace_tangent_rhs
5666
5667 subroutine trace_advance_tangent_state_rk2(x,u,v,igrid,h,threshold,status)
5668 double precision, intent(inout) :: x(ndim),u(ndim),v(ndim)
5669 integer, intent(inout) :: igrid
5670 double precision, intent(in) :: h,threshold
5671 integer, intent(out) :: status
5672
5673 double precision :: xnew(ndim),unew(ndim),vnew(ndim)
5674 integer :: igrid_mid,igrid_new
5675
5676 call trace_tangent_rk2_trial(x,u,v,igrid,h,threshold,xnew,unew,vnew, &
5677 status)
5678 if (status/=trace_status_active) return
5679
5680 call trace_debug_locate_point(xnew,igrid_new,status)
5681 if (status/=trace_status_active) return
5682
5683 x=xnew
5684 u=unew
5685 v=vnew
5686 igrid=igrid_new
5687 end subroutine trace_advance_tangent_state_rk2
5688
5689 subroutine trace_tangent_rk2_trial(x,u,v,igrid,h,threshold,xnew,unew, &
5690 vnew,status)
5691 double precision, intent(in) :: x(ndim),u(ndim),v(ndim),h,threshold
5692 integer, intent(in) :: igrid
5693 double precision, intent(out) :: xnew(ndim),unew(ndim),vnew(ndim)
5694 integer, intent(out) :: status
5695
5696 double precision :: kx1(ndim),ku1(ndim),kv1(ndim)
5697
5698 call trace_tangent_rhs(x,u,v,igrid,threshold,kx1,ku1,kv1,status)
5699 if (status/=trace_status_active) return
5700
5701 call trace_tangent_rk2_trial_from_rhs(x,u,v,igrid,h,threshold, &
5702 kx1,ku1,kv1,xnew,unew,vnew,status)
5703 end subroutine trace_tangent_rk2_trial
5704
5705 subroutine trace_tangent_rk2_trial_from_rhs(x,u,v,igrid,h,threshold, &
5706 kx1,ku1,kv1,xnew,unew,vnew,status,p,q,kp1,kq1,pnew,qnew, &
5707 sph_cache,clamp_stage)
5708 double precision, intent(in) :: x(ndim),u(ndim),v(ndim),h,threshold
5709 double precision, intent(in) :: kx1(ndim),ku1(ndim),kv1(ndim)
5710 integer, intent(in) :: igrid
5711 double precision, intent(out) :: xnew(ndim),unew(ndim),vnew(ndim)
5712 integer, intent(out) :: status
5713 double precision, intent(in), optional :: p(ndim),q(ndim)
5714 double precision, intent(in), optional :: kp1(ndim),kq1(ndim)
5715 double precision, intent(out), optional :: pnew(ndim),qnew(ndim)
5716 type(trace_sph_interp_ctx), intent(inout), optional :: sph_cache
5717 logical, intent(in), optional :: clamp_stage
5718
5719 double precision :: kx2(ndim),ku2(ndim),kv2(ndim),kp2(ndim),kq2(ndim)
5720 double precision :: xmid(ndim),xmid_sample(ndim)
5721 double precision :: umid(ndim),vmid(ndim),pmid(ndim),qmid(ndim)
5722 type(trace_sph_interp_ctx) :: ctx_mid
5723 integer :: igrid_mid
5724 integer :: ctx_status
5725 logical :: use_clamped_stage
5726
5727 xmid=x+half*h*kx1
5728 umid=u+half*h*ku1
5729 vmid=v+half*h*kv1
5730 use_clamped_stage=.false.
5731 if (present(clamp_stage)) use_clamped_stage=clamp_stage
5732 xmid_sample=xmid
5733 if (use_clamped_stage) then
5734 ^d&xmid_sample(^d)=max(xprobmin^d,min(xmid_sample(^d), &
5735 xprobmax^d-100.d0*epsilon(one)*max(one,abs(xprobmax^d))));
5736 endif
5737 call trace_locate_point_with_hint(xmid_sample,igrid,igrid_mid,status)
5738 if (status/=trace_status_active) return
5740 if (geo_coordinate==geo_spherical) then
5741 if (present(sph_cache)) then
5742 call trace_spherical_interp_ctx_build_cached(xmid_sample,igrid_mid, &
5743 sph_cache,ctx_mid,ctx_status)
5744 else
5745 call trace_spherical_interp_ctx_build(xmid_sample,igrid_mid,ctx_mid, &
5746 ctx_status)
5747 endif
5748 endif
5749
5750 if (present(p) .and. present(q) .and. present(kp1) .and. &
5751 present(kq1) .and. present(pnew) .and. present(qnew)) then
5752 pmid=p+half*h*kp1
5753 qmid=q+half*h*kq1
5754 if (ctx_status==trace_status_active) then
5755 call trace_tangent_rhs(xmid_sample,umid,vmid,igrid_mid,threshold,kx2,ku2, &
5756 kv2,status,pmid,qmid,kp2,kq2,sph_ctx=ctx_mid)
5757 else
5758 call trace_tangent_rhs(xmid_sample,umid,vmid,igrid_mid,threshold,kx2,ku2, &
5759 kv2,status,pmid,qmid,kp2,kq2)
5760 endif
5761 else
5762 if (ctx_status==trace_status_active) then
5763 call trace_tangent_rhs(xmid_sample,umid,vmid,igrid_mid,threshold,kx2,ku2, &
5764 kv2,status,sph_ctx=ctx_mid)
5765 else
5766 call trace_tangent_rhs(xmid_sample,umid,vmid,igrid_mid,threshold,kx2,ku2, &
5767 kv2,status)
5768 endif
5769 endif
5770 if (status/=trace_status_active) return
5771
5772 xnew=x+h*kx2
5773 unew=u+h*ku2
5774 vnew=v+h*kv2
5775 if (present(pnew) .and. present(qnew)) then
5776 pnew=p+h*kp2
5777 qnew=q+h*kq2
5778 endif
5779 end subroutine trace_tangent_rk2_trial_from_rhs
5780
5781 subroutine trace_project_to_perp_bhat(vec,bhat,vec_perp)
5782 double precision, intent(in) :: vec(ndim),bhat(ndim)
5783 double precision, intent(out) :: vec_perp(ndim)
5784
5785 vec_perp=vec-sum(vec*bhat)*bhat
5786 end subroutine trace_project_to_perp_bhat
5787
5788 subroutine trace_endpoint_b_bhat(x,face_id,igrid,threshold,B,bhat,status)
5789 double precision, intent(in) :: x(ndim),threshold
5790 integer, intent(in) :: face_id,igrid
5791 double precision, intent(out) :: b(ndim),bhat(ndim)
5792 integer, intent(out) :: status
5793
5794 double precision :: b3(3),bhat3(3)
5795 integer :: sample_igrid
5796
5797 b=zero
5798 bhat=zero
5799 if (geo_coordinate==geo_spherical) then
5800 call trace_spherical_endpoint_b_bhat(x,face_id,igrid,threshold,b,bhat, &
5801 status)
5802 return
5803 endif
5804 call trace_locate_face_limit_grid(x,face_id,igrid,sample_igrid,status)
5805 if (status/=trace_status_active) return
5806 call trace_sample_b_on_domain_face_limit(x,face_id,sample_igrid,threshold, &
5807 b3,bhat3,status)
5808 if (status/=trace_status_active) return
5809 b=b3(1:ndim)
5810 bhat=bhat3(1:ndim)
5812 end subroutine trace_endpoint_b_bhat
5813
5814 subroutine trace_locate_face_limit_grid(x,face_id,igrid_in,igrid_out, &
5815 status)
5816 double precision, intent(in) :: x(ndim)
5817 integer, intent(in) :: face_id,igrid_in
5818 integer, intent(out) :: igrid_out,status
5819
5820 double precision :: xprobe(ndim),xface
5821 integer :: ixo^l
5822 integer :: normal_dim,side
5823
5824 igrid_out=-1
5826 if (ndim/=3 .or. .not.trace_cartesian_like_geometry() .or. &
5827 igrid_in<0) then
5829 return
5830 endif
5831 if (.not.trace_face_is_boundary(face_id)) return
5832
5833 {^ifthreed
5834 select case(face_id)
5835 case(trace_face_xmin)
5836 normal_dim=1
5837 side=-1
5838 xface=xprobmin1
5839 case(trace_face_xmax)
5840 normal_dim=1
5841 side=1
5842 xface=xprobmax1
5843 case(trace_face_ymin)
5844 normal_dim=2
5845 side=-1
5846 xface=xprobmin2
5847 case(trace_face_ymax)
5848 normal_dim=2
5849 side=1
5850 xface=xprobmax2
5851 case(trace_face_zmin)
5852 normal_dim=3
5853 side=-1
5854 xface=xprobmin3
5855 case(trace_face_zmax)
5856 normal_dim=3
5857 side=1
5858 xface=xprobmax3
5859 case default
5860 return
5861 end select
5862
5863 ixo^l=ixm^ll;
5864 xprobe=x
5865 select case(normal_dim)
5866 case(1)
5867 if (side<0) then
5868 xprobe(normal_dim)=ps(igrid_in)%x(ixomin1,ixomin2,ixomin3,1)
5869 else
5870 xprobe(normal_dim)=ps(igrid_in)%x(ixomax1,ixomin2,ixomin3,1)
5871 endif
5872 case(2)
5873 if (side<0) then
5874 xprobe(normal_dim)=ps(igrid_in)%x(ixomin1,ixomin2,ixomin3,2)
5875 else
5876 xprobe(normal_dim)=ps(igrid_in)%x(ixomin1,ixomax2,ixomin3,2)
5877 endif
5878 case(3)
5879 if (side<0) then
5880 xprobe(normal_dim)=ps(igrid_in)%x(ixomin1,ixomin2,ixomin3,3)
5881 else
5882 xprobe(normal_dim)=ps(igrid_in)%x(ixomin1,ixomin2,ixomax3,3)
5883 endif
5884 end select
5885 call trace_debug_locate_point(xprobe,igrid_out,status)
5886 }
5887 end subroutine trace_locate_face_limit_grid
5888
5889 subroutine trace_sample_b_on_domain_face_limit(xhit,face_id,igrid, &
5890 threshold,B,bhat,status)
5891 ! Sample total B at a rectangular domain face from the inside-side limit.
5892 ! The normal direction is linearly extrapolated from the two nearest
5893 ! interior cell centers; tangential directions use bilinear interpolation.
5894 double precision, intent(in) :: xhit(ndim),threshold
5895 integer, intent(in) :: face_id,igrid
5896 double precision, intent(out) :: b(3),bhat(3)
5897 integer, intent(out) :: status
5898
5899 double precision :: bnear(3),binner(3)
5900 double precision :: xface,xnear,xinner
5901 double precision :: xnear_pt(ndim),xinner_pt(ndim)
5902 double precision :: bnorm
5903 integer :: ixo^l
5904 integer :: normal_dim,side
5905 integer :: igrid_near,igrid_inner
5906
5907 b=zero
5908 bhat=zero
5910 if (ndim/=3 .or. .not.trace_cartesian_like_geometry() .or. igrid<0) then
5912 return
5913 endif
5914 if (.not.trace_face_is_boundary(face_id)) return
5915
5916 {^ifthreed
5917 select case(face_id)
5918 case(trace_face_xmin)
5919 normal_dim=1
5920 side=-1
5921 xface=xprobmin1
5922 case(trace_face_xmax)
5923 normal_dim=1
5924 side=1
5925 xface=xprobmax1
5926 case(trace_face_ymin)
5927 normal_dim=2
5928 side=-1
5929 xface=xprobmin2
5930 case(trace_face_ymax)
5931 normal_dim=2
5932 side=1
5933 xface=xprobmax2
5934 case(trace_face_zmin)
5935 normal_dim=3
5936 side=-1
5937 xface=xprobmin3
5938 case(trace_face_zmax)
5939 normal_dim=3
5940 side=1
5941 xface=xprobmax3
5942 case default
5943 return
5944 end select
5945
5946 ixo^l=ixm^ll;
5947 select case(normal_dim)
5948 case(1)
5949 if (side<0) then
5950 xnear=ps(igrid)%x(ixomin1,ixomin2,ixomin3,1)
5951 xinner=ps(igrid)%x(ixomin1+1,ixomin2,ixomin3,1)
5952 else
5953 xnear=ps(igrid)%x(ixomax1,ixomin2,ixomin3,1)
5954 xinner=ps(igrid)%x(ixomax1-1,ixomin2,ixomin3,1)
5955 endif
5956 case(2)
5957 if (side<0) then
5958 xnear=ps(igrid)%x(ixomin1,ixomin2,ixomin3,2)
5959 xinner=ps(igrid)%x(ixomin1,ixomin2+1,ixomin3,2)
5960 else
5961 xnear=ps(igrid)%x(ixomin1,ixomax2,ixomin3,2)
5962 xinner=ps(igrid)%x(ixomin1,ixomax2-1,ixomin3,2)
5963 endif
5964 case(3)
5965 if (side<0) then
5966 xnear=ps(igrid)%x(ixomin1,ixomin2,ixomin3,3)
5967 xinner=ps(igrid)%x(ixomin1,ixomin2,ixomin3+1,3)
5968 else
5969 xnear=ps(igrid)%x(ixomin1,ixomin2,ixomax3,3)
5970 xinner=ps(igrid)%x(ixomin1,ixomin2,ixomax3-1,3)
5971 endif
5972 end select
5973 xnear_pt=xhit
5974 xinner_pt=xhit
5975 xnear_pt(normal_dim)=xnear
5976 xinner_pt(normal_dim)=xinner
5977
5978 call trace_debug_locate_point(xnear_pt,igrid_near,status)
5979 if (status/=trace_status_active) return
5980 call sample_b_at_point(xnear_pt,igrid_near,threshold,bnear,status)
5981 if (status/=trace_status_active) return
5982 call trace_debug_locate_point(xinner_pt,igrid_inner,status)
5983 if (status/=trace_status_active) return
5984 call sample_b_at_point(xinner_pt,igrid_inner,threshold,binner,status)
5985 if (status/=trace_status_active) return
5986
5987 b=bnear+(bnear-binner)/(xnear-xinner)*(xface-xnear)
5988 bnorm=dsqrt(sum(b**2))
5989 if (bnorm<=zero .or. bnorm<threshold) then
5991 return
5992 endif
5993 bhat=b/bnorm
5994 status=trace_status_active
5995 }
5996 end subroutine trace_sample_b_on_domain_face_limit
5997
5998 subroutine trace_face_normal(face_id,normal,ok)
5999 integer, intent(in) :: face_id
6000 double precision, intent(out) :: normal(ndim)
6001 logical, intent(out) :: ok
6002
6003 normal=zero
6004 ok=.true.
6005 select case(face_id)
6006 case(trace_face_xmin)
6007 normal(1)=-one
6008 case(trace_face_xmax)
6009 normal(1)=one
6010 {^ifoned
6011 case default
6012 ok=.false.
6013 }
6014 {^iftwod
6015 case(trace_face_ymin)
6016 normal(2)=-one
6017 case(trace_face_ymax)
6018 normal(2)=one
6019 case default
6020 ok=.false.
6021 }
6022 {^ifthreed
6023 case(trace_face_ymin)
6024 normal(2)=-one
6025 case(trace_face_ymax)
6026 normal(2)=one
6027 case(trace_face_zmin)
6028 normal(3)=-one
6029 case(trace_face_zmax)
6030 normal(3)=one
6031 case default
6032 ok=.false.
6033 }
6034 end select
6035 end subroutine trace_face_normal
6036
6037 subroutine trace_project_to_boundary_face(vec,bhat,face_id,vec_face)
6038 double precision, intent(in) :: vec(ndim),bhat(ndim)
6039 integer, intent(in) :: face_id
6040 double precision, intent(out) :: vec_face(ndim)
6041
6042 double precision :: normal(ndim),bn
6043 logical :: ok
6044
6045 vec_face=zero
6046 call trace_face_normal(face_id,normal,ok)
6047 if (.not.ok) return
6048 bn=sum(bhat*normal)
6049 if (abs(bn)<=smalldouble) return
6050 vec_face=vec-sum(vec*normal)/bn*bhat
6051 end subroutine trace_project_to_boundary_face
6052
6053 subroutine trace_make_perp_basis(bhat,u0,v0,status)
6054 double precision, intent(in) :: bhat(3)
6055 double precision, intent(out) :: u0(ndim),v0(ndim)
6056 integer, intent(out) :: status
6057
6058 double precision :: ref(3),u3(3),v3(3),b3(3),unorm,bnorm
6059 integer :: iref
6060
6061 u0=zero
6062 v0=zero
6063 status=trace_status_active
6064 if (ndim/=3) then
6066 return
6067 endif
6068
6069 b3=bhat
6070 bnorm=dsqrt(sum(b3**2))
6071 if (bnorm<=zero) then
6073 return
6074 endif
6075 b3=b3/bnorm
6076
6077 iref=1
6078 if (abs(b3(2))<abs(b3(iref))) iref=2
6079 if (abs(b3(3))<abs(b3(iref))) iref=3
6080 ref=zero
6081 ref(iref)=one
6082
6083 u3=ref-sum(ref*b3)*b3
6084 unorm=dsqrt(sum(u3**2))
6085 if (unorm<=smalldouble) then
6087 return
6088 endif
6089 u3=u3/unorm
6090
6091 v3(1)=b3(2)*u3(3)-b3(3)*u3(2)
6092 v3(2)=b3(3)*u3(1)-b3(1)*u3(3)
6093 v3(3)=b3(1)*u3(2)-b3(2)*u3(1)
6094 v3=v3/dsqrt(sum(v3**2))
6095
6096 u0=u3(1:ndim)
6097 v0=v3(1:ndim)
6098 end subroutine trace_make_perp_basis
6099
6100 double precision function trace_debug_nan()
6101 trace_debug_nan=ieee_value(0.d0,ieee_quiet_nan)
6102 end function trace_debug_nan
6103
6104 subroutine trace_summary_seed(seed,dL,max_steps,result,b_min)
6105 double precision, intent(in) :: seed(ndim),dl
6106 integer, intent(in) :: max_steps
6107 type(trace_length_result), intent(out) :: result
6108 double precision, intent(in), optional :: b_min
6109
6110 double precision :: field_min
6111 integer :: common_status,igrid
6112
6113 call trace_summary_validate(dl,max_steps,common_status)
6114 if (common_status/=trace_status_active) then
6115 call trace_summary_init_result(seed,result,common_status)
6116 return
6117 endif
6118
6119 field_min=smalldouble
6120 if (present(b_min)) field_min=max(b_min,zero)
6121
6122 call trace_summary_locate_seed(seed,result,igrid)
6123 if (result%forward_status/=trace_status_active) return
6124 call trace_summary_trace_seed(igrid,dl,max_steps,field_min,result)
6125 end subroutine trace_summary_seed
6126
6127 subroutine trace_summary_twist_seed(seed,dL,max_steps,result,b_min)
6128 double precision, intent(in) :: seed(ndim),dl
6129 integer, intent(in) :: max_steps
6130 type(trace_twist_result), intent(out) :: result
6131 double precision, intent(in), optional :: b_min
6132
6133 double precision :: field_min
6134 integer :: common_status,igrid
6135
6136 call trace_summary_validate(dl,max_steps,common_status)
6137 if (common_status==trace_status_active .and. ndim/=3) then
6139 endif
6140 if (common_status/=trace_status_active) then
6141 call trace_summary_init_twist_result(seed,result,common_status)
6142 return
6143 endif
6144
6145 field_min=smalldouble
6146 if (present(b_min)) field_min=max(b_min,zero)
6147
6148 call trace_summary_locate_seed(seed,result%line,igrid)
6149 result%forward_twist=zero
6150 result%backward_twist=zero
6151 result%total_twist=zero
6152 result%valid_twist=.false.
6153 result%status_twist=result%line%forward_status
6154 if (result%line%forward_status/=trace_status_active) return
6155 call trace_summary_trace_twist_seed(igrid,dl,max_steps,field_min,result)
6156 end subroutine trace_summary_twist_seed
6157
6158 subroutine trace_summary_mapping_seed(seed,dL,max_steps,result,b_min, &
6159 source_normal)
6160 double precision, intent(in) :: seed(ndim),dl
6161 integer, intent(in) :: max_steps
6162 type(trace_mapping_result), intent(out) :: result
6163 double precision, intent(in), optional :: b_min
6164 double precision, intent(in), optional :: source_normal(3)
6165
6166 type(trace_summary_state) :: forward_state,backward_state
6167 type(trace_length_result) :: located
6168 double precision :: field_min,normal(3)
6169 integer :: common_status,igrid
6170 logical :: have_normal
6171
6172 call trace_summary_validate(dl,max_steps,common_status)
6173 if (common_status==trace_status_active .and. ndim/=3) then
6175 endif
6176 if (common_status/=trace_status_active) then
6177 call trace_summary_init_mapping_result(seed,result,common_status)
6178 return
6179 endif
6180
6181 field_min=smalldouble
6182 if (present(b_min)) field_min=max(b_min,zero)
6183 normal=zero
6184 have_normal=present(source_normal)
6185 if (have_normal) normal=source_normal
6186
6187 call trace_summary_locate_seed(seed,located,igrid)
6188 call trace_summary_init_mapping_result(seed,result,located%forward_status)
6189 if (located%forward_status/=trace_status_active) return
6190
6191 call trace_summary_init_state(seed,igrid,.true.,1,.false.,forward_state)
6192 call trace_summary_trace_state_to_end(forward_state,dl,max_steps,field_min)
6193 call trace_summary_init_state(seed,igrid,.false.,1,.false.,backward_state)
6194 call trace_summary_trace_state_to_end(backward_state,dl,max_steps,field_min)
6195 call trace_summary_fill_mapping(seed,igrid,forward_state,backward_state, &
6196 field_min,have_normal,normal,result)
6197 end subroutine trace_summary_mapping_seed
6198
6199 subroutine trace_summary_multi(seeds,nseed,dL,max_steps,results,field_min)
6200 integer, intent(in) :: nseed,max_steps
6201 double precision, intent(in) :: seeds(nseed,ndim),dl,field_min
6202 type(trace_length_result), intent(out) :: results(nseed)
6203
6204 type(trace_summary_state), allocatable :: states(:)
6205 double precision :: seed_local(ndim)
6206 integer :: common_status,iseed,igrid,iforward,ibackward
6207
6208 if (nseed<=0) return
6209
6210 call trace_summary_validate(dl,max_steps,common_status)
6211 if (common_status/=trace_status_active) then
6212 do iseed=1,nseed
6213 seed_local=seeds(iseed,:)
6214 call trace_summary_init_result(seed_local,results(iseed),common_status)
6215 enddo
6216 return
6217 endif
6218
6219 allocate(states(2*nseed))
6220 do iseed=1,nseed
6221 seed_local=seeds(iseed,:)
6222 call trace_summary_locate_seed(seed_local,results(iseed),igrid)
6223 iforward=2*iseed-1
6224 ibackward=2*iseed
6225 call trace_summary_init_state(seed_local,igrid,.true.,iseed,.false., &
6226 states(iforward))
6227 call trace_summary_init_state(seed_local,igrid,.false.,iseed,.false., &
6228 states(ibackward))
6229 if (results(iseed)%forward_status/=trace_status_active) then
6230 states(iforward)%status=results(iseed)%forward_status
6231 states(iforward)%active=.false.
6232 states(ibackward)%status=results(iseed)%backward_status
6233 states(ibackward)%active=.false.
6234 endif
6235 enddo
6236
6237 call trace_summary_trace_states_grouped(states,2*nseed,dl,max_steps, &
6238 field_min)
6239
6240 do iseed=1,nseed
6241 iforward=2*iseed-1
6242 ibackward=2*iseed
6243 results(iseed)%forward_footpoint=states(iforward)%footpoint
6244 results(iseed)%forward_length=states(iforward)%length
6245 results(iseed)%forward_nstep=states(iforward)%nstep
6246 results(iseed)%forward_status=states(iforward)%status
6247 results(iseed)%backward_footpoint=states(ibackward)%footpoint
6248 results(iseed)%backward_length=states(ibackward)%length
6249 results(iseed)%backward_nstep=states(ibackward)%nstep
6250 results(iseed)%backward_status=states(ibackward)%status
6251 results(iseed)%total_length=results(iseed)%forward_length &
6252 +results(iseed)%backward_length
6253 enddo
6254 deallocate(states)
6255 end subroutine trace_summary_multi
6256
6257 subroutine trace_summary_twist_multi(seeds,nseed,dL,max_steps,results, &
6258 field_min)
6259 integer, intent(in) :: nseed,max_steps
6260 double precision, intent(in) :: seeds(nseed,ndim),dl,field_min
6261 type(trace_twist_result), intent(out) :: results(nseed)
6262
6263 type(trace_summary_state), allocatable :: states(:)
6264 double precision :: seed_local(ndim)
6265 integer :: common_status,iseed,igrid,iforward,ibackward
6266
6267 if (nseed<=0) return
6268
6269 call trace_summary_validate(dl,max_steps,common_status)
6270 if (common_status==trace_status_active .and. ndim/=3) then
6272 endif
6273 if (common_status/=trace_status_active) then
6274 do iseed=1,nseed
6275 seed_local=seeds(iseed,:)
6276 call trace_summary_init_twist_result(seed_local,results(iseed), &
6277 common_status)
6278 enddo
6279 return
6280 endif
6281
6282 allocate(states(2*nseed))
6283 do iseed=1,nseed
6284 seed_local=seeds(iseed,:)
6285 call trace_summary_locate_seed(seed_local,results(iseed)%line,igrid)
6286 results(iseed)%forward_twist=zero
6287 results(iseed)%backward_twist=zero
6288 results(iseed)%total_twist=zero
6289 results(iseed)%valid_twist=.false.
6290 results(iseed)%status_twist=results(iseed)%line%forward_status
6291 iforward=2*iseed-1
6292 ibackward=2*iseed
6293 call trace_summary_init_state(seed_local,igrid,.true.,iseed,.true., &
6294 states(iforward))
6295 call trace_summary_init_state(seed_local,igrid,.false.,iseed,.true., &
6296 states(ibackward))
6297 if (results(iseed)%line%forward_status/=trace_status_active) then
6298 states(iforward)%status=results(iseed)%line%forward_status
6299 states(iforward)%active=.false.
6300 states(ibackward)%status=results(iseed)%line%backward_status
6301 states(ibackward)%active=.false.
6302 endif
6303 enddo
6304
6305 call trace_summary_trace_states_grouped(states,2*nseed,dl,max_steps, &
6306 field_min)
6307
6308 do iseed=1,nseed
6309 iforward=2*iseed-1
6310 ibackward=2*iseed
6311 results(iseed)%line%forward_footpoint=states(iforward)%footpoint
6312 results(iseed)%line%forward_length=states(iforward)%length
6313 results(iseed)%line%forward_nstep=states(iforward)%nstep
6314 results(iseed)%line%forward_status=states(iforward)%status
6315 results(iseed)%line%backward_footpoint=states(ibackward)%footpoint
6316 results(iseed)%line%backward_length=states(ibackward)%length
6317 results(iseed)%line%backward_nstep=states(ibackward)%nstep
6318 results(iseed)%line%backward_status=states(ibackward)%status
6319 results(iseed)%line%total_length=results(iseed)%line%forward_length &
6320 +results(iseed)%line%backward_length
6321 results(iseed)%forward_twist=states(iforward)%twist
6322 results(iseed)%backward_twist=states(ibackward)%twist
6323 results(iseed)%total_twist=results(iseed)%forward_twist &
6324 +results(iseed)%backward_twist
6325 results(iseed)%status_twist=trace_summary_twist_status_from_states( &
6326 states(iforward),states(ibackward))
6327 results(iseed)%valid_twist=results(iseed)%status_twist &
6329 enddo
6330 deallocate(states)
6331 end subroutine trace_summary_twist_multi
6332
6333 subroutine trace_summary_mapping_multi(seeds,nseed,dL,max_steps,results, &
6334 field_min,have_source_normal,source_normal)
6335 integer, intent(in) :: nseed,max_steps
6336 double precision, intent(in) :: seeds(nseed,ndim),dl,field_min
6337 logical, intent(in) :: have_source_normal
6338 double precision, intent(in) :: source_normal(3)
6339 type(trace_mapping_result), intent(out) :: results(nseed)
6340
6341 type(trace_summary_state), allocatable :: states(:)
6342 integer, allocatable :: source_igrids(:)
6343 type(trace_length_result) :: located
6344 double precision :: seed_local(ndim)
6345 integer :: common_status,iseed,igrid,iforward,ibackward
6346
6347 if (nseed<=0) return
6348
6349 call trace_summary_validate(dl,max_steps,common_status)
6350 if (common_status==trace_status_active .and. ndim/=3) then
6352 endif
6353 if (common_status/=trace_status_active) then
6354 do iseed=1,nseed
6355 seed_local=seeds(iseed,:)
6356 call trace_summary_init_mapping_result(seed_local,results(iseed), &
6357 common_status)
6358 enddo
6359 return
6360 endif
6361
6362 allocate(states(2*nseed))
6363 allocate(source_igrids(nseed))
6364 source_igrids=-1
6365 do iseed=1,nseed
6366 seed_local=seeds(iseed,:)
6367 call trace_summary_locate_seed(seed_local,located,igrid)
6368 source_igrids(iseed)=igrid
6369 call trace_summary_init_mapping_result(seed_local,results(iseed), &
6370 located%forward_status)
6371 iforward=2*iseed-1
6372 ibackward=2*iseed
6373 call trace_summary_init_state(seed_local,igrid,.true.,iseed,.false., &
6374 states(iforward))
6375 call trace_summary_init_state(seed_local,igrid,.false.,iseed,.false., &
6376 states(ibackward))
6377 if (located%forward_status/=trace_status_active) then
6378 states(iforward)%status=located%forward_status
6379 states(iforward)%active=.false.
6380 states(ibackward)%status=located%backward_status
6381 states(ibackward)%active=.false.
6382 endif
6383 enddo
6384
6385 call trace_summary_trace_states_grouped(states,2*nseed,dl,max_steps, &
6386 field_min)
6387
6388 do iseed=1,nseed
6389 iforward=2*iseed-1
6390 ibackward=2*iseed
6391 if (source_igrids(iseed)>=0) then
6392 call trace_summary_fill_mapping(results(iseed)%seed, &
6393 source_igrids(iseed),states(iforward),states(ibackward), &
6394 field_min,have_source_normal,source_normal,results(iseed))
6395 endif
6396 enddo
6397 deallocate(source_igrids,states)
6398 end subroutine trace_summary_mapping_multi
6399
6400 subroutine trace_summary_topology_multi(seeds,nseed,dL,max_steps,results, &
6401 field_min,need_twist,need_mapping,have_source_normal,source_normal)
6402 integer, intent(in) :: nseed,max_steps
6403 double precision, intent(in) :: seeds(nseed,ndim),dl,field_min
6404 logical, intent(in) :: need_twist,need_mapping,have_source_normal
6405 double precision, intent(in) :: source_normal(3)
6406 type(trace_topology_result), intent(out) :: results(nseed)
6407
6408 type(trace_summary_state), allocatable :: states(:)
6409 type(trace_summary_state), allocatable :: map_states(:)
6410 integer, allocatable :: source_igrids(:)
6411 type(trace_length_result) :: located
6412 type(trace_mapping_result) :: mapping
6413 double precision :: seed_local(ndim)
6414 integer :: common_status,iseed,igrid,iforward,ibackward,idim
6415
6416 if (nseed<=0) return
6417
6418 call trace_summary_validate(dl,max_steps,common_status)
6419 if (common_status==trace_status_active .and. &
6420 (need_twist .or. need_mapping) .and. ndim/=3) then
6422 endif
6423 if (common_status/=trace_status_active) then
6424 do iseed=1,nseed
6425 do idim=1,ndim
6426 seed_local(idim)=seeds(iseed,idim)
6427 enddo
6428 call trace_summary_init_topology_result(seed_local,results(iseed), &
6429 common_status,need_twist,need_mapping)
6430 enddo
6431 return
6432 endif
6433
6434 allocate(states(2*nseed))
6435 ! Mapping summaries need only B/Bn; keep them independent of
6436 ! curl-stencil failures that can stop twist accumulation near boundaries.
6437 if (need_mapping .and. need_twist) allocate(map_states(2*nseed))
6438 allocate(source_igrids(nseed))
6439 source_igrids=-1
6440 do iseed=1,nseed
6441 do idim=1,ndim
6442 seed_local(idim)=seeds(iseed,idim)
6443 enddo
6444 call trace_summary_locate_seed(seed_local,located,igrid)
6445 source_igrids(iseed)=igrid
6446 call trace_summary_init_topology_result(seed_local,results(iseed), &
6447 located%forward_status,need_twist,need_mapping)
6448 iforward=2*iseed-1
6449 ibackward=2*iseed
6450 call trace_summary_init_state(seed_local,igrid,.true.,iseed, &
6451 need_twist,states(iforward))
6452 call trace_summary_init_state(seed_local,igrid,.false.,iseed, &
6453 need_twist,states(ibackward))
6454 if (allocated(map_states)) then
6455 call trace_summary_init_state(seed_local,igrid,.true.,iseed, &
6456 .false.,map_states(iforward))
6457 call trace_summary_init_state(seed_local,igrid,.false.,iseed, &
6458 .false.,map_states(ibackward))
6459 endif
6460 if (located%forward_status/=trace_status_active) then
6461 states(iforward)%status=located%forward_status
6462 states(iforward)%active=.false.
6463 states(ibackward)%status=located%backward_status
6464 states(ibackward)%active=.false.
6465 if (allocated(map_states)) then
6466 map_states(iforward)%status=located%forward_status
6467 map_states(iforward)%active=.false.
6468 map_states(ibackward)%status=located%backward_status
6469 map_states(ibackward)%active=.false.
6470 endif
6471 endif
6472 enddo
6473
6474 call trace_summary_trace_states_grouped(states,2*nseed,dl,max_steps, &
6475 field_min)
6476 if (allocated(map_states)) then
6477 call trace_summary_trace_states_grouped(map_states,2*nseed,dl, &
6478 max_steps,field_min)
6479 endif
6480
6481 do iseed=1,nseed
6482 iforward=2*iseed-1
6483 ibackward=2*iseed
6484 results(iseed)%forward_endpoint=states(iforward)%footpoint
6485 results(iseed)%backward_endpoint=states(ibackward)%footpoint
6486 results(iseed)%length_forward=states(iforward)%length
6487 results(iseed)%length_backward=states(ibackward)%length
6488 results(iseed)%length_total=results(iseed)%length_forward &
6489 +results(iseed)%length_backward
6490 results(iseed)%forward_nstep=states(iforward)%nstep
6491 results(iseed)%backward_nstep=states(ibackward)%nstep
6492 results(iseed)%forward_face=states(iforward)%face
6493 results(iseed)%backward_face=states(ibackward)%face
6494 results(iseed)%forward_status=states(iforward)%status
6495 results(iseed)%backward_status=states(ibackward)%status
6496 if (need_twist) then
6497 results(iseed)%twist_forward=states(iforward)%twist
6498 results(iseed)%twist_backward=states(ibackward)%twist
6499 results(iseed)%twist_total=results(iseed)%twist_forward &
6500 +results(iseed)%twist_backward
6501 results(iseed)%status_twist=trace_summary_twist_status_from_states( &
6502 states(iforward),states(ibackward))
6503 results(iseed)%valid_twist=results(iseed)%status_twist &
6505 endif
6506
6507 if (states(iforward)%status==trace_status_boundary .and. &
6508 states(ibackward)%status==trace_status_boundary) then
6509 results(iseed)%status=trace_status_boundary
6510 results(iseed)%valid=.true.
6511 else
6512 results(iseed)%valid=.false.
6513 if (states(iforward)%status/=trace_status_boundary) then
6514 results(iseed)%status=states(iforward)%status
6515 else
6516 results(iseed)%status=states(ibackward)%status
6517 endif
6518 endif
6519
6520 if (need_mapping .and. source_igrids(iseed)>=0) then
6521 if (allocated(map_states)) then
6522 call trace_summary_fill_mapping(results(iseed)%seed, &
6523 source_igrids(iseed),map_states(iforward), &
6524 map_states(ibackward),field_min,have_source_normal, &
6525 source_normal,mapping)
6526 else
6527 call trace_summary_fill_mapping(results(iseed)%seed, &
6528 source_igrids(iseed),states(iforward),states(ibackward), &
6529 field_min,have_source_normal,source_normal,mapping)
6530 endif
6531 results(iseed)%source_B=mapping%source_B
6532 results(iseed)%forward_B=mapping%forward_B
6533 results(iseed)%backward_B=mapping%backward_B
6534 results(iseed)%source_Bn=mapping%source_Bn
6535 results(iseed)%forward_Bn=mapping%forward_Bn
6536 results(iseed)%backward_Bn=mapping%backward_Bn
6537 results(iseed)%map_forward_endpoint=mapping%forward_footpoint
6538 results(iseed)%map_backward_endpoint=mapping%backward_footpoint
6539 results(iseed)%map_forward_length=mapping%forward_length
6540 results(iseed)%map_backward_length=mapping%backward_length
6541 results(iseed)%map_forward_face=mapping%forward_face
6542 results(iseed)%map_backward_face=mapping%backward_face
6543 results(iseed)%map_forward_status=mapping%forward_status
6544 results(iseed)%map_backward_status=mapping%backward_status
6545 results(iseed)%forward_status=mapping%forward_status
6546 results(iseed)%backward_status=mapping%backward_status
6547 results(iseed)%valid=mapping%valid
6548 if (mapping%valid) then
6549 results(iseed)%status=trace_status_boundary
6550 else if (mapping%forward_status/=trace_status_boundary) then
6551 results(iseed)%status=mapping%forward_status
6552 else
6553 results(iseed)%status=mapping%backward_status
6554 endif
6555 endif
6556 enddo
6557 if (allocated(map_states)) deallocate(map_states)
6558 deallocate(source_igrids,states)
6559 end subroutine trace_summary_topology_multi
6560
6561 subroutine trace_summary_validate(dL,max_steps,status)
6562 double precision, intent(in) :: dl
6563 integer, intent(in) :: max_steps
6564 integer, intent(out) :: status
6565
6566 status=trace_status_active
6567 if (npe/=1) then
6569 else if (dl<=zero .or. max_steps<0) then
6571 else if (.not.trace_is_supported_summary_geometry()) then
6573 endif
6574 end subroutine trace_summary_validate
6575
6576 logical function trace_is_supported_summary_geometry() result(is_supported)
6577 is_supported=.false.
6578 if (ndim/=3) return
6579 {^ifthreed
6580 select case (geo_coordinate)
6581 case (geo_cartesian,geo_cartesian_stretched)
6582 is_supported=.true.
6583 case (geo_spherical)
6584 ! Phase-1 spherical tracing uses native coordinates and assumes a
6585 ! non-periodic phi interval. Periodic wrapping is a later extension.
6586 is_supported=.not.periodb(3)
6587 end select
6588 }
6589 end function trace_is_supported_summary_geometry
6590
6591 subroutine trace_summary_init_result(seed,result,status)
6592 double precision, intent(in) :: seed(ndim)
6593 type(trace_length_result), intent(out) :: result
6594 integer, intent(in) :: status
6595
6596 result%seed=seed
6597 result%forward_footpoint=seed
6598 result%backward_footpoint=seed
6599 result%forward_length=zero
6600 result%backward_length=zero
6601 result%total_length=zero
6602 result%forward_nstep=0
6603 result%backward_nstep=0
6604 result%forward_status=status
6605 result%backward_status=status
6606 end subroutine trace_summary_init_result
6607
6608 subroutine trace_summary_init_twist_result(seed,result,status)
6609 double precision, intent(in) :: seed(ndim)
6610 type(trace_twist_result), intent(out) :: result
6611 integer, intent(in) :: status
6612
6613 call trace_summary_init_result(seed,result%line,status)
6614 result%forward_twist=zero
6615 result%backward_twist=zero
6616 result%total_twist=zero
6617 result%valid_twist=.false.
6618 result%status_twist=status
6619 end subroutine trace_summary_init_twist_result
6620
6621 subroutine trace_summary_init_mapping_result(seed,result,status)
6622 double precision, intent(in) :: seed(ndim)
6623 type(trace_mapping_result), intent(out) :: result
6624 integer, intent(in) :: status
6625
6626 result%seed=seed
6627 result%source_B=zero
6628 result%forward_footpoint=seed
6629 result%backward_footpoint=seed
6630 result%forward_B=zero
6631 result%backward_B=zero
6632 result%forward_length=zero
6633 result%backward_length=zero
6634 result%source_Bn=zero
6635 result%forward_Bn=zero
6636 result%backward_Bn=zero
6637 result%forward_face=trace_face_none
6638 result%backward_face=trace_face_none
6639 result%forward_status=status
6640 result%backward_status=status
6641 result%valid=.false.
6642 end subroutine trace_summary_init_mapping_result
6643
6644 subroutine trace_summary_init_topology_result(seed,result,status, &
6645 need_twist,need_mapping)
6646 double precision, intent(in) :: seed(ndim)
6647 type(trace_topology_result), intent(out) :: result
6648 integer, intent(in) :: status
6649 logical, intent(in) :: need_twist,need_mapping
6650
6651 result%seed=seed
6652 result%length_forward=zero
6653 result%length_backward=zero
6654 result%length_total=zero
6655 result%twist_forward=zero
6656 result%twist_backward=zero
6657 result%twist_total=zero
6658 result%forward_endpoint=seed
6659 result%backward_endpoint=seed
6660 result%forward_nstep=0
6661 result%backward_nstep=0
6662 result%forward_face=trace_face_none
6663 result%backward_face=trace_face_none
6664 result%forward_status=status
6665 result%backward_status=status
6666 result%map_forward_endpoint=seed
6667 result%map_backward_endpoint=seed
6668 result%map_forward_length=zero
6669 result%map_backward_length=zero
6670 result%map_forward_face=trace_face_none
6671 result%map_backward_face=trace_face_none
6672 result%map_forward_status=status
6673 result%map_backward_status=status
6674 result%source_B=zero
6675 result%forward_B=zero
6676 result%backward_B=zero
6677 result%source_Bn=zero
6678 result%forward_Bn=zero
6679 result%backward_Bn=zero
6680 result%has_twist=need_twist
6681 result%has_mapping=need_mapping
6682 result%valid_twist=.false.
6683 result%valid=.false.
6684 result%status_twist=status
6685 result%status=status
6686 end subroutine trace_summary_init_topology_result
6687
6688 subroutine trace_summary_locate_seed(seed,result,igrid)
6690
6691 double precision, intent(in) :: seed(ndim)
6692 type(trace_length_result), intent(out) :: result
6693 integer, intent(out) :: igrid
6694
6695 double precision :: x3d(3),domain_min(ndim),domain_max(ndim),tol
6696 integer :: indomain,ipe,j
6697
6698 call trace_summary_init_result(seed,result,trace_status_active)
6699 igrid=-1
6700 indomain=0
6701 ^d&domain_min(^d)=xprobmin^d;
6702 ^d&domain_max(^d)=xprobmax^d;
6703 tol=100.d0*epsilon(one)*max(one,maxval(abs(domain_max-domain_min)))
6704 do j=1,ndim
6705 if (seed(j)>=domain_min(j)-tol .and. &
6706 seed(j)<=domain_max(j)+tol) indomain=indomain+1
6707 enddo
6708 if (indomain/=ndim) then
6709 result%forward_status=trace_status_seed_outside
6710 result%backward_status=trace_status_seed_outside
6711 return
6712 endif
6713
6714 x3d=zero
6715 do j=1,ndim
6716 x3d(j)=min(max(seed(j),domain_min(j)+tol),domain_max(j)-tol)
6717 enddo
6718 call find_particle_ipe(x3d,igrid,ipe)
6719 if (igrid<0 .or. ipe/=mype) then
6720 result%forward_status=trace_status_out_of_domain
6721 result%backward_status=trace_status_out_of_domain
6722 return
6723 endif
6724 end subroutine trace_summary_locate_seed
6725
6726 subroutine trace_summary_trace_seed(igrid,dL,max_steps,field_min,result)
6727 integer, intent(in) :: igrid,max_steps
6728 double precision, intent(in) :: dl,field_min
6729 type(trace_length_result), intent(inout) :: result
6730
6731 type(trace_summary_state) :: forward_state,backward_state
6732
6733 call trace_summary_init_state(result%seed,igrid,.true.,1,.false.,forward_state)
6734 call trace_summary_trace_state_to_end(forward_state,dl,max_steps,field_min)
6735 call trace_summary_init_state(result%seed,igrid,.false.,1,.false.,backward_state)
6736 call trace_summary_trace_state_to_end(backward_state,dl,max_steps,field_min)
6737
6738 result%forward_footpoint=forward_state%footpoint
6739 result%forward_length=forward_state%length
6740 result%forward_nstep=forward_state%nstep
6741 result%forward_status=forward_state%status
6742 result%backward_footpoint=backward_state%footpoint
6743 result%backward_length=backward_state%length
6744 result%backward_nstep=backward_state%nstep
6745 result%backward_status=backward_state%status
6746 result%total_length=result%forward_length+result%backward_length
6747 end subroutine trace_summary_trace_seed
6748
6749 subroutine trace_summary_trace_twist_seed(igrid,dL,max_steps,field_min, &
6750 result)
6751 integer, intent(in) :: igrid,max_steps
6752 double precision, intent(in) :: dl,field_min
6753 type(trace_twist_result), intent(inout) :: result
6754
6755 type(trace_summary_state) :: forward_state,backward_state
6756
6757 call trace_summary_init_state(result%line%seed,igrid,.true.,1,.true., &
6758 forward_state)
6759 call trace_summary_trace_state_to_end(forward_state,dl,max_steps,field_min)
6760 call trace_summary_init_state(result%line%seed,igrid,.false.,1,.true., &
6761 backward_state)
6762 call trace_summary_trace_state_to_end(backward_state,dl,max_steps,field_min)
6763
6764 result%line%forward_footpoint=forward_state%footpoint
6765 result%line%forward_length=forward_state%length
6766 result%line%forward_nstep=forward_state%nstep
6767 result%line%forward_status=forward_state%status
6768 result%line%backward_footpoint=backward_state%footpoint
6769 result%line%backward_length=backward_state%length
6770 result%line%backward_nstep=backward_state%nstep
6771 result%line%backward_status=backward_state%status
6772 result%line%total_length=result%line%forward_length &
6773 +result%line%backward_length
6774 result%forward_twist=forward_state%twist
6775 result%backward_twist=backward_state%twist
6776 result%total_twist=result%forward_twist+result%backward_twist
6777 result%status_twist=trace_summary_twist_status_from_states( &
6778 forward_state,backward_state)
6779 result%valid_twist=result%status_twist==trace_status_boundary
6780 end subroutine trace_summary_trace_twist_seed
6781
6782 subroutine trace_summary_init_state(xseed,igrid,forward,seed_id, &
6783 accumulate_twist,state)
6784 double precision, intent(in) :: xseed(ndim)
6785 integer, intent(in) :: igrid,seed_id
6786 logical, intent(in) :: forward,accumulate_twist
6787 type(trace_summary_state), intent(out) :: state
6788
6789 state%x=xseed
6790 state%footpoint=xseed
6791 state%length=zero
6792 state%twist=zero
6793 state%nstep=0
6794 state%status=trace_status_active
6795 state%twist_status=trace_status_active
6796 state%igrid=igrid
6797 state%seed_id=seed_id
6798 state%face=trace_face_none
6799 state%forward=forward
6800 state%active=.true.
6801 state%accumulate_twist=accumulate_twist
6802 state%rk45_h=zero
6803 state%sph_cache%valid=.false.
6804 if (trace_rk45_position_integrator()) then
6805 call trace_rk45_stats_note_direction()
6806 endif
6807 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
6808 call trace_spherical_profile_add_count(trace_profile_directions,1_8)
6809 end subroutine trace_summary_init_state
6810
6811 subroutine trace_summary_trace_state_to_end(state,ds,max_nstep,threshold)
6812 type(trace_summary_state), intent(inout) :: state
6813 double precision, intent(in) :: ds,threshold
6814 integer, intent(in) :: max_nstep
6815
6816 do while(state%active .and. state%nstep<max_nstep)
6817 call trace_summary_advance_state(state,ds,threshold)
6818 enddo
6819
6820 if (state%active) then
6821 state%status=trace_status_max_steps
6822 state%active=.false.
6823 endif
6824 state%footpoint=state%x
6825 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
6826 call trace_spherical_profile_note_trace_steps(state%nstep)
6827 end subroutine trace_summary_trace_state_to_end
6828
6829 subroutine trace_summary_trace_states_grouped(states,nstate,ds,max_nstep,threshold)
6830 integer, intent(in) :: nstate,max_nstep
6831 type(trace_summary_state), intent(inout) :: states(nstate)
6832 double precision, intent(in) :: ds,threshold
6833
6834 logical, allocatable :: processed(:)
6835 integer :: istate,jstate,target_grid
6836 logical :: any_active
6837
6838 allocate(processed(nstate))
6839 do
6840 any_active=.false.
6841 do istate=1,nstate
6842 if (states(istate)%active) then
6843 any_active=.true.
6844 exit
6845 endif
6846 enddo
6847 if (.not.any_active) exit
6848
6849 processed=.false.
6850 do istate=1,nstate
6851 if (.not.states(istate)%active .or. processed(istate)) cycle
6852 target_grid=states(istate)%igrid
6853 do jstate=1,nstate
6854 if (.not.states(jstate)%active .or. processed(jstate)) cycle
6855 if (states(jstate)%igrid/=target_grid) cycle
6856 call trace_summary_advance_state_in_grid(states(jstate),target_grid, &
6857 ds,max_nstep,threshold)
6858 processed(jstate)=.true.
6859 enddo
6860 enddo
6861 enddo
6862 deallocate(processed)
6863 end subroutine trace_summary_trace_states_grouped
6864
6865 subroutine trace_summary_advance_state_in_grid(state,igrid,ds,max_nstep,threshold)
6866 type(trace_summary_state), intent(inout) :: state
6867 integer, intent(in) :: igrid,max_nstep
6868 double precision, intent(in) :: ds,threshold
6869
6870 do while(state%active .and. state%igrid==igrid .and. &
6871 state%nstep<max_nstep)
6872 call trace_summary_advance_state(state,ds,threshold)
6873 enddo
6874
6875 if (state%active .and. state%nstep>=max_nstep) then
6876 state%status=trace_status_max_steps
6877 state%active=.false.
6878 state%footpoint=state%x
6879 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
6880 call trace_spherical_profile_note_trace_steps(state%nstep)
6881 endif
6882 end subroutine trace_summary_advance_state_in_grid
6883
6884 subroutine trace_summary_advance_state(state,ds,threshold)
6885 type(trace_summary_state), intent(inout) :: state
6886 double precision, intent(in) :: ds,threshold
6887
6888 double precision :: xnext(ndim),xhit(ndim),ds_actual,segment_length
6889 double precision :: alpha_hit
6890 double precision :: xbmin^d,xbmax^d
6891 double precision :: domain_min(ndim),domain_max(ndim)
6892 double precision :: near_tol,best_dist,face_dist
6893 integer :: igrid_next,ipe_next,inblock,step_status,face_id
6894 integer :: idim,best_dim,best_side
6895 integer :: point_domain
6896 double precision :: rk45_twist_increment
6897 integer :: rk45_twist_status
6898 logical :: newpe,stop_trace,hit_ok
6899 logical :: rk45_step,rk45_twist_integrated
6900
6901 if (.not.state%active) return
6902
6903 rk45_step=.false.
6904 rk45_twist_increment=zero
6905 rk45_twist_status=trace_status_active
6906 rk45_twist_integrated=.false.
6907 if (trace_integrator_mode==trace_integrator_rk45_cartesian .and. &
6908 trace_cartesian_like_geometry()) then
6909 rk45_step=.true.
6910 call trace_summary_rk45_cartesian_step(state%x,state%igrid,ds, &
6911 state%forward,threshold,state%rk45_h,xnext,ds_actual, &
6912 step_status,state%accumulate_twist,rk45_twist_increment, &
6913 rk45_twist_status,rk45_twist_integrated)
6914 else if (trace_integrator_mode==trace_integrator_rk45_spherical .and. &
6915 geo_coordinate==geo_spherical) then
6916 rk45_step=.true.
6917 call trace_summary_rk45_spherical_step(state%x,state%igrid,ds, &
6918 state%forward,threshold,state%rk45_h,state%sph_cache,xnext, &
6919 ds_actual,step_status,state%accumulate_twist, &
6920 rk45_twist_increment,rk45_twist_status,rk45_twist_integrated)
6921 else
6922 call trace_summary_rk2_step(state%x,state%igrid,ds,state%forward, &
6923 threshold,xnext,ds_actual,step_status,sph_cache=state%sph_cache)
6924 endif
6925 if (step_status/=trace_status_active) then
6926 if (step_status==trace_status_out_of_domain .and. &
6927 trace_rk45_position_integrator()) then
6928 call trace_boundary_face_at_point(state%x,state%face,hit_ok)
6929 if (.not.hit_ok) then
6930 ^d&domain_min(^d)=xprobmin^d;
6931 ^d&domain_max(^d)=xprobmax^d;
6932 near_tol=max(100.d0*epsilon(one)* &
6933 max(one,maxval(abs(domain_max-domain_min))), &
6934 1.d-5*max(one,abs(ds)))
6935 best_dist=huge(one)
6936 best_dim=0
6937 best_side=0
6938 do idim=1,ndim
6939 face_dist=abs(state%x(idim)-domain_min(idim))
6940 if (face_dist<best_dist) then
6941 best_dist=face_dist
6942 best_dim=idim
6943 best_side=-1
6944 endif
6945 face_dist=abs(state%x(idim)-domain_max(idim))
6946 if (face_dist<best_dist) then
6947 best_dist=face_dist
6948 best_dim=idim
6949 best_side=1
6950 endif
6951 enddo
6952 if (best_dim>0 .and. best_dist<=near_tol) then
6953 if (best_side<0) then
6954 state%x(best_dim)=domain_min(best_dim)
6955 else
6956 state%x(best_dim)=domain_max(best_dim)
6957 endif
6958 state%footpoint=state%x
6959 state%face=trace_face_from_dim_side(best_dim,best_side)
6960 hit_ok=.true.
6961 endif
6962 endif
6963 if (hit_ok) then
6964 state%status=trace_status_boundary
6965 state%active=.false.
6966 return
6967 endif
6968 endif
6969 state%status=step_status
6970 state%active=.false.
6971 return
6972 endif
6973
6974 point_domain=0
6975 {if (xnext(^db)>=xprobmin^db .and. xnext(^db)<xprobmax^db) point_domain=point_domain+1\}
6976 if (point_domain/=ndim) then
6977 call trace_intersect_domain(state%x,xnext,xhit,hit_ok,face_id, &
6978 alpha_hit)
6979 if (hit_ok) then
6980 if (trace_rk45_position_integrator() .and. &
6981 rk45_step) then
6982 segment_length=abs(ds_actual)
6983 else
6984 segment_length=trace_segment_length(state%x,xhit,ds_actual, &
6985 alpha_hit)
6986 endif
6987 if (state%accumulate_twist .and. &
6988 state%twist_status==trace_status_active) then
6989 if (rk45_step .and. rk45_twist_integrated) then
6990 state%twist=state%twist+rk45_twist_increment
6991 else if (rk45_step .and. rk45_twist_status/=trace_status_active) then
6992 state%twist_status=rk45_twist_status
6993 else
6994 call trace_summary_accumulate_twist(state,xhit,segment_length, &
6995 threshold,step_status)
6996 if (step_status/=trace_status_active) then
6997 state%twist_status=step_status
6998 endif
6999 endif
7000 endif
7001 state%length=state%length+segment_length
7002 ! Count the nonzero terminal boundary segment as one completed step.
7003 state%nstep=state%nstep+1
7004 if (trace_spherical_profile_enabled .and. &
7005 geo_coordinate==geo_spherical) then
7006 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
7007 endif
7008 state%x=xhit
7009 state%footpoint=state%x
7010 state%face=face_id
7011 endif
7012 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
7013 call trace_spherical_profile_add_count(trace_profile_boundary_events, &
7014 1_8)
7015 state%status=trace_status_boundary
7016 state%active=.false.
7017 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
7018 call trace_spherical_profile_note_trace_steps(state%nstep)
7019 return
7020 endif
7021
7022 if (state%accumulate_twist .and. &
7023 state%twist_status==trace_status_active) then
7024 if (rk45_step .and. rk45_twist_integrated) then
7025 state%twist=state%twist+rk45_twist_increment
7026 else if (rk45_step .and. rk45_twist_status/=trace_status_active) then
7027 state%twist_status=rk45_twist_status
7028 else
7029 call trace_summary_accumulate_twist(state,xnext,ds_actual,threshold, &
7030 step_status)
7031 if (step_status/=trace_status_active) then
7032 state%twist_status=step_status
7033 endif
7034 endif
7035 endif
7036 if (rk45_step) then
7037 state%length=state%length+abs(ds_actual)
7038 else
7039 state%length=state%length+trace_segment_length(state%x,xnext, &
7040 ds_actual)
7041 endif
7042 state%nstep=state%nstep+1
7043 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
7044 call trace_spherical_profile_add_count(trace_profile_steps,1_8)
7045 state%x=xnext
7046 state%footpoint=state%x
7047
7048 ^d&xbmin^d=rnode(rpxmin^d_,state%igrid)\
7049 ^d&xbmax^d=rnode(rpxmax^d_,state%igrid)\
7050 inblock=0
7051 {if (state%x(^db)>=xbmin^db .and. state%x(^db)<xbmax^db) inblock=inblock+1\}
7052 if (inblock==ndim) return
7053
7054 igrid_next=state%igrid
7055 ipe_next=mype
7056 newpe=.false.
7057 stop_trace=.false.
7058 call find_next_grid(state%igrid,igrid_next,ipe_next,state%x,newpe,stop_trace)
7059 if (stop_trace) then
7060 state%status=trace_status_boundary
7061 state%active=.false.
7062 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
7063 call trace_spherical_profile_add_count(trace_profile_boundary_events, &
7064 1_8)
7065 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
7066 call trace_spherical_profile_note_trace_steps(state%nstep)
7067 return
7068 endif
7069 if (newpe .or. ipe_next/=mype) then
7070 if (trace_rk45_position_integrator()) then
7071 call trace_boundary_face_at_point(state%x,state%face,hit_ok)
7072 if (.not.hit_ok) then
7073 ^d&domain_min(^d)=xprobmin^d;
7074 ^d&domain_max(^d)=xprobmax^d;
7075 near_tol=max(100.d0*epsilon(one)* &
7076 max(one,maxval(abs(domain_max-domain_min))), &
7077 1.d-6*max(one,abs(ds_actual)))
7078 best_dist=huge(one)
7079 best_dim=0
7080 best_side=0
7081 do idim=1,ndim
7082 face_dist=abs(state%x(idim)-domain_min(idim))
7083 if (face_dist<best_dist) then
7084 best_dist=face_dist
7085 best_dim=idim
7086 best_side=-1
7087 endif
7088 face_dist=abs(state%x(idim)-domain_max(idim))
7089 if (face_dist<best_dist) then
7090 best_dist=face_dist
7091 best_dim=idim
7092 best_side=1
7093 endif
7094 enddo
7095 if (best_dim>0 .and. best_dist<=near_tol) then
7096 if (best_side<0) then
7097 state%x(best_dim)=domain_min(best_dim)
7098 else
7099 state%x(best_dim)=domain_max(best_dim)
7100 endif
7101 state%footpoint=state%x
7102 state%face=trace_face_from_dim_side(best_dim,best_side)
7103 hit_ok=.true.
7104 endif
7105 endif
7106 if (hit_ok) then
7107 state%status=trace_status_boundary
7108 state%active=.false.
7109 return
7110 endif
7111 endif
7112 state%status=trace_status_out_of_domain
7113 state%active=.false.
7114 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
7115 call trace_spherical_profile_note_trace_steps(state%nstep)
7116 return
7117 endif
7118 state%igrid=igrid_next
7119 end subroutine trace_summary_advance_state
7120
7121 subroutine trace_summary_accumulate_twist(state,xend,segment_length, &
7122 threshold,status)
7123 type(trace_summary_state), intent(inout) :: state
7124 double precision, intent(in) :: xend(ndim),segment_length,threshold
7125 integer, intent(out) :: status
7126
7127 double precision :: xmid(ndim),b(3),curlb(3),b2,twist_density
7128
7129 status=trace_status_active
7130 if (segment_length<=zero) return
7131
7132 xmid=half*(state%x+xend)
7133 call sample_b_curlb_at_point(xmid,state%igrid,threshold,b,curlb,status, &
7134 sph_cache=state%sph_cache)
7135 if (status/=trace_status_active) return
7136
7137 b2=sum(b**2)
7138 twist_density=sum(curlb*b)/(4.d0*dpi*b2)
7139 ! Both directions contribute with positive arc length to the full-line Tw.
7140 state%twist=state%twist+twist_density*segment_length
7141 end subroutine trace_summary_accumulate_twist
7142
7143 subroutine trace_twist_density_at_point(x,igrid,threshold,twist_density, &
7144 status,sph_cache)
7145 double precision, intent(in) :: x(ndim),threshold
7146 integer, intent(in) :: igrid
7147 double precision, intent(out) :: twist_density
7148 integer, intent(out) :: status
7149 type(trace_sph_interp_ctx), intent(inout), optional :: sph_cache
7150
7151 double precision :: b(3),curlb(3),b2
7152
7153 twist_density=zero
7154 call sample_b_curlb_at_point(x,igrid,threshold,b,curlb,status, &
7155 sph_cache=sph_cache)
7156 if (status/=trace_status_active) return
7157
7158 b2=sum(b**2)
7159 if (b2<=zero) then
7161 return
7162 endif
7163 twist_density=sum(curlb*b)/(4.d0*dpi*b2)
7164 end subroutine trace_twist_density_at_point
7165
7166 subroutine trace_tangent_accumulate_twist(state,xend,segment_length, &
7167 threshold)
7168 type(trace_tangent_state), intent(inout) :: state
7169 double precision, intent(in) :: xend(ndim),segment_length,threshold
7170
7171 double precision :: xmid(ndim),twist_density
7172
7173 if (.not.state%accumulate_twist) return
7174 if (state%twist_status/=trace_status_active) return
7175 if (segment_length<=zero) return
7176 xmid=half*(state%x+xend)
7177 call trace_twist_density_at_point(xmid,state%igrid,threshold, &
7178 twist_density,state%twist_status,sph_cache=state%sph_cache)
7179 if (state%twist_status/=trace_status_active) return
7180 state%twist=state%twist+twist_density*segment_length
7181 end subroutine trace_tangent_accumulate_twist
7182
7183 integer function trace_summary_twist_status_from_states(forward_state, &
7184 backward_state) result(status_twist)
7185 type(trace_summary_state), intent(in) :: forward_state,backward_state
7186
7187 status_twist=trace_status_boundary
7188 if (forward_state%status/=trace_status_boundary) then
7189 status_twist=forward_state%status
7190 else if (backward_state%status/=trace_status_boundary) then
7191 status_twist=backward_state%status
7192 else if (forward_state%twist_status/=trace_status_active) then
7193 status_twist=forward_state%twist_status
7194 else if (backward_state%twist_status/=trace_status_active) then
7195 status_twist=backward_state%twist_status
7196 endif
7197 end function trace_summary_twist_status_from_states
7198
7199 subroutine trace_tangent_fill_twist_result(seed,forward_state, &
7200 backward_state,result)
7201 double precision, intent(in) :: seed(ndim)
7202 type(trace_tangent_state), intent(in) :: forward_state,backward_state
7203 type(trace_twist_result), intent(out) :: result
7204
7205 result%line%seed=seed
7206 result%line%forward_footpoint=forward_state%endpoint
7207 result%line%backward_footpoint=backward_state%endpoint
7208 result%line%forward_length=forward_state%length
7209 result%line%backward_length=backward_state%length
7210 result%line%total_length=forward_state%length+backward_state%length
7211 result%line%forward_nstep=forward_state%nstep
7212 result%line%backward_nstep=backward_state%nstep
7213 result%line%forward_status=forward_state%status
7214 result%line%backward_status=backward_state%status
7215 result%forward_twist=forward_state%twist
7216 result%backward_twist=backward_state%twist
7217 result%total_twist=forward_state%twist+backward_state%twist
7218 result%status_twist=trace_status_boundary
7219 if (forward_state%status/=trace_status_boundary) then
7220 result%status_twist=forward_state%status
7221 else if (backward_state%status/=trace_status_boundary) then
7222 result%status_twist=backward_state%status
7223 else if (forward_state%twist_status/=trace_status_active) then
7224 result%status_twist=forward_state%twist_status
7225 else if (backward_state%twist_status/=trace_status_active) then
7226 result%status_twist=backward_state%twist_status
7227 endif
7228 result%valid_twist=result%status_twist==trace_status_boundary
7229 end subroutine trace_tangent_fill_twist_result
7230
7231 subroutine trace_total_b_at_cell(igrid,ix1,ix2,ix3,B,status)
7232 integer, intent(in) :: igrid,ix1,ix2,ix3
7233 double precision, intent(out) :: b(3)
7234 integer, intent(out) :: status
7235
7236 b=zero
7238 if (ndim/=3 .or. igrid<0) return
7239
7240 {^ifthreed
7241 if (.not.b0field .and. .not.allocated(iw_mag)) return
7242 if (allocated(iw_mag)) then
7243 b(1:3)=ps(igrid)%w(ix1,ix2,ix3,iw_mag(1:3))
7244 endif
7245 if (b0field) then
7246 b(1:3)=b(1:3)+ps(igrid)%B0(ix1,ix2,ix3,1:3,0)
7247 endif
7248 status=trace_status_active
7249 }
7250 end subroutine trace_total_b_at_cell
7251
7252 subroutine trace_bhat_at_cell(igrid,ix1,ix2,ix3,threshold,bhat,status)
7253 integer, intent(in) :: igrid,ix1,ix2,ix3
7254 double precision, intent(in) :: threshold
7255 double precision, intent(out) :: bhat(3)
7256 integer, intent(out) :: status
7257
7258 double precision :: b(3),bnorm
7259
7260 bhat=zero
7261 call trace_total_b_at_cell(igrid,ix1,ix2,ix3,b,status)
7262 if (status/=trace_status_active) return
7263
7264 bnorm=dsqrt(sum(b**2))
7265 if (bnorm<=zero .or. bnorm<threshold) then
7267 return
7268 endif
7269 bhat=b/bnorm
7270 end subroutine trace_bhat_at_cell
7271
7272 subroutine sample_bhat_gradbhat_at_point(x,igrid,threshold,bhat, &
7273 grad_bhat,status)
7274 ! Production Method-II sampler: trilinear total-B derivative + chain rule.
7275 double precision, intent(in) :: x(ndim),threshold
7276 integer, intent(in) :: igrid
7277 double precision, intent(out) :: bhat(3),grad_bhat(3,3)
7278 integer, intent(out) :: status
7279
7280 call sample_bhat_gradbhat_interpderiv_at_point(x,igrid,threshold,bhat, &
7281 grad_bhat,status)
7282 end subroutine sample_bhat_gradbhat_at_point
7283
7284 subroutine sample_bhat_gradbhat_cellfd_at_point(x,igrid,threshold,bhat, &
7285 grad_bhat,status)
7286 ! Legacy diagnostic sampler: interpolate cell-centered finite-difference grad(bhat).
7287 double precision, intent(in) :: x(ndim),threshold
7288 integer, intent(in) :: igrid
7289 double precision, intent(out) :: bhat(3),grad_bhat(3,3)
7290 integer, intent(out) :: status
7291
7292 double precision :: dxb^d,dxc^d,xd^d
7293 double precision :: bhat_cell(0:1^d&,3)
7294 double precision :: grad_cell(0:1^d&,3,3)
7295 double precision :: factor(0:1^d&)
7296 double precision :: bhat_center(3),bhat_plus(3),bhat_minus(3)
7297 integer :: ixi^l,ixbl^d,ix^d,j,k
7298
7299 bhat=zero
7300 grad_bhat=zero
7302 if (ndim/=3 .or. igrid<0) return
7303
7304 {^ifthreed
7305 ixi^l=ixg^ll;
7306 ^d&dxb^d=rnode(rpdx^d_,igrid);
7307 ^d&ixbl^d=floor((x(^d)-ps(igrid)%x(iximin^dd,^d))/dxb^d)+iximin^d;
7308 ^d&xd^d=(x(^d)-ps(igrid)%x(ixbl^dd,^d))/dxb^d;
7309
7310 if (ixbl1-1<iximin1 .or. ixbl1+2>iximax1 .or. &
7311 ixbl2-1<iximin2 .or. ixbl2+2>iximax2 .or. &
7312 ixbl3-1<iximin3 .or. ixbl3+2>iximax3) return
7313
7314 bhat_cell=zero
7315 grad_cell=zero
7316 {do ix^d=0,1\}
7317 call trace_bhat_at_cell(igrid,ixbl1+ix1,ixbl2+ix2, &
7318 ixbl3+ix3,threshold,bhat_center,status)
7319 if (status/=trace_status_active) return
7320 bhat_cell(ix^d,1:3)=bhat_center
7321
7322 call trace_bhat_at_cell(igrid,ixbl1+ix1+1,ixbl2+ix2, &
7323 ixbl3+ix3,threshold,bhat_plus,status)
7324 if (status/=trace_status_active) return
7325 call trace_bhat_at_cell(igrid,ixbl1+ix1-1,ixbl2+ix2, &
7326 ixbl3+ix3,threshold,bhat_minus,status)
7327 if (status/=trace_status_active) return
7328 grad_cell(ix^d,1:3,1)=(bhat_plus-bhat_minus)/(2.d0*dxb1)
7329
7330 call trace_bhat_at_cell(igrid,ixbl1+ix1,ixbl2+ix2+1, &
7331 ixbl3+ix3,threshold,bhat_plus,status)
7332 if (status/=trace_status_active) return
7333 call trace_bhat_at_cell(igrid,ixbl1+ix1,ixbl2+ix2-1, &
7334 ixbl3+ix3,threshold,bhat_minus,status)
7335 if (status/=trace_status_active) return
7336 grad_cell(ix^d,1:3,2)=(bhat_plus-bhat_minus)/(2.d0*dxb2)
7337
7338 call trace_bhat_at_cell(igrid,ixbl1+ix1,ixbl2+ix2, &
7339 ixbl3+ix3+1,threshold,bhat_plus,status)
7340 if (status/=trace_status_active) return
7341 call trace_bhat_at_cell(igrid,ixbl1+ix1,ixbl2+ix2, &
7342 ixbl3+ix3-1,threshold,bhat_minus,status)
7343 if (status/=trace_status_active) return
7344 grad_cell(ix^d,1:3,3)=(bhat_plus-bhat_minus)/(2.d0*dxb3)
7345 {enddo\}
7346
7347 {do ix^d=0,1\}
7348 factor(ix^d)={abs(1-ix^d-xd^d)*}
7349 {enddo\}
7350 {do ix^d=0,1\}
7351 do j=1,3
7352 bhat(j)=bhat(j)+bhat_cell(ix^d,j)*factor(ix^d)
7353 do k=1,3
7354 grad_bhat(j,k)=grad_bhat(j,k)+grad_cell(ix^d,j,k)*factor(ix^d)
7355 enddo
7356 enddo
7357 {enddo\}
7358
7359 status=trace_status_active
7360 }
7361 end subroutine sample_bhat_gradbhat_cellfd_at_point
7362
7363 subroutine sample_bhat_gradbhat_xeps_at_point(x,eps,threshold,bhat, &
7364 grad_bhat,status)
7365 ! Reference-only grad(bhat) by central differences of interpolated bhat.
7366 double precision, intent(in) :: x(ndim),eps,threshold
7367 double precision, intent(out) :: bhat(3),grad_bhat(3,3)
7368 integer, intent(out) :: status
7369
7370 double precision :: xp(ndim),xm(ndim)
7371 double precision :: b(3),bp(3),bm(3),bhat_p(3),bhat_m(3)
7372 double precision :: bnorm,bpnorm,bmnorm
7373 integer :: igrid,idir,point_domain
7374
7375 bhat=zero
7376 grad_bhat=zero
7378 if (ndim/=3 .or. .not.slab_uniform) then
7380 return
7381 endif
7382 if (eps<=zero) then
7384 return
7385 endif
7386
7387 call trace_debug_locate_point(x,igrid,status)
7388 if (status/=trace_status_active) return
7389 call sample_b_at_point(x,igrid,threshold,b,status)
7390 if (status/=trace_status_active) return
7391 bnorm=dsqrt(sum(b**2))
7392 if (bnorm<=zero .or. bnorm<threshold) then
7394 return
7395 endif
7396 bhat=b/bnorm
7397
7398 do idir=1,ndim
7399 xp=x
7400 xm=x
7401 xp(idir)=xp(idir)+eps
7402 xm(idir)=xm(idir)-eps
7403
7404 point_domain=0
7405 {if (xp(^db)>=xprobmin^db .and. xp(^db)<xprobmax^db) point_domain=point_domain+1\}
7406 if (point_domain/=ndim) then
7408 return
7409 endif
7410 call trace_debug_locate_point(xp,igrid,status)
7411 if (status/=trace_status_active) return
7412 call sample_b_at_point(xp,igrid,threshold,bp,status)
7413 if (status/=trace_status_active) return
7414 bpnorm=dsqrt(sum(bp**2))
7415 if (bpnorm<=zero .or. bpnorm<threshold) then
7417 return
7418 endif
7419 bhat_p=bp/bpnorm
7420
7421 point_domain=0
7422 {if (xm(^db)>=xprobmin^db .and. xm(^db)<xprobmax^db) point_domain=point_domain+1\}
7423 if (point_domain/=ndim) then
7425 return
7426 endif
7427 call trace_debug_locate_point(xm,igrid,status)
7428 if (status/=trace_status_active) return
7429 call sample_b_at_point(xm,igrid,threshold,bm,status)
7430 if (status/=trace_status_active) return
7431 bmnorm=dsqrt(sum(bm**2))
7432 if (bmnorm<=zero .or. bmnorm<threshold) then
7434 return
7435 endif
7436 bhat_m=bm/bmnorm
7437
7438 grad_bhat(1:3,idir)=(bhat_p-bhat_m)/(2.d0*eps)
7439 enddo
7440
7441 status=trace_status_active
7442 end subroutine sample_bhat_gradbhat_xeps_at_point
7443
7444 subroutine sample_bhat_gradbhat_interpderiv_at_point(x,igrid,threshold, &
7445 bhat,grad_bhat,status)
7446 ! Candidate sampler: differentiate trilinear total-B interpolation.
7447 double precision, intent(in) :: x(ndim),threshold
7448 integer, intent(in) :: igrid
7449 double precision, intent(out) :: bhat(3),grad_bhat(3,3)
7450 integer, intent(out) :: status
7451
7452 double precision :: dxb^d,dxc^d,xd^d
7453 double precision :: field(0:1^d&,3)
7454 double precision :: b(3),dbdx(3,3)
7455 double precision :: wx(0:1),wy(0:1),wz(0:1)
7456 double precision :: dwx(0:1),dwy(0:1),dwz(0:1)
7457 double precision :: weight,bnorm,projected
7458 integer :: ixi^l,ixbl^d,ix^d,j
7459
7460 bhat=zero
7461 grad_bhat=zero
7463 if (ndim/=3 .or. .not.trace_cartesian_like_geometry() .or. igrid<0) then
7465 return
7466 endif
7467
7468 {^ifthreed
7469 ixi^l=ixg^ll;
7470 call trace_interp_weights_block(x,igrid,ixi^l,ixbl^d,xd^d,dxc^d, &
7471 status)
7472 if (status/=trace_status_active) return
7473
7474 if (ixbl1<iximin1 .or. ixbl1+1>iximax1 .or. &
7475 ixbl2<iximin2 .or. ixbl2+1>iximax2 .or. &
7476 ixbl3<iximin3 .or. ixbl3+1>iximax3) return
7477
7479 if (.not.b0field .and. .not.allocated(iw_mag)) return
7480
7481 field=zero
7482 {do ix^d=0,1\}
7483 if (allocated(iw_mag)) then
7484 field(ix^d,1:3)=ps(igrid)%w(ixbl1+ix1,ixbl2+ix2, &
7485 ixbl3+ix3,iw_mag(1:3))
7486 endif
7487 if (b0field) then
7488 field(ix^d,1:3)=field(ix^d,1:3) &
7489 +ps(igrid)%B0(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3,1:3,0)
7490 endif
7491 {enddo\}
7492
7493 wx(0)=one-xd1
7494 wx(1)=xd1
7495 wy(0)=one-xd2
7496 wy(1)=xd2
7497 wz(0)=one-xd3
7498 wz(1)=xd3
7499 dwx(0)=-one/dxc1
7500 dwx(1)= one/dxc1
7501 dwy(0)=-one/dxc2
7502 dwy(1)= one/dxc2
7503 dwz(0)=-one/dxc3
7504 dwz(1)= one/dxc3
7505
7506 b=zero
7507 dbdx=zero
7508 {do ix^d=0,1\}
7509 weight=wx(ix1)*wy(ix2)*wz(ix3)
7510 b(1:3)=b(1:3)+field(ix^d,1:3)*weight
7511 dbdx(1:3,1)=dbdx(1:3,1)+field(ix^d,1:3)* &
7512 dwx(ix1)*wy(ix2)*wz(ix3)
7513 dbdx(1:3,2)=dbdx(1:3,2)+field(ix^d,1:3)* &
7514 wx(ix1)*dwy(ix2)*wz(ix3)
7515 dbdx(1:3,3)=dbdx(1:3,3)+field(ix^d,1:3)* &
7516 wx(ix1)*wy(ix2)*dwz(ix3)
7517 {enddo\}
7518
7519 bnorm=dsqrt(sum(b**2))
7520 if (bnorm<=zero .or. bnorm<threshold) then
7522 return
7523 endif
7524
7525 bhat=b/bnorm
7526 do j=1,3
7527 projected=sum(bhat*dbdx(1:3,j))
7528 grad_bhat(1:3,j)=(dbdx(1:3,j)-bhat(1:3)*projected)/bnorm
7529 enddo
7530
7531 status=trace_status_active
7532 }
7533 end subroutine sample_bhat_gradbhat_interpderiv_at_point
7534
7535 subroutine sample_b_curlb_at_point(x,igrid,threshold,B,curlB,status, &
7536 sph_cache)
7537 ! Interpolate total B and its cell-centered second-order curl locally.
7538 double precision, intent(in) :: x(ndim),threshold
7539 integer, intent(in) :: igrid
7540 double precision, intent(out) :: b(3),curlb(3)
7541 integer, intent(out) :: status
7542 type(trace_sph_interp_ctx), intent(inout), optional :: sph_cache
7543
7544 double precision :: dxb^d,dxc^d,xd^d
7545 double precision :: field(0:1^d&,3),current(0:1^D&,3)
7546 double precision :: factor(0:1^d&),B2
7547 double precision :: dx1c,dx2c,dx3c
7548 integer :: ixi^l,ixj^l,ixbl^d,ix^d,j
7549
7550 b=zero
7551 curlb=zero
7553 if (ndim/=3) return
7554
7555 if (geo_coordinate==geo_spherical) then
7556 if (present(sph_cache)) then
7557 call sample_b_curlb_spherical_at_point(x,igrid,threshold,b,curlb, &
7558 status,sph_cache=sph_cache)
7559 else
7560 call sample_b_curlb_spherical_at_point(x,igrid,threshold,b,curlb, &
7561 status)
7562 endif
7563 return
7564 endif
7565
7566 {^ifthreed
7567 ixi^l=ixg^ll;
7568 ixj^l=ixm^ll^ladd1;
7569 call trace_interp_weights_block(x,igrid,ixi^l,ixbl^d,xd^d,dxc^d, &
7570 status)
7571 if (status/=trace_status_active) return
7572
7573 if (ixbl1-1<iximin1 .or. ixbl1+2>iximax1 .or. &
7574 ixbl2-1<iximin2 .or. ixbl2+2>iximax2 .or. &
7575 ixbl3-1<iximin3 .or. ixbl3+2>iximax3) return
7576 if (b0field) then
7577 if (ixbl1<ixjmin1 .or. ixbl1+1>ixjmax1 .or. &
7578 ixbl2<ixjmin2 .or. ixbl2+1>ixjmax2 .or. &
7579 ixbl3<ixjmin3 .or. ixbl3+1>ixjmax3) return
7580 endif
7582 if (.not.b0field .and. .not.allocated(iw_mag)) return
7583
7584 field=zero
7585 current=zero
7586 {do ix^d=0,1\}
7587 if (allocated(iw_mag)) then
7588 field(ix^d,1:3)=ps(igrid)%w(ixbl1+ix1,ixbl2+ix2, &
7589 ixbl3+ix3,iw_mag(1:3))
7590 endif
7591 if (b0field) then
7592 field(ix^d,1:3)=field(ix^d,1:3) &
7593 +ps(igrid)%B0(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3,1:3,0)
7594 endif
7595 if (allocated(iw_mag)) then
7596 dx1c=ps(igrid)%x(ixbl1+ix1+1,ixbl2+ix2,ixbl3+ix3,1) &
7597 -ps(igrid)%x(ixbl1+ix1-1,ixbl2+ix2,ixbl3+ix3,1)
7598 dx2c=ps(igrid)%x(ixbl1+ix1,ixbl2+ix2+1,ixbl3+ix3,2) &
7599 -ps(igrid)%x(ixbl1+ix1,ixbl2+ix2-1,ixbl3+ix3,2)
7600 dx3c=ps(igrid)%x(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3+1,3) &
7601 -ps(igrid)%x(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3-1,3)
7602 if (abs(dx1c)<=smalldouble .or. abs(dx2c)<=smalldouble .or. &
7603 abs(dx3c)<=smalldouble) then
7605 return
7606 endif
7607 current(ix^d,1)=half*( &
7608 (ps(igrid)%w(ixbl1+ix1,ixbl2+ix2+1,ixbl3+ix3,iw_mag(3)) &
7609 -ps(igrid)%w(ixbl1+ix1,ixbl2+ix2-1,ixbl3+ix3,iw_mag(3)))/dx2c &
7610 -(ps(igrid)%w(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3+1,iw_mag(2)) &
7611 -ps(igrid)%w(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3-1,iw_mag(2)))/dx3c)
7612 current(ix^d,2)=half*( &
7613 (ps(igrid)%w(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3+1,iw_mag(1)) &
7614 -ps(igrid)%w(ixbl1+ix1,ixbl2+ix2,ixbl3+ix3-1,iw_mag(1)))/dx3c &
7615 -(ps(igrid)%w(ixbl1+ix1+1,ixbl2+ix2,ixbl3+ix3,iw_mag(3)) &
7616 -ps(igrid)%w(ixbl1+ix1-1,ixbl2+ix2,ixbl3+ix3,iw_mag(3)))/dx1c)
7617 current(ix^d,3)=half*( &
7618 (ps(igrid)%w(ixbl1+ix1+1,ixbl2+ix2,ixbl3+ix3,iw_mag(2)) &
7619 -ps(igrid)%w(ixbl1+ix1-1,ixbl2+ix2,ixbl3+ix3,iw_mag(2)))/dx1c &
7620 -(ps(igrid)%w(ixbl1+ix1,ixbl2+ix2+1,ixbl3+ix3,iw_mag(1)) &
7621 -ps(igrid)%w(ixbl1+ix1,ixbl2+ix2-1,ixbl3+ix3,iw_mag(1)))/dx2c)
7622 endif
7623 if (b0field) then
7624 current(ix^d,1:3)=current(ix^d,1:3) &
7625 +ps(igrid)%J0(ixbl^d+ix^d,1:3)
7626 endif
7627 {enddo\}
7628
7629 {do ix^d=0,1\}
7630 factor(ix^d)={abs(1-ix^d-xd^d)*}
7631 {enddo\}
7632 {do ix^d=0,1\}
7633 do j=1,3
7634 b(j)=b(j)+field(ix^d,j)*factor(ix^d)
7635 curlb(j)=curlb(j)+current(ix^d,j)*factor(ix^d)
7636 enddo
7637 {enddo\}
7638
7639 b2=sum(b**2)
7640 if (b2<=zero .or. dsqrt(b2)<threshold) then
7642 return
7643 endif
7644 status=trace_status_active
7645 }
7646 end subroutine sample_b_curlb_at_point
7647
7648 subroutine sample_b_curlb_spherical_at_point(x,igrid,threshold,B,curlB, &
7649 status,sph_cache)
7650 ! Reuse AMRVAC's geometry-aware curl operator for local spherical
7651 ! physical components. B0/J0 handling matches the MHD current path.
7652 double precision, intent(in) :: x(ndim),threshold
7653 integer, intent(in) :: igrid
7654 double precision, intent(out) :: b(3),curlb(3)
7655 integer, intent(out) :: status
7656 type(trace_sph_interp_ctx), intent(inout), optional :: sph_cache
7657
7658 double precision :: dxb^d,dxc^d,xd^d
7659 double precision :: bvec(ixg^t,1:3),current(ixg^t,1:3)
7660 double precision :: factor(0:1^d&),B2
7661 type(trace_sph_interp_ctx) :: ctx
7662 integer :: ixi^l,ixo^l,ixa^l,ixj^l,ixbl^d,ix^d,j,idirmin
7663
7664 b=zero
7665 curlb=zero
7667 if (ndim/=3 .or. geo_coordinate/=geo_spherical .or. igrid<0) return
7668
7669 {^ifthreed
7670 if (present(sph_cache)) then
7671 call trace_spherical_interp_ctx_build_cached(x,igrid,sph_cache,ctx, &
7672 status)
7673 else
7674 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
7675 endif
7676 if (status/=trace_status_active) return
7677 call trace_spherical_sample_bsph_ctx(ctx,threshold,b,status)
7678 if (status/=trace_status_active) return
7679 if (trace_spherical_curl_cache_ready) then
7680 call trace_spherical_sample_cached_curlb_ctx(ctx,curlb,status)
7681 if (status/=trace_status_active) return
7682 b2=sum(b**2)
7683 if (b2<=zero .or. dsqrt(b2)<threshold) then
7685 return
7686 endif
7687 status=trace_status_active
7688 return
7689 endif
7690
7691 ixi^l=ixg^ll;
7692 ^d&dxb^d=rnode(rpdx^d_,igrid);
7693 ^d&dxlevel(^d)=dxb^d;
7694 ^d&dxc^d=ctx%dxc(^d);
7695 ^d&xd^d=ctx%xd(^d);
7696 ^d&ixbl^d=ctx%ixbl(^d);
7697 ^d&ixomin^d=ixbl^d;
7698 ^d&ixomax^d=ixbl^d+1;
7699 ixa^l=ixo^l^ladd1;
7700 ixj^l=ixm^ll^ladd1;
7701
7702 if (iximin1>ixamin1 .or. iximax1<ixamax1 .or. &
7703 iximin2>ixamin2 .or. iximax2<ixamax2 .or. &
7704 iximin3>ixamin3 .or. iximax3<ixamax3) return
7705 if (b0field) then
7706 if (ixjmin1>ixomin1 .or. ixjmax1<ixomax1 .or. &
7707 ixjmin2>ixomin2 .or. ixjmax2<ixomax2 .or. &
7708 ixjmin3>ixomin3 .or. ixjmax3<ixomax3) return
7709 endif
7711 if (.not.b0field .and. .not.allocated(iw_mag)) return
7712
7713 bvec=zero
7714 current=zero
7715 if (allocated(iw_mag)) then
7716 bvec(ixa^s,1:3)=ps(igrid)%w(ixa^s,iw_mag(1:3))
7717 endif
7718
7719 block=>ps(igrid)
7720 call curlvector(bvec,ixi^l,ixo^l,current,idirmin,1,3)
7721 if (b0field) then
7722 current(ixo^s,1:3)=current(ixo^s,1:3)+ps(igrid)%J0(ixo^s,1:3)
7723 endif
7724
7725 {do ix^d=0,1\}
7726 factor(ix^d)={abs(1-ix^d-xd^d)*}
7727 {enddo\}
7728 {do ix^d=0,1\}
7729 do j=1,3
7730 curlb(j)=curlb(j)+current(ixbl^d+ix^d,j)*factor(ix^d)
7731 enddo
7732 {enddo\}
7733
7734 b2=sum(b**2)
7735 if (b2<=zero .or. dsqrt(b2)<threshold) then
7737 return
7738 endif
7739 status=trace_status_active
7740 }
7741 end subroutine sample_b_curlb_spherical_at_point
7742
7743 subroutine sample_b_at_point(x,igrid,threshold,B,status)
7744 ! Interpolate total magnetic field at x using the local block stencil.
7745 double precision, intent(in) :: x(ndim),threshold
7746 integer, intent(in) :: igrid
7747 double precision, intent(out) :: b(3)
7748 integer, intent(out) :: status
7749
7750 double precision :: dxb^d,dxc^d,xd^d
7751 double precision :: field(0:1^d&,3),factor(0:1^D&),B2
7752 double precision :: bcell(3)
7753 type(trace_sph_interp_ctx) :: ctx
7754 integer :: ixi^l,ixbl^d,ix^d,j
7755
7756 b=zero
7758 if (ndim/=3 .or. igrid<0) return
7759
7760 {^ifthreed
7761 ixi^l=ixg^ll;
7762 if (geo_coordinate==geo_spherical) then
7763 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
7764 if (status/=trace_status_active) return
7765 call trace_spherical_sample_bsph_ctx(ctx,threshold,b,status)
7766 return
7767 else
7768 call trace_interp_weights_block(x,igrid,ixi^l,ixbl^d,xd^d,dxc^d, &
7769 status)
7770 if (status/=trace_status_active) return
7771 endif
7772
7773 if (ixbl1<iximin1 .or. ixbl1+1>iximax1 .or. &
7774 ixbl2<iximin2 .or. ixbl2+1>iximax2 .or. &
7775 ixbl3<iximin3 .or. ixbl3+1>iximax3) return
7776 field=zero
7777 {do ix^d=0,1\}
7778 call trace_total_b_at_cell(igrid,ixbl1+ix1,ixbl2+ix2, &
7779 ixbl3+ix3,bcell,status)
7780 if (status/=trace_status_active) return
7781 field(ix^d,1:3)=bcell
7782 {enddo\}
7783
7784 {do ix^d=0,1\}
7785 factor(ix^d)={abs(1-ix^d-xd^d)*}
7786 {enddo\}
7787 {do ix^d=0,1\}
7788 do j=1,3
7789 b(j)=b(j)+field(ix^d,j)*factor(ix^d)
7790 enddo
7791 {enddo\}
7792
7793 b2=sum(b**2)
7794 if (b2<=zero .or. dsqrt(b2)<threshold) then
7796 return
7797 endif
7798 status=trace_status_active
7799 }
7800 end subroutine sample_b_at_point
7801
7802 subroutine trace_summary_fill_mapping(seed,source_igrid,forward_state, &
7803 backward_state,threshold,have_source_normal,source_normal,result)
7804 double precision, intent(in) :: seed(ndim),threshold,source_normal(3)
7805 integer, intent(in) :: source_igrid
7806 logical, intent(in) :: have_source_normal
7807 type(trace_summary_state), intent(in) :: forward_state,backward_state
7808 type(trace_mapping_result), intent(inout) :: result
7809
7810 integer :: status
7811
7812 call trace_summary_init_mapping_result(seed,result,trace_status_active)
7813
7814 result%forward_footpoint=forward_state%footpoint
7815 result%backward_footpoint=backward_state%footpoint
7816 result%forward_length=forward_state%length
7817 result%backward_length=backward_state%length
7818 result%forward_face=forward_state%face
7819 result%backward_face=backward_state%face
7820 result%forward_status=forward_state%status
7821 result%backward_status=backward_state%status
7822
7823 call sample_b_at_point(seed,source_igrid,threshold,result%source_B,status)
7824 if (status/=trace_status_active) then
7825 result%forward_status=status
7826 result%backward_status=status
7827 result%valid=.false.
7828 return
7829 endif
7830 if (have_source_normal) then
7831 result%source_Bn=sum(result%source_B*source_normal)
7832 endif
7833
7834 if (forward_state%status==trace_status_boundary) then
7835 call sample_b_at_point(forward_state%footpoint,forward_state%igrid, &
7836 threshold,result%forward_B,status)
7837 if (status/=trace_status_active) result%forward_status=status
7838 endif
7839 if (backward_state%status==trace_status_boundary) then
7840 call sample_b_at_point(backward_state%footpoint,backward_state%igrid, &
7841 threshold,result%backward_B,status)
7842 if (status/=trace_status_active) result%backward_status=status
7843 endif
7844
7845 result%forward_Bn=trace_face_bn(result%forward_face,result%forward_B)
7846 result%backward_Bn=trace_face_bn(result%backward_face,result%backward_B)
7847 result%valid=result%forward_status==trace_status_boundary .and. &
7848 result%backward_status==trace_status_boundary .and. &
7849 trace_face_is_boundary(result%forward_face) .and. &
7850 trace_face_is_boundary(result%backward_face)
7851 end subroutine trace_summary_fill_mapping
7852
7853 double precision function trace_face_bn(face_id,B) result(Bn)
7854 integer, intent(in) :: face_id
7855 double precision, intent(in) :: b(3)
7856
7857 bn=zero
7858 select case(face_id)
7859 case(trace_face_xmin)
7860 bn=-b(1)
7861 case(trace_face_xmax)
7862 bn=b(1)
7863 case(trace_face_ymin)
7864 bn=-b(2)
7865 case(trace_face_ymax)
7866 bn=b(2)
7867 case(trace_face_zmin)
7868 bn=-b(3)
7869 case(trace_face_zmax)
7870 bn=b(3)
7871 end select
7872 end function trace_face_bn
7873
7874 logical function trace_face_is_boundary(face_id) result(is_boundary)
7875 integer, intent(in) :: face_id
7876
7877 is_boundary=face_id>=trace_face_xmin .and. face_id<=trace_face_zmax
7878 end function trace_face_is_boundary
7879
7880 subroutine trace_boundary_face_at_point(x,face_id,on_boundary)
7881 double precision, intent(in) :: x(ndim)
7882 integer, intent(out) :: face_id
7883 logical, intent(out) :: on_boundary
7884
7885 double precision :: domain_min(ndim),domain_max(ndim),tol
7886 integer :: idim,nface,candidate_face
7887
7888 ^d&domain_min(^d)=xprobmin^d;
7889 ^d&domain_max(^d)=xprobmax^d;
7890 tol=100.d0*epsilon(one)*max(one,maxval(abs(domain_max-domain_min)))
7891 face_id=trace_face_none
7892 nface=0
7893 do idim=1,ndim
7894 if (abs(x(idim)-domain_min(idim))<=tol) then
7895 candidate_face=trace_face_from_dim_side(idim,-1)
7896 nface=nface+1
7897 if (nface==1) then
7898 face_id=candidate_face
7899 else if (candidate_face/=face_id) then
7900 face_id=trace_face_ambiguous
7901 endif
7902 endif
7903 if (abs(x(idim)-domain_max(idim))<=tol) then
7904 candidate_face=trace_face_from_dim_side(idim,1)
7905 nface=nface+1
7906 if (nface==1) then
7907 face_id=candidate_face
7908 else if (candidate_face/=face_id) then
7909 face_id=trace_face_ambiguous
7910 endif
7911 endif
7912 enddo
7913 on_boundary=nface>0
7914 end subroutine trace_boundary_face_at_point
7915
7916 subroutine trace_intersect_domain(xinside,xoutside,xhit,hit_ok,face_id, &
7917 alpha_out)
7918 double precision, intent(in) :: xinside(ndim),xoutside(ndim)
7919 double precision, intent(out) :: xhit(ndim)
7920 logical, intent(out) :: hit_ok
7921 integer, intent(out) :: face_id
7922 double precision, intent(out), optional :: alpha_out
7923
7924 double precision :: alpha,alpha_hit,alpha_tol,delta
7925 double precision :: domain_min(ndim),domain_max(ndim)
7926 integer :: idim,hit_dim,hit_side,candidate_face
7927
7928 ^d&domain_min(^d)=xprobmin^d;
7929 ^d&domain_max(^d)=xprobmax^d;
7930 alpha_hit=huge(one)
7931 alpha_tol=100.d0*epsilon(one)
7932 hit_dim=0
7933 hit_side=0
7934 face_id=trace_face_none
7935 do idim=1,ndim
7936 delta=xoutside(idim)-xinside(idim)
7937 if (xoutside(idim)<domain_min(idim) .and. abs(delta)>smalldouble) then
7938 alpha=(domain_min(idim)-xinside(idim))/delta
7939 if (alpha>=zero .and. alpha<=one) then
7940 candidate_face=trace_face_from_dim_side(idim,-1)
7941 if (alpha<alpha_hit-alpha_tol) then
7942 alpha_hit=alpha
7943 hit_dim=idim
7944 hit_side=-1
7945 face_id=candidate_face
7946 else if (abs(alpha-alpha_hit)<=alpha_tol .and. &
7947 candidate_face/=face_id) then
7948 face_id=trace_face_ambiguous
7949 endif
7950 endif
7951 else if (xoutside(idim)>=domain_max(idim) .and. &
7952 abs(delta)>smalldouble) then
7953 alpha=(domain_max(idim)-xinside(idim))/delta
7954 if (alpha>=zero .and. alpha<=one) then
7955 candidate_face=trace_face_from_dim_side(idim,1)
7956 if (alpha<alpha_hit-alpha_tol) then
7957 alpha_hit=alpha
7958 hit_dim=idim
7959 hit_side=1
7960 face_id=candidate_face
7961 else if (abs(alpha-alpha_hit)<=alpha_tol .and. &
7962 candidate_face/=face_id) then
7963 face_id=trace_face_ambiguous
7964 endif
7965 endif
7966 endif
7967 enddo
7968
7969 hit_ok=hit_dim>0
7970 xhit=xinside
7971 if (present(alpha_out)) alpha_out=zero
7972 if (.not.hit_ok) return
7973
7974 xhit=xinside+alpha_hit*(xoutside-xinside)
7975 do idim=1,ndim
7976 xhit(idim)=min(max(xhit(idim),domain_min(idim)),domain_max(idim))
7977 enddo
7978 if (hit_side<0) then
7979 xhit(hit_dim)=domain_min(hit_dim)
7980 else
7981 xhit(hit_dim)=domain_max(hit_dim)
7982 endif
7983 if (present(alpha_out)) alpha_out=alpha_hit
7984 end subroutine trace_intersect_domain
7985
7986 integer function trace_face_from_dim_side(idim,side) result(face_id)
7987 integer, intent(in) :: idim,side
7988
7989 face_id=trace_face_none
7990 select case(idim)
7991 case(1)
7992 if (side<0) then
7993 face_id=trace_face_xmin
7994 else
7995 face_id=trace_face_xmax
7996 endif
7997 case(2)
7998 if (side<0) then
7999 face_id=trace_face_ymin
8000 else
8001 face_id=trace_face_ymax
8002 endif
8003 case(3)
8004 if (side<0) then
8005 face_id=trace_face_zmin
8006 else
8007 face_id=trace_face_zmax
8008 endif
8009 end select
8010 end function trace_face_from_dim_side
8011
8012 subroutine trace_cartesian_local_cell_size(x,igrid,hcell,status)
8013 double precision, intent(in) :: x(ndim)
8014 integer, intent(in) :: igrid
8015 double precision, intent(out) :: hcell
8016 integer, intent(out) :: status
8017
8018 double precision :: dxc^d,xd^d
8019 integer :: ixi^l,ixbl^d
8020
8021 hcell=zero
8023 if (.not.trace_cartesian_like_geometry() .or. igrid<0) return
8024
8025 {^ifthreed
8026 ixi^l=ixg^ll;
8027 call trace_interp_weights_block(x,igrid,ixi^l,ixbl^d,xd^d,dxc^d, &
8028 status)
8029 if (status/=trace_status_active) return
8030 hcell=min(abs(dxc1),abs(dxc2))
8031 hcell=min(hcell,abs(dxc3))
8032 if (hcell<=zero) status=trace_status_out_of_domain
8033 }
8034 end subroutine trace_cartesian_local_cell_size
8035
8036 subroutine trace_cartesian_rhs_bhat(x,igrid_hint,threshold,bhat,igrid, &
8037 status)
8038 double precision, intent(in) :: x(ndim),threshold
8039 integer, intent(in) :: igrid_hint
8040 double precision, intent(out) :: bhat(ndim)
8041 integer, intent(out) :: igrid,status
8042
8043 double precision :: dxc^d,xd^d
8044 double precision :: field(0:1^d&,3),factor(0:1^D&)
8045 double precision :: bcell(3),b(3),bnorm
8046 integer :: ixi^l,ixbl^d,ix^d,j
8047
8048 bhat=zero
8049 igrid=-1
8051 if (.not.trace_cartesian_like_geometry()) return
8052
8053 {^ifthreed
8054 call trace_locate_point_with_hint(x,igrid_hint,igrid,status)
8055 if (status/=trace_status_active) return
8056
8057 ixi^l=ixg^ll;
8058 call trace_interp_weights_block(x,igrid,ixi^l,ixbl^d,xd^d,dxc^d, &
8059 status)
8060 if (status/=trace_status_active) return
8061
8062 field=zero
8063 {do ix^d=0,1\}
8064 call trace_total_b_at_cell(igrid,ixbl1+ix1,ixbl2+ix2, &
8065 ixbl3+ix3,bcell,status)
8066 if (status/=trace_status_active) return
8067 field(ix^d,1:3)=bcell
8068 {enddo\}
8069
8070 {do ix^d=0,1\}
8071 factor(ix^d)={abs(1-ix^d-xd^d)*}
8072 {enddo\}
8073
8074 b=zero
8075 {do ix^d=0,1\}
8076 do j=1,3
8077 b(j)=b(j)+field(ix^d,j)*factor(ix^d)
8078 enddo
8079 {enddo\}
8080
8081 bnorm=dsqrt(sum(b**2))
8082 if (bnorm<=zero .or. bnorm<threshold) then
8084 return
8085 endif
8086 bhat=b(1:ndim)/bnorm
8087 status=trace_status_active
8088 }
8089 end subroutine trace_cartesian_rhs_bhat
8090
8091 subroutine trace_rk45_try_boundary_finish(xnow,xstage,hfloor,xnext, &
8092 ds_actual,h_suggest,status,finished)
8093 double precision, intent(in) :: xnow(ndim),xstage(ndim),hfloor
8094 double precision, intent(out) :: xnext(ndim),ds_actual
8095 double precision, intent(inout) :: h_suggest
8096 integer, intent(out) :: status
8097 logical, intent(out) :: finished
8098
8099 double precision :: xhit(ndim)
8100 integer :: face_id
8101 logical :: hit_ok
8102
8103 finished=.false.
8104 call trace_intersect_domain(xnow,xstage,xhit,hit_ok,face_id)
8105 if (.not.hit_ok) return
8106
8107 xnext=xhit
8108 ds_actual=dsqrt(sum((xnext-xnow)**2))
8109 h_suggest=hfloor
8110 status=trace_status_active
8111 call trace_rk45_stats_note_attempt(.true.,.true.,ds_actual)
8112 finished=.true.
8113 end subroutine trace_rk45_try_boundary_finish
8114
8115 subroutine trace_summary_rk45_cartesian_step(xnow,igrid,ds,forward, &
8116 threshold,h_suggest,xnext,ds_actual,status,accumulate_twist, &
8117 twist_increment,twist_status,twist_integrated)
8118 double precision, intent(in) :: xnow(ndim),ds,threshold
8119 integer, intent(in) :: igrid
8120 logical, intent(in) :: forward
8121 double precision, intent(inout) :: h_suggest
8122 double precision, intent(out) :: xnext(ndim),ds_actual
8123 integer, intent(out) :: status
8124 logical, intent(in) :: accumulate_twist
8125 double precision, intent(out) :: twist_increment
8126 integer, intent(out) :: twist_status
8127 logical, intent(out) :: twist_integrated
8128
8129 double precision, parameter :: b21=1.d0/5.d0
8130 double precision, parameter :: b31=3.d0/40.d0,b32=9.d0/40.d0
8131 double precision, parameter :: b41=3.d0/10.d0,b42=-9.d0/10.d0, &
8132 b43=6.d0/5.d0
8133 double precision, parameter :: b51=-11.d0/54.d0,b52=5.d0/2.d0, &
8134 b53=-70.d0/27.d0,b54=35.d0/27.d0
8135 double precision, parameter :: b61=1631.d0/55296.d0, &
8136 b62=175.d0/512.d0,b63=575.d0/13824.d0, &
8137 b64=44275.d0/110592.d0,b65=253.d0/4096.d0
8138 double precision, parameter :: c1=37.d0/378.d0,c3=250.d0/621.d0, &
8139 c4=125.d0/594.d0,c6=512.d0/1771.d0
8140 double precision, parameter :: cs1=2825.d0/27648.d0, &
8141 cs3=18575.d0/48384.d0,cs4=13525.d0/55296.d0, &
8142 cs5=277.d0/14336.d0,cs6=one/4.d0
8143
8144 double precision :: k1(ndim),k2(ndim),k3(ndim),k4(ndim),k5(ndim),k6(ndim)
8145 double precision :: x2(ndim),x3(ndim),x4(ndim),x5s(ndim),x6(ndim)
8146 double precision :: x5(ndim),x4err(ndim),xhit(ndim)
8147 double precision :: h,hmax,hfloor,hcell,tol,err,grow,sgn
8148 double precision :: tw1,tw3,tw4,tw6
8149 double precision :: domain_min(ndim),domain_max(ndim)
8150 integer :: igrid1,igrid2,igrid3,igrid4,igrid5,igrid6
8151 integer :: iter,point_domain,face_id
8152 integer :: reject_reason
8153 logical :: hit_ok,boundary_limited,boundary_finished
8154
8155 xnext=xnow
8156 ds_actual=zero
8157 status=trace_status_active
8158 twist_increment=zero
8159 twist_status=trace_status_active
8160 twist_integrated=.false.
8161 if (.not.trace_cartesian_like_geometry()) then
8163 return
8164 endif
8165
8166 hmax=abs(ds)
8167 if (hmax<=zero) then
8169 return
8170 endif
8171 h=h_suggest
8172 if (h<=zero) h=hmax
8173 h=min(h,hmax)
8174 hfloor=max(trace_step_min,100.d0*epsilon(one)*max(one,hmax))
8175 h=max(h,hfloor)
8176 sgn=one
8177 if (.not.forward) sgn=-one
8178 ^d&domain_min(^d)=xprobmin^d;
8179 ^d&domain_max(^d)=xprobmax^d;
8180
8181 do iter=1,100
8182 h=max(hfloor,min(h,hmax))
8183 call trace_cartesian_rhs_bhat(xnow,igrid,threshold,k1,igrid1,status)
8184 if (status/=trace_status_active) return
8185
8186 x2=xnow+sgn*h*b21*k1
8187 call trace_cartesian_rhs_bhat(x2,igrid1,threshold,k2,igrid2,status)
8188 if (status/=trace_status_active) then
8189 if (status==trace_status_weak_field) return
8190 if (status==trace_status_boundary .or. &
8191 status==trace_status_out_of_domain) then
8192 call trace_rk45_try_boundary_finish(xnow,x2,hfloor,xnext, &
8193 ds_actual,h_suggest,status,boundary_finished)
8194 if (boundary_finished) return
8195 endif
8196 reject_reason=trace_rk45_reject_stage_failure
8197 if (status==trace_status_boundary .or. &
8199 reject_reason=trace_rk45_reject_stage_outside
8200 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8201 reject_reason)
8202 if (h<=hfloor*(one+epsilon(one))) exit
8203 h=max(hfloor,half*h)
8204 cycle
8205 endif
8206
8207 x3=xnow+sgn*h*(b31*k1+b32*k2)
8208 call trace_cartesian_rhs_bhat(x3,igrid2,threshold,k3,igrid3,status)
8209 if (status/=trace_status_active) then
8210 if (status==trace_status_weak_field) return
8211 if (status==trace_status_boundary .or. &
8212 status==trace_status_out_of_domain) then
8213 call trace_rk45_try_boundary_finish(xnow,x3,hfloor,xnext, &
8214 ds_actual,h_suggest,status,boundary_finished)
8215 if (boundary_finished) return
8216 endif
8217 reject_reason=trace_rk45_reject_stage_failure
8218 if (status==trace_status_boundary .or. &
8220 reject_reason=trace_rk45_reject_stage_outside
8221 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8222 reject_reason)
8223 if (h<=hfloor*(one+epsilon(one))) exit
8224 h=max(hfloor,half*h)
8225 cycle
8226 endif
8227
8228 x4=xnow+sgn*h*(b41*k1+b42*k2+b43*k3)
8229 call trace_cartesian_rhs_bhat(x4,igrid3,threshold,k4,igrid4,status)
8230 if (status/=trace_status_active) then
8231 if (status==trace_status_weak_field) return
8232 if (status==trace_status_boundary .or. &
8233 status==trace_status_out_of_domain) then
8234 call trace_rk45_try_boundary_finish(xnow,x4,hfloor,xnext, &
8235 ds_actual,h_suggest,status,boundary_finished)
8236 if (boundary_finished) return
8237 endif
8238 reject_reason=trace_rk45_reject_stage_failure
8239 if (status==trace_status_boundary .or. &
8241 reject_reason=trace_rk45_reject_stage_outside
8242 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8243 reject_reason)
8244 if (h<=hfloor*(one+epsilon(one))) exit
8245 h=max(hfloor,half*h)
8246 cycle
8247 endif
8248
8249 x5s=xnow+sgn*h*(b51*k1+b52*k2+b53*k3+b54*k4)
8250 call trace_cartesian_rhs_bhat(x5s,igrid4,threshold,k5,igrid5,status)
8251 if (status/=trace_status_active) then
8252 if (status==trace_status_weak_field) return
8253 if (status==trace_status_boundary .or. &
8254 status==trace_status_out_of_domain) then
8255 call trace_rk45_try_boundary_finish(xnow,x5s,hfloor,xnext, &
8256 ds_actual,h_suggest,status,boundary_finished)
8257 if (boundary_finished) return
8258 endif
8259 reject_reason=trace_rk45_reject_stage_failure
8260 if (status==trace_status_boundary .or. &
8262 reject_reason=trace_rk45_reject_stage_outside
8263 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8264 reject_reason)
8265 if (h<=hfloor*(one+epsilon(one))) exit
8266 h=max(hfloor,half*h)
8267 cycle
8268 endif
8269
8270 x6=xnow+sgn*h*(b61*k1+b62*k2+b63*k3+b64*k4+b65*k5)
8271 call trace_cartesian_rhs_bhat(x6,igrid5,threshold,k6,igrid6,status)
8272 if (status/=trace_status_active) then
8273 if (status==trace_status_weak_field) return
8274 if (status==trace_status_boundary .or. &
8275 status==trace_status_out_of_domain) then
8276 call trace_rk45_try_boundary_finish(xnow,x6,hfloor,xnext, &
8277 ds_actual,h_suggest,status,boundary_finished)
8278 if (boundary_finished) return
8279 endif
8280 reject_reason=trace_rk45_reject_stage_failure
8281 if (status==trace_status_boundary .or. &
8283 reject_reason=trace_rk45_reject_stage_outside
8284 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8285 reject_reason)
8286 if (h<=hfloor*(one+epsilon(one))) exit
8287 h=max(hfloor,half*h)
8288 cycle
8289 endif
8290
8291 x5=xnow+sgn*h*(c1*k1+c3*k3+c4*k4+c6*k6)
8292 x4err=xnow+sgn*h*(cs1*k1+cs3*k3+cs4*k4+cs5*k5+cs6*k6)
8293
8294 point_domain=0
8295 {if (x5(^db)>=domain_min(^db) .and. x5(^db)<domain_max(^db)) point_domain=point_domain+1\}
8296 if (point_domain/=ndim) then
8297 call trace_intersect_domain(xnow,x5,xhit,hit_ok,face_id)
8298 if (hit_ok) then
8299 xnext=xhit
8300 ds_actual=dsqrt(sum((xnext-xnow)**2))
8301 h_suggest=hfloor
8302 status=trace_status_active
8303 call trace_rk45_stats_note_attempt(.true.,.true.,ds_actual)
8304 return
8305 endif
8306 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8307 trace_rk45_reject_boundary)
8309 return
8310 endif
8311
8312 call trace_cartesian_local_cell_size(xnow,igrid1,hcell,status)
8313 if (status/=trace_status_active .or. hcell<=zero) hcell=hmax
8314 err=dsqrt(sum((x5-x4err)**2))
8315 tol=trace_rk45_atol+trace_rk45_rtol*max(h,hcell)
8316 if (err<=tol .or. h<=hfloor*(one+epsilon(one))) then
8317 xnext=x5
8318 ds_actual=h
8319 if (accumulate_twist) then
8320 call trace_twist_density_at_point(xnow,igrid1,threshold,tw1, &
8321 twist_status)
8322 if (twist_status==trace_status_active) &
8323 call trace_twist_density_at_point(x3,igrid3,threshold,tw3, &
8324 twist_status)
8325 if (twist_status==trace_status_active) &
8326 call trace_twist_density_at_point(x4,igrid4,threshold,tw4, &
8327 twist_status)
8328 if (twist_status==trace_status_active) &
8329 call trace_twist_density_at_point(x6,igrid6,threshold,tw6, &
8330 twist_status)
8331 if (twist_status==trace_status_active) then
8332 twist_increment=h*(c1*tw1+c3*tw3+c4*tw4+c6*tw6)
8333 twist_integrated=.true.
8334 endif
8335 endif
8336 if (err>zero .and. tol>zero) then
8337 grow=trace_rk45_safety*(tol/err)**0.2d0
8338 grow=max(trace_rk45_min_shrink,min(trace_rk45_max_grow,grow))
8339 else
8340 grow=trace_rk45_max_grow
8341 endif
8342 h_suggest=min(hmax,max(hfloor,h*grow))
8343 call trace_rk45_stats_note_attempt(.true.,.false.,h)
8344 status=trace_status_active
8345 return
8346 endif
8347
8348 grow=trace_rk45_safety*(tol/max(err,smalldouble))**0.25d0
8349 grow=max(trace_rk45_min_shrink,min(one,grow))
8350 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8351 trace_rk45_reject_error)
8352 h=max(hfloor,h*grow)
8353 enddo
8354
8355 call trace_intersect_domain(xnow,xnow+sgn*h*k1,xhit,hit_ok,face_id)
8356 if (hit_ok) then
8357 boundary_limited=.true.
8358 xnext=xhit
8359 ds_actual=dsqrt(sum((xnext-xnow)**2))
8360 h_suggest=hfloor
8361 call trace_rk45_stats_note_attempt(.true.,boundary_limited,ds_actual)
8362 status=trace_status_active
8363 else
8365 endif
8366 end subroutine trace_summary_rk45_cartesian_step
8367
8368 subroutine trace_summary_rk45_spherical_step(xnow,igrid,ds,forward, &
8369 threshold,h_suggest,sph_cache,xnext,ds_actual,status, &
8370 accumulate_twist,twist_increment,twist_status,twist_integrated)
8371 double precision, intent(in) :: xnow(ndim),ds,threshold
8372 integer, intent(in) :: igrid
8373 logical, intent(in) :: forward
8374 double precision, intent(inout) :: h_suggest
8375 type(trace_sph_interp_ctx), intent(inout) :: sph_cache
8376 double precision, intent(out) :: xnext(ndim),ds_actual
8377 integer, intent(out) :: status
8378 logical, intent(in) :: accumulate_twist
8379 double precision, intent(out) :: twist_increment
8380 integer, intent(out) :: twist_status
8381 logical, intent(out) :: twist_integrated
8382
8383 double precision, parameter :: b21=1.d0/5.d0
8384 double precision, parameter :: b31=3.d0/40.d0,b32=9.d0/40.d0
8385 double precision, parameter :: b41=3.d0/10.d0,b42=-9.d0/10.d0, &
8386 b43=6.d0/5.d0
8387 double precision, parameter :: b51=-11.d0/54.d0,b52=5.d0/2.d0, &
8388 b53=-70.d0/27.d0,b54=35.d0/27.d0
8389 double precision, parameter :: b61=1631.d0/55296.d0, &
8390 b62=175.d0/512.d0,b63=575.d0/13824.d0, &
8391 b64=44275.d0/110592.d0,b65=253.d0/4096.d0
8392 double precision, parameter :: c1=37.d0/378.d0,c3=250.d0/621.d0, &
8393 c4=125.d0/594.d0,c6=512.d0/1771.d0
8394 double precision, parameter :: cs1=2825.d0/27648.d0, &
8395 cs3=18575.d0/48384.d0,cs4=13525.d0/55296.d0, &
8396 cs5=277.d0/14336.d0,cs6=one/4.d0
8397
8398 double precision :: k1(ndim),k2(ndim),k3(ndim),k4(ndim),k5(ndim),k6(ndim)
8399 double precision :: x2(ndim),x3(ndim),x4(ndim),x5s(ndim),x6(ndim)
8400 double precision :: x5(ndim),x4err(ndim),xhit(ndim)
8401 double precision :: h,hmax,hfloor,hcell,tol,err,grow,sgn
8402 double precision :: tw1,tw3,tw4,tw6
8403 double precision :: r_metric,sin_theta_metric,alpha_hit
8404 double precision :: domain_min(ndim),domain_max(ndim)
8405 type(trace_sph_interp_ctx) :: ctx1,ctx2,ctx3,ctx4,ctx5,ctx6
8406 integer :: igrid1,igrid2,igrid3,igrid4,igrid5,igrid6
8407 integer :: iter,point_domain,face_id,ctx_status
8408 integer :: reject_reason
8409 logical :: hit_ok,field_ok
8410 character(len=std_len) :: field_type
8411
8412 xnext=xnow
8413 ds_actual=zero
8414 status=trace_status_active
8415 twist_increment=zero
8416 twist_status=trace_status_active
8417 twist_integrated=.false.
8418 if (geo_coordinate/=geo_spherical) then
8420 return
8421 endif
8422
8423 hmax=abs(ds)
8424 if (hmax<=zero) then
8426 return
8427 endif
8428 h=h_suggest
8429 if (h<=zero) h=hmax
8430 h=min(h,hmax)
8431 hfloor=max(trace_step_min,100.d0*epsilon(one)*max(one,hmax))
8432 h=max(h,hfloor)
8433 sgn=one
8434 if (.not.forward) sgn=-one
8435 field_type='Bfield'
8436 ^d&domain_min(^d)=xprobmin^d;
8437 ^d&domain_max(^d)=xprobmax^d;
8438
8439 do iter=1,100
8440 h=max(hfloor,min(h,hmax))
8441 call trace_spherical_interp_ctx_build_cached(xnow,igrid,sph_cache, &
8442 ctx1,ctx_status)
8443 if (ctx_status/=trace_status_active) then
8444 status=ctx_status
8445 return
8446 endif
8447 call get_k_spherical_ctx(ctx1,k1,field_type,threshold,field_ok)
8448 if (.not.field_ok) then
8450 return
8451 endif
8452 igrid1=ctx1%igrid
8453 hcell=ctx1%h_local
8454
8455 x2=xnow+sgn*h*b21*k1
8456 call trace_spherical_interp_ctx_build_cached(x2,igrid1,sph_cache, &
8457 ctx2,ctx_status)
8458 if (ctx_status/=trace_status_active) then
8459 reject_reason=trace_rk45_reject_stage_failure
8460 if (ctx_status==trace_status_boundary .or. &
8461 ctx_status==trace_status_out_of_domain) &
8462 reject_reason=trace_rk45_reject_stage_outside
8463 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8464 reject_reason)
8465 if (h<=hfloor*(one+epsilon(one))) exit
8466 h=max(hfloor,half*h)
8467 cycle
8468 endif
8469 call get_k_spherical_ctx(ctx2,k2,field_type,threshold,field_ok)
8470 if (.not.field_ok) then
8472 return
8473 endif
8474 igrid2=ctx2%igrid
8475
8476 x3=xnow+sgn*h*(b31*k1+b32*k2)
8477 call trace_spherical_interp_ctx_build_cached(x3,igrid2,sph_cache, &
8478 ctx3,ctx_status)
8479 if (ctx_status/=trace_status_active) then
8480 reject_reason=trace_rk45_reject_stage_failure
8481 if (ctx_status==trace_status_boundary .or. &
8482 ctx_status==trace_status_out_of_domain) &
8483 reject_reason=trace_rk45_reject_stage_outside
8484 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8485 reject_reason)
8486 if (h<=hfloor*(one+epsilon(one))) exit
8487 h=max(hfloor,half*h)
8488 cycle
8489 endif
8490 call get_k_spherical_ctx(ctx3,k3,field_type,threshold,field_ok)
8491 if (.not.field_ok) then
8493 return
8494 endif
8495 igrid3=ctx3%igrid
8496
8497 x4=xnow+sgn*h*(b41*k1+b42*k2+b43*k3)
8498 call trace_spherical_interp_ctx_build_cached(x4,igrid3,sph_cache, &
8499 ctx4,ctx_status)
8500 if (ctx_status/=trace_status_active) then
8501 reject_reason=trace_rk45_reject_stage_failure
8502 if (ctx_status==trace_status_boundary .or. &
8503 ctx_status==trace_status_out_of_domain) &
8504 reject_reason=trace_rk45_reject_stage_outside
8505 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8506 reject_reason)
8507 if (h<=hfloor*(one+epsilon(one))) exit
8508 h=max(hfloor,half*h)
8509 cycle
8510 endif
8511 call get_k_spherical_ctx(ctx4,k4,field_type,threshold,field_ok)
8512 if (.not.field_ok) then
8514 return
8515 endif
8516 igrid4=ctx4%igrid
8517
8518 x5s=xnow+sgn*h*(b51*k1+b52*k2+b53*k3+b54*k4)
8519 call trace_spherical_interp_ctx_build_cached(x5s,igrid4,sph_cache, &
8520 ctx5,ctx_status)
8521 if (ctx_status/=trace_status_active) then
8522 reject_reason=trace_rk45_reject_stage_failure
8523 if (ctx_status==trace_status_boundary .or. &
8524 ctx_status==trace_status_out_of_domain) &
8525 reject_reason=trace_rk45_reject_stage_outside
8526 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8527 reject_reason)
8528 if (h<=hfloor*(one+epsilon(one))) exit
8529 h=max(hfloor,half*h)
8530 cycle
8531 endif
8532 call get_k_spherical_ctx(ctx5,k5,field_type,threshold,field_ok)
8533 if (.not.field_ok) then
8535 return
8536 endif
8537 igrid5=ctx5%igrid
8538
8539 x6=xnow+sgn*h*(b61*k1+b62*k2+b63*k3+b64*k4+b65*k5)
8540 call trace_spherical_interp_ctx_build_cached(x6,igrid5,sph_cache, &
8541 ctx6,ctx_status)
8542 if (ctx_status/=trace_status_active) then
8543 reject_reason=trace_rk45_reject_stage_failure
8544 if (ctx_status==trace_status_boundary .or. &
8545 ctx_status==trace_status_out_of_domain) &
8546 reject_reason=trace_rk45_reject_stage_outside
8547 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8548 reject_reason)
8549 if (h<=hfloor*(one+epsilon(one))) exit
8550 h=max(hfloor,half*h)
8551 cycle
8552 endif
8553 call get_k_spherical_ctx(ctx6,k6,field_type,threshold,field_ok)
8554 if (.not.field_ok) then
8556 return
8557 endif
8558
8559 x5=xnow+sgn*h*(c1*k1+c3*k3+c4*k4+c6*k6)
8560 x4err=xnow+sgn*h*(cs1*k1+cs3*k3+cs4*k4+cs5*k5+cs6*k6)
8561
8562 point_domain=0
8563 {if (x5(^db)>=domain_min(^db) .and. x5(^db)<domain_max(^db)) point_domain=point_domain+1\}
8564 if (point_domain/=ndim) then
8565 call trace_intersect_domain(xnow,x5,xhit,hit_ok,face_id, &
8566 alpha_hit)
8567 if (hit_ok) then
8568 xnext=xhit
8569 ds_actual=max(zero,min(one,alpha_hit))*h
8570 h_suggest=hfloor
8571 status=trace_status_active
8572 call trace_rk45_stats_note_attempt(.true.,.true.,ds_actual)
8573 return
8574 endif
8575 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8576 trace_rk45_reject_boundary)
8578 return
8579 endif
8580
8581 err=zero
8582 {^ifthreed
8583 r_metric=max(abs(xnow(1)),smalldouble)
8584 sin_theta_metric=max(abs(dsin(xnow(2))),smalldouble)
8585 err=dsqrt((x5(1)-x4err(1))**2+ &
8586 (r_metric*(x5(2)-x4err(2)))**2+ &
8587 (r_metric*sin_theta_metric*(x5(3)-x4err(3)))**2)
8588 }
8589 if (hcell<=zero) hcell=hmax
8590 tol=trace_rk45_atol+trace_rk45_rtol*max(h,hcell)
8591 if (err<=tol .or. h<=hfloor*(one+epsilon(one))) then
8592 xnext=x5
8593 ds_actual=h
8594 if (accumulate_twist) then
8595 call trace_twist_density_at_point(xnow,ctx1%igrid,threshold,tw1, &
8596 twist_status,sph_cache)
8597 if (twist_status==trace_status_active) &
8598 call trace_twist_density_at_point(x3,ctx3%igrid,threshold,tw3, &
8599 twist_status,sph_cache)
8600 if (twist_status==trace_status_active) &
8601 call trace_twist_density_at_point(x4,ctx4%igrid,threshold,tw4, &
8602 twist_status,sph_cache)
8603 if (twist_status==trace_status_active) &
8604 call trace_twist_density_at_point(x6,ctx6%igrid,threshold,tw6, &
8605 twist_status,sph_cache)
8606 if (twist_status==trace_status_active) then
8607 twist_increment=h*(c1*tw1+c3*tw3+c4*tw4+c6*tw6)
8608 twist_integrated=.true.
8609 endif
8610 endif
8611 if (err>zero .and. tol>zero) then
8612 grow=trace_rk45_safety*(tol/err)**0.2d0
8613 grow=max(trace_rk45_min_shrink,min(trace_rk45_max_grow,grow))
8614 else
8615 grow=trace_rk45_max_grow
8616 endif
8617 h_suggest=min(hmax,max(hfloor,h*grow))
8618 call trace_rk45_stats_note_attempt(.true.,.false.,h)
8619 status=trace_status_active
8620 return
8621 endif
8622
8623 grow=trace_rk45_safety*(tol/max(err,smalldouble))**0.25d0
8624 grow=max(trace_rk45_min_shrink,min(one,grow))
8625 call trace_rk45_stats_note_attempt(.false.,.false.,h, &
8626 trace_rk45_reject_error)
8627 h=max(hfloor,h*grow)
8628 enddo
8629
8631 end subroutine trace_summary_rk45_spherical_step
8632
8633 subroutine trace_summary_rk2_step(xnow,igrid,ds,forward,threshold, &
8634 xnext,ds_actual,status,sph_cache)
8635 double precision, intent(in) :: xnow(ndim),ds,threshold
8636 integer, intent(in) :: igrid
8637 logical, intent(in) :: forward
8638 double precision, intent(out) :: xnext(ndim),ds_actual
8639 integer, intent(out) :: status
8640 type(trace_sph_interp_ctx), intent(inout), optional :: sph_cache
8641
8642 double precision :: xstage(ndim),xstage_sample(ndim),k1(ndim),k2(ndim)
8643 double precision :: ds_step
8644 double precision :: dxb^d
8645 type(trace_sph_interp_ctx) :: ctx_now,ctx_stage
8646 integer :: ixi^l
8647 integer :: igrid_stage
8648 integer :: ctx_status
8649 logical :: field_ok
8650 character(len=std_len) :: field_type
8651
8652 ixi^l=ixg^ll;
8653 ^d&dxb^d=rnode(rpdx^d_,igrid);
8654 field_type='Bfield'
8655 xnext=xnow
8656 ds_actual=zero
8657 status=trace_status_active
8658
8659 if (geo_coordinate==geo_spherical) then
8660 if (present(sph_cache)) then
8661 call trace_spherical_interp_ctx_build_cached(xnow,igrid,sph_cache, &
8662 ctx_now,ctx_status)
8663 else
8664 call trace_spherical_interp_ctx_build(xnow,igrid,ctx_now,ctx_status)
8665 endif
8666 if (ctx_status/=trace_status_active) then
8667 status=ctx_status
8668 return
8669 endif
8670 ds_step=trace_spherical_effective_step_ctx(ds,ctx_now)
8671 call get_k_spherical_ctx(ctx_now,k1,field_type,threshold,field_ok)
8672 else
8673 ds_step=trace_effective_step(xnow,ds,igrid,dxb^d)
8674 call get_k(xnow,igrid,k1,ixi^l,dxb^d,field_type,threshold,field_ok)
8675 endif
8676 if (.not.field_ok) then
8678 return
8679 endif
8680
8681 if (forward) then
8682 xstage=xnow+ds_step*k1
8683 else
8684 xstage=xnow-ds_step*k1
8685 endif
8686 xstage_sample=xstage
8687 ^d&xstage_sample(^d)=max(xprobmin^d,min(xstage_sample(^d), &
8688 xprobmax^d-100.d0*epsilon(one)*max(one,abs(xprobmax^d))));
8689 call trace_locate_point_with_hint(xstage_sample,igrid,igrid_stage,status)
8690 if (status/=trace_status_active) return
8691 ^d&dxb^d=rnode(rpdx^d_,igrid_stage);
8692 if (geo_coordinate==geo_spherical) then
8693 if (present(sph_cache)) then
8694 call trace_spherical_interp_ctx_build_cached(xstage_sample, &
8695 igrid_stage,sph_cache,ctx_stage,ctx_status)
8696 else
8697 call trace_spherical_interp_ctx_build(xstage_sample,igrid_stage, &
8698 ctx_stage,ctx_status)
8699 endif
8700 if (ctx_status/=trace_status_active) then
8701 status=ctx_status
8702 return
8703 endif
8704 call get_k_spherical_ctx(ctx_stage,k2,field_type,threshold,field_ok)
8705 else
8706 call get_k(xstage_sample,igrid_stage,k2,ixi^l,dxb^d,field_type, &
8707 threshold,field_ok)
8708 endif
8709 if (.not.field_ok) then
8711 return
8712 endif
8713
8714 if (forward) then
8715 xnext=xnow+ds_step*(half*k1+half*k2)
8716 else
8717 xnext=xnow-ds_step*(half*k1+half*k2)
8718 endif
8719 if (geo_coordinate==geo_spherical) then
8720 ds_actual=abs(ds_step)
8721 else
8722 ds_actual=dsqrt(sum((xnext-xnow)**2))
8723 endif
8724 end subroutine trace_summary_rk2_step
8725
8726 double precision function trace_effective_step(x,ds,igrid,dxb^D) &
8727 result(ds_eff)
8728 double precision, intent(in) :: x(ndim),ds
8729 integer, intent(in) :: igrid
8730 double precision, intent(in) :: dxb^d
8731
8732 double precision :: r,sin_theta,cell_scale,dxloc(ndim),step_cap,hcell
8733 type(trace_sph_interp_ctx) :: ctx
8734 integer :: status
8735
8736 ds_eff=ds
8737 if (trace_cartesian_like_geometry()) then
8738 if (trace_step_control_mode==trace_step_control_cell_fraction) then
8739 step_cap=abs(ds)
8740 call trace_cartesian_local_cell_size(x,igrid,hcell,status)
8741 if (status==trace_status_active .and. hcell>zero) then
8742 ds_eff=dsign(min(step_cap,trace_step_fraction*hcell),ds)
8743 if (trace_step_min>zero) then
8744 ds_eff=dsign(max(abs(ds_eff),min(trace_step_min,step_cap)),ds)
8745 endif
8746 if (trace_rk2_stats_enabled) call trace_rk2_stats_note_step_limit(step_cap, &
8747 trace_step_fraction*hcell,abs(ds_eff))
8748 endif
8749 endif
8750 return
8751 endif
8752 if (geo_coordinate/=geo_spherical) return
8753
8754 {^ifthreed
8755 call trace_spherical_interp_ctx_build(x,igrid,ctx,status)
8756 if (status==trace_status_active) then
8757 ds_eff=trace_spherical_effective_step_ctx(ds,ctx)
8758 return
8759 endif
8760
8761 step_cap=abs(ds)
8762 call trace_spherical_local_cell_widths(x,igrid,dxloc,status)
8763 if (status/=trace_status_active) return
8764 r=max(abs(x(1)),smalldouble)
8765 sin_theta=abs(dsin(x(2)))
8766 cell_scale=min(abs(dxloc(1)),r*abs(dxloc(2)))
8767 cell_scale=min(cell_scale,r*max(sin_theta,smalldouble)*abs(dxloc(3)))
8768 if (cell_scale>zero) then
8769 select case (trace_step_control_mode)
8770 case (trace_step_control_cell_fraction)
8771 ds_eff=min(step_cap,trace_step_fraction*cell_scale)
8772 if (trace_step_min>zero) ds_eff=max(ds_eff,min(trace_step_min,step_cap))
8773 case default
8774 ds_eff=min(step_cap,cell_scale)
8775 end select
8776 endif
8777 }
8778 end function trace_effective_step
8779
8780 logical function trace_spherical_metric_ok(x) result(is_ok)
8781 double precision, intent(in) :: x(ndim)
8782
8783 double precision :: metric_tol
8784
8785 is_ok=.false.
8786 if (ndim/=3) return
8787 {^ifthreed
8788 metric_tol=100.d0*epsilon(one)
8789 is_ok=x(1)>metric_tol .and. abs(dsin(x(2)))>metric_tol
8790 }
8791 end function trace_spherical_metric_ok
8792
8793 double precision function trace_segment_length(xa,xb,ds_full,alpha) &
8794 result(length)
8795 double precision, intent(in) :: xa(ndim),xb(ndim),ds_full
8796 double precision, intent(in), optional :: alpha
8797
8798 if (geo_coordinate==geo_spherical) then
8799 if (present(alpha)) then
8800 length=max(zero,min(one,alpha))*abs(ds_full)
8801 else
8802 length=abs(ds_full)
8803 endif
8804 else
8805 length=dsqrt(sum((xb-xa)**2))
8806 endif
8807 end function trace_segment_length
8808
8809 subroutine trace_field_length_multi(seeds,nseed,dL,max_steps,results,b_min)
8810 ! Trace multiple seeds without storing field-line paths.
8811 integer, intent(in) :: nseed,max_steps
8812 double precision, intent(in) :: seeds(nseed,ndim),dl
8813 type(trace_length_result), intent(out) :: results(nseed)
8814 double precision, intent(in), optional :: b_min
8815
8816 double precision :: field_min
8817
8818 field_min=smalldouble
8819 if (present(b_min)) field_min=max(b_min,zero)
8820 call trace_summary_multi(seeds,nseed,dl,max_steps,results,field_min)
8821 end subroutine trace_field_length_multi
8822
8823 subroutine trace_field_twist_multi(seeds,nseed,dL,max_steps,results,b_min)
8824 ! Trace multiple seeds and integrate twist without storing field-line paths.
8825 integer, intent(in) :: nseed,max_steps
8826 double precision, intent(in) :: seeds(nseed,ndim),dl
8827 type(trace_twist_result), intent(out) :: results(nseed)
8828 double precision, intent(in), optional :: b_min
8829
8830 double precision :: field_min
8831
8832 field_min=smalldouble
8833 if (present(b_min)) field_min=max(b_min,zero)
8834 call trace_summary_twist_multi(seeds,nseed,dl,max_steps,results,field_min)
8835 end subroutine trace_field_twist_multi
8836
8837 subroutine trace_field_mapping_multi(seeds,nseed,dL,max_steps,results, &
8838 b_min,source_normal)
8839 ! Trace multiple seeds and return endpoint metadata without storing paths.
8840 integer, intent(in) :: nseed,max_steps
8841 double precision, intent(in) :: seeds(nseed,ndim),dl
8842 type(trace_mapping_result), intent(out) :: results(nseed)
8843 double precision, intent(in), optional :: b_min
8844 double precision, intent(in), optional :: source_normal(3)
8845
8846 double precision :: field_min,normal(3)
8847 logical :: have_normal
8848
8849 field_min=smalldouble
8850 if (present(b_min)) field_min=max(b_min,zero)
8851 normal=zero
8852 have_normal=present(source_normal)
8853 if (have_normal) normal=source_normal
8854 call trace_summary_mapping_multi(seeds,nseed,dl,max_steps,results, &
8855 field_min,have_normal,normal)
8856 end subroutine trace_field_mapping_multi
8857
8858 subroutine trace_field_topology_multi(seeds,nseed,dL,max_steps,results, &
8859 need_twist,need_mapping,b_min,source_normal)
8860 ! Trace multiple seeds once and return length plus optional Tw/mapping.
8861 integer, intent(in) :: nseed,max_steps
8862 double precision, intent(in) :: seeds(nseed,ndim),dl
8863 type(trace_topology_result), intent(out) :: results(nseed)
8864 logical, intent(in), optional :: need_twist,need_mapping
8865 double precision, intent(in), optional :: b_min
8866 double precision, intent(in), optional :: source_normal(3)
8867
8868 double precision :: field_min,normal(3)
8869 logical :: do_twist,do_mapping,have_normal
8870
8871 field_min=smalldouble
8872 if (present(b_min)) field_min=max(b_min,zero)
8873 do_twist=.false.
8874 do_mapping=.false.
8875 if (present(need_twist)) do_twist=need_twist
8876 if (present(need_mapping)) do_mapping=need_mapping
8877 normal=zero
8878 have_normal=present(source_normal)
8879 if (have_normal) normal=source_normal
8880
8881 call trace_summary_topology_multi(seeds,nseed,dl,max_steps,results, &
8882 field_min,do_twist,do_mapping,have_normal,normal)
8883 end subroutine trace_field_topology_multi
8884
8885 subroutine trace_field_qperp_multi(seeds,nseed,dL,max_steps,results,b_min, &
8886 twist_results)
8887 ! Block-grouped tangent-state tracing for multiple Method-II/Q_perp seeds.
8888 integer, intent(in) :: nseed,max_steps
8889 double precision, intent(in) :: seeds(nseed,ndim),dl
8890 type(trace_qperp_result), intent(out) :: results(nseed)
8891 double precision, intent(in), optional :: b_min
8892 type(trace_twist_result), intent(out), optional :: twist_results(nseed)
8893
8894 type(trace_tangent_state), allocatable :: states(:)
8895 double precision :: field_min
8896 double precision :: seed_local(ndim),zero_vec(ndim)
8897 integer :: common_status,iseed,idim,igrid,iforward,ibackward
8898 integer :: seed_status
8899 logical :: do_twist
8900
8901 if (nseed<=0) return
8902
8903 field_min=smalldouble
8904 if (present(b_min)) field_min=max(b_min,zero)
8905 zero_vec=zero
8906 do_twist=present(twist_results)
8907
8908 common_status=trace_status_active
8909 if (npe/=1) then
8910 common_status=trace_status_mpi_unsupported
8911 else if (ndim/=3 .or. .not.trace_cartesian_like_geometry()) then
8913 else if (dl<=zero .or. max_steps<=0) then
8914 common_status=trace_status_invalid_input
8915 endif
8916 if (common_status/=trace_status_active) then
8917 do iseed=1,nseed
8918 do idim=1,ndim
8919 seed_local(idim)=seeds(iseed,idim)
8920 enddo
8921 call trace_init_qperp_result(seed_local,results(iseed))
8922 results(iseed)%status=common_status
8923 if (do_twist) call trace_summary_init_twist_result(seed_local, &
8924 twist_results(iseed),common_status)
8925 enddo
8926 return
8927 endif
8928
8929 allocate(states(2*nseed))
8930 do iseed=1,nseed
8931 do idim=1,ndim
8932 seed_local(idim)=seeds(iseed,idim)
8933 enddo
8934 call trace_qperp_prepare_seed_result(seed_local,field_min, &
8935 results(iseed),igrid,seed_status)
8936 iforward=2*iseed-1
8937 ibackward=2*iseed
8938 if (seed_status==trace_status_active) then
8939 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
8940 results(iseed)%v0,igrid,iseed,1,states(iforward), &
8941 accumulate_twist=do_twist)
8942 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
8943 results(iseed)%v0,igrid,iseed,-1,states(ibackward), &
8944 accumulate_twist=do_twist)
8945 if (trace_integrator_mode==trace_integrator_rk45_cartesian .or. &
8946 trace_integrator_mode==trace_integrator_rk45_spherical) then
8947 call trace_rk45_stats_note_direction()
8948 call trace_rk45_stats_note_direction()
8949 else if (trace_integrator_mode==trace_integrator_rk2) then
8950 call trace_rk2_stats_note_direction()
8951 call trace_rk2_stats_note_direction()
8952 endif
8953 else
8954 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
8955 iseed,1,states(iforward),accumulate_twist=do_twist)
8956 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
8957 iseed,-1,states(ibackward),accumulate_twist=do_twist)
8958 states(iforward)%active=.false.
8959 states(ibackward)%active=.false.
8960 states(iforward)%status=seed_status
8961 states(ibackward)%status=seed_status
8962 endif
8963 enddo
8964
8965 select case (trace_integrator_mode)
8966 case (trace_integrator_rk45_cartesian)
8967 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
8968 field_min,trace_mode=trace_tangent_group_rk45_cartesian)
8969 case (trace_integrator_rk45_spherical)
8970 call trace_tangent_trace_states_grouped_rk45_spherical(states, &
8971 2*nseed,dl,max_steps,field_min)
8972 case default
8973 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
8974 field_min)
8975 end select
8976
8977 do iseed=1,nseed
8978 iforward=2*iseed-1
8979 ibackward=2*iseed
8980 if (results(iseed)%status==trace_status_active) then
8981 call trace_qperp_finalize_from_states(states(iforward), &
8982 states(ibackward),results(iseed),field_min)
8983 endif
8984 if (do_twist) call trace_tangent_fill_twist_result(seeds(iseed,:), &
8985 states(iforward),states(ibackward),twist_results(iseed))
8986 enddo
8987 deallocate(states)
8988 end subroutine trace_field_qperp_multi
8989
8990 subroutine trace_field_q0_multi_rk45_cartesian(seeds,nseed,dL, &
8991 max_steps,results,b_min,twist_results)
8992 ! Cartesian standard-q0 tangent transport using RK45 stages.
8993 ! The final q0 projection is valid for Cartesian-like grids because it
8994 ! uses domain-face endpoints, endpoint face-limit B, and transported
8995 ! tangent vectors from the same RK45 tangent states as Qperp.
8996 integer, intent(in) :: nseed,max_steps
8997 double precision, intent(in) :: seeds(nseed,ndim),dl
8998 type(trace_qperp_result), intent(out) :: results(nseed)
8999 double precision, intent(in), optional :: b_min
9000 type(trace_twist_result), intent(out), optional :: twist_results(nseed)
9001
9002 type(trace_tangent_state), allocatable :: states(:)
9003 double precision :: field_min
9004 double precision :: seed_local(ndim),zero_vec(ndim)
9005 integer :: common_status,iseed,idim,igrid,iforward,ibackward
9006 integer :: seed_status
9007 logical :: do_twist
9008
9009 if (nseed<=0) return
9010
9011 field_min=smalldouble
9012 if (present(b_min)) field_min=max(b_min,zero)
9013 zero_vec=zero
9014 do_twist=present(twist_results)
9015
9016 common_status=trace_status_active
9017 if (npe/=1) then
9018 common_status=trace_status_mpi_unsupported
9019 else if (ndim/=3 .or. .not.trace_cartesian_like_geometry()) then
9021 else if (dl<=zero .or. max_steps<=0) then
9022 common_status=trace_status_invalid_input
9023 endif
9024 if (common_status/=trace_status_active) then
9025 do iseed=1,nseed
9026 do idim=1,ndim
9027 seed_local(idim)=seeds(iseed,idim)
9028 enddo
9029 call trace_init_qperp_result(seed_local,results(iseed))
9030 results(iseed)%status=common_status
9031 results(iseed)%status_q0=common_status
9032 if (do_twist) call trace_summary_init_twist_result(seed_local, &
9033 twist_results(iseed),common_status)
9034 enddo
9035 return
9036 endif
9037
9038 allocate(states(2*nseed))
9039 do iseed=1,nseed
9040 do idim=1,ndim
9041 seed_local(idim)=seeds(iseed,idim)
9042 enddo
9043 call trace_qperp_prepare_seed_result(seed_local,field_min, &
9044 results(iseed),igrid,seed_status)
9045 iforward=2*iseed-1
9046 ibackward=2*iseed
9047 if (seed_status==trace_status_active) then
9048 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9049 results(iseed)%v0,igrid,iseed,1,states(iforward), &
9050 accumulate_twist=do_twist)
9051 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9052 results(iseed)%v0,igrid,iseed,-1,states(ibackward), &
9053 accumulate_twist=do_twist)
9054 call trace_rk45_stats_note_direction()
9055 call trace_rk45_stats_note_direction()
9056 else
9057 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9058 iseed,1,states(iforward),accumulate_twist=do_twist)
9059 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9060 iseed,-1,states(ibackward),accumulate_twist=do_twist)
9061 states(iforward)%active=.false.
9062 states(ibackward)%active=.false.
9063 states(iforward)%status=seed_status
9064 states(ibackward)%status=seed_status
9065 results(iseed)%status_q0=seed_status
9066 endif
9067 enddo
9068
9069 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
9070 field_min,trace_mode=trace_tangent_group_rk45_cartesian)
9071
9072 do iseed=1,nseed
9073 iforward=2*iseed-1
9074 ibackward=2*iseed
9075 if (results(iseed)%status==trace_status_active) then
9076 call trace_q0_finalize_from_states(states(iforward), &
9077 states(ibackward),results(iseed),field_min)
9078 endif
9079 if (do_twist) call trace_tangent_fill_twist_result(seeds(iseed,:), &
9080 states(iforward),states(ibackward),twist_results(iseed))
9081 enddo
9082 deallocate(states)
9084
9086 max_steps,results,b_min)
9087 integer, intent(in) :: nseed,max_steps
9088 double precision, intent(in) :: seeds(nseed,ndim),dl
9089 type(trace_qperp_result), intent(out) :: results(nseed)
9090 double precision, intent(in), optional :: b_min
9091
9092 if (present(b_min)) then
9093 call trace_field_q0_multi_rk45_cartesian(seeds,nseed,dl,max_steps, &
9094 results,b_min)
9095 else
9096 call trace_field_q0_multi_rk45_cartesian(seeds,nseed,dl,max_steps, &
9097 results)
9098 endif
9100
9101 subroutine trace_field_spherical_rmin_q_multi(seeds,nseed,dL,max_steps, &
9102 results,b_min,twist_results)
9103 ! Legacy-named radial-boundary spherical Q from tangent transport.
9104 integer, intent(in) :: nseed,max_steps
9105 double precision, intent(in) :: seeds(nseed,ndim),dl
9106 type(trace_qperp_result), intent(out) :: results(nseed)
9107 double precision, intent(in), optional :: b_min
9108 type(trace_twist_result), intent(out), optional :: twist_results(nseed)
9109
9110 type(trace_tangent_state), allocatable :: states(:)
9111 double precision :: field_min
9112 double precision :: seed_local(ndim),zero_vec(ndim)
9113 integer :: common_status,iseed,idim,igrid,iforward,ibackward
9114 integer :: seed_status
9115 logical :: do_twist
9116
9117 if (nseed<=0) return
9118 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
9119 call trace_spherical_profile_add_count(trace_profile_q_tangent_systems, &
9120 int(2*nseed,kind=8))
9121
9122 field_min=smalldouble
9123 if (present(b_min)) field_min=max(b_min,zero)
9124 zero_vec=zero
9125 do_twist=present(twist_results)
9126
9127 common_status=trace_status_active
9128 if (npe/=1) then
9129 common_status=trace_status_mpi_unsupported
9130 else if (ndim/=3 .or. geo_coordinate/=geo_spherical) then
9132 else if (dl<=zero .or. max_steps<=0) then
9133 common_status=trace_status_invalid_input
9134 endif
9135 if (common_status/=trace_status_active) then
9136 do iseed=1,nseed
9137 do idim=1,ndim
9138 seed_local(idim)=seeds(iseed,idim)
9139 enddo
9140 call trace_init_qperp_result(seed_local,results(iseed))
9141 results(iseed)%status=common_status
9142 results(iseed)%status_q0=common_status
9143 if (do_twist) call trace_summary_init_twist_result(seed_local, &
9144 twist_results(iseed),common_status)
9145 enddo
9146 return
9147 endif
9148
9149 allocate(states(2*nseed))
9150 do iseed=1,nseed
9151 do idim=1,ndim
9152 seed_local(idim)=seeds(iseed,idim)
9153 enddo
9154 call trace_spherical_rmin_q_prepare_seed_result(seed_local,field_min, &
9155 results(iseed),igrid,seed_status)
9156 iforward=2*iseed-1
9157 ibackward=2*iseed
9158 if (seed_status==trace_status_active) then
9159 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9160 results(iseed)%v0,igrid,iseed,1,states(iforward), &
9161 accumulate_twist=do_twist)
9162 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9163 results(iseed)%v0,igrid,iseed,-1,states(ibackward), &
9164 accumulate_twist=do_twist)
9165 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9166 call trace_rk45_stats_note_direction()
9167 call trace_rk45_stats_note_direction()
9168 else if (trace_integrator_mode==trace_integrator_rk2) then
9169 call trace_rk2_stats_note_direction()
9170 call trace_rk2_stats_note_direction()
9171 endif
9172 else
9173 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9174 iseed,1,states(iforward),accumulate_twist=do_twist)
9175 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9176 iseed,-1,states(ibackward),accumulate_twist=do_twist)
9177 states(iforward)%active=.false.
9178 states(ibackward)%active=.false.
9179 states(iforward)%status=seed_status
9180 states(ibackward)%status=seed_status
9181 endif
9182 enddo
9183
9184 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9185 call trace_tangent_trace_states_grouped_rk45_spherical(states, &
9186 2*nseed,dl,max_steps,field_min)
9187 else
9188 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
9189 field_min)
9190 endif
9191
9192 do iseed=1,nseed
9193 iforward=2*iseed-1
9194 ibackward=2*iseed
9195 if (results(iseed)%status==trace_status_active) then
9196 call trace_spherical_rmin_q_finalize_from_states(states(iforward), &
9197 states(ibackward),results(iseed),field_min)
9198 endif
9199 if (do_twist) call trace_tangent_fill_twist_result(seeds(iseed,:), &
9200 states(iforward),states(ibackward),twist_results(iseed))
9201 enddo
9202 deallocate(states)
9204
9205 subroutine trace_field_rk2_short_boundary_q_multi(seeds,nseed,dL, &
9206 max_steps,results,b_min,twist_results)
9207 ! RK2 q0/logQ tangent tracing with a shortened final boundary step.
9208 ! Seed-products use this as the unified non-Qperp length/Tw/logQ trace.
9209 integer, intent(in) :: nseed,max_steps
9210 double precision, intent(in) :: seeds(nseed,ndim),dl
9211 type(trace_qperp_result), intent(out) :: results(nseed)
9212 double precision, intent(in), optional :: b_min
9213 type(trace_twist_result), intent(out), optional :: twist_results(nseed)
9214
9215 type(trace_tangent_state), allocatable :: states(:)
9216 double precision :: field_min
9217 double precision :: seed_local(ndim),zero_vec(ndim)
9218 integer :: common_status,iseed,idim,igrid,iforward,ibackward
9219 integer :: seed_status
9220 logical :: do_twist,is_spherical
9221
9222 if (nseed<=0) return
9223
9224 field_min=smalldouble
9225 if (present(b_min)) field_min=max(b_min,zero)
9226 zero_vec=zero
9227 do_twist=present(twist_results)
9228 is_spherical=(geo_coordinate==geo_spherical)
9229
9230 common_status=trace_status_active
9231 if (npe/=1) then
9232 common_status=trace_status_mpi_unsupported
9233 else if (ndim/=3) then
9235 else if (geo_coordinate==geo_cartesian .and. .not.slab_uniform) then
9237 else if (geo_coordinate/=geo_cartesian .and. &
9238 geo_coordinate/=geo_spherical) then
9240 else if (dl<=zero .or. max_steps<=0) then
9241 common_status=trace_status_invalid_input
9242 endif
9243 if (common_status/=trace_status_active) then
9244 do iseed=1,nseed
9245 do idim=1,ndim
9246 seed_local(idim)=seeds(iseed,idim)
9247 enddo
9248 call trace_init_qperp_result(seed_local,results(iseed))
9249 results(iseed)%status=common_status
9250 results(iseed)%status_q0=common_status
9251 if (do_twist) call trace_summary_init_twist_result(seed_local, &
9252 twist_results(iseed),common_status)
9253 enddo
9254 return
9255 endif
9256
9257 allocate(states(2*nseed))
9258 do iseed=1,nseed
9259 do idim=1,ndim
9260 seed_local(idim)=seeds(iseed,idim)
9261 enddo
9262 if (is_spherical) then
9263 call trace_spherical_rmin_q_prepare_seed_result(seed_local,field_min, &
9264 results(iseed),igrid,seed_status)
9265 else
9266 call trace_qperp_prepare_seed_result(seed_local,field_min, &
9267 results(iseed),igrid,seed_status)
9268 endif
9269 iforward=2*iseed-1
9270 ibackward=2*iseed
9271 if (seed_status==trace_status_active) then
9272 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9273 results(iseed)%v0,igrid,iseed,1,states(iforward), &
9274 accumulate_twist=do_twist)
9275 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9276 results(iseed)%v0,igrid,iseed,-1,states(ibackward), &
9277 accumulate_twist=do_twist)
9278 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9279 call trace_rk45_stats_note_direction()
9280 call trace_rk45_stats_note_direction()
9281 else if (trace_integrator_mode==trace_integrator_rk2) then
9282 call trace_rk2_stats_note_direction()
9283 call trace_rk2_stats_note_direction()
9284 endif
9285 else
9286 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9287 iseed,1,states(iforward),accumulate_twist=do_twist)
9288 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9289 iseed,-1,states(ibackward),accumulate_twist=do_twist)
9290 states(iforward)%active=.false.
9291 states(ibackward)%active=.false.
9292 states(iforward)%status=seed_status
9293 states(ibackward)%status=seed_status
9294 results(iseed)%status_q0=seed_status
9295 endif
9296 enddo
9297
9298 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
9299 field_min,trace_mode=trace_tangent_group_rk2_short_boundary)
9300
9301 do iseed=1,nseed
9302 iforward=2*iseed-1
9303 ibackward=2*iseed
9304 if (results(iseed)%status==trace_status_active) then
9305 if (is_spherical) then
9306 call trace_spherical_rmin_q_finalize_from_states(states(iforward), &
9307 states(ibackward),results(iseed),field_min)
9308 else
9309 call trace_q0_finalize_from_states(states(iforward), &
9310 states(ibackward),results(iseed),field_min)
9311 endif
9312 endif
9313 if (do_twist) call trace_tangent_fill_twist_result(seeds(iseed,:), &
9314 states(iforward),states(ibackward),twist_results(iseed))
9315 enddo
9316 deallocate(states)
9318
9319 subroutine trace_field_spherical_qperp_multi(seeds,nseed,dL,max_steps, &
9320 results,b_min,twist_results)
9321 ! Spherical Q_perp from physical Cartesian tangent transport.
9322 integer, intent(in) :: nseed,max_steps
9323 double precision, intent(in) :: seeds(nseed,ndim),dl
9324 type(trace_qperp_result), intent(out) :: results(nseed)
9325 double precision, intent(in), optional :: b_min
9326 type(trace_twist_result), intent(out), optional :: twist_results(nseed)
9327
9328 type(trace_tangent_state), allocatable :: states(:)
9329 double precision :: field_min
9330 double precision :: seed_local(ndim),zero_vec(ndim)
9331 integer :: common_status,iseed,idim,igrid,iforward,ibackward
9332 integer :: seed_status
9333 integer :: cache_status
9334 logical :: do_twist
9335
9336 if (nseed<=0) return
9337 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) &
9338 call trace_spherical_profile_add_count( &
9339 trace_profile_qperp_tangent_systems,int(2*nseed,kind=8))
9340
9341 field_min=smalldouble
9342 if (present(b_min)) field_min=max(b_min,zero)
9343 zero_vec=zero
9344 do_twist=present(twist_results)
9345
9346 common_status=trace_status_active
9347 if (npe/=1) then
9348 common_status=trace_status_mpi_unsupported
9349 else if (ndim/=3 .or. geo_coordinate/=geo_spherical) then
9351 else if (dl<=zero .or. max_steps<=0) then
9352 common_status=trace_status_invalid_input
9353 endif
9354 if (common_status/=trace_status_active) then
9355 do iseed=1,nseed
9356 do idim=1,ndim
9357 seed_local(idim)=seeds(iseed,idim)
9358 enddo
9359 call trace_init_qperp_result(seed_local,results(iseed))
9360 results(iseed)%status=common_status
9361 results(iseed)%status_qperp0=common_status
9362 if (do_twist) call trace_summary_init_twist_result(seed_local, &
9363 twist_results(iseed),common_status)
9364 enddo
9365 return
9366 endif
9367
9368 if (do_twist) then
9369 call trace_spherical_curl_cache_build(cache_status)
9370 if (cache_status/=trace_status_active) then
9371 do iseed=1,nseed
9372 do idim=1,ndim
9373 seed_local(idim)=seeds(iseed,idim)
9374 enddo
9375 call trace_init_qperp_result(seed_local,results(iseed))
9376 results(iseed)%status=cache_status
9377 results(iseed)%status_qperp0=cache_status
9378 call trace_summary_init_twist_result(seed_local, &
9379 twist_results(iseed),cache_status)
9380 enddo
9381 return
9382 endif
9383 endif
9384
9385 allocate(states(2*nseed))
9386 do iseed=1,nseed
9387 do idim=1,ndim
9388 seed_local(idim)=seeds(iseed,idim)
9389 enddo
9390 call trace_spherical_qperp_prepare_seed_result(seed_local,field_min, &
9391 results(iseed),igrid,seed_status)
9392 iforward=2*iseed-1
9393 ibackward=2*iseed
9394 if (seed_status==trace_status_active) then
9395 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9396 results(iseed)%v0,igrid,iseed,1,states(iforward), &
9397 accumulate_twist=do_twist)
9398 call trace_tangent_state_init(seed_local,results(iseed)%u0, &
9399 results(iseed)%v0,igrid,iseed,-1,states(ibackward), &
9400 accumulate_twist=do_twist)
9401 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9402 call trace_rk45_stats_note_direction()
9403 call trace_rk45_stats_note_direction()
9404 else if (trace_integrator_mode==trace_integrator_rk2) then
9405 call trace_rk2_stats_note_direction()
9406 call trace_rk2_stats_note_direction()
9407 endif
9408 else
9409 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9410 iseed,1,states(iforward),accumulate_twist=do_twist)
9411 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid, &
9412 iseed,-1,states(ibackward),accumulate_twist=do_twist)
9413 states(iforward)%active=.false.
9414 states(ibackward)%active=.false.
9415 states(iforward)%status=seed_status
9416 states(ibackward)%status=seed_status
9417 endif
9418 enddo
9419
9420 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9421 call trace_tangent_trace_states_grouped_rk45_spherical(states, &
9422 2*nseed,dl,max_steps,field_min)
9423 else
9424 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
9425 field_min)
9426 endif
9427
9428 do iseed=1,nseed
9429 iforward=2*iseed-1
9430 ibackward=2*iseed
9431 if (results(iseed)%status==trace_status_active) then
9432 call trace_spherical_qperp_finalize_from_states(states(iforward), &
9433 states(ibackward),results(iseed),field_min)
9434 endif
9435 if (do_twist) call trace_tangent_fill_twist_result(seeds(iseed,:), &
9436 states(iforward),states(ibackward),twist_results(iseed))
9437 enddo
9438 deallocate(states)
9439 if (do_twist) call trace_spherical_curl_cache_clear()
9441
9442 subroutine trace_field_spherical_rmin_q_qperp_multi(seeds,nseed,dL, &
9443 max_steps,q_results,qperp_results,b_min,twist_results)
9444 ! Shared spherical radial-boundary Q and Q_perp tangent transport.
9445 integer, intent(in) :: nseed,max_steps
9446 double precision, intent(in) :: seeds(nseed,ndim),dl
9447 type(trace_qperp_result), intent(out) :: q_results(nseed)
9448 type(trace_qperp_result), intent(out) :: qperp_results(nseed)
9449 double precision, intent(in), optional :: b_min
9450 type(trace_twist_result), intent(out), optional :: twist_results(nseed)
9451
9452 type(trace_tangent_state), allocatable :: states(:)
9453 type(trace_tangent_state) :: qperp_forward_state,qperp_backward_state
9454 double precision :: field_min
9455 double precision :: seed_local(ndim),zero_vec(ndim)
9456 integer :: common_status,iseed,idim,igrid_q,igrid_qperp
9457 integer :: iforward,ibackward,seed_status_q,seed_status_qperp
9458 logical :: do_twist
9459
9460 if (nseed<=0) return
9461 if (trace_spherical_profile_enabled .and. geo_coordinate==geo_spherical) then
9462 call trace_spherical_profile_add_count(trace_profile_q_tangent_systems, &
9463 int(2*nseed,kind=8))
9464 call trace_spherical_profile_add_count( &
9465 trace_profile_qperp_tangent_systems,int(2*nseed,kind=8))
9466 call trace_spherical_profile_add_count( &
9467 trace_profile_combined_tangent_systems,int(2*nseed,kind=8))
9468 endif
9469
9470 field_min=smalldouble
9471 if (present(b_min)) field_min=max(b_min,zero)
9472 zero_vec=zero
9473 do_twist=present(twist_results)
9474
9475 common_status=trace_status_active
9476 if (npe/=1) then
9477 common_status=trace_status_mpi_unsupported
9478 else if (ndim/=3 .or. geo_coordinate/=geo_spherical) then
9480 else if (dl<=zero .or. max_steps<=0) then
9481 common_status=trace_status_invalid_input
9482 endif
9483 if (common_status/=trace_status_active) then
9484 do iseed=1,nseed
9485 do idim=1,ndim
9486 seed_local(idim)=seeds(iseed,idim)
9487 enddo
9488 call trace_init_qperp_result(seed_local,q_results(iseed))
9489 q_results(iseed)%status=common_status
9490 q_results(iseed)%status_q0=common_status
9491 call trace_init_qperp_result(seed_local,qperp_results(iseed))
9492 qperp_results(iseed)%status=common_status
9493 qperp_results(iseed)%status_qperp0=common_status
9494 if (do_twist) call trace_summary_init_twist_result(seed_local, &
9495 twist_results(iseed),common_status)
9496 enddo
9497 return
9498 endif
9499
9500 allocate(states(2*nseed))
9501 do iseed=1,nseed
9502 do idim=1,ndim
9503 seed_local(idim)=seeds(iseed,idim)
9504 enddo
9505 call trace_spherical_rmin_q_prepare_seed_result(seed_local,field_min, &
9506 q_results(iseed),igrid_q,seed_status_q)
9507 call trace_spherical_qperp_prepare_seed_result(seed_local,field_min, &
9508 qperp_results(iseed),igrid_qperp,seed_status_qperp)
9509
9510 iforward=2*iseed-1
9511 ibackward=2*iseed
9512 if (seed_status_q==trace_status_active .and. &
9513 seed_status_qperp==trace_status_active) then
9514 call trace_tangent_state_init(seed_local,q_results(iseed)%u0, &
9515 q_results(iseed)%v0,igrid_q,iseed,1,states(iforward), &
9516 accumulate_twist=do_twist)
9517 call trace_tangent_state_init(seed_local,q_results(iseed)%u0, &
9518 q_results(iseed)%v0,igrid_q,iseed,-1,states(ibackward), &
9519 accumulate_twist=do_twist)
9520 states(iforward)%has_extra=.true.
9521 states(ibackward)%has_extra=.true.
9522 states(iforward)%p=qperp_results(iseed)%u0
9523 states(iforward)%q=qperp_results(iseed)%v0
9524 states(ibackward)%p=qperp_results(iseed)%u0
9525 states(ibackward)%q=qperp_results(iseed)%v0
9526 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9527 call trace_rk45_stats_note_direction()
9528 call trace_rk45_stats_note_direction()
9529 else if (trace_integrator_mode==trace_integrator_rk2) then
9530 call trace_rk2_stats_note_direction()
9531 call trace_rk2_stats_note_direction()
9532 endif
9533 else
9534 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid_q, &
9535 iseed,1,states(iforward),accumulate_twist=do_twist)
9536 call trace_tangent_state_init(seed_local,zero_vec,zero_vec,igrid_q, &
9537 iseed,-1,states(ibackward),accumulate_twist=do_twist)
9538 states(iforward)%active=.false.
9539 states(ibackward)%active=.false.
9540 if (seed_status_q/=trace_status_active) then
9541 states(iforward)%status=seed_status_q
9542 states(ibackward)%status=seed_status_q
9543 else
9544 states(iforward)%status=seed_status_qperp
9545 states(ibackward)%status=seed_status_qperp
9546 endif
9547 endif
9548 enddo
9549
9550 if (trace_integrator_mode==trace_integrator_rk45_spherical) then
9551 call trace_tangent_trace_states_grouped_rk45_spherical(states, &
9552 2*nseed,dl,max_steps,field_min)
9553 else
9554 call trace_tangent_trace_states_grouped(states,2*nseed,dl,max_steps, &
9555 field_min)
9556 endif
9557
9558 do iseed=1,nseed
9559 iforward=2*iseed-1
9560 ibackward=2*iseed
9561 if (q_results(iseed)%status==trace_status_active) then
9562 call trace_spherical_rmin_q_finalize_from_states(states(iforward), &
9563 states(ibackward),q_results(iseed),field_min)
9564 endif
9565 if (qperp_results(iseed)%status==trace_status_active) then
9566 qperp_forward_state=states(iforward)
9567 qperp_backward_state=states(ibackward)
9568 qperp_forward_state%u_perp=states(iforward)%p_perp
9569 qperp_forward_state%v_perp=states(iforward)%q_perp
9570 qperp_backward_state%u_perp=states(ibackward)%p_perp
9571 qperp_backward_state%v_perp=states(ibackward)%q_perp
9572 call trace_spherical_qperp_finalize_from_states(qperp_forward_state, &
9573 qperp_backward_state,qperp_results(iseed),field_min)
9574 endif
9575 if (do_twist) call trace_tangent_fill_twist_result(seeds(iseed,:), &
9576 states(iforward),states(ibackward),twist_results(iseed))
9577 enddo
9578 deallocate(states)
9580
9581 subroutine trace_field_multi(xfm,wPm,wLm,dL,numL,numP,nwP,nwL,forwardm,ftype,tcondi)
9582 ! trace multiple field lines
9583 ! xfm: locations of points at the field lines. User should provide xfm(1:numL,1,1:ndim)
9584 ! as seed points, then field line wills be traced from the seed points. xfm(1:numL,1,:)
9585 ! are user given and other points are given by the subroutine as feedback.
9586 ! numL: number of field lines user wants to trace. user given
9587 ! numP: maximum number of points at the field line. User defined. Note that not every
9588 ! point of numP is valid, as the tracing will stop when the field line leave the
9589 ! simulation box. The number of valid point is given in wLm(1)
9590 ! wPm: point variables, which have values at each point of xfm. The way to get wP is
9591 ! user defined, with help of IO given by subroutine set_field_w in mod_usr_methods.
9592 ! User can calculate the density/temperature at the point and then store the values
9593 ! into wPm, or do something else.
9594 ! nwP: number of point variables. user given
9595 ! wLm: line variables, variables for the lines rather than for each point of the lines.
9596 ! For example, wLm(1) stores the number of valid points at the field lines. The way to
9597 ! get wLm is also user defined with set_field_w, the same as wPm. User can calculate
9598 ! the maximum density/temperature at the field lines and stores it in wLm
9599 ! nwL: number of line variables. user given
9600 ! dL: length step for field line tracing. user given
9601 ! forwardm: true--trace field forward; false--trace field line backward. user given
9602 ! ftype: type of field user wants to trace. User can trace velocity field by setting
9603 ! ftype='Vfield' or trace magnetic field by setting ftype='Bfield'. It is possible
9604 ! to trace other fields, e.g. electric field, where user can define the field with
9605 ! IO given by subroutine set_field in mod_usr_methods. user given
9606 ! tcondi: user given
9608
9609 integer, intent(in) :: numL,numP,nwP,nwL
9610 double precision, intent(inout) :: xfm(numL,numP,ndim),wPm(numL,numP,nwP),wLm(numL,1+nwL)
9611 double precision, intent(in) :: dL
9612 logical, intent(in) :: forwardm(numL)
9613 character(len=std_len), intent(in) :: ftype,tcondi
9614
9615 double precision :: x3d(3),statusF(4+ndim),statusL(numL,4+ndim+nwL),statusS(numL,4+ndim+nwL)
9616 double precision :: xf(numP,ndim),wP(numP,nwP),wL(1+nwL)
9617 double precision, allocatable :: data_send(:,:,:),data_recv(:,:,:)
9618 integer :: indomain,ipe_now,igrid_now,igrid,j,iL
9619 integer :: ipoint_in,ipoint_out,numSend,nRT,nRTmax
9620 integer :: ipointm(numL),igridm(numL)
9621 logical :: continueL(numL),myL(numL)
9622 logical :: stopT,forward
9623
9624 if (tcondi/='TRAC') then
9625 wpm=zero
9626 else
9627 wpm=-1
9628 endif
9629 wlm=zero
9630 xfm(1:numl,2:nump,:)=zero
9631 stopt=.true.
9632 myl=.false.
9633 xf=zero
9634 wp=zero
9635
9636 ! find the pe and igrid for the first point
9637 do il=1,numl
9638 indomain=0
9639 wlm(il,1)=0
9640 {if (xfm(il,1,^db)>=xprobmin^db .and. xfm(il,1,^db)<xprobmax^db) indomain=indomain+1\}
9641 if (indomain==ndim) then
9642 if (tcondi/='TRAC') wlm(il,1)=1
9643 continuel(il)=.true.
9644 ! find pe and igrid
9645 x3d=0.d0
9646 do j=1,ndim
9647 x3d(j)=xfm(il,1,j)
9648 enddo
9649 call find_particle_ipe(x3d,igrid_now,ipe_now)
9650 stopt=.false.
9651 ipointm(il)=1
9652 igridm(il)=igrid_now
9653 if (mype==ipe_now) then
9654 myl(il)=.true.
9655 else
9656 xfm(il,1,:)=zero
9657 endif
9658 else
9659 continuel(il)=.false.
9660 wlm(il,1)=zero
9661 endif
9662 enddo
9663
9664 do while(stopt .eqv. .false.)
9665 ! tracing multiple field lines inside pe
9666 statuss=zero
9667 do il=1,numl
9668 if (myl(il) .and. continuel(il)) then
9669 igrid=igridm(il)
9670 ipoint_in=ipointm(il)
9671 xf(ipoint_in,:)=xfm(il,ipoint_in,:)
9672 wl(:)=wlm(il,:)
9673 forward=forwardm(il)
9674 statusf=zero
9675 call find_points_in_pe(igrid,ipoint_in,xf,wp,wl,dl,nump,nwp,nwl,forward,ftype,tcondi,statusf)
9676 ipoint_out=int(statusf(1))
9677 xfm(il,ipoint_in:ipoint_out-1,:)=xf(ipoint_in:ipoint_out-1,:)
9678 wpm(il,ipoint_in:ipoint_out-1,:)=wp(ipoint_in:ipoint_out-1,:)
9679 ! status for each field line
9680 ! 1: index of next point
9681 ! 2: ipe of next point
9682 ! 3: igrid of next point
9683 ! 4: trace_status_active -> continue; otherwise stop tracing
9684 ! 5:4+ndim: coordinate of next point
9685 ! 4+ndim+1:4+ndim+nwL: wL(2:1+nwL)
9686 ! for TRAC nwL=2 -> wL(2): current Tcoff; wL(3): Tmax
9687 ! for TRAC nwP=2 -> wP(:,1):ipe; wP(:,2):igrid
9688 statuss(il,1:4+ndim)=statusf(1:4+ndim)
9689 statuss(il,4+ndim+1:4+ndim+nwl)=wl(2:1+nwl)
9690 if (tcondi=='TRAC') wlm(il,1)=ipoint_out-1
9691 endif
9692 enddo
9693
9694 ! comunicating tracing results
9695 numsend=numl*(4+ndim+nwl)
9696 call mpi_allreduce(statuss,statusl,numsend,mpi_double_precision,&
9697 mpi_sum,icomm,ierrmpi)
9698
9699 ! for next step
9700 stopt=.true.
9701 myl=.false.
9702 do il=1,numl
9703 if (continuel(il)) then
9704 ipointm(il)=int(statusl(il,1))
9705 if (mype==int(statusl(il,2))) myl(il)=.true.
9706 igridm(il)=int(statusl(il,3))
9707 if (int(statusl(il,4))==trace_status_active) then
9708 stopt=.false.
9709 else
9710 continuel(il)=.false.
9711 endif
9712 if (myl(il)) xfm(il,ipointm(il),1:ndim)=statusl(il,4+1:4+ndim)
9713 if (tcondi/='TRAC') then
9714 if (int(statusl(il,4))==trace_status_weak_field) then
9715 wlm(il,1)=ipointm(il)
9716 else
9717 wlm(il,1)=ipointm(il)-1
9718 endif
9719 endif
9720 wlm(il,2:1+nwl)=statusl(il,4+ndim+1:4+ndim+nwl)
9721 endif
9722 enddo
9723 enddo
9724
9725 ! communication after tracing
9726 if (tcondi/='TRAC') then
9727 nrtmax=0
9728 do il=1,numl
9729 if (nrtmax<int(wlm(il,1))) nrtmax=int(wlm(il,1))
9730 enddo
9731 numsend=numl*nrtmax*(ndim+nwp)
9732
9733 allocate(data_send(numl,nrtmax,ndim+nwp),data_recv(numl,nrtmax,ndim+nwp))
9734 data_send(:,:,:)=zero
9735 do il=1,numl
9736 nrt=int(wlm(il,1))
9737 data_send(il,1:nrt,1:ndim)=xfm(il,1:nrt,1:ndim)
9738 if (nwp>0) data_send(il,1:nrt,1+ndim:ndim+nwp)=wpm(il,1:nrt,1:nwp)
9739 enddo
9740 call mpi_allreduce(data_send,data_recv,numsend,mpi_double_precision,&
9741 mpi_sum,icomm,ierrmpi)
9742 do il=1,numl
9743 nrt=int(wlm(il,1))
9744 xfm(il,1:nrt,1:ndim)=data_recv(il,1:nrt,1:ndim)
9745 if (nwp>0) wpm(il,1:nrt,1:nwp)=data_recv(il,1:nrt,1+ndim:ndim+nwp)
9746 enddo
9747 deallocate(data_send,data_recv)
9748 endif
9749
9750 end subroutine trace_field_multi
9751
9752 subroutine trace_field_single(xf,wP,wL,dL,numP,nwP,nwL,forward,ftype,tcondi)
9753 ! trace a field line
9754 ! xf: locations of points at the field line. User should provide xf(1,1:ndim)
9755 ! as seed point, then field line will be traced from the seed point. xf(1,:) is
9756 ! user given and xf(2:wL(1),:) are given by the subroutine as feedback.
9757 ! numP: maximum number of points at the field line. User defined. Note that not every
9758 ! point of numP is valid, as the tracing will stop when the field line leave the
9759 ! simulation box. The number of valid point is given in wL(1)
9760 ! wP: point variables, which have values at each point of xf. The way to get wP is
9761 ! user defined, with help of IO given by subroutine set_field_w in mod_usr_methods.
9762 ! User can calculate the density/temperature at the point and then store the values
9763 ! into wP, or do something else.
9764 ! nwP: number of point variables. user given
9765 ! wL: line variables, variables for the line rather than for each point of the line.
9766 ! For example, wL(1) stores the number of valid points at the field line. The way to
9767 ! get wL is also user defined with set_field_w, the same as wP. User can calculate
9768 ! the maximum density/temperature at the field line and stores it in wL
9769 ! nwL: number of line variables. user given
9770 ! dL: length step for field line tracing. user given
9771 ! forward: true--trace field forward; false--trace field line backward. user given
9772 ! ftype: type of field user wants to trace. User can trace velocity field by setting
9773 ! ftype='Vfield' or trace magnetic field by setting ftype='Bfield'. It is possible
9774 ! to trace other fields, e.g. electric field, where user can define the field with
9775 ! IO given by subroutine set_field in mod_usr_methods. user given
9776 ! tcondi: user given
9777 use mod_usr_methods
9779
9780 integer, intent(in) :: numP,nwP,nwL
9781 double precision, intent(inout) :: xf(numP,ndim),wP(numP,nwP),wL(1+nwL)
9782 double precision, intent(in) :: dL
9783 logical, intent(in) :: forward
9784 character(len=std_len), intent(in) :: ftype,tcondi
9785
9786 double precision :: x3d(3),statusF(4+ndim),status_bcast(4+ndim+nwL)
9787 double precision, allocatable :: data_send(:,:),data_recv(:,:)
9788 integer :: indomain,ipoint_in,ipe_now,igrid_now,igrid,j
9789 integer :: ipoint_out,ipe_next,igrid_next,numRT
9790 logical :: stopT
9791
9792 wp=zero
9793 wl=zero
9794 xf(2:nump,:)=zero
9795
9796 ! check whether or the first point is inside simulation box. if yes, find
9797 ! the pe and igrid for the point
9798 indomain=0
9799 wl(1)=0
9800 {if (xf(1,^db)>=xprobmin^db .and. xf(1,^db)<xprobmax^db) indomain=indomain+1\}
9801 if (indomain==ndim) then
9802 wl(1)=1
9803
9804 ! find pe and igrid
9805 x3d=0.d0
9806 do j=1,ndim
9807 x3d(j)=xf(1,j)
9808 enddo
9809 call find_particle_ipe(x3d,igrid_now,ipe_now)
9810 stopt=.false.
9811 ipoint_in=1
9812 if (mype/=ipe_now) xf(1,:)=zero
9813 else
9814 if (mype==0) then
9815 call mpistop('Field tracing error: given point is not in simulation box!')
9816 endif
9817 endif
9818
9819
9820 ! other points in field line
9821 do while(stopt .eqv. .false.)
9822
9823 if (mype==ipe_now) then
9824 igrid=igrid_now
9825 ! looking for points in one pe
9826 call find_points_in_pe(igrid,ipoint_in,xf,wp,wl,dl,nump,nwp,nwl,forward,ftype,tcondi,statusf)
9827 status_bcast(1:4+ndim)=statusf(1:4+ndim)
9828 status_bcast(4+ndim+1:4+ndim+nwl)=wl(2:1+nwl)
9829 endif
9830 ! comunication
9831 call mpi_bcast(status_bcast,4+ndim+nwl,mpi_double_precision,ipe_now,icomm,ierrmpi)
9832 statusf(1:4+ndim)=status_bcast(1:4+ndim)
9833 wl(2:1+nwl)=status_bcast(4+ndim+1:4+ndim+nwl)
9834
9835 ! prepare for next step
9836 ipoint_out=int(statusf(1))
9837 ipe_next=int(statusf(2))
9838 igrid_next=int(statusf(3))
9839 if (int(statusf(4))/=trace_status_active) then
9840 stopt=.true.
9841 if (int(statusf(4))==trace_status_weak_field) then
9842 wl(1)=ipoint_out
9843 else
9844 wl(1)=ipoint_out-1
9845 endif
9846 endif
9847 if (mype==ipe_next) then
9848 do j=1,ndim
9849 xf(ipoint_out,j)=statusf(4+j)
9850 enddo
9851 else
9852 xf(ipoint_out,:)=zero
9853 endif
9854
9855 ! pe and grid of next point
9856 ipe_now=ipe_next
9857 igrid_now=igrid_next
9858 ipoint_in=ipoint_out
9859 enddo
9860
9861 if (tcondi/='TRAC') then
9862 numrt=int(wl(1))
9863 allocate(data_send(numrt,ndim+nwp),data_recv(numrt,ndim+nwp))
9864 data_send(:,:)=zero
9865 data_recv(:,:)=zero
9866 data_send(1:numrt,1:ndim)=xf(1:numrt,1:ndim)
9867 if (nwp>0) data_send(1:numrt,1+ndim:ndim+nwp)=wp(1:numrt,1:nwp)
9868 call mpi_allreduce(data_send,data_recv,numrt*(ndim+nwp),mpi_double_precision,&
9869 mpi_sum,icomm,ierrmpi)
9870 xf(1:numrt,1:ndim)=data_recv(1:numrt,1:ndim)
9871 if (nwp>0) wp(1:numrt,1:nwp)=data_recv(1:numrt,1+ndim:ndim+nwp)
9872 deallocate(data_send,data_recv)
9873 endif
9874
9875 end subroutine trace_field_single
9876
9877 subroutine find_points_in_pe(igrid,ipoint_in,xf,wP,wL,dL,numP,nwP,nwL,forward,ftype,tcondi,statusF)
9878
9879 integer, intent(inout) :: igrid
9880 integer, intent(in) :: ipoint_in,numP,nwP,nwL
9881 double precision, intent(inout) :: xf(numP,ndim),wP(numP,nwP),wL(1+nwL)
9882 double precision, intent(in) :: dL
9883 logical, intent(in) :: forward
9884 character(len=std_len), intent(in) :: ftype,tcondi
9885 double precision, intent(inout) :: statusF(4+ndim)
9886
9887 double precision :: xfout(ndim)
9888 integer :: ipe_next,igrid_next,ip_in,ip_out,j,indomain,trace_status
9889 logical :: newpe,stopT
9890
9891 ip_in=ipoint_in
9892 newpe=.false.
9893 ipe_next=mype
9894 igrid_next=igrid
9895 statusf=zero
9896
9897 do while(newpe .eqv. .false.)
9898 ! looking for points in given grid
9899 call find_points_interp(igrid,ip_in,ip_out,xf,wp,wl,nump,nwp,nwl, &
9900 dl,forward,ftype,tcondi,trace_status)
9901 ip_in=ip_out
9902
9903 ! when next point is out of given grid, find next grid
9904 if (trace_status/=trace_status_active) then
9905 newpe=.true.
9906 stopt=.true.
9907 else
9908 indomain=0
9909 {if (xf(ip_out,^db)>=xprobmin^db .and. xf(ip_out,^db)<=xprobmax^db) indomain=indomain+1\}
9910 if (ip_out<nump .and. indomain==ndim) then
9911 if (tcondi/='TRAC') then
9912 stopt=.false.
9913 xfout=xf(ip_out,:)
9914 call find_next_grid(igrid,igrid_next,ipe_next,xfout,newpe,stopt)
9915 if (stopt) trace_status=trace_status_boundary
9916 else
9917 if (xf(ip_out,ndim)>phys_trac_mask) then
9918 newpe=.true.
9919 stopt=.true.
9920 trace_status=trace_status_trac_stop
9921 else
9922 stopt=.false.
9923 xfout=xf(ip_out,:)
9924 call find_next_grid(igrid,igrid_next,ipe_next,xfout,newpe,stopt)
9925 if (stopt) trace_status=trace_status_boundary
9926 endif
9927 endif
9928 else
9929 newpe=.true.
9930 stopt=.true.
9931 if (ip_out>=nump) then
9932 trace_status=trace_status_max_steps
9933 else
9934 trace_status=trace_status_boundary
9935 endif
9936 endif
9937 endif
9938
9939 if (newpe) then
9940 statusf(1)=ip_out
9941 statusf(2)=ipe_next
9942 statusf(3)=igrid_next
9943 statusf(4)=trace_status_active
9944 if (stopt) statusf(4)=trace_status
9945 do j=1,ndim
9946 statusf(4+j)=xf(ip_out,j)
9947 enddo
9948 endif
9949
9950 if (newpe .eqv. .false.) igrid=igrid_next
9951 enddo
9952
9953 end subroutine find_points_in_pe
9954
9955 subroutine find_next_grid(igrid,igrid_next,ipe_next,xf1,newpe,stopT)
9956 ! check the grid and pe of next point
9957 use mod_usr_methods
9959 use mod_forest
9960
9961 integer, intent(inout) :: igrid,igrid_next,ipe_next
9962 double precision, intent(in) :: xf1(ndim)
9963 logical, intent(inout) :: newpe,stopT
9964
9965 double precision :: dxb^D,xb^L,xbmid^D
9966 double precision :: xbn^L
9967 integer :: idn^D,my_neighbor_type,inblock
9968 integer :: ic^D,inc^D,ipe_neighbor,igrid_neighbor
9969
9970 igrid_next=igrid
9971 ipe_next=mype
9972
9973 ^d&xbmin^d=rnode(rpxmin^d_,igrid)\
9974 ^d&xbmax^d=rnode(rpxmax^d_,igrid)\
9975 inblock=0
9976
9977 ! direction of next grid
9978 idn^d=0\
9979 {if (xf1(^d)<=xbmin^d) idn^d=-1\}
9980 {if (xf1(^d)>=xbmax^d) idn^d=1\}
9981 my_neighbor_type=neighbor_type(idn^d,igrid)
9982 igrid_neighbor=neighbor(1,idn^d,igrid)
9983 ipe_neighbor=neighbor(2,idn^d,igrid)
9984
9985 ! ipe and igrid of next grid
9986 select case(my_neighbor_type)
9987 case (neighbor_boundary)
9988 ! next point is not in simulation box
9989 newpe=.true.
9990 stopt=.true.
9991
9992 case(neighbor_coarse)
9993 ! neighbor grid has lower refinement level
9994 igrid_next=igrid_neighbor
9995 ipe_next=ipe_neighbor
9996 if (mype==ipe_neighbor) then
9997 newpe=.false.
9998 else
9999 newpe=.true.
10000 endif
10001
10002 case(neighbor_sibling)
10003 ! neighbor grid has lower refinement level
10004 igrid_next=igrid_neighbor
10005 ipe_next=ipe_neighbor
10006 if (mype==ipe_neighbor) then
10007 newpe=.false.
10008 else
10009 newpe=.true.
10010 endif
10011
10012 case(neighbor_fine)
10013 ! neighbor grid has higher refinement level
10014 {xbmid^d=(xbmin^d+xbmax^d)/2.d0\}
10015 ^d&inc^d=1\
10016 {if (xf1(^d)<=xbmin^d) inc^d=0\}
10017 {if (xf1(^d)>xbmin^d .and. xf1(^d)<=xbmid^d) inc^d=1\}
10018 {if (xf1(^d)>xbmid^d .and. xf1(^d)<xbmax^d) inc^d=2\}
10019 {if (xf1(^d)>=xbmax^d) inc^d=3\}
10020 ipe_next=neighbor_child(2,inc^d,igrid)
10021 igrid_next=neighbor_child(1,inc^d,igrid)
10022 if (mype==ipe_next) then
10023 newpe=.false.
10024 else
10025 newpe=.true.
10026 endif
10027 end select
10028
10029 end subroutine find_next_grid
10030
10031 subroutine find_points_interp(igrid,ip_in,ip_out,xf,wP,wL,numP,nwP,nwL, &
10032 dL,forward,ftype,tcondi,trace_status)
10034 use mod_usr_methods
10035
10036 integer, intent(in) :: igrid,ip_in,numP,nwP,nwL
10037 integer, intent(inout) :: ip_out
10038 double precision, intent(inout) :: xf(numP,ndim),wP(numP,nwP),wL(1+nwL)
10039 double precision, intent(in) :: dL
10040 logical, intent(in) :: forward
10041 character(len=std_len), intent(in) :: ftype,tcondi
10042 integer, intent(out) :: trace_status
10043
10044 double precision :: dxb^D,xb^L
10045 double precision :: field(ixg^T,ndir)
10046 double precision :: xs1(ndim),xs2(ndim),K1(ndim),K2(ndim)
10047 double precision :: xfpre(ndim),xfnow(ndim),xfnext(ndim)
10048 double precision :: Tpre,Tnow,Tnext,dTds,Lt,Lr,ds,T_bott,trac_delta
10049 integer :: ip,inblock,ixI^L,ixO^L,j
10050 logical :: field_ok
10051
10052 ixi^l=ixg^ll;
10053 ixo^l=ixm^ll;
10054 ^d&dxb^d=rnode(rpdx^d_,igrid);
10055 ^d&xbmin^d=rnode(rpxmin^d_,igrid);
10056 ^d&xbmax^d=rnode(rpxmax^d_,igrid);
10057 ip_out=ip_in
10058 trace_status=trace_status_active
10059
10060 if (tcondi/='TRAC') then
10061 ds=dl
10062 else
10063 ds=dxb^nd
10064 t_bott=2.d4/unit_temperature
10065 trac_delta=0.25d0
10066 endif
10067
10068 ! main loop
10069 mainloop: do ip=ip_in,nump-1
10070
10071 ! integrate magnetic field with Runge-Kutta method
10072 xs1(:)=xf(ip,:)
10073 call get_k(xs1,igrid,k1,ixi^l,dxb^d,ftype,smalldouble,field_ok)
10074 if (.not.field_ok) then
10075 ip_out=ip
10076 trace_status=trace_status_weak_field
10077 return
10078 endif
10079 if (forward) then
10080 xs2(:)=xf(ip,:)+ds*k1(:)
10081 else
10082 xs2(:)=xf(ip,:)-ds*k1(:)
10083 endif
10084 call get_k(xs2,igrid,k2,ixi^l,dxb^d,ftype,smalldouble,field_ok)
10085 if (.not.field_ok) then
10086 ip_out=ip
10087 trace_status=trace_status_weak_field
10088 return
10089 endif
10090 if (forward) then
10091 xf(ip+1,:)=xf(ip,:)+ds*(0.5*k1(:)+0.5*k2(:))
10092 else
10093 xf(ip+1,:)=xf(ip,:)-ds*(0.5*k1(:)+0.5*k2(:))
10094 endif
10095 ip_out=ip+1
10096
10097 ! get local values for variable via interpolation
10098 if (tcondi/='TRAC') then
10099 if (associated(usr_set_field_w)) then
10100 call usr_set_field_w(igrid,ip,xf,wp,wl,nump,nwp,nwl,dl,forward,ftype,tcondi)
10101 endif
10102 else ! get TRAC Tcoff
10103 wp(ip,1)=mype
10104 wp(ip,2)=igrid
10105 if (ip==ip_in) then
10106 if (forward) then
10107 xfpre(:)=xf(ip,:)-ds*k1(:)
10108 else
10109 xfpre(:)=xf(ip,:)+ds*k1(:)
10110 endif
10111 xfnow(:)=xf(ip,:)
10112 xfnext(:)=xf(ip+1,:)
10113 call get_t_loc_trac(igrid,xfpre,tpre,ixi^l,dxb^d)
10114 call get_t_loc_trac(igrid,xfnow,tnow,ixi^l,dxb^d)
10115 call get_t_loc_trac(igrid,xfnext,tnext,ixi^l,dxb^d)
10116 else
10117 xfpre=xf(ip-1,:)
10118 xfnow(:)=xf(ip,:)
10119 xfnext(:)=xf(ip+1,:)
10120 tpre=tnow
10121 tnow=tnext
10122 call get_t_loc_trac(igrid,xfnext,tnext,ixi^l,dxb^d)
10123 endif
10124 dtds=abs(tnext-tpre)/(2*ds)
10125 if (ip==1) then
10126 lt=0.d0
10127 wl(2)=t_bott ! current Tcofl
10128 wl(3)=tnow ! current Tlmax
10129 else
10130 lt=0.d0
10131 if (dtds>0.d0) then
10132 lt=tnow/dtds
10133 lr=ds
10134 ! renew cutoff temperature
10135 if(lr>trac_delta*lt) then
10136 if (tnow>wl(2)) wl(2)=tnow
10137 endif
10138 endif
10139 if (tnow>wl(3)) wl(3)=tnow
10140 endif
10141 endif
10142
10143 ! exit loop if next point is not in this block
10144 inblock=0
10145 {if (xf(ip+1,^db)>=xbmin^db .and. xf(ip+1,^db)<xbmax^db) inblock=inblock+1\}
10146 if (tcondi=='TRAC' .and. xf(ip+1,ndim)>phys_trac_mask) inblock=0
10147 if (inblock/=ndim) exit mainloop
10148
10149 enddo mainloop
10150
10151 end subroutine find_points_interp
10152
10153 subroutine get_k(xfn,igrid,K,ixI^L,dxb^D,ftype,b_min,field_ok)
10154 use mod_usr_methods
10155
10156 integer :: ixI^L,igrid
10157 double precision :: dxb^D
10158 double precision :: xfn(ndim),K(ndim)
10159 character(len=std_len) :: ftype
10160 double precision, intent(in), optional :: b_min
10161 logical, intent(out), optional :: field_ok
10162
10163 double precision :: dxc^D,xd^D
10164 double precision :: field(0:1^D&,ndir),Fx(ndim),factor(0:1^D&)
10165 double precision :: Ftotal,field_min
10166 double precision :: w_stencil(0:1^D&,1:nw)
10167 double precision :: x_stencil(0:1^D&,1:ndim)
10168 double precision :: vector_stencil(0:1^D&,1:ndir)
10169 logical :: valid_field
10170 integer :: ixb^D,ix^D,ixbl^D,j,status_interp
10171 integer :: ixS^L
10172
10173 if (geo_coordinate==geo_spherical) then
10174 call trace_interp_weights_block(xfn,igrid,ixi^l,ixbl^d,xd^d,dxc^d, &
10175 status_interp)
10176 if (status_interp/=trace_status_active) then
10177 if (present(field_ok)) field_ok=.false.
10178 k=zero
10179 return
10180 endif
10181 else
10182 ^d&ixbl^d=floor((xfn(^d)-ps(igrid)%x(iximin^dd,^d))/dxb^d)+iximin^d;
10183 ^d&xd^d=(xfn(^d)-ps(igrid)%x(ixbl^dd,^d))/dxb^d;
10184 endif
10185
10186 field=zero
10187 if(ftype=='Bfield') then
10188 if(b0field) then
10189 if(allocated(iw_mag)) then
10190 {do ix^d=0,1\}
10191 field(ix^d,1:ndir)=ps(igrid)%w(ixbl^d+ix^d,iw_mag(1:ndir)) &
10192 +ps(igrid)%B0(ixbl^d+ix^d,1:ndir,0)
10193 {enddo\}
10194 else
10195 {do ix^d=0,1\}
10196 field(ix^d,1:ndir)=ps(igrid)%B0(ixbl^d+ix^d,1:ndir,0)
10197 {enddo\}
10198 endif
10199 else
10200 {do ix^d=0,1\}
10201 field(ix^d,1:ndir)=ps(igrid)%w(ixbl^d+ix^d,iw_mag(1:ndir))
10202 {enddo\}
10203 endif
10204 else if (ftype=='Vfield') then
10205 ! stencil is the two cells ixbl:ixbl+1 in each direction, held at 0:1 so
10206 ! the bounds do not depend on the runtime ixbl
10207 ^d&ixsmin^d=0;
10208 ^d&ixsmax^d=1;
10209 w_stencil(ixs^s,1:nw)=ps(igrid)%w(ixbl^d:ixbl^d+1^d&,1:nw)
10210 x_stencil(ixs^s,1:ndim)=ps(igrid)%x(ixbl^d:ixbl^d+1^d&,1:ndim)
10211 call phys_get_v(w_stencil,x_stencil,ixs^l,ixs^l,vector_stencil)
10212 {do ix^d=0,1\}
10213 field(ix^d,1:ndir)=vector_stencil(ix^d,1:ndir)
10214 {enddo\}
10215 endif
10216 {do ix^d=0,1\}
10217 factor(ix^d)={abs(1-ix^d-xd^d)*}
10218 {enddo\}
10219
10220 if (ftype=='Bfield' .or. ftype=='Vfield') then
10221 fx=0.d0
10222 {do ix^db=0,1\}
10223 do j=1,ndim
10224 fx(j)=fx(j)+field(ix^d,j)*factor(ix^d)
10225 enddo
10226 {enddo\}
10227 else if (associated(usr_set_field)) then
10228 call usr_set_field(xfn,igrid,fx,ftype)
10229 else
10230 call mpistop('Field tracing error: wrong field type!')
10231 endif
10232
10233 ftotal=zero
10234 do j=1,ndim
10235 ftotal=ftotal+(fx(j))**2
10236 enddo
10237 ftotal=dsqrt(ftotal)
10238
10239 field_min=smalldouble
10240 if (present(b_min)) field_min=max(b_min,zero)
10241 valid_field=ftotal>zero .and. ftotal>=field_min
10242 if (present(field_ok)) field_ok=valid_field
10243
10244 k=zero
10245 if (valid_field) then
10246 select case (geo_coordinate)
10247 case (geo_spherical)
10248 {^ifthreed
10249 if (.not.trace_spherical_metric_ok(xfn)) then
10250 valid_field=.false.
10251 if (present(field_ok)) field_ok=.false.
10252 return
10253 endif
10254 k(1)=fx(1)/ftotal
10255 k(2)=fx(2)/(xfn(1)*ftotal)
10256 k(3)=fx(3)/(xfn(1)*dsin(xfn(2))*ftotal)
10257 }
10258 case default
10259 k(1:ndim)=fx(1:ndim)/ftotal
10260 end select
10261 endif
10262
10263 end subroutine get_k
10264
10265 subroutine get_t_loc_trac(igrid,xloc,Tloc,ixI^L,dxb^D)
10266 ! grid T has been calculated and stored in wextra(ixI^S,Tcoff_)
10267 ! see mod_trac
10268 integer, intent(in) :: igrid,ixI^L
10269 double precision, intent(inout) :: xloc(ndim)
10270 double precision, intent(inout) :: Tloc
10271 double precision, intent(in) :: dxb^D
10272
10273 double precision :: xd^D
10274 double precision :: factor(0:1^D&),Tnear(0:1^D&)
10275 integer :: ixb^D,ix^D,ixbl^D,j,ixO^L
10276
10277 ^d&ixbl^d=floor((xloc(^d)-ps(igrid)%x(iximin^dd,^d))/dxb^d)+iximin^d;
10278 ^d&xd^d=(xloc(^d)-ps(igrid)%x(ixbl^dd,^d))/dxb^d;
10279 ^d&ixomin^d=ixbl^d;
10280 ^d&ixomax^d=ixomin^d+1;
10281
10282 {do ix^d=0,1\}
10283 factor(ix^d)={abs(1-ix^d-xd^d)*}
10284 tnear(ix^d)=ps(igrid)%wextra(ixbl^d+ix^d,iw_tcoff)
10285 {enddo\}
10286
10287 tloc=0.d0
10288 ! interpolation
10289 {do ix^db=0,1\}
10290 tloc=tloc+tnear(ix^d)*factor(ix^d)
10291 {enddo\}
10292
10293 end subroutine get_t_loc_trac
10294
10295end module mod_trace_field
This module contains variables that describe the connectivity of the mesh and also data structures fo...
integer, dimension(:), allocatable igrids
integer, parameter neighbor_coarse
integer, dimension(:,:^d &,:), allocatable neighbor
integer, parameter neighbor_boundary
integer, dimension(:,:^d &,:), allocatable neighbor_child
integer, parameter neighbor_fine
integer, parameter neighbor_sibling
integer, dimension(:^d &,:), allocatable neighbor_type
Module with basic grid data structures.
Definition mod_forest.t:2
Module with geometry-related routines (e.g., divergence, curl)
Definition mod_geometry.t:2
integer coordinate
Definition mod_geometry.t:7
integer, parameter spherical
integer, parameter cartesian
Definition mod_geometry.t:8
integer, parameter cartesian_stretched
Definition mod_geometry.t:9
subroutine curlvector(qvec, ixil, ixol, curlvec, idirmin, idirmin0, ndir0, fourthorder)
Calculate curl of a vector qvec within ixL Options to employ standard second order CD evaluations use...
This module contains definitions of global parameters and variables and some generic functions/subrou...
type(state), pointer block
Block pointer for using one block and its previous state.
integer nstep
How many sub-steps the time integrator takes.
integer istep
Index of the sub-step in a multi-step time integrator.
integer, parameter ndim
Number of spatial dimensions for grid variables.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
double precision dt
global time step
integer ixm
the mesh range of a physical block without ghost cells
integer ierrmpi
A global MPI error return code.
double precision, dimension(:), allocatable, parameter d
integer npe
The number of MPI tasks.
logical, dimension(ndim) periodb
True for dimensions with periodic boundaries.
logical b0field
split magnetic field as background B0 field
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
double precision unit_temperature
Physical scaling factor for temperature.
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
integer max_blocks
The maximum number of grid blocks in a processor.
Module with shared functionality for all the particle movers.
subroutine find_particle_ipe(x, igrid_particle, ipe_particle)
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
procedure(sub_get_v), pointer phys_get_v
Definition mod_physics.t:69
integer, parameter, public trace_face_ymin
integer, parameter, public trace_face_zmax
integer, parameter, public trace_status_out_of_domain
subroutine, public trace_spherical_profile_set(enabled)
subroutine, public trace_rk45_stats_reset()
subroutine, public trace_debug_transport_tangents_to_boundary(seed, u0, v0, dl, max_steps, forward, x_end, u_end, v_end, bhat_end, b_end, u_perp_end, v_perp_end, u_face_end, v_face_end, length, face_id, status, b_min)
subroutine trace_field_single(xf, wp, wl, dl, nump, nwp, nwl, forward, ftype, tcondi)
subroutine trace_field_multi(xfm, wpm, wlm, dl, numl, nump, nwp, nwl, forwardm, ftype, tcondi)
subroutine, public trace_field_twist_multi(seeds, nseed, dl, max_steps, results, b_min)
integer, parameter, public trace_status_max_steps
subroutine, public trace_field_rk2_short_boundary_q_multi(seeds, nseed, dl, max_steps, results, b_min, twist_results)
integer, parameter, public trace_status_bad_grad_stencil
subroutine, public trace_cartesian_global_min_cell_size(hmin, status)
subroutine, public trace_spherical_curl_cache_clear()
subroutine, public trace_debug_sample_bhat_gradbhat(seed, bhat, grad_bhat, status, b_min)
subroutine find_points_interp(igrid, ip_in, ip_out, xf, wp, wl, nump, nwp, nwl, dl, forward, ftype, tcondi, trace_status)
subroutine, public trace_debug_qperp_single(seed, dl, max_steps, qperp, logqperp, x_f, x_b, u_f_perp, v_f_perp, u_b_perp, v_b_perp, b_seed, b_f, b_b, length_f, length_b, face_f, face_b, status, n2, bfactor, b_min)
subroutine, public trace_field_qperp_multi(seeds, nseed, dl, max_steps, results, b_min, twist_results)
subroutine, public trace_field_spherical_rmin_q_multi(seeds, nseed, dl, max_steps, results, b_min, twist_results)
subroutine, public trace_rk45_stats_report(label)
subroutine, public trace_field_topology_multi(seeds, nseed, dl, max_steps, results, need_twist, need_mapping, b_min, source_normal)
integer, parameter, public trace_face_xmax
subroutine, public trace_field_twist_single(seed, dl, max_steps, result, b_min)
subroutine, public trace_debug_sample_endpoint_b_face_limit(xhit, face_id, b, bhat, status, b_min)
subroutine, public trace_field_mapping_single(seed, dl, max_steps, result, b_min, source_normal)
integer, parameter, public trace_status_trac_stop
integer, parameter, public trace_face_none
integer, parameter, public trace_status_boundary
subroutine, public trace_spherical_profile_report(label)
integer, parameter, public trace_face_ambiguous
subroutine, public trace_field_qperp_single(seed, dl, max_steps, result, b_min)
subroutine, public trace_debug_transport_tangents(seed, u0, v0, dl, nstep, forward, x_end, u_end, v_end, bhat_end, u_perp_end, v_perp_end, status, b_min)
integer, parameter, public trace_face_zmin
subroutine, public trace_debug_compare_gradbhat_methods(seed, eps, bhat_current, grad_current, status_current, bhat_xeps, grad_xeps, status_xeps, bhat_interp, grad_interp, status_interp, b_min)
integer, parameter, public trace_status_unsupported_geometry
integer, parameter, public trace_status_bad_q_bound
subroutine, public trace_set_step_control(mode, step_fraction, step_min)
subroutine, public trace_debug_cartesian_rk45_tangent_q0_multi(seeds, nseed, dl, max_steps, results, b_min)
integer, parameter, public trace_face_ymax
subroutine, public trace_spherical_global_min_cell_size(hmin, status)
subroutine get_k(xfn, igrid, k, ixil, dxbd, ftype, b_min, field_ok)
subroutine, public trace_rk2_stats_reset()
subroutine, public trace_spherical_curl_cache_build(status)
subroutine, public trace_spherical_profile_count_seeds(nseed)
integer, parameter, public trace_status_bad_curl_stencil
subroutine, public trace_spherical_profile_reset()
integer, parameter, public trace_status_invalid_input
subroutine, public trace_field_spherical_rmin_q_qperp_multi(seeds, nseed, dl, max_steps, q_results, qperp_results, b_min, twist_results)
subroutine, public trace_field_length_multi(seeds, nseed, dl, max_steps, results, b_min)
subroutine, public trace_rk45_stats_set_enabled(enabled)
subroutine, public trace_set_integrator(mode, atol, rtol, safety, min_shrink, max_grow, tangent_floor, tangent_rtol)
subroutine, public trace_rk2_stats_set_enabled(enabled)
subroutine, public trace_field_spherical_qperp_multi(seeds, nseed, dl, max_steps, results, b_min, twist_results)
subroutine, public trace_rk2_stats_report(label)
subroutine find_points_in_pe(igrid, ipoint_in, xf, wp, wl, dl, nump, nwp, nwl, forward, ftype, tcondi, statusf)
integer, parameter, public trace_status_mpi_unsupported
integer, parameter, public trace_status_weak_field
subroutine, public trace_field_length_single(seed, dl, max_steps, result, b_min)
integer, parameter, public trace_status_seed_outside
subroutine find_next_grid(igrid, igrid_next, ipe_next, xf1, newpe, stopt)
integer, parameter, public trace_status_singular_q
subroutine, public trace_debug_compare_gradbhat(seed, eps, bhat_current, grad_current, status_current, bhat_xeps, grad_xeps, status_xeps, b_min)
integer, parameter, public trace_face_xmin
subroutine get_t_loc_trac(igrid, xloc, tloc, ixil, dxbd)
integer, parameter, public trace_status_bad_face_limit_sample
subroutine, public trace_field_mapping_multi(seeds, nseed, dl, max_steps, results, b_min, source_normal)
integer, parameter, public trace_status_active
subroutine, public trace_field_q0_multi_rk45_cartesian(seeds, nseed, dl, max_steps, results, b_min, twist_results)
Module with all the methods that users can customize in AMRVAC.
procedure(set_field_w), pointer usr_set_field_w
procedure(set_field), pointer usr_set_field