V3FIT
vmec_utils.f
1  MODULE vmec_utils
2  USE stel_kinds
3  USE stel_constants, ONLY: twopi, one, zero
4  USE cyl_flux, ONLY: flx2cyl, cyl2flx
5  IMPLICIT NONE
6 
7 !-------------------------------------------------------------------------------!
8 ! THIS MODULE CONTAINS USEFUL UTILITIES FOR PROCESSING VMEC
9 ! DATA. MOST FUNCTIONS ARE OVERLOADED TO BE ABLE TO USE EITHER
10 ! INTERNALLY DATA (LOCAL FROM WITHIN VMEC) OR DATA FROM WOUT FILE
11 !
12 ! CONTAINS:
13 ! GetBcyl_WOUT - call as GetBcyl
14 ! GetBcyl_VMEC - call as GetBcyl
15 ! GetJcyl_WOUT - call as GetJcyl
16 ! MSE_pitch_WOUT - call as MSE_pitch
17 ! MSE_pitch_VMEC - call as MSE_pitch
18 !
19 ! 2011-09-08 JDH. rzl* declaration 2*ntmax) -> 3*ntmax), consistency
20 ! with rzl* usage in VMEC, even though lambdas aren't used here.
21 !
22 ! 2011-09-06 JDH
23 ! Split subroutines flx2cyl and cyl2flx into the module cyl_flux
24 ! - to clarify interfaces and dependencies
25 !-------------------------------------------------------------------------------
26 !
27 
28 !
29 ! OVERLOADED FUNCTIONS
30 !
31  INTERFACE getbcyl
32  MODULE PROCEDURE getbcyl_wout, getbcyl_vmec
33  END INTERFACE
34 
35  INTERFACE getjcyl
36  MODULE PROCEDURE getjcyl_wout
37  END INTERFACE
38 
39  INTERFACE mse_pitch
40  MODULE PROCEDURE mse_pitch_wout
41  MODULE PROCEDURE mse_pitch_vmec
42  END INTERFACE
43 
44  CONTAINS
45 
46 !-------------------------------------------------------------------------------
47 !-------------------------------------------------------------------------------
48  SUBROUTINE getbcyl_wout(R1, Phi, Z1, Br, Bphi, Bz,
49  1 sflx, uflx, info)
50  USE read_wout_mod, phi_wout=>phi, ns_w=>ns, ntor_w=>ntor,
51  1 mpol_w=>mpol, ntmax_w=>ntmax, lthreed_w=>lthreed,
52  2 lasym_w=>lasym
53  IMPLICIT NONE
54 C-----------------------------------------------
55 C D u m m y A r g u m e n t s
56 C-----------------------------------------------
57  INTEGER, OPTIONAL, INTENT(out) :: info
58  REAL(rprec), INTENT(in) :: R1, Z1, Phi
59  REAL(rprec), INTENT(out) :: Br, Bphi, Bz
60  REAL(rprec), INTENT(out), OPTIONAL :: sflx, uflx
61 C-----------------------------------------------
62 C L o c a l V a r i a b l e s
63 C-----------------------------------------------
64  REAL(rprec), PARAMETER :: fmin_acceptable = 1.e-6_dp
65  INTEGER :: nfe, info_loc
66  REAL(rprec) :: r_cyl(3), c_flx(3), fmin
67  REAL(rprec) :: Ru1, Zu1, Rv1, Zv1
68  REAL(rprec) :: bsupu1, bsupv1
69 C-----------------------------------------------
70  IF (.not.lwout_opened) THEN
71  WRITE(6, '(2a,/,a)')
72  1 ' This form of GetBcyl can only be called if WOUT has been',
73  2 ' previously opened!',' Try GetBcyl_VMEC form instead.'
74  RETURN
75  END IF
76 
77  CALL loadrzl
78 
79 ! Computes cylindrical components of the magnetic field, Br, Bphi, Bz,
80 ! at the specified cylindrical coordinate point (R1, Phi, Z1), where
81 ! Phi is the true geometric toroidal angle (NOT N*Phi)
82 !
83 ! INPUT
84 ! R1, Phi, Z1 : cylindrical coordinates at which evaluation is to take place
85 !
86 ! OUTPUT
87 ! Br, Bphi, Bz : computed cylindrical components of B at input point
88 ! sflx, uflx : computed flux and theta angle at the cylindrical point
89 !
90 ! 1. Convert to point in flux-coordinates: cflux = (s, u, v=N*phi)
91 ! and evaluate Ru, Zu, Rv, Zv at that point
92 !
93  r_cyl(1) = r1; r_cyl(2) = nfp*phi; r_cyl(3) = z1
94  c_flx(1) = 0; c_flx(2) = 0; c_flx(3) = r_cyl(2)
95  IF (PRESENT(sflx)) c_flx(1) = sflx
96  IF (PRESENT(uflx)) c_flx(2) = uflx
97  CALL cyl2flx(rzl_local, r_cyl, c_flx, ns_w, ntor_w, mpol_w,
98  1 ntmax_w, lthreed_w, lasym_w, info_loc, nfe, fmin,
99  2 ru=ru1, zu=zu1, rv=rv1, zv=zv1)
100  rv1 = nfp*rv1; zv1 = nfp*zv1
101 
102  IF (info_loc.eq.-1 .and. (fmin .le. fmin_acceptable)) info_loc = 0
103 
104  IF (PRESENT(info)) info = info_loc
105  IF (info_loc .ne. 0) RETURN
106 
107  IF (PRESENT(sflx)) sflx = c_flx(1)
108  IF (PRESENT(uflx)) uflx = c_flx(2)
109 
110  IF (c_flx(1) .gt. one) THEN
111  br = 0; bphi = 0; bz = 0
112  RETURN
113  END IF
114 !
115 ! 2. Evaluate Bsupu, Bsupv at this point
116 !
117  CALL tosuvspace (c_flx(1), c_flx(2), c_flx(3),
118  1 bsupu=bsupu1, bsupv=bsupv1)
119 !
120 ! 3. Form Br, Bphi, Bz
121 !
122  br = ru1*bsupu1 + rv1*bsupv1
123  bphi = r1 *bsupv1
124  bz = zu1*bsupu1 + zv1*bsupv1
125 
126  END SUBROUTINE getbcyl_wout
127 
128 !-------------------------------------------------------------------------------
129 !-------------------------------------------------------------------------------
130  SUBROUTINE getbcyl_vmec(R1, Phi, Z1, Br, Bphi, Bz, sflx, uflx,
131  1 bsupu, bsupv, rzl_array, ns_in, ntor_in, mpol_in, ntmax_in,
132  2 nzeta, ntheta3, nper, mscale, nscale, lthreed_in, lasym_in,
133  3 info)
134  IMPLICIT NONE
135 C-----------------------------------------------
136 C D u m m y A r g u m e n t s
137 C-----------------------------------------------
138  INTEGER, INTENT(in) :: ns_in, ntor_in, mpol_in, ntmax_in,
139  1 nzeta, ntheta3, nper
140  INTEGER, OPTIONAL, INTENT(out) :: info
141  LOGICAL, INTENT(in) :: lthreed_in, lasym_in
142  REAL(rprec), INTENT(in) :: R1, Z1, Phi
143  REAL(rprec), INTENT(in) ::
144  1 rzl_array(ns_in,0:ntor_in,0:mpol_in-1,3*ntmax_in),
145  2 mscale(0:mpol_in-1), nscale(0:ntor_in)
146  REAL(rprec), DIMENSION(ns_in,nzeta,ntheta3), INTENT(in)
147  1 :: bsupu, bsupv
148  REAL(rprec), INTENT(out) :: Br, Bphi, Bz, sflx, uflx
149 C-----------------------------------------------
150 C L o c a l V a r i a b l e s
151 C-----------------------------------------------
152  REAL(rprec), PARAMETER :: c1p5 = 1.5_dp
153  REAL(rprec), PARAMETER :: fmin_acceptable = 1.e-6_dp
154  INTEGER :: nfe, info_loc, jslo, jshi, julo, juhi,
155  1 kvlo, kvhi, ntheta1
156  REAL(rprec) :: r_cyl(3), c_flx(3), vflx, vflx_norm,
157  1 uflx_norm, fmin
158  REAL(rprec) :: wgt_s, wgt_u, wgt_v, hs1, hu1, hv1
159  REAL(rprec) :: Ru1, Zu1, Rv1, Zv1
160  REAL(rprec) :: bsupu1, bsupv1, bsupu2, bsupv2
161 C-----------------------------------------------
162 
163 ! Computes cylindrical components of the magnetic field, Br, Bphi, Bz,
164 ! at the specified cylindrical coordinate point (R1, Phi, Z1), where
165 ! Phi is the true geometric toroidal angle (NOT NPER*Phi)
166 ! Also, sflx, uflx are the computed flux and theta angle at the point
167 !
168 ! This routine is callable from within the VMEC code (in contrast to
169 ! the GetBcyl routine, which requires WOUT output file).
170 !
171 ! 1. Convert to point in flux-coordinates: cflux = (s, u, v=N*phi)
172 ! and evaluate Ru, Zu, Rv, Zv at that point
173 !
174  r_cyl(1) = r1; r_cyl(2) = nper*phi; r_cyl(3) = z1
175  c_flx(1) = 0; c_flx(2) = 0; c_flx(3) = r_cyl(2)
176  CALL cyl2flx(rzl_array, r_cyl, c_flx, ns_in, ntor_in, mpol_in,
177  1 ntmax_in, lthreed_in, lasym_in, info_loc, nfe, fmin,
178  2 mscale, nscale, ru=ru1, zu=zu1, rv=rv1, zv=zv1)
179  rv1 = nper*rv1; zv1 = nper*zv1
180 
181  IF (info_loc.eq.-1 .and. (fmin .le. fmin_acceptable)) info_loc = 0
182 
183  IF (PRESENT(info)) info = info_loc
184  IF (info_loc .ne. 0) RETURN
185 
186  sflx = c_flx(1); uflx = c_flx(2); vflx = c_flx(3)
187  IF (c_flx(1) .gt. one) THEN
188  br = 0; bphi = 0; bz = 0
189  RETURN
190  END IF
191 
192 !
193 ! 2. Evaluate Bsupu, Bsupv at this flux coordinate point by 2D interpolation in s, u space
194 ! This is not quite as accurate as the 1D (s) interpolation based on the Fourier coefficients
195 ! of bsupu, bsupv...done in GetBcyl...
196 ! Formula 25.2.66 (Bivariate, 4pt Formula) in Abramowitz and Stegun
197 !
198  hs1 = one/(ns_in - 1)
199  jslo = int(c1p5 + sflx/hs1)
200  jshi = jslo+1
201  wgt_s = (sflx - hs1*(jslo-c1p5))/hs1
202  IF (jslo .eq. ns_in) THEN
203 ! USE Xhalf(ns+1) = 2*Xhalf(ns) - Xhalf(ns-1) FOR "GHOST" POINT VALUE hs/2 OUTSIDE EDGE
204 ! THEN, X = wlo*Xhalf(ns) + whi*Xhalf(ns+1) == Xhalf(ns) + whi*(Xhalf(ns) - Xhalf(ns-1))
205 ! WHERE wlo = 1 - wgt_s, whi = wgt_s
206  jshi = jslo-1
207  wgt_s = 1+wgt_s
208  ELSE IF (jslo .eq. 1) THEN
209  jslo = 2
210  END IF
211 
212  IF (lasym_in) THEN
213  ntheta1 = ntheta3
214  ELSE
215  ntheta1 = 2*(ntheta3 - 1)
216  END IF
217 
218  uflx = mod(uflx, twopi)
219  DO WHILE (uflx .lt. zero)
220  uflx = uflx+twopi
221  END DO
222 
223  hu1 = one/ntheta1
224  uflx_norm = uflx/twopi
225  julo = int(1 + uflx_norm/hu1)
226  IF (julo .gt. ntheta3) THEN
227  IF (abs(uflx_norm - 1) .lt. 1.e-2*hu1) THEN
228  julo = 1
229  uflx_norm = 0
230  ELSE IF (abs(uflx_norm - .5_dp) .lt. 1.e-2*hu1) THEN
231  julo = ntheta3
232  uflx_norm = .5_dp
233  ELSE
234  print *, 'julo=', julo,' > ntheta3=', ntheta3,
235  1 ' uflx_norm=', uflx_norm, ' in GetBcyl!'
236  IF (PRESENT(info)) info = -10
237  RETURN
238  END IF
239  END IF
240  juhi = julo + 1
241  IF (julo .eq. ntheta3) juhi = 1 !Periodic point at u = 0
242  wgt_u = (uflx_norm - hu1*(julo-1))/hu1
243 
244 
245  DO WHILE (vflx .lt. zero)
246  vflx = vflx+twopi
247  END DO
248  vflx = mod(vflx, twopi)
249  hv1 = one/nzeta
250  vflx_norm = vflx/twopi
251  kvlo = int(1 + vflx_norm/hv1)
252  kvhi = kvlo+1
253  IF (kvlo .eq. nzeta) kvhi = 1
254  wgt_v = (vflx_norm - hv1*(kvlo-1))/hv1
255 
256 !
257 ! BIVARIATE INTERPOLATION IN S, U AT 2 kv PLANES
258 !
259  bsupu1 = (1-wgt_s)*((1-wgt_u)*bsupu(jslo,kvlo,julo)
260  2 + wgt_u *bsupu(jslo,kvlo,juhi))
261  1 + wgt_s* ((1-wgt_u)*bsupu(jshi,kvlo,julo)
262  3 + wgt_u *bsupu(jshi,kvlo,juhi))
263 
264  bsupv1 = (1-wgt_s)*((1-wgt_u)*bsupv(jslo,kvlo,julo)
265  2 + wgt_u *bsupv(jslo,kvlo,juhi))
266  1 + wgt_s* ((1-wgt_u)*bsupv(jshi,kvlo,julo)
267  3 + wgt_u *bsupv(jshi,kvlo,juhi))
268 
269  bsupu2 = (1-wgt_s)*((1-wgt_u)*bsupu(jslo,kvhi,julo)
270  2 + wgt_u *bsupu(jslo,kvhi,juhi))
271  1 + wgt_s* ((1-wgt_u)*bsupu(jshi,kvhi,julo)
272  3 + wgt_u *bsupu(jshi,kvhi,juhi))
273 
274  bsupv2 = (1-wgt_s)*((1-wgt_u)*bsupv(jslo,kvhi,julo)
275  2 + wgt_u *bsupv(jslo,kvhi,juhi))
276  1 + wgt_s* ((1-wgt_u)*bsupv(jshi,kvhi,julo)
277  3 + wgt_u *bsupv(jshi,kvhi,juhi))
278 
279 !
280 ! LINEAR INTERPOLATION IN V
281 !
282  bsupu1 = (1-wgt_v)*bsupu1 + wgt_v*bsupu2
283  bsupv1 = (1-wgt_v)*bsupv1 + wgt_v*bsupv2
284 
285 !
286 ! 3. Form Br, Bphi, Bz
287 !
288  br = ru1*bsupu1 + rv1*bsupv1
289  bphi = r1 *bsupv1
290  bz = zu1*bsupu1 + zv1*bsupv1
291 
292  END SUBROUTINE getbcyl_vmec
293 
294 !-------------------------------------------------------------------------------
295 !-------------------------------------------------------------------------------
296  SUBROUTINE getjcyl_wout(R1, Phi, Z1, JR, JPHI, JZ,
297  1 sflx, uflx, info)
298  USE read_wout_mod, phi_wout1=>phi, ns_w1=>ns, ntor_w1=>ntor,
299  1 mpol_w1=>mpol, ntmax_w1=>ntmax, lthreed_w1=>lthreed,
300  2 lasym_w1=>lasym
301  IMPLICIT NONE
302 C-----------------------------------------------
303 C D u m m y A r g u m e n t s
304 C-----------------------------------------------
305  INTEGER, OPTIONAL, INTENT(out) :: info
306  REAL(rprec), INTENT(in) :: R1, Z1, Phi
307  REAL(rprec), INTENT(out) :: JR, JPHI, JZ
308  REAL(rprec), INTENT(out), OPTIONAL :: sflx, uflx
309 C-----------------------------------------------
310 C L o c a l V a r i a b l e s
311 C-----------------------------------------------
312  REAL(rprec), PARAMETER :: fmin_acceptable = 1.e-6_dp
313  INTEGER :: nfe, info_loc
314  REAL(rprec) :: r_cyl(3), c_flx(3), fmin
315  REAL(rprec) :: Ru1, Zu1, Rv1, Zv1
316  REAL(rprec) :: jsupu1, jsupv1, gsqrt1
317 C-----------------------------------------------
318  IF (.not.lwout_opened) THEN
319  WRITE(6, '(2a,/,a)')
320  1 ' This form of GetBcyl can only be called if WOUT has been',
321  2 ' previously opened!'
322  RETURN
323  END IF
324 
325  CALL loadrzl
326 
327 ! Computes cylindrical components of the current, Jr, Jphi, Jz,
328 ! at the specified cylindrical coordinate point (R1, Phi, Z1), where
329 ! Phi is the true geometric toroidal angle (NOT N*Phi)
330 !
331 ! INPUT
332 ! R1, Phi, Z1 : cylindrical coordinates at which evaluation is to take place
333 !
334 ! OUTPUT
335 ! Br, Bphi, Bz : computed cylindrical components of B at input point
336 ! sflx, uflx : computed flux and theta angle at the cylindrical point
337 !
338 ! 1. Convert to point in flux-coordinates: cflux = (s, u, v=N*phi)
339 ! and evaluate Ru, Zu, Rv, Zv at that point
340 !
341  r_cyl(1) = r1; r_cyl(2) = nfp*phi; r_cyl(3) = z1
342  c_flx(1) = 0; c_flx(2) = 0; c_flx(3) = r_cyl(2)
343  CALL cyl2flx(rzl_local, r_cyl, c_flx, ns_w1, ntor_w1, mpol_w1,
344  1 ntmax_w1, lthreed_w1, lasym_w1, info_loc, nfe, fmin,
345  2 ru=ru1, zu=zu1, rv=rv1, zv=zv1)
346  rv1 = nfp*rv1; zv1 = nfp*zv1
347 
348  IF (info_loc.eq.-1 .and. (fmin .le. fmin_acceptable)) info_loc = 0
349 
350  IF (PRESENT(info)) info = info_loc
351  IF (info_loc .ne. 0) RETURN
352 
353  IF (PRESENT(sflx)) sflx = c_flx(1)
354  IF (PRESENT(uflx)) uflx = c_flx(2)
355 
356  IF (c_flx(1) .gt. one) THEN
357  jr = 0; jphi = 0; jz = 0
358  RETURN
359  END IF
360 
361 ! 3. Evaluate d(Bsubs)/du and d(Bsubs)/dv, d(Bsubu)/ds, d(Bsubv)/ds at this point
362  CALL tosuvspace (c_flx(1), c_flx(2), c_flx(3),
363  1 gsqrt=gsqrt1, jsupu=jsupu1, jsupv=jsupv1)
364 
365 ! WRITE (36, '(1p4e12.4)') R1*jsupv1, dbsubuds1, dbsubsdu1, gsqrt1
366 !
367 ! 4. Return Jr, Jphi, Jz
368 !
369  jr = ru1*jsupu1 + rv1*jsupv1
370  jphi = r1 *jsupv1
371  jz = zu1*jsupu1 + zv1*jsupv1
372 
373  END SUBROUTINE getjcyl_wout
374 
375 !-------------------------------------------------------------------------------
376 !-------------------------------------------------------------------------------
377  FUNCTION mse_pitch_wout(r1, phi1, z1, acoef, efield, info)
378  IMPLICIT NONE
379 C-----------------------------------------------
380 C D u m m y A r g u m e n t s
381 C-----------------------------------------------
382  INTEGER, INTENT(out) :: info
383  REAL(rprec), INTENT(in) :: r1, phi1, z1, acoef(6)
384  REAL(rprec), INTENT(in), OPTIONAL :: efield(2)
385 C-----------------------------------------------
386 C L o c a l V a r i a b l e s
387 C-----------------------------------------------
388  REAL(rprec) :: MSE_pitch_WOUT, Er, Ez, Br, Bphi, Bz
389 C-----------------------------------------------
390 !
391 ! Computes the Motional Stark Effect tan(pitch angle) == mse_pitch
392 ! at a given cylindrical point (R, phi, Z) inside the plasma
393 !
394 ! INPUT
395 ! Acoef : array of constants defined by the viewing geometry and beam velocity
396 ! r1, f1, z1: cylindrical coordinate of measurement point
397 ! Efield: (optional) electric field components (Er, Ez) in rest frame
398 !
399 ! OUTPUT
400 ! MSE_pitch pitch angle at the input point
401 ! info info = 0, calculation is valid
402 !
403  IF (PRESENT(efield)) THEN
404  er = efield(1); ez = efield(2)
405  ELSE
406  er = 0; ez = 0
407  END IF
408  info = -1
409 !
410 ! Compute cylindrical components of B-field at given point R1, phi=f1, Z1
411 !
412  CALL getbcyl_wout(r1, phi1, z1, br, bphi, bz, info=info)
413 
414  mse_pitch_wout = (acoef(1)*bz + acoef(5)*er)/
415  1 (acoef(2)*bphi + acoef(3)*br
416  2 + acoef(4)*bz + acoef(6)*ez)
417 
418  END FUNCTION mse_pitch_wout
419 
420 !-------------------------------------------------------------------------------
421 !-------------------------------------------------------------------------------
422  FUNCTION mse_pitch_vmec(r1, phi1, z1, acoef, efield, sflx, uflx,
423  1 bsupu, bsupv, rzl_array, ns_in, ntor_in, mpol_in, ntmax_in,
424  2 nzeta, ntheta3, nper, mscale, nscale, lthreed_in, lasym_in,
425  3 info)
426  IMPLICIT NONE
427 C-----------------------------------------------
428 C D u m m y A r g u m e n t s
429 C-----------------------------------------------
430  REAL(rprec), INTENT(in) :: r1, phi1, z1, acoef(6), efield(2)
431  INTEGER, INTENT(in) :: ns_in, ntor_in, mpol_in, ntmax_in,
432  1 nzeta, ntheta3, nper
433  LOGICAL, INTENT(in) :: lthreed_in, lasym_in
434  REAL(rprec), INTENT(in) ::
435  1 rzl_array(ns_in,0:ntor_in,0:mpol_in-1,3*ntmax_in),
436  2 mscale(0:mpol_in-1), nscale(0:ntor_in)
437  REAL(rprec), DIMENSION(ns_in,nzeta,ntheta3), INTENT(in)
438  1 :: bsupu, bsupv
439  REAL(rprec), INTENT(out) :: sflx, uflx
440  INTEGER, INTENT(out) :: info
441 C-----------------------------------------------
442 C L o c a l V a r i a b l e s
443 C-----------------------------------------------
444  REAL(rprec) :: MSE_pitch_VMEC, Er, Ez, Br, Bphi, Bz
445 C-----------------------------------------------
446 !
447 ! Computes the Motional Stark Effect tan(pitch angle) == mse_pitch
448 ! at a given cylindrical point (R, phi, Z) inside the plasma
449 !
450 ! INPUT
451 ! Acoef : array of constants defined by the viewing geometry and beam velocity
452 ! r1, f1, z1: cylindrical coordinate of measurement point
453 ! Efield: (optional) electric field components (Er, Ez) in rest frame
454 !
455 ! OUTPUT
456 ! MSE_pitch pitch angle at the input point
457 ! info info = 0, calculation is valid
458 !
459  er = efield(1); ez = efield(2)
460  info = -1
461 !
462 ! Compute cylindrical components of B-field at given point R1, phi=f1, Z1
463 !
464  CALL getbcyl_vmec(r1, phi1, z1, br, bphi, bz, sflx, uflx,
465  1 bsupu, bsupv, rzl_array, ns_in, ntor_in, mpol_in, ntmax_in,
466  2 nzeta, ntheta3, nper, mscale, nscale, lthreed_in, lasym_in,
467  3 info)
468 
469  mse_pitch_vmec = (acoef(1)*bz + acoef(5)*er)/
470  1 (acoef(2)*bphi + acoef(3)*br
471  2 + acoef(4)*bz + acoef(6)*ez)
472 
473  END FUNCTION mse_pitch_vmec
474 
475  END MODULE vmec_utils
vmec_utils::getjcyl
Definition: vmec_utils.f:35
vmec_utils::mse_pitch
Definition: vmec_utils.f:39
vmec_utils::getbcyl
Definition: vmec_utils.f:31