11 double precision,
parameter :: nlfff_step_grow=1.01d0
12 double precision,
parameter :: nlfff_step_shrink=0.5d0
16 double precision,
parameter :: nlfff_step_floor_dx2=1.d-7
19 integer :: fft_padding_factor=-1
20 character(len=16) :: fft_top_boundary=
''
21 character(len=16) :: flux_treatment=
''
22 double precision :: max_flux_imbalance=-1.d0
23 integer :: buffer_cells=-1
24 integer :: max_iterations=-1
25 double precision :: initial_step_scale=-1.d0
26 integer :: log_interval=-1
27 character(len=24) :: update_preconditioner=
''
28 character(len=16) :: initialization_mode=
'potential'
29 logical :: write_detailed_history=.false.
30 logical :: plateau_enabled=.true.
31 integer :: plateau_interval=10
32 integer :: plateau_window=10
33 double precision :: plateau_tolerance=1.d-4
38 integer :: accepted_steps=0
39 integer :: rejected_steps=0
40 integer :: functional_evaluations=0
41 character(len=32) :: stop_reason=
'not_started'
42 double precision :: initial_l=0.d0
43 double precision :: initial_l_force=0.d0
44 double precision :: initial_l_div=0.d0
45 double precision :: initial_b2_integral=0.d0
46 double precision :: initial_epsilon_force=0.d0
47 double precision :: initial_epsilon_div=0.d0
48 double precision :: step_floor=0.d0
49 double precision :: final_l=0.d0
50 double precision :: final_l_force=0.d0
51 double precision :: final_l_div=0.d0
52 double precision :: final_b2_integral=0.d0
53 double precision :: final_epsilon_force=0.d0
54 double precision :: final_epsilon_div=0.d0
55 double precision :: final_step=0.d0
56 integer :: plateau_count=0
57 double precision :: final_relative_functional_change=1.d0
63 type :: nlfff_optimization_timing
64 double precision :: update_kernel=0.d0
65 double precision :: binomial_halo_x=0.d0
66 double precision :: binomial_halo_y=0.d0
67 double precision :: trial_b_exchange=0.d0
68 double precision :: qs_exchange=0.d0
69 double precision :: functional_local=0.d0
70 double precision :: functional_reductions=0.d0
71 double precision :: unified_diagnostics=0.d0
72 end type nlfff_optimization_timing
75 logical,
save :: optimization_timing_active=.false.
77 double precision,
allocatable,
save :: bottom_b(:,:,:)
81 double precision,
allocatable,
save :: nlfff_weight_x(:)
82 double precision,
allocatable,
save :: nlfff_weight_y(:)
83 double precision,
allocatable,
save :: nlfff_weight_z(:)
87 type(
state),
allocatable,
target,
save :: accepted_qs_state(:)
88 type(
state),
allocatable,
target,
save :: trial_qs_state(:)
110 character(len=*),
intent(in) :: filename
111 double precision,
intent(in) :: qlunit,qbunit
112 double precision,
intent(in),
optional :: qxc1,qxc2
114 if(
allocated(bottom_b))
deallocate(bottom_b)
129 integer,
intent(in) :: iw_b(3)
133 double precision :: step,step_floor,lold,lforce_old,ldiv_old,b2old
134 double precision :: ltrial,lforce_trial,ldiv_trial,b2trial
135 double precision :: epsilon_force,epsilon_div,characteristic_spacing
136 double precision :: plateau_reference_l,plateau_relative_change
137 double precision :: timing_start
139 integer :: log_unit,metrics_unit
140 integer :: plateau_count
141 logical :: accepted_trial,plateau_stop
145 optimization_timing_active=.true.
146 call validate_configuration(iw_b,config)
147 call initialize_nlfff_weight_cache(config%buffer_cells)
148 call validate_exchange_state()
149 call allocate_qs_states()
150 call prepare_partial_exchange_types(iw_b)
152 if(trim(config%initialization_mode)==
'potential')
then
155 0.5d0*
dx(3,1),0.d0,config%fft_top_boundary,&
156 config%flux_treatment,config%max_flux_imbalance)
159 call apply_physical_boundaries(
ps,iw_b)
160 call exchange_b_internal(iw_b)
161 call evaluate_functional(
ps,iw_b,accepted_qs_state,config%buffer_cells,&
162 lold,lforce_old,ldiv_old,b2old)
163 result%functional_evaluations=1
165 step=config%initial_step_scale*minval(
dx(:,1))**2
166 step_floor=nlfff_step_floor_dx2*minval(
dx(:,1))**2
167 result%step_floor=step_floor
169 plateau_reference_l=lold
170 plateau_relative_change=1.d0
172 result%final_relative_functional_change=plateau_relative_change
173 characteristic_spacing=product(
dx(:,1))**(1.d0/3.d0)
175 characteristic_spacing,epsilon_force,epsilon_div)
176 result%initial_L=lold
177 result%initial_L_force=lforce_old
178 result%initial_L_div=ldiv_old
179 result%initial_B2_integral=b2old
180 result%initial_epsilon_force=epsilon_force
181 result%initial_epsilon_div=epsilon_div
183 result%final_L_force=lforce_old
184 result%final_L_div=ldiv_old
185 result%final_B2_integral=b2old
186 result%final_epsilon_force=epsilon_force
187 result%final_epsilon_div=epsilon_div
188 result%final_step=step
192 accepted_trial=.false.
194 if(config%write_detailed_history)
then
195 open(newunit=log_unit,file=trim(
base_filename)//
'_nlfff_opt.csv',&
196 status=
'replace',action=
'write')
197 write(log_unit,
'(a)')
'attempt,accepted,rejected,step,L,L_force,L_div,'//&
198 'B2_integral,epsilon_force,epsilon_div,functional_evaluations,'//&
199 'step_floor,plateau_count,plateau_relative_change,accepted_trial'
200 call write_log_row(log_unit,result,step,lold,lforce_old,ldiv_old,b2old,&
201 epsilon_force,epsilon_div,accepted_trial)
203 open(newunit=metrics_unit,file=trim(
base_filename)//
'_nlfff_metrics.csv',&
204 status=
'replace',action=
'write')
206 write(*,*)
'Weighted NLFFF optimization initial L:',lold
208 timing_start=mpi_wtime()
213 do while(result%accepted_steps<config%max_iterations .and. step>=step_floor .and. &
215 result%attempts=result%attempts+1
216 call form_trial_field(iw_b,config%buffer_cells,&
217 config%update_preconditioner,step)
218 call apply_physical_boundaries(
ps1,iw_b)
219 timing_start=mpi_wtime()
220 call exchange_trial_b_internal(iw_b)
222 call evaluate_functional(
ps1,iw_b,trial_qs_state,config%buffer_cells,&
223 ltrial,lforce_trial,ldiv_trial,b2trial)
224 result%functional_evaluations=result%functional_evaluations+1
226 accepted_trial=(ltrial<lold)
227 if(accepted_trial)
then
228 result%accepted_steps=result%accepted_steps+1
229 call accept_trial_field(iw_b)
230 call swap_qs_states()
232 lforce_old=lforce_trial
235 step=step*nlfff_step_grow
237 result%rejected_steps=result%rejected_steps+1
238 step=step*nlfff_step_shrink
242 result%final_L_force=lforce_old
243 result%final_L_div=ldiv_old
244 result%final_B2_integral=b2old
246 characteristic_spacing,epsilon_force,epsilon_div)
247 result%final_epsilon_force=epsilon_force
248 result%final_epsilon_div=epsilon_div
249 result%final_step=step
255 if(config%plateau_enabled .and. accepted_trial .and. &
256 result%accepted_steps>=config%plateau_interval .and. &
257 mod(result%accepted_steps,config%plateau_interval)==0)
then
258 plateau_relative_change=abs((lold-plateau_reference_l)/&
259 max(abs(lold),tiny(1.d0)))
260 if(plateau_relative_change<config%plateau_tolerance)
then
261 plateau_count=plateau_count+1
265 plateau_reference_l=lold
266 result%plateau_count=plateau_count
267 result%final_relative_functional_change=plateau_relative_change
268 if(plateau_count>=config%plateau_window) plateau_stop=.true.
270 if(mod(result%attempts,config%log_interval)==0)
then
271 timing_start=mpi_wtime()
275 if(config%write_detailed_history)
call write_log_row(log_unit,result,&
276 step,lold,lforce_old,ldiv_old,b2old,epsilon_force,epsilon_div,&
280 write(*,*)
'NLFFF optimization:',result%accepted_steps,&
281 result%rejected_steps,step,lold
286 if(plateau_stop)
then
287 result%stop_reason=
'plateau'
288 else if(result%accepted_steps>=config%max_iterations)
then
289 result%stop_reason=
'max_iterations'
291 result%stop_reason=
'step_floor'
293 result%final_step=step
294 if(mod(result%attempts,config%log_interval)/=0)
then
295 timing_start=mpi_wtime()
299 if(config%write_detailed_history)
call write_log_row(log_unit,result,&
300 step,lold,lforce_old,ldiv_old,b2old,epsilon_force,epsilon_div,&
307 if(config%write_detailed_history)
close(log_unit)
309 write(*,*)
'Weighted NLFFF optimization stopped:',trim(result%stop_reason)
310 write(*,*)
'accepted, rejected, final L:',result%accepted_steps,&
311 result%rejected_steps,result%final_L
312 write(*,*)
'step floor, plateau count, relative dL/L:',&
313 result%step_floor,result%plateau_count,&
314 result%final_relative_functional_change
315 write(*,*)
'functional evaluations:',result%functional_evaluations
317 call report_nlfff_timing()
318 optimization_timing_active=.false.
319 call deallocate_nlfff_weight_cache()
320 call restore_full_exchange_types()
321 call deallocate_qs_states()
324 subroutine allocate_qs_states()
328 integer :: iigrid,igrid
330 if(
allocated(accepted_qs_state))
deallocate(accepted_qs_state)
331 if(
allocated(trial_qs_state))
deallocate(trial_qs_state)
333 do iigrid=1,igridstail
337 deallocate(accepted_qs_state(igrid)%w,trial_qs_state(igrid)%w)
338 allocate(accepted_qs_state(igrid)%w(ixglo1:ixghi1,ixglo2:ixghi2,&
340 allocate(trial_qs_state(igrid)%w(ixglo1:ixghi1,ixglo2:ixghi2,&
342 accepted_qs_state(igrid)%w=0.d0
343 trial_qs_state(igrid)%w=0.d0
345 end subroutine allocate_qs_states
347 subroutine deallocate_qs_states()
348 if(
allocated(accepted_qs_state))
deallocate(accepted_qs_state)
349 if(
allocated(trial_qs_state))
deallocate(trial_qs_state)
350 end subroutine deallocate_qs_states
352 subroutine initialize_nlfff_weight_cache(buffer_cells)
355 integer,
intent(in) :: buffer_cells
356 integer :: ig1,ig2,ig3
358 if(
allocated(nlfff_weight_x))
deallocate(nlfff_weight_x)
359 if(
allocated(nlfff_weight_y))
deallocate(nlfff_weight_y)
360 if(
allocated(nlfff_weight_z))
deallocate(nlfff_weight_z)
364 do ig1=lbound(nlfff_weight_x,1),ubound(nlfff_weight_x,1)
367 do ig2=lbound(nlfff_weight_y,1),ubound(nlfff_weight_y,1)
370 do ig3=lbound(nlfff_weight_z,1),ubound(nlfff_weight_z,1)
373 end subroutine initialize_nlfff_weight_cache
375 subroutine deallocate_nlfff_weight_cache()
376 if(
allocated(nlfff_weight_x))
deallocate(nlfff_weight_x)
377 if(
allocated(nlfff_weight_y))
deallocate(nlfff_weight_y)
378 if(
allocated(nlfff_weight_z))
deallocate(nlfff_weight_z)
379 end subroutine deallocate_nlfff_weight_cache
381 logical function cached_fixed_active_cell(igrid,ix1,ix2,ix3)
383 pig1_,pig2_,pig3_,domain_nx1,domain_nx2,domain_nx3
385 integer,
intent(in) :: igrid,ix1,ix2,ix3
386 integer :: ig1,ig2,ig3
392 domain_nx1,domain_nx2,domain_nx3)
393 end function cached_fixed_active_cell
395 double precision function cached_cell_weight(igrid,ix1,ix2,ix3)
399 integer,
intent(in) :: igrid,ix1,ix2,ix3
400 integer :: ig1,ig2,ig3
405 cached_cell_weight=nlfff_weight_x(ig1)*nlfff_weight_y(ig2)*nlfff_weight_z(ig3)
406 end function cached_cell_weight
408 subroutine report_nlfff_timing()
412 double precision :: local_values(8),global_values(8)
422 call mpi_allreduce(local_values,global_values,8,mpi_double_precision,&
425 write(*,
'(a,8(1x,es16.8))')
'NLFFF timing rank-max [s]:',global_values
426 write(*,
'(a)')
' update_kernel binomial_halo_x binomial_halo_y '//&
427 'trial_b_exchange qs_exchange functional_local '//&
428 'functional_reductions unified_diagnostics'
430 end subroutine report_nlfff_timing
432 subroutine accumulate_timing(counter,timing_start)
435 double precision,
intent(inout) :: counter
436 double precision,
intent(in) :: timing_start
438 if(optimization_timing_active) counter=counter+mpi_wtime()-timing_start
439 end subroutine accumulate_timing
441 subroutine validate_configuration(iw_b,config)
446 use,
intrinsic :: ieee_arithmetic, only: ieee_is_finite
448 integer,
intent(in) :: iw_b(3)
450 double precision :: dx1m,dx2m,tol
453 if(
ndim/=3 .or.
ndir/=3)
call mpistop(
'NLFFF optimization requires 3D vectors')
457 call mpistop(
'NLFFF optimization requires refine_max_level=1')
459 if(
b0field)
call mpistop(
'NLFFF optimization does not support B0 splitting')
460 if(any(
periodb))
call mpistop(
'NLFFF optimization requires physical side and top boundaries')
461 if(
nghostcells<2)
call mpistop(
'NLFFF optimization requires at least two ghost cells')
462 if(block_nx1<3 .or. block_nx2<3 .or. block_nx3<3) &
463 call mpistop(
'NLFFF one-sided boundary stencil requires at least three cells per block')
464 if(nw<3)
call mpistop(
'NLFFF optimization requires three magnetic variables')
465 if(iw_b(1)<1 .or. iw_b(3)>nw) &
466 call mpistop(
'NLFFF magnetic variable indices are outside the state')
467 if(any(iw_b/=(/iw_b(1),iw_b(1)+1,iw_b(1)+2/))) &
468 call mpistop(
'NLFFF optimization requires contiguous B components')
469 if(.not.
allocated(bottom_b))
call mpistop(
'NLFFF vector boundary has not been initialized')
470 if(.not.all(ieee_is_finite(bottom_b)))
call mpistop(
'NLFFF vector boundary is non-finite')
472 if(config%fft_padding_factor<1)
call mpistop(
'NLFFF fft_padding_factor must be set')
473 if(trim(config%fft_top_boundary)/=
'open' .and. &
474 trim(config%fft_top_boundary)/=
'closed') &
475 call mpistop(
"NLFFF fft_top_boundary must be 'open' or 'closed'")
476 if(trim(config%flux_treatment)/=
'strict' .and. &
477 trim(config%flux_treatment)/=
'subtract_mean') &
478 call mpistop(
"NLFFF flux_treatment must be 'strict' or 'subtract_mean'")
479 if(.not.ieee_is_finite(config%max_flux_imbalance) .or. &
480 config%max_flux_imbalance<0.d0 .or. config%max_flux_imbalance>1.d0) &
481 call mpistop(
'NLFFF max_flux_imbalance must be explicitly set in [0,1]')
482 if(config%buffer_cells<2)
call mpistop(
'NLFFF buffer_cells must be at least two')
483 if(2*config%buffer_cells>=domain_nx1 .or. &
484 2*config%buffer_cells>=domain_nx2 .or. &
485 config%buffer_cells>=domain_nx3) &
486 call mpistop(
'NLFFF cosine buffer leaves no interior physical region')
487 if(config%max_iterations<1)
call mpistop(
'NLFFF max_iterations must be positive')
488 if(.not.ieee_is_finite(config%initial_step_scale) .or. &
489 config%initial_step_scale<=0.d0) &
490 call mpistop(
'NLFFF initial_step_scale must be positive')
491 if(config%log_interval<1)
call mpistop(
'NLFFF log_interval must be positive')
492 if(config%plateau_interval<1) &
493 call mpistop(
'NLFFF plateau_interval must be positive')
494 if(config%plateau_window<1) &
495 call mpistop(
'NLFFF plateau_window must be positive')
496 if(.not.ieee_is_finite(config%plateau_tolerance) .or. &
497 config%plateau_tolerance<0.d0) &
498 call mpistop(
'NLFFF plateau_tolerance must be non-negative')
499 if(trim(config%update_preconditioner)/=
'none' .and. &
500 trim(config%update_preconditioner)/=
'binomial_xy') &
501 call mpistop(
"NLFFF update_preconditioner must be 'none' or 'binomial_xy'")
502 if(trim(config%initialization_mode)/=
'potential' .and. &
503 trim(config%initialization_mode)/=
'current_state') &
504 call mpistop(
"NLFFF initialization_mode must be 'potential' or 'current_state'")
506 if(
size(bottom_b,1)<domain_nx1 .or.
size(bottom_b,2)<domain_nx2) &
507 call mpistop(
'NLFFF vector boundary is smaller than the physical domain')
508 if(mod(
size(bottom_b,1)-domain_nx1,2)/=0 .or. &
509 mod(
size(bottom_b,2)-domain_nx2,2)/=0) &
510 call mpistop(
'NLFFF vector boundary padding must be symmetric')
511 pad1=(
size(bottom_b,1)-domain_nx1)/2
512 pad2=(
size(bottom_b,2)-domain_nx2)/2
514 call mpistop(
'NLFFF vector boundary lacks horizontal ghost padding')
515 if(
size(
xa1)<2 .or.
size(
xa2)<2)
call mpistop(
'NLFFF boundary coordinates are incomplete')
518 tol=1.
d-10*max(1.d0,abs(
dx(1,1)),abs(
dx(2,1)))
519 if(abs(dx1m-
dx(1,1))>tol .or. abs(dx2m-
dx(2,1))>tol) &
520 call mpistop(
'NLFFF vector boundary spacing does not match the grid')
521 if(abs(
xa1(pad1+1)-(xprobmin1+0.5d0*
dx(1,1)))>tol .or. &
522 abs(
xa1(pad1+domain_nx1)-(xprobmax1-0.5d0*
dx(1,1)))>tol .or. &
523 abs(
xa2(pad2+1)-(xprobmin2+0.5d0*
dx(2,1)))>tol .or. &
524 abs(
xa2(pad2+domain_nx2)-(xprobmax2-0.5d0*
dx(2,1)))>tol) &
525 call mpistop(
'NLFFF vector boundary center does not match the grid')
526 end subroutine validate_configuration
531 subroutine validate_exchange_state()
541 call mpistop(
'NLFFF optimization requires full-state ghost exchange on entry')
542 end subroutine validate_exchange_state
544 subroutine prepare_partial_exchange_types(iw_b)
547 integer,
intent(in) :: iw_b(3)
564 call restore_full_exchange_types()
565 end subroutine prepare_partial_exchange_types
567 subroutine restore_full_exchange_types()
577 end subroutine restore_full_exchange_types
579 subroutine exchange_b_internal(iw_b)
583 integer,
intent(in) :: iw_b(3)
593 call restore_full_exchange_types()
594 end subroutine exchange_b_internal
596 subroutine exchange_qs_internal(qs_state)
610 call restore_full_exchange_types()
611 end subroutine exchange_qs_internal
613 subroutine exchange_trial_b_internal(iw_b)
617 integer,
intent(in) :: iw_b(3)
627 call restore_full_exchange_types()
628 end subroutine exchange_trial_b_internal
631 subroutine apply_physical_boundaries(field_state,iw_b)
636 integer,
intent(in) :: iw_b(3)
638 integer :: iigrid,igrid,idir,ix1,ix2,ix3,ib1,ib2
639 double precision :: dx1m,dx2m
643 do iigrid=1,igridstail
645 if(
ps(igrid)%is_physical_boundary(1))
then
646 do ix1=ixglo1,ixmlo1-1
648 field_state(igrid)%w(ix1,ixglo2:ixghi2,ixglo3:ixghi3,iw_b(idir))=&
649 field_state(igrid)%w(ixmlo1,ixglo2:ixghi2,ixglo3:ixghi3,iw_b(idir))
653 if(
ps(igrid)%is_physical_boundary(2))
then
654 do ix1=ixmhi1+1,ixghi1
656 field_state(igrid)%w(ix1,ixglo2:ixghi2,ixglo3:ixghi3,iw_b(idir))=&
657 field_state(igrid)%w(ixmhi1,ixglo2:ixghi2,ixglo3:ixghi3,iw_b(idir))
661 if(
ps(igrid)%is_physical_boundary(3))
then
662 do ix2=ixglo2,ixmlo2-1
664 field_state(igrid)%w(ixglo1:ixghi1,ix2,ixglo3:ixghi3,iw_b(idir))=&
665 field_state(igrid)%w(ixglo1:ixghi1,ixmlo2,ixglo3:ixghi3,iw_b(idir))
669 if(
ps(igrid)%is_physical_boundary(4))
then
670 do ix2=ixmhi2+1,ixghi2
672 field_state(igrid)%w(ixglo1:ixghi1,ix2,ixglo3:ixghi3,iw_b(idir))=&
673 field_state(igrid)%w(ixglo1:ixghi1,ixmhi2,ixglo3:ixghi3,iw_b(idir))
677 if(
ps(igrid)%is_physical_boundary(6))
then
678 do ix3=ixmhi3+1,ixghi3
680 field_state(igrid)%w(ixglo1:ixghi1,ixglo2:ixghi2,ix3,iw_b(idir))=&
681 field_state(igrid)%w(ixglo1:ixghi1,ixglo2:ixghi2,ixmhi3,iw_b(idir))
687 if(
ps(igrid)%is_physical_boundary(5))
then
688 do ix3=ixglo3,ixmlo3-1
691 ib1=nint((
ps(igrid)%x(ix1,ix2,ix3,1)-
xa1(1))/dx1m)+1
692 ib2=nint((
ps(igrid)%x(ix1,ix2,ix3,2)-
xa2(1))/dx2m)+1
693 if(ib1<1 .or. ib1>
size(bottom_b,1) .or. &
694 ib2<1 .or. ib2>
size(bottom_b,2))
then
695 write(*,*)
'NLFFF boundary pixel outside frame:',
mype,igrid,ib1,ib2
696 call mpistop(
'NLFFF lower-boundary mapping failed')
699 field_state(igrid)%w(ix1,ix2,ix3,iw_b(idir))=&
700 bottom_b(ib1,ib2,idir)
707 end subroutine apply_physical_boundaries
710 subroutine evaluate_functional(field_state,iw_b,qs_state,buffer_cells,L,&
711 Lforce,Ldiv,B2integral)
715 use,
intrinsic :: ieee_arithmetic, only: ieee_is_finite
717 integer,
intent(in) :: iw_b(3),buffer_cells
720 double precision,
intent(out) ::
l,lforce,ldiv,b2integral
722 double precision :: local_force,local_div,local_b2
723 double precision :: part_force,part_div,part_b2
724 double precision :: cell_force,cell_div,diagnostic(3)
725 double precision :: local_values(3),global_values(3)
726 double precision :: timing_start
727 double precision :: bvec(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3,1:3)
728 double precision :: current(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3,1:3)
729 double precision :: divb_array(ixglo1:ixghi1,ixglo2:ixghi2,&
731 double precision :: oa(3),ob(3),b(3),j(3),divb,b2,wcell,cell_volume
732 integer :: iigrid,igrid,ix1,ix2,ix3,ixb3,idirmin
737 timing_start=mpi_wtime()
738 do iigrid=1,igridstail
740 qs_state(igrid)%w(ixg^t,1:4)=0.d0
743 do iigrid=1,igridstail
750 bvec(ix1,ix2,ix3,:)=&
751 field_state(igrid)%w(ix1,ix2,ix3,iw_b(:))
760 call override_fixed_boundary_curl_div(bvec,igrid,current,divb_array)
767 b=bvec(ix1,ix2,ix3,:)
768 j=current(ix1,ix2,ix3,:)
769 divb=divb_array(ix1,ix2,ix3)
772 qs_state(igrid)%w(ix1,ix2,ix3,1:3)=cross3(oa,b)
773 qs_state(igrid)%w(ix1,ix2,ix3,4)=dot_product(ob,b)
774 wcell=cached_cell_weight(igrid,ix1,ix2,ix3)
775 cell_volume=
ps(igrid)%dvolume(ix1,ix2,ix3)
779 cell_force=wcell*b2*dot_product(oa,oa)*cell_volume
780 cell_div=wcell*b2*dot_product(ob,ob)*cell_volume
782 if(.not.all(ieee_is_finite(b)) .or. &
783 .not.all(ieee_is_finite(j)) .or. &
784 .not.ieee_is_finite(divb) .or. &
785 .not.all(ieee_is_finite(oa)) .or. &
786 .not.all(ieee_is_finite(ob)) .or. &
787 .not.ieee_is_finite(cell_force) .or. &
788 .not.ieee_is_finite(cell_div))
then
789 diagnostic=(/wcell,cell_force,cell_div/)
790 call report_nonfinite(
'functional',igrid,ix1,ix2,ix3,&
791 b,j,divb,oa,ob,diagnostic)
793 part_force=part_force+cell_force
794 part_div=part_div+cell_div
795 part_b2=part_b2+wcell*b2*cell_volume
802 if(
ps(igrid)%is_physical_boundary(5))
then
806 b=bvec(ix1,ix2,ixb3,:)
807 call compute_curl_div_node(bvec,igrid,ix1,ix2,ixb3,.true.,j,divb)
810 qs_state(igrid)%w(ix1,ix2,ixb3,1:3)=cross3(oa,b)
811 qs_state(igrid)%w(ix1,ix2,ixb3,4)=dot_product(ob,b)
812 wcell=cached_cell_weight(igrid,ix1,ix2,ixb3)
813 cell_volume=
ps(igrid)%dvolume(ix1,ix2,ixmlo3)
817 cell_force=wcell*b2*dot_product(oa,oa)*cell_volume
818 cell_div=wcell*b2*dot_product(ob,ob)*cell_volume
820 if(.not.all(ieee_is_finite(b)) .or. &
821 .not.all(ieee_is_finite(j)) .or. &
822 .not.ieee_is_finite(divb) .or. &
823 .not.all(ieee_is_finite(oa)) .or. &
824 .not.all(ieee_is_finite(ob)) .or. &
825 .not.ieee_is_finite(cell_force) .or. &
826 .not.ieee_is_finite(cell_div))
then
827 diagnostic=(/wcell,cell_force,cell_div/)
828 call report_nonfinite(
'lower-boundary functional',igrid,ix1,&
829 ix2,ixb3,b,j,divb,oa,ob,diagnostic)
831 part_force=part_force+cell_force
832 part_div=part_div+cell_div
833 part_b2=part_b2+wcell*b2*cell_volume
837 local_force=local_force+part_force
838 local_div=local_div+part_div
839 local_b2=local_b2+part_b2
842 timing_start=mpi_wtime()
843 local_values=(/local_force,local_div,local_b2/)
844 call mpi_allreduce(local_values,global_values,3,mpi_double_precision,&
846 lforce=global_values(1)
847 ldiv=global_values(2)
848 b2integral=global_values(3)
851 timing_start=mpi_wtime()
852 call exchange_qs_internal(qs_state)
854 end subroutine evaluate_functional
856 subroutine form_trial_field(iw_b,buffer_cells,update_preconditioner,step)
860 use,
intrinsic :: ieee_arithmetic, only: ieee_is_finite
862 integer,
intent(in) :: iw_b(3),buffer_cells
863 character(len=*),
intent(in) :: update_preconditioner
864 double precision,
intent(in) :: step
865 double precision :: bvec(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3,1:3)
866 double precision :: current(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3,1:3)
867 double precision :: divb_array(ixglo1:ixghi1,ixglo2:ixghi2,&
869 double precision :: qvec(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3,1:3)
870 double precision :: scalar_s(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3)
871 double precision :: curlq(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3,1:3)
872 double precision :: weight(ixglo1:ixghi1,ixglo2:ixghi2,ixglo3:ixghi3)
873 double precision :: grad_s_array(ixglo1:ixghi1,ixglo2:ixghi2,&
875 double precision :: grad_w(ixglo1:ixghi1,ixglo2:ixghi2,&
877 double precision :: b(3),j(3),oa(3),ob(3),ftilde(3),gw(3)
878 double precision :: q(3),curl_q(3),grad_scalar_s(3),divb,wcell
879 double precision :: timing_start
880 integer :: iigrid,igrid,ix1,ix2,ix3,idir,idirmin
882 timing_start=mpi_wtime()
883 do iigrid=1,igridstail
885 ps1(igrid)%w(ixg^t,iw_b(:))=0.d0
887 do iigrid=1,igridstail
894 bvec(ix1,ix2,ix3,:)=
ps(igrid)%w(ix1,ix2,ix3,iw_b(:))
895 qvec(ix1,ix2,ix3,:)=accepted_qs_state(igrid)%w(ix1,ix2,ix3,1:3)
896 scalar_s(ix1,ix2,ix3)=accepted_qs_state(igrid)%w(ix1,ix2,ix3,4)
897 weight(ix1,ix2,ix3)=cached_cell_weight(igrid,ix1,ix2,ix3)
913 grad_s_array(ixg^t,idir),1)
919 if(cached_fixed_active_cell(igrid,ix1,ix2,ix3)) cycle
920 b=bvec(ix1,ix2,ix3,:)
921 j=current(ix1,ix2,ix3,:)
922 divb=divb_array(ix1,ix2,ix3)
923 q=qvec(ix1,ix2,ix3,:)
924 curl_q=curlq(ix1,ix2,ix3,:)
925 grad_scalar_s=grad_s_array(ix1,ix2,ix3,:)
926 gw=grad_w(ix1,ix2,ix3,:)
929 scalar_s(ix1,ix2,ix3),curl_q,grad_scalar_s,gw,&
930 weight(ix1,ix2,ix3),ftilde)
931 if(.not.all(ieee_is_finite(b)) .or. &
932 .not.all(ieee_is_finite(j)) .or. &
933 .not.ieee_is_finite(divb) .or. &
934 .not.all(ieee_is_finite(oa)) .or. &
935 .not.all(ieee_is_finite(ob)) .or. &
936 .not.all(ieee_is_finite(ftilde))) &
937 call report_nonfinite(
'update force',igrid,ix1,ix2,ix3,&
938 b,j,divb,oa,ob,ftilde)
939 ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))=ftilde
946 if(trim(update_preconditioner)==
'binomial_xy')
then
947 timing_start=mpi_wtime()
948 call exchange_trial_b_internal(iw_b)
950 do iigrid=1,igridstail
952 trial_qs_state(igrid)%w(ixg^t,1:3)=0.d0
956 trial_qs_state(igrid)%w(ix1,ix2,ix3,1:3)=&
957 0.25d0*
ps1(igrid)%w(ix1-1,ix2,ix3,iw_b(:))+&
958 0.5d0*
ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))+&
959 0.25d0*
ps1(igrid)%w(ix1+1,ix2,ix3,iw_b(:))
966 ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))=&
967 trial_qs_state(igrid)%w(ix1,ix2,ix3,1:3)
972 timing_start=mpi_wtime()
973 call exchange_trial_b_internal(iw_b)
975 do iigrid=1,igridstail
980 trial_qs_state(igrid)%w(ix1,ix2,ix3,1:3)=&
981 0.25d0*
ps1(igrid)%w(ix1,ix2-1,ix3,iw_b(:))+&
982 0.5d0*
ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))+&
983 0.25d0*
ps1(igrid)%w(ix1,ix2+1,ix3,iw_b(:))
990 ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))=&
991 trial_qs_state(igrid)%w(ix1,ix2,ix3,1:3)
998 do iigrid=1,igridstail
1000 do ix3=ixmlo3,ixmhi3
1001 do ix2=ixmlo2,ixmhi2
1002 do ix1=ixmlo1,ixmhi1
1003 if(cached_fixed_active_cell(igrid,ix1,ix2,ix3))
then
1004 ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))=&
1005 ps(igrid)%w(ix1,ix2,ix3,iw_b(:))
1007 ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))=&
1008 ps(igrid)%w(ix1,ix2,ix3,iw_b(:))+&
1009 step*
ps1(igrid)%w(ix1,ix2,ix3,iw_b(:))
1015 end subroutine form_trial_field
1017 subroutine accept_trial_field(iw_b)
1020 integer,
intent(in) :: iw_b(3)
1021 integer :: iigrid,igrid
1023 do iigrid=1,igridstail
1024 igrid=igrids(iigrid)
1025 ps(igrid)%w(ixg^t,iw_b(:))=
ps1(igrid)%w(ixg^t,iw_b(:))
1027 end subroutine accept_trial_field
1029 subroutine swap_qs_states()
1030 type(
state),
allocatable :: temporary(:)
1032 call move_alloc(accepted_qs_state,temporary)
1033 call move_alloc(trial_qs_state,accepted_qs_state)
1034 call move_alloc(temporary,trial_qs_state)
1035 end subroutine swap_qs_states
1039 subroutine override_fixed_boundary_curl_div(bvec,igrid,current,divb)
1042 integer,
intent(in) :: igrid
1043 double precision,
intent(in) :: bvec(ixglo1:ixghi1,ixglo2:ixghi2,&
1045 double precision,
intent(inout) :: current(ixglo1:ixghi1,&
1046 ixglo2:ixghi2,ixglo3:ixghi3,1:3)
1047 double precision,
intent(inout) :: divb(ixglo1:ixghi1,&
1048 ixglo2:ixghi2,ixglo3:ixghi3)
1049 double precision :: j(3),div
1050 integer :: ix1,ix2,ix3
1052 do ix3=ixmlo3,ixmhi3
1053 do ix2=ixmlo2,ixmhi2
1054 do ix1=ixmlo1,ixmhi1
1055 if((
ps(igrid)%is_physical_boundary(1) .and. ix1==ixmlo1) .or. &
1056 (
ps(igrid)%is_physical_boundary(2) .and. ix1==ixmhi1) .or. &
1057 (
ps(igrid)%is_physical_boundary(3) .and. ix2==ixmlo2) .or. &
1058 (
ps(igrid)%is_physical_boundary(4) .and. ix2==ixmhi2) .or. &
1059 (
ps(igrid)%is_physical_boundary(6) .and. ix3==ixmhi3))
then
1060 call compute_curl_div_node(bvec,igrid,ix1,ix2,ix3,.false.,j,div)
1061 current(ix1,ix2,ix3,:)=j
1062 divb(ix1,ix2,ix3)=div
1067 end subroutine override_fixed_boundary_curl_div
1069 subroutine compute_curl_div_node(bvec,igrid,ix1,ix2,ix3,bottom_node,&
1073 integer,
intent(in) :: igrid,ix1,ix2,ix3
1074 logical,
intent(in) :: bottom_node
1075 double precision,
intent(in) :: bvec(ixglo1:ixghi1,ixglo2:ixghi2,&
1077 double precision,
intent(out) :: current(3),divb
1078 double precision :: db(3,3)
1083 call derivative_at_node(bvec,igrid,ix1,ix2,ix3,ic,idir,&
1084 bottom_node,db(ic,idir))
1087 current(1)=db(3,2)-db(2,3)
1088 current(2)=db(1,3)-db(3,1)
1089 current(3)=db(2,1)-db(1,2)
1090 divb=db(1,1)+db(2,2)+db(3,3)
1091 end subroutine compute_curl_div_node
1093 subroutine derivative_at_node(bvec,igrid,ix1,ix2,ix3,ic,idir,&
1094 bottom_node,derivative)
1097 integer,
intent(in) :: igrid,ix1,ix2,ix3,ic,idir
1098 logical,
intent(in) :: bottom_node
1099 double precision,
intent(in) :: bvec(ixglo1:ixghi1,ixglo2:ixghi2,&
1101 double precision,
intent(out) :: derivative
1105 if(
ps(igrid)%is_physical_boundary(1) .and. ix1==ixmlo1)
then
1107 bvec(ix1+1,ix2,ix3,ic),bvec(ix1+2,ix2,ix3,ic),
dx(1,1),.true.)
1108 else if(
ps(igrid)%is_physical_boundary(2) .and. ix1==ixmhi1)
then
1110 bvec(ix1-1,ix2,ix3,ic),bvec(ix1-2,ix2,ix3,ic),
dx(1,1),.false.)
1112 derivative=(bvec(ix1+1,ix2,ix3,ic)-&
1113 bvec(ix1-1,ix2,ix3,ic))/(2.d0*
dx(1,1))
1116 if(
ps(igrid)%is_physical_boundary(3) .and. ix2==ixmlo2)
then
1118 bvec(ix1,ix2+1,ix3,ic),bvec(ix1,ix2+2,ix3,ic),
dx(2,1),.true.)
1119 else if(
ps(igrid)%is_physical_boundary(4) .and. ix2==ixmhi2)
then
1121 bvec(ix1,ix2-1,ix3,ic),bvec(ix1,ix2-2,ix3,ic),
dx(2,1),.false.)
1123 derivative=(bvec(ix1,ix2+1,ix3,ic)-&
1124 bvec(ix1,ix2-1,ix3,ic))/(2.d0*
dx(2,1))
1127 if(bottom_node)
then
1129 bvec(ix1,ix2,ix3+1,ic),bvec(ix1,ix2,ix3+2,ic),
dx(3,1),.true.)
1130 else if(
ps(igrid)%is_physical_boundary(6) .and. ix3==ixmhi3)
then
1132 bvec(ix1,ix2,ix3-1,ic),bvec(ix1,ix2,ix3-2,ic),
dx(3,1),.false.)
1134 derivative=(bvec(ix1,ix2,ix3+1,ic)-&
1135 bvec(ix1,ix2,ix3-1,ic))/(2.d0*
dx(3,1))
1138 end subroutine derivative_at_node
1141 double precision,
intent(in) :: f0,f1,f2,h
1142 logical,
intent(in) :: at_low
1152 double precision,
intent(in) :: b(3),j(3),divb
1153 double precision,
intent(out) :: omega_a(3),omega_b(3)
1154 double precision :: b2
1158 omega_a=cross3(j,b)/b2
1170 curl_q,grad_s,grad_w,weight,ftilde)
1171 double precision,
intent(in) :: b(3),j(3),divb
1172 double precision,
intent(in) :: omega_a(3),omega_b(3),q(3),s
1173 double precision,
intent(in) :: curl_q(3),grad_s(3),grad_w(3),weight
1174 double precision,
intent(out) :: ftilde(3)
1175 double precision :: f(3)
1177 f=curl_q-cross3(omega_a,j)+grad_s-omega_b*divb+&
1178 (dot_product(omega_a,omega_a)+dot_product(omega_b,omega_b))*b
1179 ftilde=weight*f+cross3(q,grad_w)+s*grad_w
1183 characteristic_spacing,epsilon_force,epsilon_div)
1184 double precision,
intent(in) :: lforce,ldiv,b2integral
1185 double precision,
intent(in) :: characteristic_spacing
1186 double precision,
intent(out) :: epsilon_force,epsilon_div
1188 if(b2integral>0.d0)
then
1189 epsilon_force=characteristic_spacing*sqrt(max(0.d0,lforce)/b2integral)
1190 epsilon_div=characteristic_spacing*sqrt(max(0.d0,ldiv)/b2integral)
1200 pure function nlfff_binomial_triplet(left,center,right)
result(filtered)
1201 double precision,
intent(in) :: left(3),center(3),right(3)
1202 double precision :: filtered(3)
1204 filtered=0.25d0*left+0.5d0*center+0.25d0*right
1205 end function nlfff_binomial_triplet
1207 pure function cross3(a,b)
result(c)
1208 double precision,
intent(in) :: a(3),b(3)
1209 double precision :: c(3)
1211 c(1)=a(2)*b(3)-a(3)*b(2)
1212 c(2)=a(3)*b(1)-a(1)*b(3)
1213 c(3)=a(1)*b(2)-a(2)*b(1)
1217 integer,
intent(in) :: index,ncell,buffer_cells
1218 double precision :: s,pi
1221 if(index<1 .or. index>ncell)
then
1223 else if(index<=buffer_cells)
then
1224 s=dble(index-1)/dble(buffer_cells-1)
1226 else if(index>=ncell-buffer_cells+1)
then
1227 s=dble(ncell-index)/dble(buffer_cells-1)
1235 integer,
intent(in) :: index,ncell,buffer_cells
1236 double precision :: s,pi
1239 if(index>ncell)
then
1241 else if(index<1)
then
1243 else if(index>=ncell-buffer_cells+1)
then
1244 s=dble(ncell-index)/dble(buffer_cells-1)
1252 ncell1,ncell2,ncell3,buffer_cells)
1253 integer,
intent(in) :: index1,index2,index3
1254 integer,
intent(in) :: ncell1,ncell2,ncell3,buffer_cells
1263 ncell1,ncell2,ncell3)
1264 integer,
intent(in) :: index1,index2,index3,ncell1,ncell2,ncell3
1267 index1==ncell1 .or. index2==1 .or. index2==ncell2)
1270 pure double precision function cell_weight(x1,x2,x3,buffer_cells)
1272 domain_nx1,domain_nx2,domain_nx3
1274 double precision,
intent(in) :: x1,x2,x3
1275 integer,
intent(in) :: buffer_cells
1276 integer :: ig1,ig2,ig3
1278 ig1=nint((x1-xprobmin1)/
dx(1,1)+0.5d0)
1279 ig2=nint((x2-xprobmin2)/
dx(2,1)+0.5d0)
1280 ig3=nint((x3-xprobmin3)/
dx(3,1)+0.5d0)
1282 domain_nx3,buffer_cells)
1283 end function cell_weight
1285 pure logical function fixed_active_cell(x1,x2,x3)
1287 domain_nx1,domain_nx2,domain_nx3
1289 double precision,
intent(in) :: x1,x2,x3
1290 integer :: ig1,ig2,ig3
1292 ig1=nint((x1-xprobmin1)/
dx(1,1)+0.5d0)
1293 ig2=nint((x2-xprobmin2)/
dx(2,1)+0.5d0)
1294 ig3=nint((x3-xprobmin3)/
dx(3,1)+0.5d0)
1296 domain_nx2,domain_nx3)
1297 end function fixed_active_cell
1299 subroutine report_nonfinite(quantity,igrid,ix1,ix2,ix3,b,j,divb,oa,ob,extra)
1303 character(len=*),
intent(in) :: quantity
1304 integer,
intent(in) :: igrid,ix1,ix2,ix3
1305 double precision,
intent(in) :: b(3),j(3),divb,oa(3),ob(3),extra(3)
1307 write(*,*)
'Non-finite NLFFF ',trim(quantity),
' at rank/grid/cell:',&
1308 mype,igrid,ix1,ix2,ix3
1310 write(*,*)
'curl(B):',j
1311 write(*,*)
'div(B):',divb
1312 write(*,*)
'Omega_a:',oa
1313 write(*,*)
'Omega_b:',ob
1314 write(*,*)
'stage values:',extra
1315 call mpistop(
'non-finite value in NLFFF optimization')
1316 end subroutine report_nonfinite
1318 subroutine write_log_row(unit,result,step,L,Lforce,Ldiv,B2integral,&
1319 epsilon_force,epsilon_div,accepted_trial)
1320 integer,
intent(in) :: unit
1322 double precision,
intent(in) :: step,l,lforce,ldiv,b2integral
1323 double precision,
intent(in) :: epsilon_force,epsilon_div
1324 logical,
intent(in) :: accepted_trial
1326 write(unit,
'(i0,a,i0,a,i0,7(a,es24.16),a,i0,a,es24.16,a,i0,a,es24.16,a,l1)') result%attempts,
',',&
1327 result%accepted_steps,
',',result%rejected_steps,
',',step,
',',l,
',',&
1328 lforce,
',',ldiv,
',',b2integral,
',',epsilon_force,
',',epsilon_div,
',',&
1329 result%functional_evaluations,
',',result%step_floor,
',',&
1330 result%plateau_count,
',',result%final_relative_functional_change,
',',&
1333 end subroutine write_log_row
subroutine, public alloc_state(igrid, s, ixgl, ixgextl, alloc_once_for_ps)
allocate memory to physical state of igrid node
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module with geometry-related routines (e.g., divergence, curl)
subroutine divvector(qvec, ixil, ixol, divq, nth_in)
integer, parameter cartesian
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...
subroutine gradient(q, ixil, ixol, idir, gradq, nth_in)
update ghost cells of all blocks including physical boundaries
integer, dimension(0:3^d &), target type_recv_p_p1
integer, dimension( :^d &), pointer type_recv_r
integer, dimension(-1:1^d &), target type_send_srl_p1
integer, dimension(-1:1^d &), target type_send_r_p2
subroutine getbc(time, qdt, psb, nwstart, nwbc)
do update ghost cells of all blocks including physical boundaries
integer, dimension(0:3^d &), target type_send_p_f
subroutine create_bc_mpi_datatype(nwstart, nwbc, nwfull)
integer, dimension( :^d &), pointer type_send_p
integer, dimension(0:3^d &), target type_recv_p_p2
integer, dimension( :^d &), pointer type_send_srl
integer, dimension(-1:1^d &), target type_send_r_p1
integer, dimension(-1:1^d &), target type_send_srl_p2
integer, dimension(0:3^d &), target type_send_p_p2
integer, dimension(0:3^d &), target type_recv_r_f
integer, dimension(-1:1^d &), target type_recv_srl_f
integer, dimension(-1:1^d &), target type_send_r_f
integer, dimension(-1:1^d &), target type_send_srl_f
integer, dimension(0:3^d &), target type_send_p_p1
integer, dimension( :^d &), pointer type_send_r
integer, dimension(0:3^d &), target type_recv_r_p1
integer, dimension(0:3^d &), target type_recv_r_p2
integer, dimension(-1:1^d &), target type_recv_srl_p2
integer, dimension( :^d &), pointer type_recv_p
integer, dimension(-1:1^d &), target type_recv_srl_p1
integer, dimension(0:3^d &), target type_recv_p_f
integer, dimension( :^d &), pointer type_recv_srl
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.
double precision global_time
The global simulation time.
integer, parameter ndim
Number of spatial dimensions for grid variables.
logical stagger_grid
True for using stagger grid.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer ndir
Number of spatial dimensions (components) for vector variables.
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
logical, dimension(ndim) stretched_dim
True if a dimension is stretched.
logical, dimension(ndim) periodb
True for dimensions with periodic boundaries.
logical b0field
split magnetic field as background B0 field
double precision, dimension(:,:), allocatable dx
spatial steps for all dimensions at all levels
integer nghostcells
Number of ghost cells surrounding a grid.
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
character(len=std_len) base_filename
Base file name for simulation output, which will be followed by a number.
integer refine_max_level
Maximal number of AMR levels.
integer max_blocks
The maximum number of grid blocks in a processor.
integer, dimension(:,:), allocatable node
Program to extrapolate linear force-free fields in 3D Cartesian coordinates, based on exact Green fun...
subroutine init_b_fff_data_driven_boundary(boundaryname, qlunit, qbunit, qxc1, qxc2, bvector)
double precision, dimension(:), allocatable, save xa2
subroutine extrapolate_potential_fft(iw_b, padding_factor, source_plane_depth, alpha, top_boundary, flux_treatment, max_flux_imbalance)
Extrapolate a Cartesian potential field with horizontal Fourier modes. The bottom magnetogram must ma...
double precision, dimension(:), allocatable, save xa1
Common user-facing diagnostics for NLFFF relaxation and extrapolation.
subroutine, public write_nlfff_metrics_header(unit)
subroutine, public write_nlfff_metrics_row(unit, iteration, metrics)
subroutine, public evaluate_nlfff_metrics_amrvac(iw_b, metrics)
Evaluate the common metrics on all active AMRVAC cells. The local cell size makes epsilon_force and e...
Nonlinear force-free extrapolation by the weighted optimization method.
subroutine, public init_nlfff_optimization_boundary(filename, qlunit, qbunit, qxc1, qxc2)
Read one Python V1 data-driven vector magnetogram. The same read also initializes mod_lfff's normaliz...
pure double precision function, public nlfff_weight_product(index1, index2, index3, ncell1, ncell2, ncell3, buffer_cells)
pure subroutine, public nlfff_dimensionless_metrics(lforce, ldiv, b2integral, characteristic_spacing, epsilon_force, epsilon_div)
pure double precision function, public nlfff_cosine_top_weight(index, ncell, buffer_cells)
pure double precision function, public nlfff_boundary_derivative(f0, f1, f2, h, at_low)
pure subroutine, public nlfff_compute_omega_cell(b, j, divb, omega_a, omega_b)
subroutine, public extrapolate_nlfff_optimization(iw_b, config, result)
Perform a one-shot, fixed-grid weighted optimization extrapolation.
type(nlfff_optimization_timing), save optimization_timing
pure logical function, public nlfff_fixed_active_index(index1, index2, index3, ncell1, ncell2, ncell3)
pure subroutine, public nlfff_compose_update_cell(b, j, divb, omega_a, omega_b, q, s, curl_q, grad_s, grad_w, weight, ftilde)
Pointwise terms in Equations (13) and (15) of Wiegelmann (2004). Derivatives and Q=Omega_a x B,...
pure double precision function, public nlfff_cosine_side_weight(index, ncell, buffer_cells)
type(state), dimension(:), allocatable, target ps
array of physical states for all blocks on my processor
type(state), dimension(:), allocatable, target ps1
array of physical states, temp 1 for multi-step time integrator