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