PB3D [2.47]
Ideal linear high-n MHD stability in 3-D
Loading...
Searching...
No Matches
vac_ops.f90
Go to the documentation of this file.
1!------------------------------------------------------------------------------!
2!> Operations and variables pertaining to the vacuum response.
3!!
4!! The vacuum response is calculated using the Boundary Element Method. There
5!! are two possibilities, indicated by the variable \c style in \c vac_vars.
6!! -# 3-D field aligned:
7!! - Makes use of the collocation method for the grid points along the
8!! magnetic field lines.
9!! - The 3-D integrals are therefore integrated using Weyl's theorem
10!! \cite Helander2014, p. 5.
11!! -# axisymmetric:
12!! - Makes use of an analytical expression for the toroidal integration of
13!! the Green's functions, as shown in \cite Cohl1999.
14!! - The integration in the poloidal angle is done using the collocation
15!! method.
16!!
17!! The final matrix equation is solved through Strumpack \cite Meiser2016.
18!!
19!! \see See \cite Weyens3D.
20!!
21!! \todo The vacuum part of PB3D is still under construction and not usable yet.
22!------------------------------------------------------------------------------!
23module vac_ops
24#include <PB3D_macros.h>
25#include <wrappers.h>
26 use strumpackdensepackage
28 use messages
29 use output_ops
30 use num_vars, only: dp, pi, max_str_ln, iu
31 use grid_vars, only: grid_type
32 use eq_vars, only: eq_1_type, eq_2_type
33 use x_vars, only: x_2_type, modes_type
34 use vac_vars, only: copy_vac, &
36
37 implicit none
38 private
40#if ldebug
42#endif
43
44 ! global variables
45#if ldebug
46 logical :: debug_calc_gh = .false. !< plot debug information for calc_GH() \ldebug
47 logical :: debug_calc_vac_res = .false. !< plot debug information for calc_vac_res() \ldebug
48 logical :: debug_vac_pot_plot = .false. !< plot debug information for vac_pot_plot() \ldebug
49#endif
50
51contains
52 !> Stores calculation of the vacuum response by storing the necessary
53 !! variables.
54 !!
55 !! This is done by the last process, which is the one that contains the
56 !! variables at the plasma edge, and then this is broadcasted to the other
57 !! processes.
58 !!
59 !! The workings of this routine are summarized in the following diagram for
60 !! VMEC:
61 !! @dot
62 !! digraph G {
63 !! eq_job -> restart [label=yes]
64 !! eq_job -> store [label=no]
65 !! restart -> jump [label=yes]
66 !! restart -> copy [label=no]
67 !! jump -> reconstructcur [label=yes]
68 !! jump -> restartone [label=no]
69 !! reconstructcur -> return
70 !! restartone -> reconstruct [label=no]
71 !! copy -> store
72 !! reconstruct -> store
73 !! restartone -> store [label=yes]
74 !! store -> return
75 !!
76 !! eq_job [shape=diamond,label=eq_job_1]
77 !! store [label=store_new_vec_res]
78 !! restart [shape=diamond,label=restart]
79 !! jump [shape=diamond,label=jump]
80 !! copy [label=copy_prev_level]
81 !! reconstructcur [label=reconstruct_cur_level]
82 !! return [label=return]
83 !! restartone [shape=diamond,label=restart_1]
84 !! reconstruct [label=reconstruct_prev_level]
85 !! }
86 !! @enddot
87 !!
88 !! Before storing the new variables, the procedure checks the following
89 !! things:
90 !! - For the first equilibrium job, this routine copies the results from
91 !! the previous Richardson level into the appropriate subranges of the new
92 !! vacuum variables if no restart of Richardson levels was done.
93 !! - If a restart was done and the level is greater than one, there is a
94 !! reconstruction of the previous level's variables. But this will happen
95 !! in calc_vac_res(), not in this procedure.
96 !!
97 !! If there is a jump straight to the solution, however, the procedure
98 !! returns after reconstructing the current level's variables.
99 !!
100 !! If the previous \c G and \c H variables are empty, they are regenerated
101 !! later, in calc_gh(). This indicates that a reconstruction happened,
102 !! either because a Richardson restart was performed, or because a new
103 !! Richardson level was started after a previous level in which it was
104 !! jumped straight to the solution.
105 !!
106 !! For HELENA, there is only 1 equilibrium job at the first Richardson
107 !! level, and the vacuum has to be calculated only once then. If there is a
108 !! jump to solution or if there is Richardson restart, it only needs to be
109 !! reconstructed.
110 !!
111 !! \return ierr
112 integer function store_vac(grid,eq_1,eq_2,vac) result(ierr)
116 use rich_vars, only: rich_lvl
118 use num_utilities, only: c
119 use grid_vars, only: n_r_eq
120 use x_vars, only: prim_x
121
122 character(*), parameter :: rout_name = 'store_vac'
123
124 ! input / output
125 type(grid_type), intent(in) :: grid !< equilibrium grid
126 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
127 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
128 type(vac_type), intent(inout) :: vac !< vacuum variables
129
130 ! local variables
131 integer :: id, jd ! counter
132 integer :: r_id ! normal index
133 real(dp) :: jq ! iota or q
134 character(len=max_str_ln) :: err_msg ! error message
135
136 ! initialize ierr
137 ierr = 0
138
139 ! set jq
140 if (use_pol_flux_f) then
141 jq = eq_1%q_saf_FD(grid%loc_n_r,0)
142 else
143 jq = eq_1%rot_t_FD(grid%loc_n_r,0)
144 ierr = 1
145 err_msg = 'TOR. FLUX HAS NOT BEEN TESTED AND IS MOST PROBABLY NOT &
146 &CORRECT YET'
147 chckerr(err_msg)
148 end if
149 ierr = broadcast_var(jq,n_procs-1)
150 chckerr('')
151
152 ! call the approriate procedure
153 select case (eq_style)
154 case (1) ! VMEC
155 ierr = store_vac_vmec()
156 chckerr('')
157 case (2) ! HELENA
158 ierr = store_vac_hel()
159 chckerr('')
160 end select
161 contains
162 ! VMEC version
163 !> \private
164 integer function store_vac_vmec() result(ierr)
165 use num_vars, only: jump_to_sol
166 use grid_vars, only: n_alpha
167 use rich_vars, only: n_par_x
168 use eq_utilities, only: calc_inv_met
170
171 character(*), parameter :: rout_name = 'store_vac_VMEC'
172
173 ! local variables
174 integer :: par_id(3) ! total parallel index
175 real(dp), allocatable :: norm_com_c(:,:,:) ! Cylindrical components of norm
176 real(dp), allocatable :: t_cv_loc(:,:,:,:,:,:,:) ! local T_CV
177 type(vac_type) :: vac_old ! old vacuum variables
178 logical :: interlaced_vac_copy_needed ! interlaced copy vacuum needed
179
180 ! initialize ierr
181 ierr = 0
182
183 ! user output
184 call writo('Start storing vacuum quantities')
185
186 call lvl_ud(1)
187
188 ! if start of Richardson level that is not the first, copy previous
189 ! results
190 if (eq_job_nr.eq.1) then
191 if (rich_lvl.eq.rich_restart_lvl) then ! restarting
192 if (jump_to_sol) then ! jumping straight to solution
193 ! nothing needs to be done here
194 return
195 else ! not jumping to solution
196 if (rich_restart_lvl.eq.1) then ! not actually restarting, just starting
197 ! nothing extra needed
198 interlaced_vac_copy_needed = .false.
199 else ! restarting
200 ! reconstruct old vacuum
201 ierr = reconstruct_pb3d_vac(vac_old,'vac',&
203 chckerr('')
204
205 ! interlaced copy necessary
206 interlaced_vac_copy_needed = .true.
207 end if
208 end if
209 else ! not restarting
210 ! copy old vacuum temporarily
211 ierr = copy_vac(vac,vac_old)
212 chckerr('')
213
214 ! deallocate
215 call vac%dealloc()
216
217 ! interlaced copy necessary
218 interlaced_vac_copy_needed = .true.
219 end if
220
221 ! allocate
222 ierr = vac%init(1,n_par_x*n_alpha,prim_x,[n_par_x,n_alpha],jq)
223 chckerr('')
224
225 ! interlaced copy if needed
226 if (interlaced_vac_copy_needed) then
227 ierr = interlaced_vac_copy(vac_old,vac)
228 chckerr('')
229 end if
230 end if
231
232 ! add results from current equilibrium job to the variables
233 if (rank.eq.n_procs-1) then
234 ! grid point to save is last local
235 r_id = grid%loc_n_r
236
237 ! calculate Cartesian components of J nabla psi
238 ! = J_F (Dpsi_pol/Dr_V) / 2pi (T_C^V)^T (e^C)
239 ! = J_F T_F^V(1,2) (T_C^V)^T (e^C)
240 allocate(norm_com_c(grid%n(1),grid%n(2),3))
241 norm_com_c = 0._dp
242 allocate(t_cv_loc(size(eq_2%T_VC,1),size(eq_2%T_VC,2),1:1,&
243 &size(eq_2%T_VC,4),0:0,0:0,0:0))
244 ierr = calc_inv_met(t_cv_loc,eq_2%T_VC(:,:,r_id:r_id,:,:,:,:),&
245 &[0,0,0])
246 chckerr('')
247 do id = 1,3
248 norm_com_c(:,:,id) = eq_2%jac_FD(:,:,r_id,0,0,0) * &
249 &eq_2%T_EF(:,:,r_id,c([1,2],.false.),0,0,0)*&
250 &t_cv_loc(:,:,1,c([id,1],.false.),0,0,0) ! Cylindrical contravariant components
251 end do
252 deallocate(t_cv_loc)
253
254 ! iterate over all field lines
255 do jd = 1,n_alpha
256 ! set total parallel id
257 if (rich_lvl.eq.1) then
258 par_id = [eq_jobs_lims(:,eq_job_nr),1]
259 else
260 par_id = [2*eq_jobs_lims(:,eq_job_nr),2]
261 end if
262 par_id(1:2) = par_id(1:2) + (jd-1)*n_par_x
263
264 ! save X, Y and Z
265 vac%x_vec(par_id(1):par_id(2):par_id(3),1) = &
266 &eq_2%R_E(:,jd,r_id,0,0,0) * cos(grid%zeta_E(:,jd,r_id))
267 vac%x_vec(par_id(1):par_id(2):par_id(3),2) = &
268 &eq_2%R_E(:,jd,r_id,0,0,0) * sin(grid%zeta_E(:,jd,r_id))
269 vac%x_vec(par_id(1):par_id(2):par_id(3),3) = &
270 &eq_2%Z_E(:,jd,r_id,0,0,0)
271
272 ! save norm
273 vac%norm(par_id(1):par_id(2):par_id(3),1) = &
274 &norm_com_c(:,jd,1)*cos(grid%zeta_E(:,jd,r_id)) - &
275 &norm_com_c(:,jd,2)*sin(grid%zeta_E(:,jd,r_id)) / &
276 &eq_2%R_E(:,jd,r_id,0,0,0) ! ~ e^X
277 vac%norm(par_id(1):par_id(2):par_id(3),2) = &
278 &norm_com_c(:,jd,1)*sin(grid%zeta_E(:,jd,r_id)) + &
279 &norm_com_c(:,jd,2)*cos(grid%zeta_E(:,jd,r_id)) / &
280 &eq_2%R_E(:,jd,r_id,0,0,0) ! ~ e^X
281 vac%norm(par_id(1):par_id(2):par_id(3),3) = &
282 &norm_com_c(:,jd,3) ! ~ e^Z
283
284 ! save metric factors and H factor
285 vac%h_fac(par_id(1):par_id(2):par_id(3),1) = &
286 &eq_2%g_FD(:,jd,r_id,c([1,1],.true.),0,0,0) ! g_11
287 vac%h_fac(par_id(1):par_id(2):par_id(3),2) = &
288 &eq_2%g_FD(:,jd,r_id,c([1,3],.true.),0,0,0) ! g_13
289 vac%h_fac(par_id(1):par_id(2):par_id(3),3) = &
290 &eq_2%g_FD(:,jd,r_id,c([3,3],.true.),0,0,0) ! g_33
291 vac%h_fac(par_id(1):par_id(2):par_id(3),4) = 0.5_dp* &
292 &eq_2%jac_FD(:,jd,r_id,0,0,0)**2 * &
293 &(eq_2%h_FD(:,jd,r_id,c([2,2],.true.),0,0,0) / &
294 &eq_2%g_FD(:,jd,r_id,c([1,1],.true.),0,0,0))**1.5 * ( &
295 &eq_2%jac_FD(:,jd,r_id,0,1,0)/&
296 &eq_2%jac_FD(:,jd,r_id,0,0,0) + &
297 &0.5_dp*eq_2%h_FD(:,jd,r_id,c([2,2],.true.),0,1,0)/&
298 &eq_2%h_FD(:,jd,r_id,c([2,2],.true.),0,0,0) - &
299 &0.5_dp*eq_2%g_FD(:,jd,r_id,c([1,1],.true.),0,1,0)/&
300 &eq_2%g_FD(:,jd,r_id,c([1,1],.true.),0,0,0) &
301 &) ! J/2 h^22/g_11 d/d2 (J sqrt(h^22/g_11)))
302 end do
303 !call print_ex_2D(['norm'],'norm_'//trim(i2str(rich_lvl)),&
304 !&vac%norm,persistent=.true.)
305 !call print_ex_2D(['x_vec'],'x_vec'//trim(i2str(rich_lvl)),&
306 !&vac%x_vec,persistent=.true.)
307 end if
308
309 ! broadcast to other processes
310 do id = 1,size(vac%x_vec,2)
311 ierr = broadcast_var(vac%x_vec(:,id),n_procs-1)
312 chckerr('')
313 ierr = broadcast_var(vac%norm(:,id),n_procs-1)
314 chckerr('')
315 end do
316 do id = 1,size(vac%h_fac,2)
317 ierr = broadcast_var(vac%h_fac(:,id),n_procs-1)
318 chckerr('')
319 end do
320
321 call lvl_ud(-1)
322
323 call writo('Done storing vacuum quantities')
324 end function store_vac_vmec
325 ! HELENA version
326 !> \private
327 integer function store_vac_hel() result(ierr)
328 use helena_vars, only: r_h, z_h, nchi, chi_h, ias
329 use grid_utilities, only: nufft
330
331 character(*), parameter :: rout_name = 'store_vac_HEL'
332
333 ! local variables
334 integer :: n_theta ! total number of points, including overlap
335 real(dp), allocatable :: x_vec_f(:,:) ! Fourier components of x_vec
336 character(len=max_str_ln) :: err_msg ! error message
337
338 ! initialize ierr
339 ierr = 0
340
341 ierr = 2
342 chckerr('Vacuum has not been implemented yet!')
343
344 ! user output
345 call writo('Start storing vacuum quantities')
346
347 call lvl_ud(1)
348
349 ! if restarting a Richardson level, reconstruct. If not, only
350 ! calculate for the first level
351 if (rich_lvl.eq.rich_restart_lvl) then
352 ! check for start from zero versus restart
353 if (rich_restart_lvl.eq.1) then ! starting from zero
354 ! test
355 if (eq_job_nr.ne.1) then
356 ierr = 1
357 err_msg = 'HELENA should only have 1 equilibrium job &
358 &for Richardson level 1'
359 chckerr(err_msg)
360 end if
361
362 if (ias.eq.0) then ! top-bottom symmmetric
363 n_theta = 2*(nchi-1)+1
364 else
365 n_theta = nchi
366 end if
367
368 ! allocate
369 ierr = vac%init(eq_style,n_theta,prim_x,[n_theta,1],jq)
370 chckerr('')
371 ! save points
372 if (rank.eq.n_procs-1) then
373 ! grid point to save is last total
374 r_id = n_r_eq
375
376 ! save only R and Z
377 if (ias.eq.0) then
378 vac%x_vec(1:nchi,1) = r_h(1:nchi,r_id)
379 vac%x_vec(1:nchi,2) = z_h(1:nchi,r_id)
380 vac%ang(1:nchi,1) = chi_h(1:nchi)
381 vac%x_vec(nchi+1:n_theta,1) = r_h(nchi-1:1:-1,r_id)
382 vac%x_vec(nchi+1:n_theta,2) = -z_h(nchi-1:1:-1,r_id)
383 vac%ang(nchi+1:n_theta,1) = chi_h(2:nchi) + pi
384 else
385 vac%x_vec(:,1) = r_h(:,r_id)
386 vac%x_vec(:,2) = z_h(:,r_id)
387 vac%ang(:,1) = chi_h
388 end if
389 !call print_ex_2D(['R','Z'],'',vac%x_vec,&
390 !&x=vac%ang(:,1:1),persistent=.true.)
391 !call print_ex_2D('cs','',vac%x_vec(:,2),&
392 !&x=vac%x_vec(:,1),persistent=.true.)
393
394 ! calculate Cylindrical components of - J nabla psi
395 ! = - R (Z_theta nabla R - R_theta nabla Z)
396 ! and its poloidal derivative
397 vac%norm = 0._dp
398 vac%dnorm = 0._dp
399 ! nufft of Z
400 ierr = nufft(vac%ang(1:n_theta-1,1),&
401 &vac%x_vec(1:n_theta-1,2),x_vec_f)
402 chckerr('')
403 ! dZ/dtheta
404 do id = 1,size(x_vec_f,1)
405 vac%norm(1:n_theta-1,1) = &
406 &vac%norm(1:n_theta-1,1) + (id-1) * &
407 &(-x_vec_f(id,1)*sin((id-1)*&
408 &vac%ang(1:n_theta-1,1))&
409 &+x_vec_f(id,2)*cos((id-1)*&
410 &vac%ang(1:n_theta-1,1)))
411 end do
412 ! nufft of -R
413 ierr = nufft(vac%ang(1:n_theta-1,1),&
414 &-vac%x_vec(1:n_theta-1,1),x_vec_f)
415 chckerr('')
416 ! d(-R)/dtheta
417 do id = 1,size(x_vec_f,1)
418 vac%norm(1:n_theta-1,2) = &
419 &vac%norm(1:n_theta-1,2) + (id-1) * &
420 &(-x_vec_f(id,1)*sin((id-1)*&
421 &vac%ang(1:n_theta-1,1))&
422 &+x_vec_f(id,2)*cos((id-1)*&
423 &vac%ang(1:n_theta-1,1)))
424 end do
425 vac%norm(n_theta,:) = vac%norm(1,:)
426 ! multiply by -R
427 do id = 1,2
428 vac%norm(:,id) = - vac%x_vec(:,1)*vac%norm(:,id)
429 end do
430 ! nufft of norm
431 do jd = 1,2
432 ierr = nufft(vac%ang(1:n_theta-1,1),&
433 &vac%norm(1:n_theta-1,jd),x_vec_f)
434 chckerr('')
435 do id = 1,size(x_vec_f,1)
436 vac%dnorm(1:n_theta-1,jd) = &
437 &vac%dnorm(1:n_theta-1,jd) + (id-1) * &
438 &(-x_vec_f(id,1)*sin((id-1)*&
439 &vac%ang(1:n_theta-1,1))&
440 &+x_vec_f(id,2)*cos((id-1)*&
441 &vac%ang(1:n_theta-1,1)))
442 end do
443 end do
444 vac%dnorm(n_theta,:) = vac%dnorm(1,:)
445 end if
446
447 ! broadcast to other processes
448 do id = 1,2
449 ierr = broadcast_var(vac%x_vec(:,id),n_procs-1)
450 chckerr('')
451 ierr = broadcast_var(vac%norm(:,id),n_procs-1)
452 chckerr('')
453 ierr = broadcast_var(vac%dnorm(:,id),n_procs-1)
454 chckerr('')
455 end do
456 ierr = broadcast_var(vac%ang(:,1),n_procs-1)
457 chckerr('')
458 else ! restarting
459 ! reconstruct old vacuum
460 ierr = reconstruct_pb3d_vac(vac,'vac')
461 chckerr('')
462 end if
463 end if
464
465 call lvl_ud(-1)
466
467 call writo('Done storing vacuum quantities')
468 end function store_vac_hel
469 end function store_vac
470
471 !> Calculate the matrices \c G and \c H.
472 !!
473 !! This is done by iterating over the subintervals, calculating the
474 !! contributions to the potential values on the left and right boundary of
475 !! the interval, and adding these to the appropriate values in G and H.
476 !!
477 !! As each process in the blacs grid only has access to its own values, an
478 !! extra interval is is calculated to the left and right of the internal
479 !! process grid edges.
480 !!
481 !! Optionally, this procedure can be used to calculate the coefficients G
482 !! and H with respect to other points, outside of the boundary. This is
483 !! useful when the potential is to be calculated at external points.
484 !!
485 !! In this case, however, no (near-) singular points are calculated, and if
486 !! they appear any way, perhaps by accident, they will not be accurate.
487 !!
488 !! \Note Multiple field lines are stored sequentially, which implies that
489 !! the integral between the last point on a field line and the first point
490 !! on the next field lines needs to be left out.
491 !!
492 !! \todo For 3-D vacuums, step sizes are constant. Subsequent Richardson
493 !! levels should therefore make use of the fact that the contribution to the
494 !! points inherited from the previous levels can be just scaled by 1/2 and
495 !! do not need to be recalculated. Currently, the copy is done correctly in
496 !! interlaced_vac_copy(), but they are afterwards overwritten.
497 !!
498 !! \return ierr
499 integer function calc_gh(vac,n_r_in,lims_r_in,x_vec_in,G_in,H_in) &
500 &result(ierr)
501
502 use num_vars, only: rank
503 use vac_vars, only: in_context
504 use vac_utilities, only: vec_dis2loc
505#if ldebug
506 use num_vars, only: n_procs
507 use vac_utilities, only: mat_dis2loc
508#endif
509
510 character(*), parameter :: rout_name = 'calc_GH'
511
512 ! input / output
513 type(vac_type), intent(inout), target :: vac !< vacuum variables
514 integer, intent(in), optional :: n_r_in !< total number of rows of external poins of influence
515 integer, intent(in), optional, target :: lims_r_in(:,:) !< row limits of external points of influence
516 real(dp), intent(in), optional, target :: x_vec_in(:,:) !< external position of influence
517 real(dp), intent(in), optional, target :: g_in(:,:) !< external G
518 real(dp), intent(in), optional, target :: h_in(:,:) !< external H
519
520 ! local variables
521 integer :: n_r ! number of rows
522 integer :: id, jd ! counters
523 integer, pointer :: lims_r(:,:) ! row limits
524 real(dp), pointer :: g(:,:) ! G
525 real(dp), pointer :: h(:,:) ! H
526 real(dp), pointer :: x_vec(:,:) ! x_vec used in row
527 character(len=max_str_ln) :: err_msg ! error message
528 logical :: ext_in ! external influence point
529#if ldebug
530 integer :: rd ! global counter for row
531 integer :: rdl ! local counter for row
532 integer :: i_rd ! index of subrow
533 integer :: desc_res(blacsctxtsize) ! descriptor for the result
534 real(dp), allocatable :: loc_ser(:,:) ! dummy serial variable
535 real(dp), allocatable :: r_sph(:) ! spherical r with r^2 = R^2+Z^2
536 real(dp), allocatable :: phi(:,:) ! test phi
537 real(dp), allocatable :: dphi(:,:) ! test dphi/dn
538 real(dp), allocatable :: res(:,:,:) ! H phi, G dphi
539 character(len=max_str_ln) :: plot_title(3) ! plot title
540 character(len=max_str_ln) :: plot_name ! plot name
541#endif
542
543 ! initialize ierr
544 ierr = 0
545
546 ! user output
547 call writo('Calculate the G and H matrices')
548 call lvl_ud(1)
549
550 ! test
551 if (present(n_r_in) .and. .not.present(x_vec_in) .or. &
552 &present(n_r_in) .and. .not.present(lims_r_in) .or. &
553 &present(n_r_in) .and. .not.present(g_in) .or. &
554 &present(n_r_in) .and. .not.present(h_in)) then
555 ierr = 1
556 chckerr('Need all optional variables')
557 end if
558
559 ! set limits and variables
560 if (present(lims_r_in)) then
561 n_r = n_r_in
562 lims_r => lims_r_in
563 x_vec => x_vec_in
564 g => g_in
565 h => h_in
566 ext_in = .true.
567 else
568 n_r = vac%n_bnd
569 allocate(lims_r(2,size(vac%lims_r,2)))
570 allocate(x_vec(vac%n_bnd,size(vac%x_vec,2)))
571 lims_r = vac%lims_r
572 x_vec = vac%x_vec
573 g => vac%G
574 h => vac%H
575 ext_in = .false.
576 end if
577
578 ! call specific procedure for vacuum style
579 select case (vac%style)
580 case (1) ! field-line 3-D
581 ierr = calc_gh_1(vac,n_r,lims_r,x_vec,g,h,ext_in)
582 chckerr('')
583 case (2) ! axisymmetric
584 ierr = calc_gh_2(vac,n_r,lims_r,x_vec,g,h,ext_in)
585 chckerr('')
586 case default
587 ierr = 1
588 err_msg = 'No vacuum style '//trim(i2str(vac%style))//&
589 &' possible'
590 chckerr(err_msg)
591 end select
592
593 ! clean up
594 if (.not.present(lims_r_in)) then
595 deallocate(lims_r)
596 deallocate(x_vec)
597 end if
598 nullify(lims_r)
599 nullify(x_vec)
600 nullify(g)
601 nullify(h)
602
603#if ldebug
604 if (debug_calc_gh .and. in_context(vac%ctxt_HG)) then
605 ! initialize
606 call descinit(desc_res,vac%n_bnd,1,vac%bs,vac%bs,0,0,&
607 &vac%ctxt_HG,max(1,vac%n_loc(1)),ierr)
608 chckerr('descinit failed for res')
609
610 ! plot H and G in HDF5
611 allocate(loc_ser(vac%n_bnd,vac%n_bnd))
612 ierr = mat_dis2loc(vac%ctxt_HG,vac%H,vac%lims_r,vac%lims_c,loc_ser,&
613 &proc=n_procs-1)
614 chckerr('')
615 if (rank.eq.n_procs-1) then
616 call plot_hdf5('H','H',reshape(loc_ser,[vac%n_bnd,vac%n_bnd,1]))
617 call plot_diff_hdf5(reshape(loc_ser,[vac%n_bnd,vac%n_bnd,1]),&
618 &reshape(transpose(loc_ser),[vac%n_bnd,vac%n_bnd,1]),&
619 &'H_transp')
620 end if
621 ierr = mat_dis2loc(vac%ctxt_HG,vac%G,vac%lims_r,vac%lims_c,loc_ser,&
622 &proc=n_procs-1)
623 chckerr('')
624 if (rank.eq.n_procs-1) then
625 call plot_hdf5('G','G',reshape(loc_ser,[vac%n_bnd,vac%n_bnd,1]))
626 call plot_diff_hdf5(reshape(loc_ser,[vac%n_bnd,vac%n_bnd,1]),&
627 &reshape(transpose(loc_ser),[vac%n_bnd,vac%n_bnd,1]),&
628 &'G_transp')
629 end if
630 deallocate(loc_ser)
631
632 ! the rest is only for axisymmetric vacua.
633 if (vac%style.eq.2) then
634 ! test whether G and H hold for test potentials phi
635 if (vac%lims_c(1,1).eq.1) then ! this process owns (part of) first column
636 ! set up variables
637 allocate(res(vac%n_loc(1),3,2))
638 allocate(r_sph(vac%n_loc(1)))
639 allocate(phi(vac%n_loc(1),2))
640 allocate(dphi(vac%n_loc(1),2))
641
642 ! set up rhs for H (phi) and G (dphi)
643 subrows: do i_rd = 1,size(vac%lims_r,2)
644 row: do rd = vac%lims_r(1,i_rd),vac%lims_r(2,i_rd)
645 ! set local row index
646 rdl = sum(vac%lims_r(2,1:i_rd-1)-&
647 &vac%lims_r(1,1:i_rd-1)+1) + &
648 &rd-vac%lims_r(1,i_rd)+1
649
650 ! spherical radius
651 r_sph(rdl) = sqrt(sum(vac%x_vec(rd,:)**2))
652
653 ! test 1
654 phi(rdl,1) = vac%x_vec(rd,1)**vac%prim_X
655 dphi(rdl,1) = - vac%prim_X*vac%norm(rd,1)/&
656 &vac%x_vec(rd,1) * phi(rdl,1)
657
658 ! test 2
659 phi(rdl,2) = (vac%x_vec(rd,1)/&
660 &(r_sph(rdl)+abs(vac%x_vec(rd,2))))**vac%prim_X
661 dphi(rdl,2) = vac%prim_X*(vac%norm(rd,2)-&
662 &vac%norm(rd,1)*vac%x_vec(rd,2)/&
663 &vac%x_vec(rd,1)) / &
664 &r_sph(rdl)*sign(1._dp,vac%x_vec(rd,2)) * &
665 &phi(rdl,2)
666 end do row
667 end do subrows
668 else
669 allocate(res(0,3,2))
670 allocate(r_sph(0))
671 allocate(phi(0,2))
672 allocate(dphi(0,2))
673 end if
674
675 ! output
676 allocate(loc_ser(vac%n_bnd,3))
677 ierr = vec_dis2loc(vac%ctxt_HG,r_sph,vac%lims_r,loc_ser(:,1),&
678 &proc=n_procs-1)
679 chckerr('')
680 if (rank.eq.n_procs-1) then
681 plot_title(1) = 'spherical r'
682 plot_name = 'r_sph'
683 call print_ex_2d(plot_title(1),plot_name,loc_ser(:,1),&
684 &x=vac%ang(:,1),draw=.false.)
685 call draw_ex([plot_title],plot_name,1,1,.false.)
686 end if
687 do jd = 1,size(phi,2)
688 ierr = vec_dis2loc(vac%ctxt_HG,phi(:,jd),vac%lims_r,&
689 &loc_ser(:,jd),proc=n_procs-1)
690 chckerr('')
691 end do
692 if (rank.eq.n_procs-1) then
693 plot_title(1) = 'test potential Ï•'
694 plot_name = 'phi'
695 call print_ex_2d([plot_title(1)],plot_name,&
696 &loc_ser(:,1:size(phi,2)),x=vac%ang(:,1:1),draw=.false.)
697 call draw_ex([plot_title(1)],plot_name,size(phi,2),1,&
698 &.false.)
699 end if
700 do jd = 1,size(dphi,2)
701 ierr = vec_dis2loc(vac%ctxt_HG,dphi(:,jd),vac%lims_r,&
702 &loc_ser(:,jd),proc=n_procs-1)
703 chckerr('')
704 end do
705 if (rank.eq.n_procs-1) then
706 plot_title(1) = 'normal derivative of test potential dϕ/dn'
707 plot_name = 'dphi'
708 call print_ex_2d([plot_title(1)],plot_name,&
709 &loc_ser(:,1:size(dphi,2)),x=vac%ang(:,1:1),&
710 &draw=.false.)
711 call draw_ex([plot_title(1)],plot_name,size(dphi,2),1,&
712 &.false.)
713 end if
714 deallocate(loc_ser)
715
716 ! loop over test potentials
717 do jd = 1,size(phi,2)
718 ! calculate H phi
719 call pdgemv('N',vac%n_bnd,vac%n_bnd,1._dp,vac%H,1,1,&
720 &vac%desc_H,phi(:,jd),1,1,desc_res,1,0._dp,res(:,1,jd),&
721 &1,1,desc_res,1)
722
723 ! calculate - G dphi
724 call pdgemv('N',vac%n_bnd,vac%n_bnd,-1._dp,vac%G,1,1,&
725 &vac%desc_G,dphi(:,jd),1,1,desc_res,1,0._dp,&
726 &res(:,2,jd),1,1,desc_res,1)
727
728 ! add them together
729 res(:,3,jd) = sum(res(:,1:2,jd),2)
730 end do
731
732 ! output of tests
733 allocate(loc_ser(vac%n_bnd,3))
734 do id = 1,size(phi,2)
735 do jd = 1,3
736 ierr = vec_dis2loc(vac%ctxt_HG,res(:,jd,id),vac%lims_r,&
737 &loc_ser(:,jd),proc=n_procs-1)
738 chckerr('')
739 end do
740 if (rank.eq.n_procs-1) then
741 select case (id)
742 case (1)
743 plot_title(1) = 'H R^n'
744 plot_title(2) = '-G n Z_θ R^n'
745 plot_title(3) = '(H - G n Z_θ) R^n'
746 plot_name = 'test_vac_1'
747 case (2)
748 plot_title(1) = 'H (R/(r+|Z|))^n'
749 plot_title(2) = &
750 &'- G |Z|/Z n dr/dθ (R/(r+|Z|))^n'
751 plot_title(3) = '(H - G |Z|/Z n dr/dθ) &
752 &(R/(r+|Z|))^n'
753 plot_name = 'test_vac_2'
754 end select
755 call print_ex_2d(plot_title,trim(plot_name)//'_all',&
756 &loc_ser(:,:),x=vac%ang(:,1:1),&
757 &draw=.false.)
758 call draw_ex(plot_title,trim(plot_name)//'_all',3,1,&
759 &.false.)
760 call print_ex_2d(plot_title(3),plot_name,&
761 &loc_ser(:,3),x=vac%ang(:,1),&
762 &draw=.false.)
763 call draw_ex(plot_title(3:3),plot_name,1,1,.false.)
764 end if
765 end do
766 deallocate(loc_ser)
767 end if
768 end if
769#endif
770
771 call lvl_ud(-1)
772 end function calc_gh
773
774 !> \public Calculates matrices \c G and \c H in 3-D configuration.
775 !!
776 !! \see calc_gh.
777 integer function calc_gh_1(vac,n_r_in,lims_r_in,x_vec_in,G_in,H_in,ext_in) &
778 &result(ierr)
779
782 use rich_vars, only: n_par_x
783 use num_vars, only: tol_zero
784 use vac_vars, only: in_context
785 use vac_utilities, only: vec_dis2loc
786
787 character(*), parameter :: rout_name = 'calc_GH_1'
788
789 ! input / output
790 type(vac_type), intent(inout) :: vac !< vacuum variables
791 integer, intent(in) :: n_r_in !< total number of rows of external poins of influence
792 integer, intent(in) :: lims_r_in(:,:) !< row limits of external points of influence
793 real(dp), intent(in) :: x_vec_in(:,:) !< position of influence
794 real(dp), intent(in), pointer :: g_in(:,:) !< G at position of influence
795 real(dp), intent(in), pointer :: h_in(:,:) !< H at position of influence
796 logical, intent(in) :: ext_in !< position of influence is external
797
798 ! local variables
799 integer :: kd ! counter
800 integer :: i_rd, i_cd ! index of subrow and column
801 integer :: rd, cd ! global counters for row and column
802 integer :: rdl, cdl ! local counters for row and column, used for G and H
803 integer :: lims_c_loc(2) ! local column limits
804 integer :: desc_res(blacsctxtsize) ! descriptor for the result
805 real(dp) :: dpar_x ! step size in par_X
806 real(dp) :: dalpha ! step size in alpha
807 real(dp) :: tol_loc ! local tolerance on rho for singular points
808 real(dp) :: g_loc(2) ! local G
809 real(dp) :: h_loc(2) ! local H
810 real(dp), allocatable :: res(:,:) ! sums of rows of H and vector of ones
811 real(dp), allocatable :: loc_res(:) ! local res
812
813 ! initialize ierr
814 ierr = 0
815
816 call writo('Using field-line 3-D Boundary Element Method')
817
818 ! initialize variables
819 dpar_x = (max_par_x-min_par_x)/(n_par_x-1)*pi
820 if (n_alpha.gt.1) then
821 dalpha = (max_alpha-min_alpha)/(n_alpha-1)*pi
822 else
823 dalpha = 0._dp
824 end if
825
826 ! set up local tolerance
827 tol_loc = tol_zero*10 ! tolerance for singular interval
828
829 ! iterate over all subrows: position of singular point
830 subrows: do i_rd = 1,size(lims_r_in,2)
831 ! iterate over all rows of this subrow
832 row: do rd = lims_r_in(1,i_rd),lims_r_in(2,i_rd)
833 ! set local row index
834 rdl = sum(lims_r_in(2,1:i_rd-1)-lims_r_in(1,1:i_rd-1)+1) + &
835 &rd-lims_r_in(1,i_rd)+1
836
837 ! iterate over all subcolumns: subintegrals
838 subcols: do i_cd = 1,size(vac%lims_c,2)
839 ! set up local limits, including possible overlap
840 lims_c_loc(1) = max(1,vac%lims_c(1,i_cd)-1)
841 lims_c_loc(2) = min(vac%n_bnd,vac%lims_c(2,i_cd)+1)
842
843 ! iterate over all pairs of columns of this subcolumn
844 ! (upper limit is one lower as lims_c_loc
845 col: do cd = lims_c_loc(1),lims_c_loc(2)-1
846 ! set local column index
847 cdl = sum(vac%lims_c(2,1:i_cd-1)-&
848 &vac%lims_c(1,1:i_cd-1)+1) + &
849 &cd-vac%lims_c(1,i_cd)+1
850
851 ! calculate contributions to H and G
852 call calc_gh_int_1(g_loc,h_loc,&
853 &vac%x_vec(cd:cd+1,:),x_vec_in(rd,:),&
854 &vac%norm(cd:cd+1,:),vac%h_fac(rd,:),&
855 &[dpar_x,dalpha],tol_loc**2)
856
857 ! loop over left and right side of interval
858 do kd = 0,1
859 if (cd+kd.ge.vac%lims_c(1,i_cd) .and. & ! lower local bound
860 &cd+kd.le.vac%lims_c(2,i_cd)) then ! upper local bound
861 ! cycle if first or last global point
862 if (.not.on_field_line(cd,kd,vac%n_ang(1))) &
863 &cycle
864 g_in(rdl,cdl+kd) = g_in(rdl,cdl+kd) + &
865 &g_loc(1+kd)
866 h_in(rdl,cdl+kd) = h_in(rdl,cdl+kd) + &
867 &h_loc(1+kd)
868 end if
869 end do
870 end do col
871 end do subcols
872 end do row
873 end do subrows
874
875 ! if in context and not external, indirctly calculate contribution due
876 ! to fundament solution through constant potential
877 if (in_context(vac%ctxt_HG) .and. .not.ext_in) then
878 if (vac%lims_c(1,1).eq.1) then ! this process owns (part of) first column
879 allocate(res(vac%n_loc(1),2))
880 else
881 allocate(res(0,2))
882 end if
883 call descinit(desc_res,vac%n_bnd,1,vac%bs,vac%bs,0,0,&
884 &vac%ctxt_HG,max(1,vac%n_loc(1)),ierr)
885 chckerr('descinit failed for res')
886
887 ! set diagonal of H to zero
888 ! iterate over all subrows: position of singular point
889 subrows2: do i_rd = 1,size(lims_r_in,2)
890 ! iterate over all rows of this subrow
891 row2: do rd = lims_r_in(1,i_rd),lims_r_in(2,i_rd)
892 ! set local row index
893 rdl = sum(lims_r_in(2,1:i_rd-1)-&
894 &lims_r_in(1,1:i_rd-1)+1) + &
895 &rd-lims_r_in(1,i_rd)+1
896
897 ! iterate over all subcolumns: subintegrals
898 subcols2: do i_cd = 1,size(vac%lims_c,2)
899 cd = rd
900 if (vac%lims_c(1,i_cd).le.cd .and. &
901 &cd.le.vac%lims_c(2,i_cd)) then ! this subcolumn includes the diagonal
902 ! set local column index
903 cdl = sum(vac%lims_c(2,1:i_cd-1)-&
904 &vac%lims_c(1,1:i_cd-1)+1) + &
905 &cd-vac%lims_c(1,i_cd)+1
906
907 ! set diagonal of H to zero
908 h_in(rdl,cdl) = 0._dp
909 end if
910 end do subcols2
911 end do row2
912 end do subrows2
913
914 ! set res(1) to ones
915 res(:,1) = 1._dp
916
917 ! calculate sum of rows of H
918 call pdgemv('N',vac%n_bnd,vac%n_bnd,1._dp,vac%H,1,1,&
919 &vac%desc_H,res(:,1),1,1,desc_res,1,0._dp,res(:,2),&
920 &1,1,desc_res,1)
921
922 ! add 4pi
923 res(:,2) = res(:,2) + 4*pi
924
925 ! distribute to all processes
926 allocate(loc_res(vac%n_bnd))
927 ierr = vec_dis2loc(vac%ctxt_HG,res(:,2),vac%lims_r,loc_res,&
928 &proc=-1)
929 chckerr('')
930
931 ! Put in diagonal of H
932 ! iterate over all subrows: position of singular point
933 subrows3: do i_rd = 1,size(lims_r_in,2)
934 ! iterate over all rows of this subrow
935 row3: do rd = lims_r_in(1,i_rd),lims_r_in(2,i_rd)
936 ! set local row index
937 rdl = sum(lims_r_in(2,1:i_rd-1)-&
938 &lims_r_in(1,1:i_rd-1)+1) + &
939 &rd-lims_r_in(1,i_rd)+1
940
941 ! iterate over all subcolumns: subintegrals
942 subcols3: do i_cd = 1,size(vac%lims_c,2)
943 cd = rd
944 if (vac%lims_c(1,i_cd).le.cd .and. &
945 &cd.le.vac%lims_c(2,i_cd)) then ! this subcolumn includes the diagonal
946 ! set local column index
947 cdl = sum(vac%lims_c(2,1:i_cd-1)-&
948 &vac%lims_c(1,1:i_cd-1)+1) + &
949 &cd-vac%lims_c(1,i_cd)+1
950
951 ! set diagonal of H to inverse of sum
952 h_in(rdl,cdl) = - loc_res(rd)
953 end if
954 end do subcols3
955 end do row3
956 end do subrows3
957
958 ! clean up
959 deallocate(res)
960 deallocate(loc_res)
961 end if
962 contains
963 ! checks whether an interval is on the field line
964 logical function on_field_line(cd,kd,n_ang) result(res)
965 ! input / output
966 integer, intent(in) :: cd ! position of interval on field line
967 integer, intent(in) :: kd ! left or right point of interval
968 integer, intent(in) :: n_ang ! number of points on field line
969
970 res = .true.
971 if (mod(cd,n_ang).eq.1 .and. kd.eq.0) res = .false. ! start of field line
972 if (mod(cd,n_ang).eq.0 .and. kd.eq.1) res = .false. ! end of field line
973 end function on_field_line
974 end function calc_gh_1
975
976 !> \public Calculates matrices \c G and \c H in axisymmetric configurations.
977 !!
978 !! It makes use of toroidal functions.
979 !!
980 !! \see calc_gh.
981 integer function calc_gh_2(vac,n_r_in,lims_r_in,x_vec_in,G_in,H_in,ext_in) &
982 &result(ierr)
983
984 use dtorh, only: dtorh1
986 use mpi_utilities, only: get_ser_var
988 use num_vars, only: rank
989
990 character(*), parameter :: rout_name = 'calc_GH_2'
991
992 ! input / output
993 type(vac_type), intent(inout) :: vac !< vacuum variables
994 integer, intent(in) :: n_r_in !< total number of rows of external poins of influence
995 integer, intent(in) :: lims_r_in(:,:) !< row limits of external points of influence
996 real(dp), intent(in) :: x_vec_in(:,:) !< position of influence
997 real(dp), intent(in), pointer :: g_in(:,:) !< G at position of influence
998 real(dp), intent(in), pointer :: h_in(:,:) !< H at position of influence
999 logical, intent(in) :: ext_in !< position of influence is external
1000
1001 ! local variables
1002 integer :: kd ! counter
1003 integer :: i_rd, i_cd ! index of subrow and column
1004 integer :: rd, cd ! global counters for row and column
1005 integer :: rdl, cdl ! local counters for row and column, used for G and H
1006 integer :: max_n ! maximum degree
1007 integer :: lims_c_loc(2) ! local column limits
1008 real(dp) :: b_coeff(2) ! sum_k=1^n 2/2k-1 for n and n-1
1009 real(dp) :: tol_loc ! local tolerance on rho for singular points
1010 real(dp) :: del_r(2,2) ! unit vectors of next and previous subinterval
1011 real(dp) :: g_loc(2) ! local G
1012 real(dp) :: h_loc(2) ! local H
1013 real(dp) :: beta_comp ! complementary beta
1014 real(dp) :: ang ! local angle
1015 real(dp), parameter :: tol_q = 1.e-6_dp ! tolerance on Q approximation
1016 real(dp), allocatable :: beta(:) ! beta
1017 real(dp), allocatable :: loc_ser(:) ! dummy serial variable
1018 real(dp), allocatable :: rho2(:) ! square of distance in projected poloidal plane for one column
1019 real(dp), allocatable :: arg(:) ! argument 1 + rho^2/(RiRj)(
1020 real(dp), allocatable :: aij(:) ! Aij helper variable for H
1021 real(dp), allocatable :: ql(:,:,:) ! ql for all rho2's
1022 real(dp), allocatable :: pl_loc(:), ql_loc(:) ! local toroidal functions
1023 logical, allocatable :: was_calc(:,:) ! was already calculated
1024 logical, allocatable :: sing_col(:) ! singular column, used only with external influence
1025 logical :: was_calc_sym ! symmetrical was_calc
1026 character(len=max_str_ln) :: err_msg ! error message
1027#if ldebug
1028 real(dp) :: rho2_lims(2) ! limits on rho2
1029 real(dp) :: gamma_lims(2) ! limits on gamma
1030 integer :: beta_lims(2) ! limits to plot beta
1031 !integer :: lims_cl(2) ! vac%lims_c in local coordinates
1032 !character(len=max_str_ln) :: plot_title ! plot title
1033 !character(len=max_str_ln) :: plot_name ! plot name
1034#endif
1035
1036 ! initialize ierr
1037 ierr = 0
1038
1039 call writo('Using axisymmetric Boundary Element Method')
1040
1041 ! set up b_coeff and local tolerance
1042 b_coeff = 0._dp
1043 do kd = 1,vac%prim_X
1044 b_coeff(2) = b_coeff(2) + 2._dp/(2*kd-1)
1045 end do
1046 b_coeff(1) = b_coeff(2) - 2._dp/(2*vac%prim_X-1)
1047 err_msg = calc_zero_zhang(tol_loc,fun_tol,&
1048 &[1.e-10_dp,& ! very low lower limit
1049 &exp(-2*b_coeff(2))]) ! upper limit for -1/2ln(x) = b
1050 if (trim(err_msg).ne.'') then
1051 ierr = 1
1052 chckerr(trim(err_msg))
1053 end if
1054 tol_loc = 8*minval(vac%x_vec(:,1))*tol_loc ! tolerance on rho
1055 ierr = get_ser_var([tol_loc],loc_ser,scatter=.true.)
1056 chckerr('')
1057 tol_loc = maxval(loc_ser)
1058 deallocate(loc_ser)
1059 call writo('Tolerance on rho for analytical integral: '//&
1060 &trim(r2str(tol_loc)))
1061
1062 ! allocate helper variables
1063 allocate(was_calc(1:n_r_in,1:vac%n_bnd))
1064 was_calc = .false. ! will remain false if external influence
1065 allocate(ql(1:n_r_in,1:vac%n_bnd,2))
1066 allocate(pl_loc(0:max(vac%prim_X,1)))
1067 allocate(ql_loc(0:max(vac%prim_X,1)))
1068#if ldebug
1069 rho2_lims = [huge(1._dp),-huge(1._dp)]
1070 gamma_lims = [huge(1._dp),-huge(1._dp)]
1071#endif
1072
1073 ! iterate over all subrows: position of singular point
1074 subrows: do i_rd = 1,size(lims_r_in,2)
1075 ! initialize helper variables
1076 allocate(beta(lims_r_in(1,i_rd):lims_r_in(2,i_rd))) ! global index
1077#if ldebug
1078 beta_lims = [lims_r_in(2,i_rd)+1,lims_r_in(1,i_rd)-1]
1079#endif
1080
1081 ! iterate over all rows of this subrow
1082 row: do rd = lims_r_in(1,i_rd),lims_r_in(2,i_rd)
1083 ! set local row index
1084 rdl = sum(lims_r_in(2,1:i_rd-1)-lims_r_in(1,1:i_rd-1)+1) + &
1085 &rd-lims_r_in(1,i_rd)+1
1086
1087 ! iterate over all subcolumns: subintegrals
1088 subcols: do i_cd = 1,size(vac%lims_c,2)
1089 ! set up local limits, including possible overlap
1090 lims_c_loc(1) = max(1,vac%lims_c(1,i_cd)-1)
1091 lims_c_loc(2) = min(vac%n_bnd,vac%lims_c(2,i_cd)+1)
1092
1093 ! initialize helper variables
1094 allocate(rho2(lims_c_loc(1):lims_c_loc(2))) ! global index
1095 allocate(arg(lims_c_loc(1):lims_c_loc(2))) ! global index
1096 allocate(aij(lims_c_loc(1):lims_c_loc(2))) ! global index
1097
1098 ! initialize singular column flags
1099 if (ext_in) then
1100 if (allocated(sing_col)) deallocate(sing_col)
1101 allocate(sing_col(&
1102 &vac%lims_c(1,i_cd):vac%lims_c(2,i_cd)))
1103 sing_col = .false.
1104 end if
1105
1106 ! iterate over all columns of this subcolumn, possibly with
1107 ! an overlap left and/or right to set up the Aij
1108 col: do cd = lims_c_loc(1),lims_c_loc(2)
1109 ! check rho2 and set arg to calculate tor. functions
1110 rho2(cd) = sum((vac%x_vec(cd,:)-x_vec_in(rd,:))**2)
1111 arg(cd) = 1._dp + &
1112 &rho2(cd)/(2*vac%x_vec(cd,1)*x_vec_in(rd,1))
1113 was_calc_sym = .false.
1114 if (.not.ext_in) was_calc_sym = was_calc(cd,rd)
1115 if (rho2(cd).le.tol_loc**2) then
1116 ! don't set it
1117 else if (was_calc_sym) then ! check symmetric element
1118 ql(rd,cd,:) = ql(cd,rd,:) ! set it
1119 else ! not yet calculated by this process
1120#if ldebug
1121 ! update extrema
1122 if (rho2(cd).lt.rho2_lims(1)) &
1123 &rho2_lims(1) = rho2(cd)
1124 if (rho2(cd).gt.rho2_lims(2)) &
1125 &rho2_lims(2) = rho2(cd)
1126 if (arg(cd).lt.gamma_lims(1)) &
1127 &gamma_lims(1) = arg(cd)
1128 if (arg(cd).gt.gamma_lims(2)) &
1129 &gamma_lims(2) = arg(cd)
1130#endif
1131 err_msg = 'The solutions did not converge for &
1132 &rho^2 = '//trim(r2str(rho2(cd)))//&
1133 &' and n = '//trim(i2str(vac%prim_X))
1134 if (vac%prim_X.gt.0) then ! positive n
1135 ierr = dtorh1(arg(cd),0,vac%prim_X,pl_loc,&
1136 &ql_loc,max_n)
1137 chckerr('')
1138 if (max_n.lt.vac%prim_X) then
1139 ierr = 1
1140 chckerr(err_msg)
1141 end if
1142 ql(rd,cd,:) = ql_loc(vac%prim_X-1:vac%prim_X)
1143 else if (vac%prim_X.eq.0) then
1144 ierr = dtorh1(arg(cd),0,1,pl_loc,ql_loc,max_n)
1145 chckerr('')
1146 if (max_n.lt.vac%prim_X) then
1147 ierr = 1
1148 chckerr(err_msg)
1149 end if
1150 ql(rd,cd,1) = ql_loc(1) ! Q_-3/2 = Q_1/2
1151 ql(rd,cd,2) = ql_loc(0) ! Q_-1/2
1152 else ! negative n
1153 ierr = 1
1154 err_msg = 'negative n not yet implemented' ! SHOULD JUST BE Q_{n-1/2} = Q_{-n-1/2}
1155 chckerr('')
1156 end if
1157 if (.not.ext_in) was_calc(rd,cd) = .true.
1158 end if
1159
1160 ! set up Aij
1161 if (rho2(cd).le.tol_loc**2) then
1162 if (ext_in) then
1163 if (cd.ge.vac%lims_c(1,i_cd) .and. &
1164 &cd.le.vac%lims_c(2,i_cd)) &
1165 &sing_col(cd) = .true.
1166 !!Aij(cd) = 0._dp
1167 else
1168 aij(cd) = 0.5_dp*(vac%prim_X - 0.5_dp) * &
1169 &(x_vec_in(rd,1) * &
1170 &(vac%norm(rd,1)*vac%dnorm(rd,2)-&
1171 &vac%norm(rd,2)*vac%dnorm(rd,1))/&
1172 &sum(vac%norm(rd,:)**2) &
1173 &+vac%norm(rd,1)/x_vec_in(rd,1))
1174 end if
1175 else
1176 aij(cd) = 2._dp*x_vec_in(rd,1)*vac%x_vec(cd,1) / &
1177 &(4._dp*x_vec_in(rd,1)*vac%x_vec(cd,1)+&
1178 &rho2(cd)) *(vac%prim_X - 0.5_dp) * &
1179 &(- 2._dp/rho2(cd) * sum(vac%norm(cd,:)*&
1180 &(vac%x_vec(cd,:)-x_vec_in(rd,:))) + &
1181 &vac%norm(cd,1)/vac%x_vec(cd,1))
1182 end if
1183 end do col
1184
1185#if ldebug
1186 if (debug_calc_gh) then
1187 !! output
1188 !plot_title = 'for row '//trim(i2str(rd))//&
1189 !&' and starting column '//trim(i2str(lims_c_loc(1)))
1190 !plot_name = '_'//trim(i2str(rd))//'_'//&
1191 !&trim(i2str(lims_c_loc(1)))
1192 !call print_ex_2D('rho^2 '//trim(plot_title),&
1193 !&'rho2'//trim(plot_name),rho2,&
1194 !&x=vac%ang(lims_c_loc(1):lims_c_loc(2),1),&
1195 !&draw=.false.)
1196 !call draw_ex(['rho^2 '//trim(plot_title)],&
1197 !&'rho2'//trim(plot_name),1,1,.false.)
1198 !call print_ex_2D('γ '//trim(plot_title),&
1199 !&'gamma'//trim(plot_name),arg,&
1200 !&x=vac%ang(lims_c_loc(1):lims_c_loc(2),1),&
1201 !&draw=.false.)
1202 !call draw_ex(['γ '//trim(plot_title)],&
1203 !&'gamma'//trim(plot_name),1,1,.false.)
1204 !call print_ex_2D('A_{ij} '//trim(plot_title),&
1205 !&'Aij'//trim(plot_name),arg,&
1206 !&x=vac%ang(lims_c_loc(1):lims_c_loc(2),1),&
1207 !&draw=.false.)
1208 !call draw_ex(['A_{ij} '//trim(plot_title)],&
1209 !&'Aij'//trim(plot_name),1,1,.false.)
1210 end if
1211#endif
1212
1213 ! iterate over all pairs of columns of this subcolumn
1214 ! (upper limit is one lower as previous)
1215 col2: do cd = lims_c_loc(1),lims_c_loc(2)-1
1216 ! set local column index
1217 cdl = sum(vac%lims_c(2,1:i_cd-1)-&
1218 &vac%lims_c(1,1:i_cd-1)+1) + &
1219 &cd-vac%lims_c(1,i_cd)+1
1220
1221 ! check for singular columns if external influence
1222 if (ext_in) then
1223 if (any(sing_col)) then
1224 do kd = 0,1
1225 if (cd+kd.ge.vac%lims_c(1,i_cd) .and. &
1226 &cd+kd.le.vac%lims_c(2,i_cd)) then
1227 g_in(rdl,cdl+kd) = 0._dp
1228 h_in(rdl,cdl+kd) = 4*pi
1229 end if
1230 end do
1231 cycle col2 ! pass to next column
1232 end if
1233 end if
1234
1235 ! set ang
1236 if (ext_in) then
1237 ang = 0._dp
1238 else
1239 ang = vac%ang(rd,1)
1240 end if
1241
1242 ! calculate contributions to H and G
1243 call calc_gh_int_2(g_loc,h_loc,&
1244 &vac%ang(cd:cd+1,1),ang,&
1245 &vac%x_vec(cd:cd+1,:),x_vec_in(rd,:),&
1246 &vac%norm(cd:cd+1,:),vac%norm(rd,:),&
1247 &aij(cd:cd+1),ql(rd,cd:cd+1,:),&
1248 &tol_loc**2,b_coeff,vac%prim_X)
1249
1250 do kd = 0,1
1251 if (cd+kd.ge.vac%lims_c(1,i_cd) .and. &
1252 &cd+kd.le.vac%lims_c(2,i_cd)) then
1253 g_in(rdl,cdl+kd) = g_in(rdl,cdl+kd) + &
1254 &g_loc(1+kd)
1255 h_in(rdl,cdl+kd) = h_in(rdl,cdl+kd) + &
1256 &h_loc(1+kd)
1257 end if
1258 end do
1259 end do col2
1260
1261#if ldebug
1262 if (debug_calc_gh) then
1263 !! local limits
1264 !lims_cl = sum(vac%lims_c(2,1:i_cd-1)-&
1265 !&vac%lims_c(1,1:i_cd-1)+1) + &
1266 !&vac%lims_c(:,i_cd)-vac%lims_c(1,i_cd)+1
1267 !! output
1268 !call print_ex_2D('G '//trim(plot_title),&
1269 !&'G'//trim(plot_name),&
1270 !&G_in(rdl,lims_cl(1):lims_cl(2)),x&
1271 !&=vac%ang(vac%lims_c(1,i_cd):vac%lims_c(2,i_cd),1),&
1272 !&draw=.false.)
1273 !call draw_ex(['G '//trim(plot_title)],&
1274 !&'G'//trim(plot_name),1,1,.false.)
1275 !call print_ex_2D('H '//trim(plot_title),&
1276 !&'H'//trim(plot_name),&
1277 !&H_in(rdl,lims_cl(1):lims_cl(2)),x&
1278 !&=vac%ang(vac%lims_c(1,i_cd):vac%lims_c(2,i_cd),1),&
1279 !&draw=.false.)
1280 !call draw_ex(['H '//trim(plot_title)],&
1281 !&'H'//trim(plot_name),1,1,.false.)
1282 end if
1283#endif
1284
1285 ! calculate contribution due to fundamental solution
1286 ! (only if not external)
1287 if (.not.ext_in .and. vac%lims_c(1,i_cd).le.rd .and. &
1288 &rd.le.vac%lims_c(2,i_cd)) then ! this subcolumn includes the diagonal
1289 ! set local column index
1290 cdl = sum(vac%lims_c(2,1:i_cd-1)-&
1291 &vac%lims_c(1,1:i_cd-1)+1) + &
1292 &rd-vac%lims_c(1,i_cd)+1
1293
1294 if (rd.eq.1 .or. rd.eq.vac%n_bnd) then ! first or last global point
1295 ! loop around to get previous direction vector
1296 del_r(1,:) = vac%x_vec(vac%n_bnd,:)-&
1297 &vac%x_vec(vac%n_bnd-1,:)
1298 del_r(2,:) = vac%x_vec(2,:)-vac%x_vec(1,:)
1299 else ! all points in between
1300 del_r(1,:) = vac%x_vec(rd,:)-vac%x_vec(rd-1,:)
1301 del_r(2,:) = vac%x_vec(rd+1,:)-vac%x_vec(rd,:)
1302 end if
1303 do kd = 1,2
1304 del_r(kd,:) = del_r(kd,:)/sqrt(sum(del_r(kd,:)**2))
1305 end do
1306 beta_comp = acos(sum(product(del_r,1)))
1307 if (del_r(1,1)*del_r(2,2) .lt. &
1308 &del_r(1,2)*del_r(2,1)) beta_comp = - beta_comp ! concave
1309 beta(rd) = beta_comp + pi
1310#if ldebug
1311 if (debug_calc_gh) then
1312 if (rd.lt.beta_lims(1)) beta_lims(1) = rd
1313 if (rd.gt.beta_lims(2)) beta_lims(2) = rd
1314 end if
1315#endif
1316
1317 h_in(rdl,cdl) = h_in(rdl,cdl) - 2*beta(rd)
1318 end if
1319
1320 ! clean up
1321 deallocate(arg,rho2,aij)
1322 end do subcols
1323 end do row
1324
1325#if ldebug
1326 if (debug_calc_gh) then
1327 !! output
1328 !if (beta_lims(2).ge.beta_lims(1)) then
1329 !plot_title = 'beta/Ï€ for starting row '//&
1330 !&trim(i2str(beta_lims(1)))
1331 !plot_name = 'beta_'//trim(i2str(beta_lims(1)))
1332 !call print_ex_2D(plot_title,plot_name,&
1333 !&beta(beta_lims(1):beta_lims(2))/pi,&
1334 !&x=vac%ang(beta_lims(1):beta_lims(2),1),draw=.false.)
1335 !call draw_ex([plot_title],plot_name,1,1,.false.)
1336 !end if
1337 end if
1338#endif
1339
1340 ! clean up
1341 deallocate(beta)
1342 end do subrows
1343
1344#if ldebug
1345 if (debug_calc_gh) then
1346 ! output
1347 ierr = get_ser_var([rho2_lims(1)],loc_ser)
1348 chckerr('')
1349 if (rank.eq.0) rho2_lims(1) = minval(loc_ser)
1350 deallocate(loc_ser)
1351 ierr = get_ser_var([rho2_lims(2)],loc_ser)
1352 chckerr('')
1353 if (rank.eq.0) rho2_lims(2) = maxval(loc_ser)
1354 deallocate(loc_ser)
1355 ierr = get_ser_var([gamma_lims(1)],loc_ser)
1356 chckerr('')
1357 if (rank.eq.0) gamma_lims(1) = minval(loc_ser)
1358 deallocate(loc_ser)
1359 ierr = get_ser_var([gamma_lims(2)],loc_ser)
1360 chckerr('')
1361 if (rank.eq.0) gamma_lims(2) = maxval(loc_ser)
1362 deallocate(loc_ser)
1363 if (rank.eq.0) then
1364 call writo('limits in rho: '//trim(r2str(sqrt(rho2_lims(1))))//&
1365 &'..'//trim(r2str(sqrt(rho2_lims(2)))))
1366 call writo('limits in gamma: '//trim(r2str(gamma_lims(1)))//&
1367 &'..'//trim(r2str(gamma_lims(2))))
1368 end if
1369 end if
1370#endif
1371 contains
1372 ! function that returns f = -1/2 ln(x)-b - x/T.
1373 !
1374 ! It uses b_coeff (b) and tol_Q (T) from the parent function where T is
1375 ! a hard-coded positive value << 1.
1376 !
1377 !> \private
1378 function fun_tol(val)
1379 ! input / output
1380 real(dp), intent(in) :: val
1381 real(dp) :: fun_tol
1382
1383 fun_tol = -0.5_dp*log(val)-b_coeff(2)-val/tol_q
1384 end function fun_tol
1385 end function calc_gh_2
1386
1387 !> Calculates the vacuum response.
1388 !!
1389 !! First \c G and \c H are completed if this is not the first Richardson
1390 !! level.
1391 !!
1392 !! Then the vacuum contribution is calculated using the two-fold strategy of
1393 !! first solving
1394 !! \f$\overline{\text{H}}\overline{\Phi} =
1395 !! \overline{\text{G}}\overline{\text{E}} \overline{\text{P}}\f$,
1396 !! followed by left-multiplication of \f$\overline{\Phi}\f$ by
1397 !! \f$\overline{\text{P}}\overline{\text{E}}^\dagger\f$.
1398 !!
1399 !! The non-square matrix \f$\overline{\text{E}}\f$ contains the exponents
1400 !! for different mode numbers and different poloidal grid points, whereas
1401 !! \f$\overline{\text{P}}\f$ are diagonal matrices that contain the factors
1402 !! \f$(nq-m)\f$.
1403 !!
1404 !! In practice, the complex matrix \f$\overline{\text{E}}\f$ is split in the
1405 !! two components of a real matrix twice the width.
1406 !!
1407 !! For vacuum style 1, the poloidal grid points correspond to the paralell
1408 !! grid points and have to be provied by a \c grid variable.
1409 !!
1410 !! If \c jump_to_sol is used for the current Richardson level, the vacuum
1411 !! quantities are not calculated, but just restored.
1412 !!
1413 !! Currently, this procedure only works for vacuum style 2 (axisymmetric).
1414 !!
1415 !! \return ierr
1416 integer function calc_vac_res(mds,vac) result(ierr)
1418 use mpi
1419 use rich_vars, only: rich_lvl, n_par_x
1423 use x_vars, only: n_mod_x
1424 use vac_vars, only: set_loc_lims, in_context
1425 use vac_utilities, only: mat_dis2loc
1426 use eq_vars, only: vac_perm
1427 use num_utilities, only: lcm
1428 use mpi_utilities, only: broadcast_var
1429
1430 character(*), parameter :: rout_name = 'calc_vac_res'
1431
1432 ! input / output
1433 type(modes_type), intent(in) :: mds !< general modes variables
1434 type(vac_type), intent(inout) :: vac !< vacuum variables
1435
1436 ! local variables
1437 integer :: jd ! counter
1438 integer :: rd, cd ! global counter for row and col
1439 integer :: rdl, cdl ! local counter for row and col
1440 integer :: i_rd, i_cd ! index of subrow and subcol
1441 integer :: rich_lvl_name ! either the Richardson level or zero, to append to names
1442 integer :: sec_x_loc ! local sec_X
1443 integer :: n_loc(2) ! n_loc for n_mod_X instead of n_bnd
1444 integer :: lims_cl(2) ! lims_c_loc in local coordinates
1445 integer :: par_id ! parallel index
1446 integer :: alpha_id ! field line label
1447 integer, allocatable :: lims_r_loc(:,:) ! local row limits for n_loc
1448 integer, allocatable :: lims_c_loc(:,:) ! local column limits for n_loc
1449 integer, target :: desc_phiep(blacsctxtsize) ! descriptor for Phi and EP
1450 integer, target :: desc_gep(blacsctxtsize) ! descriptor for GEP
1451 integer, target :: desc_res2(blacsctxtsize) ! descriptor for res2
1452 real(dp) :: arg_loc ! local argument
1453 real(dp) :: dpar_x ! step size in par_X
1454 real(dp) :: dalpha ! step size in alpha
1455 real(dp) :: i_loc ! local integration rule
1456 real(dp) :: par_loc ! local parallel angle
1457 real(dp) :: alpha_loc ! local field line label
1458 real(dp), allocatable, target :: ep(:,:) ! EP
1459 real(dp), allocatable, target :: gep(:,:) ! GEP
1460 real(dp), allocatable, target :: phi(:,:) ! intermediate Phi
1461 real(dp), allocatable, target :: res2(:,:) ! vacuum response, real version
1462 real(dp), allocatable, target :: res2_loc(:,:) ! local version of res2
1463 logical :: rank_o ! output rank
1464#if ldebug
1465 integer :: lwork ! size of work array
1466 real(dp), allocatable :: res2_ev(:,:) ! eigenvalues of res2
1467 real(dp), allocatable :: tau(:) ! array tau in upper Hessenberg form
1468 real(dp), allocatable :: work(:) ! work array
1469 real(dp), allocatable :: x_plot(:,:) ! X of plot
1470 real(dp), allocatable :: y_plot(:,:) ! Y of plot
1471 complex(dp), allocatable :: res_loc(:,:) ! local vac%res
1472 complex(dp), allocatable :: res_ev(:) ! eigenvalues of res2
1473 complex(dp), allocatable :: work_c(:) ! work array
1474 complex(dp), allocatable :: tau_c(:) ! array tau in upper Hessenberg form
1475#endif
1476
1477 ! initialize ierr
1478 ierr = 0
1479
1480 ! decide whether to append Richardson level to name
1481 select case (eq_style)
1482 case (1) ! VMEC
1483 rich_lvl_name = rich_lvl ! append richardson level
1484 case (2) ! HELENA
1485 rich_lvl_name = 0 ! do not append name
1486 end select
1487
1488 if (rich_lvl.eq.rich_restart_lvl .and. jump_to_sol) then
1489 ! reconstruct old vacuum
1490 ierr = reconstruct_pb3d_vac(vac,'vac',rich_lvl=rich_lvl_name)
1491 chckerr('')
1492 return
1493 end if
1494
1495 call writo('Start calculation of vacuum quantities')
1496
1497 call lvl_ud(1)
1498
1499 ! set ouptut rank persistency
1500 if (rank.eq.n_procs-1) then
1501 rank_o = .true.
1502 else
1503 rank_o = .false.
1504 end if
1505
1506 ! calculate matrices G and H
1507 ierr = calc_gh(vac)
1508 chckerr('')
1509
1510 ! Only for processes that are in the blacs context
1511 if (in_context(vac%ctxt_HG)) then
1512 ! user output
1513 call writo('Use STRUMPack to solve system',persistent=rank_o)
1514 call lvl_ud(1)
1515
1516 ! set step sizes if 3-D vacuum
1517 if (vac%style.eq.1) then
1518 dpar_x = (max_par_x-min_par_x)/(n_par_x-1)*pi
1519 if (n_alpha.gt.1) then
1520 dalpha = (max_alpha-min_alpha)/(n_alpha-1)*pi
1521 else
1522 dalpha = 0._dp
1523 end if
1524 end if
1525
1526 ! set secondary mode numbers
1527 allocate(vac%sec_X(n_mod_x))
1528 if (use_pol_flux_f) then
1529 vac%sec_X = mds%m(size(mds%m,1),:)
1530 else
1531 vac%sec_X = mds%n(size(mds%n,1),:)
1532 end if
1533
1534 ! set sizes
1535 do jd = 1,2
1536 n_loc(jd) = numroc(2*n_mod_x,vac%bs,vac%ind_p(jd),0,vac%n_p(jd))
1537 end do
1538 call set_loc_lims(n_loc(1),vac%bs,vac%ind_p(1),vac%n_p(1),&
1539 &lims_r_loc)
1540 call set_loc_lims(n_loc(2),vac%bs,vac%ind_p(2),vac%n_p(2),&
1541 &lims_c_loc)
1542
1543 ! initialize descriptors
1544 call descinit(desc_phiep,vac%n_bnd,2*n_mod_x,vac%bs,vac%bs,0,0,&
1545 &vac%ctxt_HG,max(1,vac%n_loc(1)),ierr)
1546 chckerr('descinit failed for EP')
1547 call descinit(desc_gep,vac%n_bnd,2*n_mod_x,vac%bs,vac%bs,0,0,&
1548 &vac%ctxt_HG,max(1,vac%n_loc(1)),ierr)
1549 chckerr('descinit failed for GEP')
1550 call descinit(desc_res2,2*n_mod_x,2*n_mod_x,vac%bs,vac%bs,0,0,&
1551 &vac%ctxt_HG,max(1,n_loc(1)),ierr)
1552 chckerr('descinit failed for res2')
1553
1554 ! allocate auxililary matrices
1555 allocate(ep(vac%n_loc(1),n_loc(2)))
1556 allocate(gep(vac%n_loc(1),n_loc(2)))
1557 allocate(phi(vac%n_loc(1),n_loc(2)))
1558 allocate(res2(n_loc(1),n_loc(2)))
1559 allocate(res2_loc(2*n_mod_x,2*n_mod_x))
1560
1561 ! set EP
1562 ! iterate over all subcolumns of matrix with 2n_mod_X columns
1563 subcols: do i_cd = 1,size(lims_c_loc,2)
1564 col: do cd = lims_c_loc(1,i_cd),lims_c_loc(2,i_cd)
1565 ! set local column index
1566 cdl = sum(lims_c_loc(2,1:i_cd-1)-&
1567 &lims_c_loc(1,1:i_cd-1)+1) + &
1568 &cd-lims_c_loc(1,i_cd)+1
1569
1570 ! set local secondary mode number
1571 sec_x_loc = vac%sec_X(mod(cd-1,n_mod_x)+1)
1572
1573 ! iterate over all subrows of matrix with n_bnd rows
1574 subrows: do i_rd = 1,size(vac%lims_r,2)
1575 row: do rd = vac%lims_r(1,i_rd),vac%lims_r(2,i_rd)
1576 ! set local row index
1577 rdl = sum(vac%lims_r(2,1:i_rd-1)-&
1578 &vac%lims_r(1,1:i_rd-1)+1) + &
1579 &rd-vac%lims_r(1,i_rd)+1
1580
1581 ! set parallel index and field line label
1582 par_id = mod(rd-1,vac%n_ang(1))+1
1583 alpha_id = (rd-1)/vac%n_ang(1) + 1
1584
1585 ! set up local argument
1586 select case (vac%style)
1587 case (1) ! field-line 3-D
1588 par_loc = pi*min_par_x + &
1589 &(par_id-1)*dpar_x
1590 alpha_loc = pi*min_alpha + &
1591 &(alpha_id-1)*dalpha
1592 if (use_pol_flux_f) then
1593 arg_loc = vac%prim_X*alpha_loc + &
1594 &(vac%prim_X*vac%jq-sec_x_loc)*&
1595 &par_loc
1596 else
1597 arg_loc = vac%prim_X*alpha_loc + &
1598 &(sec_x_loc-vac%prim_X*vac%jq)*&
1599 &par_loc
1600 end if
1601 case (2) ! axisymmetric
1602 arg_loc = -sec_x_loc*&
1603 &vac%ang(par_id,alpha_id)
1604 end select
1605
1606 ! set up argument
1607
1608 ! save EP
1609 if (cd.le.n_mod_x) then ! real part of rhs
1610 ep(rdl,cdl) = cos(arg_loc) ! rp(i exp)
1611 else
1612 ep(rdl,cdl) = sin(arg_loc) ! ip(i exp)
1613 end if
1614 if (use_pol_flux_f) then
1615 ep(rdl,cdl) = (vac%prim_X*vac%jq-sec_x_loc)*&
1616 &ep(rdl,cdl)
1617 else
1618 ep(rdl,cdl) = (sec_x_loc-vac%prim_X*vac%jq)*&
1619 &ep(rdl,cdl)
1620 end if
1621 end do row
1622 end do subrows
1623 end do col
1624 end do subcols
1625 call plot_hdf5('EP','EP',reshape(ep,[vac%n_bnd,n_loc(2),1]))
1626
1627 ! solve for Phi
1628 ierr = solve_phi_bem(vac,ep,phi,[vac%n_bnd,2*n_mod_x],&
1629 &[vac%n_loc(1),n_loc(2)],lims_c_loc,desc_phiep)
1630 chckerr('')
1631
1632 ! calculate GEP
1633 call pdgemm('N','N',vac%n_bnd,2*n_mod_x,vac%n_bnd,1._dp,vac%G,1,1,&
1634 &vac%desc_G,ep,1,1,desc_phiep,0._dp,gep,1,1,desc_gep)
1635
1636 call lvl_ud(-1)
1637
1638 ! user output
1639 call writo('Combine results into vacuum response',persistent=rank_o)
1640 call lvl_ud(1)
1641
1642 ! Convert EP into IEP where I contains the integration rule:
1643 ! (θ_{i+1}-θ_{i-1})/2 for i = 2..n-1
1644 ! I_i = (θ_2-θ_1}/2 for i = 1
1645 ! (θ_{n}-θ_{n-1}}/2 for i = n
1646 ! with n = vac%n_bnd.
1647 ! For vacua of type 1, the step size is constant. Also, there is an
1648 ! additional step size in the alpha direction.
1649 subrows3: do i_rd = 1,size(vac%lims_r,2)
1650 row3: do rd = vac%lims_r(1,i_rd),vac%lims_r(2,i_rd)
1651 ! set local row index
1652 rdl = sum(vac%lims_r(2,1:i_rd-1)-&
1653 &vac%lims_r(1,1:i_rd-1)+1) + &
1654 &rd-vac%lims_r(1,i_rd)+1
1655
1656 subcols3: do i_cd = 1,size(lims_c_loc,2)
1657 ! set local column limits
1658 lims_cl = sum(lims_c_loc(2,1:i_cd-1)-&
1659 &lims_c_loc(1,1:i_cd-1)+1) + &
1660 &lims_c_loc(:,i_cd)-lims_c_loc(1,i_cd)+1
1661
1662 select case (vac%style)
1663 case (1) ! field-line 3-D
1664 i_loc = dpar_x*dalpha
1665 if ((rd-1)/vac%n_ang(1).eq.0 .or. & ! first point on field line
1666 &(rd-1)/vac%n_ang(1).eq.vac%n_ang(1)-1) & ! last point on field line
1667 &i_loc = i_loc*0.5_dp
1668 case (2) ! axisymmetric
1669 i_loc = 0.5_dp*&
1670 &(vac%ang(min(rd+1,vac%n_bnd),1)-&
1671 &vac%ang(max(rd-1,1),1))
1672 end select
1673
1674 ep(rdl,lims_cl(1):lims_cl(2)) = &
1675 &ep(rdl,lims_cl(1):lims_cl(2)) * i_loc
1676 end do subcols3
1677 end do row3
1678 end do subrows3
1679
1680 ! calculate (EP)^T Phi
1681 ! Note that this means that the second half of the rows in EP^T are
1682 ! still missing a minus sign if they are to represent EP^†. This
1683 ! will be taken into account when res2_loc is converted to vac%res.
1684 call pdgemm('T','N',2*n_mod_x,2*n_mod_x,vac%n_bnd,1._dp,ep,1,1,&
1685 &desc_phiep,phi,1,1,desc_phiep,0._dp,res2,1,1,desc_res2)
1686
1687 ! get serial version on last process
1688 ierr = mat_dis2loc(vac%ctxt_HG,res2,lims_r_loc,lims_c_loc,res2_loc,&
1689 &proc=n_procs-1)
1690 chckerr('')
1691
1692#if ldebug
1693 ! user output
1694 if (debug_calc_vac_res) then
1695 call writo('Calculate the eigenvalues of res_loc to &
1696 &investigate whether it is positive definite',&
1697 &persistent=rank_o)
1698 call lvl_ud(1)
1699 end if
1700#endif
1701
1702 ! convert to complex numbers and save in vac%res
1703 if (rank.eq.n_procs-1) then
1704 vac%res = res2_loc(1:n_mod_x,1:n_mod_x)-&
1705 &(-res2_loc(n_mod_x+1:2*n_mod_x,n_mod_x+1:2*n_mod_x)) ! minus sign for lower rows
1706 vac%res = vac%res + iu * &
1707 &(res2_loc(1:n_mod_x,n_mod_x+1:2*n_mod_x)-&
1708 &res2_loc(n_mod_x+1:2*n_mod_x,1:n_mod_x)) ! minus sign for lower rows
1709 vac%res = vac%res/vac_perm
1710#if ldebug
1711 allocate(x_plot(n_mod_x,n_mod_x))
1712 allocate(y_plot(n_mod_x,n_mod_x))
1713 do jd = 1,n_mod_x
1714 x_plot(jd,:) = vac%sec_X(jd)
1715 y_plot(:, jd) = vac%sec_X(jd)
1716 end do
1717 call print_ex_2d(['real vacuum response'],'vac_res_Re',&
1718 &rp(vac%res),x=x_plot,draw=.false.)
1719 call draw_ex(['real vacuum response'],'vac_res_Re',n_mod_x,1,&
1720 &.false.)
1721 call print_ex_2d(['imag vacuum response'],'vac_res_Im',&
1722 &ip(vac%res),x=x_plot,draw=.false.)
1723 call draw_ex(['imag vacuum response'],'vac_res_Im',n_mod_x,1,&
1724 &.false.)
1725 call plot_hdf5(['real part','imag part','imag diff'],'vac_res',&
1726 &reshape([rp(vac%res),ip(vac%res),&
1727 &ip(vac%res+transpose(vac%res))],[n_mod_x,n_mod_x,1,3]),&
1728 &x=reshape(x_plot,[n_mod_x,n_mod_x,1,1]),&
1729 &y=reshape(y_plot,[n_mod_x,n_mod_x,1,1]))
1730 deallocate(x_plot,y_plot)
1731
1732 if (debug_calc_vac_res) then
1733 ! copy vacuum response
1734 allocate(res_loc(n_mod_x,n_mod_x))
1735 res_loc = vac%res
1736
1737 ! get size of work matrix required for zgehrd
1738 allocate(work_c(1))
1739 allocate(tau_c(n_mod_x-1))
1740 call zgehrd(n_mod_x,1,n_mod_x,res_loc,n_mod_x,tau_c,work_c,&
1741 &-1,ierr)
1742 chckerr('zgehrd failed')
1743 lwork = nint(rp(work_c(1)))
1744 deallocate(work_c)
1745 allocate(work_c(lwork))
1746
1747 ! convert to upper Heisenberg form
1748 call zgehrd(n_mod_x,1,n_mod_x,res_loc,n_mod_x,tau_c,work_c,&
1749 &lwork,ierr)
1750 chckerr('zgehrd failed')
1751 deallocate(work_c)
1752
1753 ! get size of work matrix required for zhseqr
1754 allocate(work_c(1))
1755 allocate(res_ev(n_mod_x))
1756 call zhseqr('E','N',n_mod_x,1,n_mod_x,res_loc,n_mod_x,&
1757 &res_ev,0._dp,n_mod_x,work_c,-1,ierr)
1758 chckerr('zhseqr failed')
1759 lwork = nint(rp(work_c(1)))
1760 deallocate(work_c)
1761 allocate(work_c(lwork))
1762
1763 ! calculate eigenvalues
1764 call zhseqr('E','N',n_mod_x,1,n_mod_x,res_loc,n_mod_x,&
1765 &res_ev,0._dp,n_mod_x,work_c,lwork,ierr)
1766 chckerr('zhseqr failed')
1767 call print_ex_2d(['real part','imag part'],'res_ev',&
1768 &reshape([rp(res_ev),ip(res_ev)],[n_mod_x,2]),&
1769 &draw=.false.)
1770 call draw_ex(['real part','imag part'],'res_ev',2,1,&
1771 &.false.)
1772 deallocate(work_c)
1773 deallocate(res_ev)
1774 end if
1775#endif
1776 end if
1777
1778#if ldebug
1779 ! calculate the Eigenvalues of the original real res2
1780 if (debug_calc_vac_res) then
1781 ! get size of work matrix required for pdgehrd
1782 allocate(work(1))
1783 allocate(tau(2*n_mod_x-1))
1784 call pdgehrd(2*n_mod_x,1,2*n_mod_x,res2,1,1,desc_res2,tau,work,&
1785 &-1,ierr)
1786 chckerr('pdgehrd failed')
1787 lwork = nint(work(1))
1788 deallocate(work)
1789 allocate(work(lwork))
1790
1791 ! convert to upper Heisenberg form
1792 call pdgehrd(2*n_mod_x,1,2*n_mod_x,res2,1,1,desc_res2,tau,&
1793 &work,lwork,ierr)
1794 chckerr('pdgehrd failed')
1795 deallocate(work)
1796 deallocate(tau)
1797
1798 ! calculate eigenvalues
1799 lwork = 2*n_mod_x / vac%bs
1800 if (lwork*vac%bs.lt.2*n_mod_x) lwork = lwork + 1
1801 lwork = 7*lwork / lcm(vac%n_p(1),vac%n_p(2))
1802 lwork = 3*2*n_mod_x + max(2*2*n_mod_x+2*n_loc(2) , lwork)
1803 allocate(work(lwork))
1804 allocate(res2_ev(2*n_mod_x,2))
1805 call pdlahqr(.false.,.false.,2*n_mod_x,1,2*n_mod_x,res2,&
1806 &desc_res2,res2_ev(:,1),res2_ev(:,2),1,2*n_mod_x,[0._dp],&
1807 &desc_res2,work,lwork,[0],1,ierr)
1808 chckerr('pdlahqr failed')
1809 call print_ex_2d(['real part','imag part'],'res2_ev',&
1810 &res2_ev,draw=.false.)
1811 call draw_ex(['real part','imag part'],'res2_ev',2,1,.false.)
1812 deallocate(work)
1813 deallocate(res2_ev)
1814
1815 ! user output
1816 call lvl_ud(-1)
1817 call writo('They should all be positive',persistent=rank_o)
1818 end if
1819#endif
1820
1821 call lvl_ud(-1)
1822 end if
1823
1824 call lvl_ud(-1)
1825
1826 call writo('Done calculating vacuum quantities')
1827 end function calc_vac_res
1828
1829 !> Calculate vacuum potential at some positions that are not resonant.
1830 !!
1831 !! This is done by using the relation between H and G on the plasma
1832 !! boundary,
1833 !! \f$\overline{\text{H}}_\text{s} \overline{\Phi}_\text{s} =
1834 !! \overline{\text{G}}_\text{s} \overline{\text{D}\phi}_\text{s}\f$,
1835 !! where \f$\overline{\text{D}\phi}_\text{s}\f$ is the normal derivative of
1836 !! the potential on the plasma edge. The matrices \f$\overline{\text{H}}\f$
1837 !! and \f$\overline{\text{G}}\f$ have to be calculated in advance.
1838 !!
1839 !! This relation is then used at different positions to calculate the
1840 !! potential at a different position through
1841 !! \f$4 \pi \phi = \left[ - \overline{\text{H}} \overline{\text{I}}
1842 !! \overline{\text{H}}_\text{s}^{-1} \overline{\text{G}}_\text{s} +
1843 !! \overline{\text{G}} \overline{\text{I}} \right]
1844 !! \overline{\text{D}\phi}_\text{s}\f$,
1845 !! where \f$\overline{\text{I}}\f$ is an integration rule.
1846 !!
1847 !! This system of equations contains one row per point at which the
1848 !! potential is to be calculated.
1849 !!
1850 !! Currently, this routine creates a 3-D regular grid that is equidistant
1851 !! in cylindrical coordinates, with \c n_vac_plot values in R and Z,
1852 !! and \c n_zeta_plot values in the cylindrical angle. The limits in
1853 !! these variables, furthermore, are given by \c min_Rvac_plot, \c
1854 !! max_Rvac_plot, \c min_Zvac_plot, \c max_Zvac_plot, \c min_zeta_plot and
1855 !! \c max_zeta_plot.
1856 integer function vac_pot_plot(grid_sol,vac,sol,X_id) result(ierr)
1860 use sol_vars, only: sol_type
1861 use mpi_utilities, only: broadcast_var
1862 use iso_c_binding
1863 use vac_vars, only: in_context
1864 use vac_utilities, only: mat_dis2loc
1865 use x_vars, only: prim_x, n_mod_x
1866 use vac_vars, only: set_loc_lims
1867
1868 ! input / output
1869 type(grid_type), intent(in) :: grid_sol !< solution grid
1870 type(vac_type), intent(inout) :: vac !< vacuum variables
1871 type(sol_type), intent(in) :: sol !< solution variables
1872 integer, intent(in) :: x_id !< nr. of Eigenvalue
1873
1874 ! local variables
1875 integer :: id, jd, kd ! counters
1876 integer :: rd, cd ! global counter for row and col
1877 integer :: i_rd, i_cd ! index of subrow and subcol
1878 integer :: rdl, cdl ! local counter for row and col
1879 integer :: desc_rhs(blacsctxtsize) ! descriptor for RHS
1880 integer :: desc_gh_loc(blacsctxtsize) ! descriptor for local G and H
1881 integer :: desc_phi(blacsctxtsize) ! descriptor for Phi
1882 integer :: n_row_plot ! n_loc(1) for n_vac_plot rows
1883 integer :: n_col_2 ! n_loc(2) for 2 columns (real and imaginary part)
1884 integer :: lims_cl(2) ! lims_c_loc in local coordinates
1885 integer, allocatable :: lims_r_loc(:,:) ! local column limits for n_row_plot
1886 integer, allocatable :: lims_c_loc(:,:) ! local column limits for n_col_2
1887 integer :: sec_x_loc ! local sec_X
1888 integer :: fac_x(2) ! (n,-m) for poloidal or (-m,n) for toroidal flux
1889 real(dp) :: zeta_loc ! local zeta
1890 real(dp), allocatable :: x_vec(:,:) ! x_vec of influence points
1891 real(dp), allocatable :: g_loc(:,:) ! local G
1892 real(dp), allocatable :: h_loc(:,:) ! local H
1893 real(dp), allocatable :: rhs(:,:,:) ! 2 right-hand sides, real and complex
1894 real(dp), allocatable :: phi(:,:) ! Phi
1895 real(dp), allocatable :: phi_ser(:,:) ! serial version of Phi
1896 real(dp), allocatable :: phi_2d(:,:) ! 2-D serial version of Phi
1897 complex(dp), allocatable :: sol_vec(:) ! solution vector at edge
1898 complex(dp) :: rhs_complex ! complex local RHS
1899 character(len=max_str_ln) :: err_msg ! error message
1900#if ldebug
1901 real(dp), allocatable :: rhs_loc(:,:) ! local version of RHS
1902 !character(len=max_str_ln) :: plot_title ! plot title
1903 !character(len=max_str_ln) :: plot_name ! plot name
1904#endif
1905
1906 character(*), parameter :: rout_name = 'vac_pot_plot'
1907
1908 ! initialize ierr
1909 ierr = 0
1910
1911 ! distribute solution vector
1912 allocate(sol_vec(n_mod_x))
1913 if (rank.eq.n_procs-1) sol_vec = sol%vec(:,grid_sol%loc_n_r,x_id)
1914 ierr = broadcast_var(sol_vec,n_procs-1)
1915 chckerr('')
1916
1917 ! calculate matrices G and H
1918 ierr = calc_gh(vac)
1919 chckerr('')
1920
1921 ! Only for processes that are in the blacs context
1922 if (in_context(vac%ctxt_HG)) then
1923 ! set sizes
1924 n_row_plot = numroc(product(n_vac_plot),vac%bs,vac%ind_p(1),0,&
1925 &vac%n_p(1))
1926 n_col_2 = numroc(2,vac%bs,vac%ind_p(2),0,vac%n_p(2))
1927 call set_loc_lims(n_row_plot,vac%bs,vac%ind_p(1),vac%n_p(1),&
1928 &lims_r_loc)
1929 call set_loc_lims(n_col_2,vac%bs,vac%ind_p(2),vac%n_p(2),&
1930 &lims_c_loc)
1931
1932 ! allocate RHS and set descriptor
1933 allocate(rhs(vac%n_loc(1),n_col_2,2))
1934 rhs = 0._dp
1935 call descinit(desc_rhs,vac%n_bnd,2,vac%bs,vac%bs,0,0,&
1936 &vac%ctxt_HG,max(1,vac%n_loc(1)),ierr)
1937 chckerr('descinit failed for RHS')
1938
1939 ! initialize the right hand sides as the normal derivative of Phi at
1940 ! the plasma edge
1941 subcols: do i_cd = 1,size(lims_c_loc,2)
1942 col: do cd = lims_c_loc(1,i_cd),lims_c_loc(2,i_cd)
1943 ! set local column index
1944 cdl = sum(lims_c_loc(2,1:i_cd-1)-&
1945 &lims_c_loc(1,1:i_cd-1)+1) + &
1946 &cd-lims_c_loc(1,i_cd)+1
1947
1948 ! iterate over all subrows of matrix with n_bnd rows
1949 subrows: do i_rd = 1,size(vac%lims_r,2)
1950 row: do rd = vac%lims_r(1,i_rd),vac%lims_r(2,i_rd)
1951 ! set local row index
1952 rdl = sum(vac%lims_r(2,1:i_rd-1)-&
1953 &vac%lims_r(1,1:i_rd-1)+1) + &
1954 &rd-vac%lims_r(1,i_rd)+1
1955
1956 ! sum over all modes to get parallel derivative of X
1957 do jd = 1,n_mod_x
1958 sec_x_loc = vac%sec_X(jd)
1959 if (use_pol_flux_f) then
1960 fac_x = [prim_x,-sec_x_loc]
1961 else
1962 ierr = 1
1963 err_msg = 'Currently must use poloidal flux'
1964 chckerr(err_msg)
1965 !fac_X = [-prim_X,sec_X_loc]
1966 end if
1967
1968 rhs_complex = iu*(fac_x(1)*vac%jq+fac_x(2))*&
1969 &exp(iu*fac_x(2)*vac%ang(rd,1))*sol_vec(jd)
1970 if (cd.eq.1) rhs(rdl,cdl,:) = &
1971 &rhs(rdl,cdl,:) + rp(rhs_complex)
1972 if (cd.eq.2) rhs(rdl,cdl,:) = &
1973 &rhs(rdl,cdl,:) + ip(rhs_complex)
1974 end do
1975 end do row
1976 end do subrows
1977 end do col
1978 end do subcols
1979
1980 ! solve for Phi and save in second index of RHS
1981 ierr = solve_phi_bem(vac,rhs(:,:,1),rhs(:,:,2),[vac%n_bnd,2],&
1982 &[vac%n_loc(1),n_col_2],lims_c_loc,desc_rhs)
1983 chckerr('')
1984
1985#if ldebug
1986 ! get serial version on last process
1987 allocate(rhs_loc(vac%n_bnd,2))
1988 do jd = 1,2
1989 ierr = mat_dis2loc(vac%ctxt_HG,rhs(:,:,jd),vac%lims_r,&
1990 &lims_c_loc,rhs_loc,proc=n_procs-1)
1991 chckerr('')
1992 if (rank.eq.n_procs-1) then
1993 call print_ex_2d(['dphi','phi '],'RHS_'//trim(i2str(jd)),&
1994 &rhs_loc,x=vac%ang(:,1:1),draw=.false.)
1995 call draw_ex(['dphi','phi '],'RHS_'//trim(i2str(jd)),2,1,&
1996 &.false.)
1997 end if
1998 end do
1999 deallocate(rhs_loc)
2000#endif
2001
2002 ! convert RHS into IRHS where I contains the integration rule:
2003 ! (θ_{i+1}-θ_{i-1})/2 for i = 2..n-1
2004 ! I_i = (θ_2-θ_1}/2 for i = 1
2005 ! (θ_{n}-θ_{n-1}}/2 for i = n
2006 ! with n = vac%n_bnd.
2007 subrows2: do i_rd = 1,size(vac%lims_r,2)
2008 row2: do rd = vac%lims_r(1,i_rd),vac%lims_r(2,i_rd)
2009 ! set local row index
2010 rdl = sum(vac%lims_r(2,1:i_rd-1)-&
2011 &vac%lims_r(1,1:i_rd-1)+1) + &
2012 &rd-vac%lims_r(1,i_rd)+1
2013
2014 subcols2: do i_cd = 1,size(lims_c_loc,2)
2015 ! local limits
2016 lims_cl = sum(lims_c_loc(2,1:i_cd-1)-&
2017 &lims_c_loc(1,1:i_cd-1)+1) + &
2018 &lims_c_loc(:,i_cd)-lims_c_loc(1,i_cd)+1
2019
2020 rhs(rdl,lims_cl(1):lims_cl(2),:) = &
2021 &rhs(rdl,lims_cl(1):lims_cl(2),:) * 0.5_dp*&
2022 &(vac%ang(min(rd+1,vac%n_bnd),1)-&
2023 &vac%ang(max(rd-1,1),1))
2024 end do subcols2
2025 end do row2
2026 end do subrows2
2027
2028 ! allocate G, H and x_vec and set descriptor
2029 allocate(g_loc(n_row_plot,vac%n_loc(2)))
2030 allocate(h_loc(n_row_plot,vac%n_loc(2)))
2031 allocate(x_vec(product(n_vac_plot),2))
2032 g_loc = 0._dp
2033 h_loc = 0._dp
2034 call descinit(desc_gh_loc,product(n_vac_plot),vac%n_bnd,vac%bs,&
2035 &vac%bs,0,0,vac%ctxt_HG,max(1,n_row_plot),ierr)
2036 chckerr('descinit failed for GH_loc')
2037
2038 ! loop over all toroidal points
2039 do kd = 1,n_zeta_plot
2040 ! set local zeta
2041 zeta_loc = min_zeta_plot + (max_zeta_plot-min_zeta_plot) * &
2042 &(kd-1._dp)/max(1._dp,n_zeta_plot-1._dp)
2043
2044 ! set up variables to calculate local G and H
2045 row3: do rd = 1,product(n_vac_plot)
2046 ! set up 2-D index for x_vec
2047 id = mod(rd-1,n_vac_plot(1))+1
2048 jd = (rd-1)/n_vac_plot(1)+1
2049
2050 ! set up x_vec
2051 x_vec(rd,1) = min_rvac_plot + &
2053 &(id-1._dp)/(n_vac_plot(1)-1._dp)
2054 x_vec(rd,2) = min_zvac_plot + &
2056 &(jd-1._dp)/(n_vac_plot(2)-1._dp)
2057 end do row3
2058 !call print_ex_2D(['R','Z'],'x_vec_'//trim(i2str(rank)),x_vec,draw=.false.)
2059 !call draw_ex(['R','Z'],'x_vec_'//trim(i2str(rank)),2,1,.false.)
2060
2061 ! calc local G and H
2062 ierr = calc_gh(vac,n_r_in=product(n_vac_plot),&
2063 &lims_r_in=lims_r_loc,x_vec_in=x_vec,g_in=g_loc,h_in=h_loc)
2064 chckerr('')
2065
2066#if ldebug
2067 !if (debug_vac_pot_plot) then
2068 !subrows4: do i_rd = 1,size(lims_r_loc,2)
2069 !row4: do rd = lims_r_loc(1,i_rd),lims_r_loc(2,i_rd)
2070 !subcols4: do i_cd = 1,size(vac%lims_c,2)
2071 !! output
2072 !plot_title = 'for row '//trim(i2str(rd))//&
2073 !&' and starting column '//&
2074 !&trim(i2str(vac%lims_c(1,i_cd)))
2075 !plot_name = '_'//trim(i2str(kd))//'_'//&
2076 !&trim(i2str(rd))//'_'//&
2077 !&trim(i2str(vac%lims_c(1,i_cd)))
2078
2079 !! local limits
2080 !lims_cl = sum(vac%lims_c(2,1:i_cd-1)-&
2081 !&vac%lims_c(1,1:i_cd-1)+1) + &
2082 !&vac%lims_c(:,i_cd)-vac%lims_c(1,i_cd)+1
2083
2084 !! output
2085 !call print_ex_2D('G '//trim(plot_title),&
2086 !&'G_loc'//trim(plot_name),&
2087 !&G_loc(rdl,lims_cl(1):lims_cl(2)),x=vac%ang&
2088 !&(vac%lims_c(1,i_cd):vac%lims_c(2,i_cd),1),&
2089 !&draw=.false.)
2090 !call draw_ex(['G '//trim(plot_title)],&
2091 !&'G_loc'//trim(plot_name),1,1,.false.)
2092 !call print_ex_2D('H '//trim(plot_title),&
2093 !&'H_loc'//trim(plot_name),&
2094 !&H_loc(rdl,lims_cl(1):lims_cl(2)),x=vac%ang&
2095 !&(vac%lims_c(1,i_cd):vac%lims_c(2,i_cd),1),&
2096 !&draw=.false.)
2097 !call draw_ex(['H '//trim(plot_title)],&
2098 !&'H_loc'//trim(plot_name),1,1,.false.)
2099 !end do subcols4
2100 !end do row4
2101 !end do subrows4
2102 !end if
2103#endif
2104
2105 ! multiply G with RHS(1) and save in Phi
2106 allocate(phi(n_row_plot,n_col_2))
2107 call descinit(desc_phi,product(n_vac_plot),2,vac%bs,&
2108 &vac%bs,0,0,vac%ctxt_HG,max(1,n_row_plot),ierr)
2109 chckerr('descinit failed for Phi')
2110 call pdgemm('N','N',product(n_vac_plot),2,vac%n_bnd,1._dp,&
2111 &g_loc,1,1,desc_gh_loc,rhs(:,:,1),1,1,desc_rhs,0._dp,&
2112 &phi,1,1,desc_phi)
2113
2114 ! add -H multiplied by RHS(2)
2115 call pdgemm('N','N',product(n_vac_plot),2,vac%n_bnd,-1._dp,&
2116 &h_loc,1,1,desc_gh_loc,rhs(:,:,2),1,1,desc_rhs,1._dp,&
2117 &phi,1,1,desc_phi)
2118
2119 ! get serial version on last process
2120 allocate(phi_ser(product(n_vac_plot),2))
2121 ierr = mat_dis2loc(vac%ctxt_HG,phi,lims_r_loc,&
2122 &lims_c_loc,phi_ser,proc=n_procs-1)
2123 chckerr('')
2124#if ldebug
2125 if (rank.eq.n_procs-1) then
2126 call print_ex_2d(['dphi','phi '],'Phi'//trim(i2str(kd)),&
2127 &phi_ser,draw=.false.)
2128 call draw_ex(['dphi','phi '],'Phi'//trim(i2str(kd)),2,1,&
2129 &.false.)
2130 end if
2131#endif
2132
2133 ! multiply with exponential of toroidal angle and take real part
2134 ! (phi_C = - zeta_F)
2135 if (rank.eq.n_procs-1) then
2136 allocate(phi_2d(n_vac_plot(1),n_vac_plot(2)))
2137 phi_2d = -1./(4*pi)*reshape(&
2138 &phi_ser(:,1)*cos(vac%prim_X*zeta_loc) + &
2139 &phi_ser(:,2)*sin(vac%prim_X*zeta_loc),n_vac_plot)
2140 call plot_hdf5('Phi','Phi',reshape(phi_2d,[n_vac_plot,1]),&
2141 &x=cos(vac%prim_X*zeta_loc)*&
2142 &reshape(x_vec(:,1),[n_vac_plot,1]),&
2143 &y=sin(vac%prim_X*zeta_loc)*&
2144 &reshape(x_vec(:,1),[n_vac_plot,1]),&
2145 &z=reshape(x_vec(:,2),[n_vac_plot,1]))
2146 end if
2147 deallocate(x_vec)
2148 end do
2149 end if
2150 end function vac_pot_plot
2151
2152 !> \public Calculates potential \c Phi on boundary of BEM in terms of some
2153 !! right-hand side .
2154 !!
2155 !! This is done by solving with STRUMPack the system of equations
2156 !! \f$\overline{\text{H}}\overline{\Phi} =
2157 !! \overline{\text{G}}\overline{\text{R}}\f$,
2158 !! where \f$\overline{\text{R}}\f$ is the right-hand side, for example equal
2159 !! to \f$\overline{\text{E}}\overline{\text{P}}\f$ to solve in function of
2160 !! the individual Fourier modes.
2161 integer function solve_phi_bem(vac,R,Phi,n_RPhi,n_loc_RPhi,lims_c_RPhi,&
2162 &desc_RPhi) result(ierr)
2163
2164 use vac_vars, only: in_context
2165#if ldebug
2166 use grid_vars, only: min_par_x, max_par_x
2167 use rich_vars, only: n_par_x
2168#endif
2169
2170 character(*), parameter :: rout_name = 'solve_Phi_BEM'
2171
2172 ! input / output
2173 type(vac_type), intent(in), target :: vac !< vacuum variables
2174 real(dp), intent(inout), target :: phi(:,:) !< \f$\overline{\Phi}\f$
2175 real(dp), intent(in) :: r(:,:) !< \f$\overline{\text{R}}\f$
2176 integer, intent(in) :: n_rphi(2) !< \c n for \f$\overline{\text{R}}\f$ and \f$\overline{\Phi}\f$
2177 integer, intent(in) :: n_loc_rphi(2) !< local \c n for \f$\overline{\text{R}}\f$ and \f$\overline{\Phi}\f$
2178 integer, intent(in) :: lims_c_rphi(:,:) !< local limits for row and columns of \f$\overline{\text{R}}\f$ and \f$\overline{\Phi}\f$
2179 integer, intent(in), target :: desc_rphi(blacsctxtsize) !< descriptor for \f$\overline{\text{R}}\f$ and \f$\overline{\Phi}\f$
2180
2181 ! local variables
2182 integer, target :: desc_gr(blacsctxtsize) ! descriptor for GR
2183 real(dp) :: sdp_err ! error
2184 real(dp), allocatable, target :: gr(:,:) ! GR
2185 type(c_ptr) :: cdesch, ch ! C pointers to descriptor of H and H itself
2186 type(c_ptr) :: cdescgr, cgr ! C pointers to descriptor of GR and GR itself
2187 type(c_ptr) :: cdescphi, cphi ! C pointers to descriptor of Phi and Phi itself
2188 type(strumpackdensepackage_f90_double) :: sdp_loc ! Strumpack object for local solve H Phi = GR
2189#if ldebug
2190 integer :: lims_rl(2) ! vac%lims_r in local coordinates
2191 integer :: id ! counter
2192 integer :: cd ! global counter for row and col
2193 integer :: cdl ! local counter for col
2194 integer :: i_rd, i_cd ! index of subrow and subcol
2195 character(len=max_str_ln) :: plot_title ! plot title
2196 character(len=max_str_ln) :: plot_name ! plot name
2197 real(dp), allocatable :: ang(:) ! angle
2198#endif
2199
2200 ! initialize ierr
2201 ierr = 0
2202
2203 ! Only for processes that are in the blacs context
2204 if (in_context(vac%ctxt_HG)) then
2205 ! allocate auxililary matrices
2206 allocate(gr(vac%n_loc(1),n_loc_rphi(2)))
2207
2208 ! initialize descriptors
2209 call descinit(desc_gr,vac%n_bnd,n_rphi(2),vac%bs,vac%bs,0,0,&
2210 &vac%ctxt_HG,max(1,vac%n_loc(1)),ierr)
2211 chckerr('descinit failed for GR')
2212
2213 ! calculate GR
2214 call pdgemm('N','N',vac%n_bnd,n_rphi(2),vac%n_bnd,1._dp,vac%G,1,1,&
2215 &vac%desc_G,r,1,1,desc_rphi,0._dp,gr,1,1,desc_gr)
2216
2217 ! set C pointers
2218 ch = c_loc(vac%H)
2219 cdesch = c_loc(vac%desc_H)
2220 cgr = c_loc(gr)
2221 cdescgr = c_loc(desc_gr)
2222 cphi = c_loc(phi)
2223 cdescphi = c_loc(desc_rphi)
2224
2225 ! initialize the solver and set parameters
2226 call sdp_f90_double_init(sdp_loc,vac%MPI_Comm)
2227 sdp_loc%use_HSS=1
2228 sdp_loc%levels_HSS=4
2229 sdp_loc%min_rand_HSS=10
2230 sdp_loc%lim_rand_HSS=5
2231 sdp_loc%inc_rand_HSS=10
2232 sdp_loc%max_rand_HSS=100
2233 sdp_loc%tol_HSS=1d-12
2234 sdp_loc%steps_IR=10
2235 sdp_loc%tol_IR=1d-10
2236
2237 ! compression
2238 call sdp_f90_double_compress_a(sdp_loc,ch,cdesch)
2239
2240#if ldebug
2241 ! accuracy checking
2242 sdp_err = sdp_f90_double_check_compression(sdp_loc,ch,cdesch)
2243#endif
2244
2245 ! factorization
2246 call sdp_f90_double_factor(sdp_loc,ch,cdesch)
2247
2248 ! solve H Phi = G R
2249 call sdp_f90_double_solve(sdp_loc,cphi,cdescphi,cgr,cdescgr)
2250
2251#if ldebug
2252 ! accuracy checking
2253 sdp_err = sdp_f90_double_check_solution(sdp_loc,ch,cdesch,cphi,&
2254 &cdescphi,cgr,cdescgr)
2255#endif
2256
2257 ! iterative refinement
2258 sdp_err = sdp_f90_double_refine(sdp_loc,ch,cdesch,cphi,cdescphi,&
2259 &cgr,cdescgr)
2260
2261#if ldebug
2262 ! statistics
2263 call sdp_f90_double_print_statistics(sdp_loc)
2264
2265 ! user output
2266 subcols: do i_cd = 1,size(lims_c_rphi,2)
2267 col: do cd = lims_c_rphi(1,i_cd),lims_c_rphi(2,i_cd)
2268 ! set local column index
2269 cdl = sum(lims_c_rphi(2,1:i_cd-1)-&
2270 &lims_c_rphi(1,1:i_cd-1)+1) + &
2271 &cd-lims_c_rphi(1,i_cd)+1
2272
2273 subrows2: do i_rd = 1,size(vac%lims_r,2)
2274 ! set local row limits
2275 lims_rl = sum(vac%lims_r(2,1:i_rd-1)-&
2276 &vac%lims_r(1,1:i_rd-1)+1) + &
2277 &vac%lims_r(:,i_rd)-vac%lims_r(1,i_rd)+1
2278
2279 ! set angle
2280 allocate(ang(vac%lims_r(2,i_rd)-vac%lims_r(1,i_rd)+1))
2281 select case (vac%style)
2282 case (1) ! field-line 3-D
2283 do id = 1,vac%n_ang(1)
2284 ang(id:&
2285 &id+vac%n_ang(1)*(vac%n_ang(2)-1):&
2286 &vac%n_ang(1)) = &
2287 &pi * (min_par_x + (id-1)*&
2289 end do
2290 case (2) ! axisymmetric
2291 ang = reshape(vac%ang,[vac%n_bnd])
2292 end select
2293
2294 ! output
2295 plot_title = 'for column '//trim(i2str(cd))//&
2296 &' and starting row '//&
2297 &trim(i2str(vac%lims_r(1,i_rd)))
2298 plot_name = '_'//trim(i2str(cd))//'_'//&
2299 &trim(i2str(vac%lims_r(1,i_rd)))
2300 call print_ex_2d('Phi '//trim(plot_title),&
2301 &'Phi'//trim(plot_name),&
2302 &phi(lims_rl(1):lims_rl(2),cdl),&
2303 &x=ang(vac%lims_r(1,i_rd):vac%lims_r(2,i_rd)),&
2304 &draw=.false.)
2305 call draw_ex(['Phi '//trim(plot_title)],&
2306 &'Phi'//trim(plot_name),1,1,.false.)
2307
2308 ! clean up
2309 deallocate(ang)
2310 end do subrows2
2311 end do col
2312 end do subcols
2313#endif
2314
2315 ! destroy Strumpack object
2316 call sdp_f90_double_destroy(sdp_loc)
2317 end if
2318 end function solve_phi_bem
2319
2320 !> Print vacuum quantities of a certain order to an output file
2321 !!
2322 !! - vac:
2323 !! - \c misc_vac
2324 !! - \c norm
2325 !! - \c x_vec
2326 !! - \c vac_res
2327 !!
2328 !! If \c rich_lvl is provided, \c "_R_[rich_lvl]" is appended to the data
2329 !! name if it is \c >0.
2330 !!
2331 !! \note
2332 !! -# This routine should be called by a single process, in contrast
2333 !! to the other output routines such as eq_ops.print_output_eq(),
2334 !! print_output_sol(), ...
2335 !! -# This process should be the last one, as it will set the boundary
2336 !! contribution.
2337 !! -# This routine does not save the \c H and \c G matrices because they
2338 !! are large and foreseen to be saved directly as Hierarchical matrices in
2339 !! the future. They therefore need to be regenerated if Richardson restart
2340 !! is done, or when after a jump to solution, another Richardson level is
2341 !! attempted. In calc_vac, it is checked when copying the vacuum variables
2342 !! from previous to current Richardson level, whether the \c G and \c H
2343 !! matrices are allocated and if not, they are calculated.
2344 !!
2345 !! \return ierr
2346 integer function print_output_vac(vac,data_name,rich_lvl) result(ierr)
2347 use x_vars, only: n_mod_x
2348 use num_vars, only: pb3d_name
2349 use hdf5_ops, only: print_hdf5_arrs
2350 use hdf5_vars, only: dealloc_var_1d, var_1d_type, &
2352
2353 character(*), parameter :: rout_name = 'print_output_vac'
2354
2355 ! input / output
2356 type(vac_type), intent(in) :: vac !< vacuum variables
2357 character(len=*), intent(in) :: data_name !< name under which to store
2358 integer, intent(in), optional :: rich_lvl !< Richardson level to print
2359
2360 ! local variables
2361 type(var_1d_type), allocatable, target :: vac_1d(:) ! 1D equivalent of vac variables
2362 type(var_1d_type), pointer :: vac_1d_loc => null() ! local element in vac_1D
2363 integer :: id ! counter
2364
2365 ! initialize ierr
2366 ierr = 0
2367
2368 ! user output
2369 call writo('Write vacuum variables to output file')
2370 call lvl_ud(1)
2371
2372 ! Set up the 1D equivalents of the perturbation variables
2373 allocate(vac_1d(max_dim_var_1d))
2374
2375 ! set up variables vac_1D
2376 id = 1
2377
2378 ! misc_vac
2379 vac_1d_loc => vac_1d(id); id = id+1
2380 vac_1d_loc%var_name = 'misc_vac'
2381 allocate(vac_1d_loc%tot_i_min(1),vac_1d_loc%tot_i_max(1))
2382 allocate(vac_1d_loc%loc_i_min(1),vac_1d_loc%loc_i_max(1))
2383 vac_1d_loc%loc_i_min = [1]
2384 vac_1d_loc%loc_i_max = [6]
2385 vac_1d_loc%tot_i_min = vac_1d_loc%loc_i_min
2386 vac_1d_loc%tot_i_max = vac_1d_loc%loc_i_max
2387 allocate(vac_1d_loc%p(6))
2388 vac_1d_loc%p = [vac%style*1._dp,vac%n_bnd*1._dp,vac%prim_X*1._dp,&
2389 &vac%n_ang*1._dp,vac%jq]
2390
2391 ! sec_X
2392 vac_1d_loc => vac_1d(id); id = id+1
2393 vac_1d_loc%var_name = 'sec_X'
2394 allocate(vac_1d_loc%tot_i_min(1),vac_1d_loc%tot_i_max(1))
2395 allocate(vac_1d_loc%loc_i_min(1),vac_1d_loc%loc_i_max(1))
2396 vac_1d_loc%tot_i_min = [1]
2397 vac_1d_loc%tot_i_max = n_mod_x
2398 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2399 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2400 allocate(vac_1d_loc%p(n_mod_x))
2401 vac_1d_loc%p = vac%sec_X*1._dp
2402
2403 ! norm
2404 vac_1d_loc => vac_1d(id); id = id+1
2405 vac_1d_loc%var_name = 'norm'
2406 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2407 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2408 vac_1d_loc%tot_i_min = [1,1]
2409 vac_1d_loc%tot_i_max = shape(vac%norm)
2410 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2411 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2412 allocate(vac_1d_loc%p(size(vac%norm)))
2413 vac_1d_loc%p = reshape(vac%norm,[size(vac%norm)])
2414
2415 ! x_vec
2416 vac_1d_loc => vac_1d(id); id = id+1
2417 vac_1d_loc%var_name = 'x_vec'
2418 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2419 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2420 vac_1d_loc%tot_i_min = [1,1]
2421 vac_1d_loc%tot_i_max = shape(vac%x_vec)
2422 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2423 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2424 allocate(vac_1d_loc%p(size(vac%x_vec)))
2425 vac_1d_loc%p = reshape(vac%x_vec,[size(vac%x_vec)])
2426
2427 ! RE_res
2428 vac_1d_loc => vac_1d(id); id = id+1
2429 vac_1d_loc%var_name = 'RE_res'
2430 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2431 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2432 vac_1d_loc%tot_i_min = [1,1]
2433 vac_1d_loc%tot_i_max = [n_mod_x,n_mod_x]
2434 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2435 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2436 allocate(vac_1d_loc%p(size(vac%res)))
2437 vac_1d_loc%p = reshape(rp(vac%res),[size(vac%res)])
2438
2439 ! IM_res
2440 vac_1d_loc => vac_1d(id); id = id+1
2441 vac_1d_loc%var_name = 'IM_res'
2442 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2443 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2444 vac_1d_loc%tot_i_min = [1,1]
2445 vac_1d_loc%tot_i_max = [n_mod_x,n_mod_x]
2446 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2447 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2448 allocate(vac_1d_loc%p(size(vac%res)))
2449 vac_1d_loc%p = reshape(ip(vac%res),[size(vac%res)])
2450
2451 ! copy variables specific to style
2452 select case (vac%style)
2453 case (1) ! field-line 3-D
2454 ! h_fac
2455 vac_1d_loc => vac_1d(id); id = id+1
2456 vac_1d_loc%var_name = 'h_fac'
2457 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2458 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2459 vac_1d_loc%tot_i_min = [1,1]
2460 vac_1d_loc%tot_i_max = shape(vac%h_fac)
2461 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2462 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2463 allocate(vac_1d_loc%p(size(vac%h_fac)))
2464 vac_1d_loc%p = reshape(vac%h_fac,[size(vac%h_fac)])
2465 case (2) ! axisymmetric
2466 ! ang
2467 vac_1d_loc => vac_1d(id); id = id+1
2468 vac_1d_loc%var_name = 'ang'
2469 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2470 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2471 vac_1d_loc%tot_i_min = [1,1]
2472 vac_1d_loc%tot_i_max = shape(vac%ang)
2473 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2474 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2475 allocate(vac_1d_loc%p(size(vac%ang)))
2476 vac_1d_loc%p = reshape(vac%ang,[size(vac%ang)])
2477
2478 ! dnorm
2479 vac_1d_loc => vac_1d(id); id = id+1
2480 vac_1d_loc%var_name = 'dnorm'
2481 allocate(vac_1d_loc%tot_i_min(2),vac_1d_loc%tot_i_max(2))
2482 allocate(vac_1d_loc%loc_i_min(2),vac_1d_loc%loc_i_max(2))
2483 vac_1d_loc%tot_i_min = [1,1]
2484 vac_1d_loc%tot_i_max = shape(vac%dnorm)
2485 vac_1d_loc%loc_i_min = vac_1d_loc%tot_i_min
2486 vac_1d_loc%loc_i_max = vac_1d_loc%tot_i_max
2487 allocate(vac_1d_loc%p(size(vac%dnorm)))
2488 vac_1d_loc%p = reshape(vac%dnorm,[size(vac%dnorm)])
2489 end select
2490
2491 ! write
2492 ierr = print_hdf5_arrs(vac_1d(1:id-1),pb3d_name,trim(data_name),&
2493 &rich_lvl=rich_lvl,ind_print=.true.)
2494 chckerr('')
2495
2496 ! clean up
2497 call dealloc_var_1d(vac_1d)
2498 nullify(vac_1d_loc)
2499
2500 ! user output
2501 call lvl_ud(-1)
2502 end function print_output_vac
2503end module vac_ops
Calculate from and where and , according to weyens3d.
Deallocates 1D variable.
Definition HDF5_vars.f90:68
Wrapper function to broadcast a single variable using MPI.
Gather parallel variable in serial version on group master.
Finds the zero of a function using Householder iteration.
Definition num_ops.f90:35
Prints variables vars with names var_names in an HDF5 file with name c file_name and accompanying XDM...
Print 2-D output on a file.
Calculation of toroidal functions and .
Definition dtorh.f90:8
integer function, public dtorh1(z, m, nmax, pl, ql, newn, mode, ipre)
Calculates toroidal harmonics of a fixed order m and degrees up to nmax.
Definition dtorh.f90:75
Numerical utilities related to equilibrium variables.
Variables that have to do with equilibrium quantities and the grid used in the calculations:
Definition eq_vars.f90:27
real(dp), public vac_perm
either usual mu_0 (default) or normalized
Definition eq_vars.f90:48
Numerical utilities related to the grids and different coordinate systems.
integer function, public nufft(x, f, f_f, plot_name)
calculates the cosine and sine mode numbers of a function defined on a non-regular grid.
Variables pertaining to the different grids used.
Definition grid_vars.f90:4
real(dp), public min_par_x
min. of parallel coordinate [ ] in field-aligned grid
Definition grid_vars.f90:24
integer, public n_alpha
nr. of field-lines
Definition grid_vars.f90:23
integer, public n_r_eq
nr. of normal points in equilibrium grid
Definition grid_vars.f90:20
real(dp), public max_par_x
max. of parallel coordinate [ ] in field-aligned grid
Definition grid_vars.f90:25
integer, public n_r_in
nr. of normal points in input grid
Definition grid_vars.f90:19
real(dp), public max_alpha
max. of field-line label [ ] in field-aligned grid
Definition grid_vars.f90:27
real(dp), public min_alpha
min. of field-line label [ ] in field-aligned grid
Definition grid_vars.f90:26
Operations on HDF5 and XDMF variables.
Definition HDF5_ops.f90:27
integer function, public print_hdf5_arrs(vars, pb3d_name, head_name, rich_lvl, disp_info, ind_print, remove_previous_arrs)
Prints a series of arrays, in the form of an array of pointers, to an HDF5 file.
Variables pertaining to HDF5 and XDMF.
Definition HDF5_vars.f90:4
integer, parameter, public max_dim_var_1d
maximum dimension of var_1D
Definition HDF5_vars.f90:21
Variables that have to do with HELENA quantities.
integer, public ias
0 if top-bottom symmetric, 1 if not
integer, public nchi
nr. of poloidal points
real(dp), dimension(:,:), allocatable, public r_h
major radius (xout)
real(dp), dimension(:,:), allocatable, public z_h
height (yout)
real(dp), dimension(:), allocatable, public chi_h
poloidal angle
Numerical utilities related to giving output.
Definition messages.f90:4
subroutine, public lvl_ud(inc)
Increases/decreases lvl of output.
Definition messages.f90:254
subroutine, public writo(input_str, persistent, error, warning, alert)
Write output to file identified by output_i.
Definition messages.f90:275
Numerical utilities related to MPI.
Numerical operations.
Definition num_ops.f90:4
character(len=max_str_ln) function, public calc_zero_zhang(zero, fun, x_int_in)
Finds the zero of a function using Zhang's method, which is simpler than Brent's method.
Definition num_ops.f90:462
Numerical utilities.
recursive integer function, public lcm(u, v)
Returns common multiple using the Euclid's algorithm.
integer function, public c(ij, sym, n, lim_n)
Convert 2-D coordinates (i,j) to the storage convention used in matrices.
Numerical variables used by most other modules.
Definition num_vars.f90:4
integer, parameter, public dp
double precision
Definition num_vars.f90:46
real(dp), parameter, public pi
Definition num_vars.f90:83
real(dp), public max_zeta_plot
max. of zeta_plot
Definition num_vars.f90:162
integer, public n_procs
nr. of MPI processes
Definition num_vars.f90:69
complex(dp), parameter, public iu
complex unit
Definition num_vars.f90:85
integer, parameter, public max_str_ln
maximum length of strings
Definition num_vars.f90:50
integer, dimension(2), public n_vac_plot
nr. of points in R and Z in vacuum
Definition num_vars.f90:158
real(dp), public max_zvac_plot
max. of Z in which to plot vacuum
Definition num_vars.f90:168
integer, public n_zeta_plot
nr. of toroidal points in plot
Definition num_vars.f90:157
real(dp), public max_rvac_plot
max. of R in which to plot vacuum
Definition num_vars.f90:166
integer, dimension(:,:), allocatable, public eq_jobs_lims
data about eq jobs: [ , ] for all jobs
Definition num_vars.f90:77
real(dp), public min_zeta_plot
min. of zeta_plot
Definition num_vars.f90:161
integer, public eq_style
either 1 (VMEC) or 2 (HELENA)
Definition num_vars.f90:89
character(len=max_str_ln), public pb3d_name
name of PB3D output file
Definition num_vars.f90:139
real(dp), public min_rvac_plot
min. of R in which to plot vacuum
Definition num_vars.f90:165
integer, public rank
MPI rank.
Definition num_vars.f90:68
real(dp), public min_zvac_plot
min. of Z in which to plot vacuum
Definition num_vars.f90:167
integer, public rich_restart_lvl
starting Richardson level (0: none [default])
Definition num_vars.f90:173
logical, public jump_to_sol
jump to solution
Definition num_vars.f90:141
integer, public eq_job_nr
nr. of eq job
Definition num_vars.f90:79
logical, public use_pol_flux_f
whether poloidal flux is used in F coords.
Definition num_vars.f90:114
real(dp), public tol_zero
tolerance for zeros
Definition num_vars.f90:133
Operations concerning giving output, on the screen as well as in output files.
Definition output_ops.f90:5
subroutine, public plot_diff_hdf5(a, b, file_name, tot_dim, loc_offset, descr, output_message)
Takes two input vectors and plots these as well as the relative and absolute difference in a HDF5 fil...
subroutine, public draw_ex(var_names, draw_name, nplt, draw_dim, plot_on_screen, ex_plot_style, data_name, draw_ops, extra_ops, is_animated, ranges, delay, persistent)
Use external program to draw a plot.
Operations on PB3D output.
Definition PB3D_ops.f90:8
integer function, public reconstruct_pb3d_vac(vac, data_name, rich_lvl)
Reconstructs the vacuum variables from PB3D HDF5 output.
Variables concerning Richardson extrapolation.
Definition rich_vars.f90:4
integer, public rich_lvl
current level of Richardson extrapolation
Definition rich_vars.f90:19
integer, public n_par_x
nr. of parallel points in field-aligned grid
Definition rich_vars.f90:20
Variables pertaining to the solution quantities.
Definition sol_vars.f90:4
Operations on strings.
elemental character(len=max_str_ln) function, public i2str(k)
Convert an integer to string.
elemental character(len=max_str_ln) function, public r2str(k)
Convert a real (double) to string.
Operations and variables pertaining to the vacuum response.
Definition vac_ops.f90:23
logical, public debug_vac_pot_plot
plot debug information for vac_pot_plot()
Definition vac_ops.f90:48
integer function, public store_vac(grid, eq_1, eq_2, vac)
Stores calculation of the vacuum response by storing the necessary variables.
Definition vac_ops.f90:113
integer function, public calc_vac_res(mds, vac)
Calculates the vacuum response.
Definition vac_ops.f90:1417
logical, public debug_calc_vac_res
plot debug information for calc_vac_res()
Definition vac_ops.f90:47
integer function calc_gh_2(vac, n_r_in, lims_r_in, x_vec_in, g_in, h_in, ext_in)
Calculates matrices G and H in axisymmetric configurations.
Definition vac_ops.f90:983
integer function, public vac_pot_plot(grid_sol, vac, sol, x_id)
Calculate vacuum potential at some positions that are not resonant.
Definition vac_ops.f90:1857
integer function, public print_output_vac(vac, data_name, rich_lvl)
Print vacuum quantities of a certain order to an output file.
Definition vac_ops.f90:2347
logical, public debug_calc_gh
plot debug information for calc_GH()
Definition vac_ops.f90:46
integer function calc_gh_1(vac, n_r_in, lims_r_in, x_vec_in, g_in, h_in, ext_in)
Calculates matrices G and H in 3-D configuration.
Definition vac_ops.f90:779
integer function solve_phi_bem(vac, r, phi, n_rphi, n_loc_rphi, lims_c_rphi, desc_rphi)
Calculates potential Phi on boundary of BEM in terms of some right-hand side .
Definition vac_ops.f90:2163
Numerical utilities related to the vacuum response.
subroutine, public calc_gh_int_2(g, h, t_s, t_in, x_s, x_in, norm_s, norm_in, aij, ql, tol, b_coeff, n_tor)
Calculate G_ij and H_ij on an interval for axisymmetric configurations.
integer function, public vec_dis2loc(ctxt, vec_dis, lims, vec_loc, proc)
Gathers a distributed vector on a single process.
subroutine, public calc_gh_int_1(g, h, x_s, x_in, norm_s, h_fac_in, step_size, tol)
Calculate G_ij and H_ij on an interval for field line aligned configurations.
integer function, public interlaced_vac_copy(vac_old, vac)
Copies vacuum variables of a previous level into the appropriate, interlaced place of a next level.
integer function, public mat_dis2loc(ctxt, mat_dis, lims_r, lims_c, mat_loc, proc)
Gathers a distributed vector on a single process.
Variables pertaining to the vacuum quantities.
Definition vac_vars.f90:4
subroutine, public set_loc_lims(n_loc, bs, ind_p, n_p, lims)
Calculates the limits in local index.
Definition vac_vars.f90:261
integer function, public copy_vac(vac, vac_copy)
Copy a vacuum type.
Definition vac_vars.f90:339
logical function, public in_context(ctxt)
Indicates whether current process is in the context.
Definition vac_vars.f90:378
Variables pertaining to the perturbation quantities.
Definition X_vars.f90:4
integer, public n_mod_x
size of m_X (pol. flux) or n_X (tor. flux)
Definition X_vars.f90:129
integer, public prim_x
n_X (pol. flux) or m_X (tor. flux)
Definition X_vars.f90:126
flux equilibrium type
Definition eq_vars.f90:63
metric equilibrium type
Definition eq_vars.f90:114
Type for grids.
Definition grid_vars.f90:59
1D equivalent of multidimensional variables, used for internal HDF5 storage.
Definition HDF5_vars.f90:48
solution type
Definition sol_vars.f90:30
vacuum type
Definition vac_vars.f90:46
mode number type
Definition X_vars.f90:36
tensorial perturbation type
Definition X_vars.f90:81
logical function on_field_line(cd, kd, n_ang)
Definition vac_ops.f90:965