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