VMEC 8.52
3D Equilibrium solver with nested flux surfaces.
Loading...
Searching...
No Matches
mercier.f90
Go to the documentation of this file.
1
3
22SUBROUTINE mercier(gsqrt, bsq, bdotj, iotas, wint, &
23 r1, rt, rz, zt, zz, &
24 bsubu, vp, phips, pres, ns, nznt)
26 USE vmercier
28 USE vparams, ONLY: one, zero, twopi, nmercier0
29 use stel_kinds, only: dp
30
31 use vmec_dim, only: ntheta3
32 use dbgout
33
34 IMPLICIT NONE
35
36 REAL(rprec), DIMENSION(ns,nznt), INTENT(in) :: gsqrt
37 REAL(rprec), DIMENSION(ns,nznt), INTENT(in) :: bsq
38 REAL(rprec), DIMENSION(ns,nznt), INTENT(inout) :: bdotj
39 REAL(rprec), DIMENSION(ns), INTENT(in) :: iotas
40 REAL(rprec), DIMENSION(ns*nznt), INTENT(in) :: wint
41 REAL(rprec), DIMENSION(ns,nznt,0:1), INTENT(in) :: r1
42 REAL(rprec), DIMENSION(ns,nznt,0:1), INTENT(in) :: rt
43 REAL(rprec), DIMENSION(ns,nznt,0:1), INTENT(in) :: rz
44 REAL(rprec), DIMENSION(ns,nznt,0:1), INTENT(in) :: zt
45 REAL(rprec), DIMENSION(ns,nznt,0:1), INTENT(in) :: zz
46 REAL(rprec), DIMENSION(ns*nznt), INTENT(in) :: bsubu
47 REAL(rprec), DIMENSION(ns), INTENT(in) :: vp
48 REAL(rprec), DIMENSION(ns), INTENT(in) :: phips
49 REAL(rprec), DIMENSION(ns), INTENT(in) :: pres
50 INTEGER, INTENT(in) :: ns
51 INTEGER, INTENT(in) :: nznt
52
53 REAL(rprec), PARAMETER :: p5 = 0.5_dp, two = 2.0_dp
54
55 INTEGER :: ns1, i, imercier0, nmerc = nmercier0, nrzt
56 REAL(rprec) :: sign_jac, hs, sqs, denom
57 REAL(rprec), DIMENSION(:,:), ALLOCATABLE :: gpp, gsqrt_full, b2
58 REAL(rprec), DIMENSION(nznt) :: gtt, rtf, ztf, &
59 rzf, zzf, r1f, jdotb, ob2, b2i
60 REAL(rprec), DIMENSION(ns) :: vp_real, phip_real, &
61 shear, vpp, presp, torcur, ip, sj, tpp, tjb, tbb, tjj
62 CHARACTER(LEN=120) mercier_file
63
64 nrzt = ns*nznt
65 mercier_file = 'mercier.'//trim(input_extension)
66 CALL safe_open (nmerc, imercier0, mercier_file, 'replace', 'formatted')
67 IF (imercier0 .ne. 0) RETURN
68
69 ALLOCATE (gpp(ns,nznt), gsqrt_full(ns,nznt), b2(ns,nznt), stat=i)
70 IF (i .ne. 0) stop 'allocation error in Mercier'
71
72 ! SCALE VP, PHIPS TO REAL UNITS (VOLUME, TOROIDAL FLUX DERIVATIVES)
73 ! AND PUT GSQRT IN ABS UNITS (SIGNGS MAY BE NEGATIVE)
74 ! NOTE: VP has (coming into this routine) the sign of the jacobian multiplied out
75 ! i.e., vp = signgs*<gsqrt>
76 ! THE SHEAR TERM MUST BE MULTIPLIED BY THE SIGN OF THE JACOBIAN
77 ! (OR A BETTER SOLUTION IS TO RETAIN THE JACOBIAN SIGN IN ALL TERMS, INCLUDING
78 ! VP, THAT DEPEND EXPLICITLY ON THE JACOBIAN. WE CHOOSE THIS LATTER METHOD...)
79 !
80 ! COMING INTO THIS ROUTINE, THE JACOBIAN(gsqrt) = 1./(grad-s . grad-theta X grad-zeta)
81 ! WE CONVERT THIS FROM grad-s to grad-phi DEPENDENCE BY DIVIDING gsqrt by PHIP_real
82 !
83 ! NOTE: WE ARE USING 0 < s < 1 AS THE FLUX VARIABLE, BEING CAREFUL
84 ! TO KEEP d(phi)/ds == PHIP_real FACTORS WHERE REQUIRED
85 ! THE V'' TERM IS d2V/d(PHI)**2, PHI IS REAL TOROIDAL FLUX
86 !
87 ! SHEAR = d(iota)/d(phi) : FULL MESH
88 ! VPP = d(vp)/d(phi) : FULL MESH
89 ! PRESP = d(pres)/d(phi) : FULL MESH (PRES IS REAL PRES*mu0)
90 ! IP = d(Itor)/d(phi) : FULL MESH
91 !
92 ! ON ENTRY, BDOTJ = Jacobian * J*B ON THE FULL RADIAL GRID
93 ! BSQ = 0.5*|B**2| + p IS ON THE HALF RADIAL GRID
94
95 ns1 = ns - 1
96 IF (ns1 .le. 0) RETURN
97 hs = one/ns1
98
99 ! TODO: why not take signgs here ???
100 sign_jac = zero
101 IF (gsqrt(ns,1) .ne. zero) then
102 sign_jac = abs(gsqrt(ns,1))/gsqrt(ns,1)
103 end if
104 IF (sign_jac .eq. zero) RETURN
105
106 ! NOTE: phip_real should be > 0 to get the correct physical sign of REAL-space gradients
107 ! For example, grad-p, grad-Ip, etc. However, with phip_real defined this way,
108 ! Mercier will be correct
109 phip_real = twopi * phips * sign_jac
110
111 ! dV/d(PHI) on half mesh
112 vp_real(2:ns) = sign_jac*(twopi*twopi)*vp(2:ns)/phip_real(2:ns)
113
114 ! COMPUTE INTEGRATED TOROIDAL CURRENT
115 DO i = 2,ns
116 torcur(i)=sign_jac*twopi*sum(bsubu(i:nrzt:ns)*wint(i:nrzt:ns))
117 END DO
118
119 ! COMPUTE SURFACE AVERAGE VARIABLES ON FULL RADIAL MESH
120 DO i = 2,ns1
121 phip_real(i) = p5*(phip_real(i+1) + phip_real(i))
122 denom = one/(hs*phip_real(i))
123
124 shear(i) = (iotas(i+1) - iotas(i))*denom ! d(iota)/d(PHI)
125 vpp(i) = (vp_real(i+1) - vp_real(i))*denom ! d(VP)/d(PHI)
126 presp(i) = (pres(i+1) - pres(i))*denom ! d(p)/d(PHI)
127 ip(i) = (torcur(i+1) - torcur(i))*denom ! d(Itor)/d(PHI)
128 END DO
129
130 ! COMPUTE GPP == |grad-phi|**2 = PHIP**2*|grad-s|**2 (on full mesh)
131 ! Gsqrt_FULL = JACOBIAN/PHIP == jacobian based on flux (on full mesh)
132 DO i = 2, ns1
133 gsqrt_full(i,:) = p5*(gsqrt(i,:) + gsqrt(i+1,:))
134 bdotj(i,:) = bdotj(i,:)/gsqrt_full(i,:)
135 gsqrt_full(i,:) = gsqrt_full(i,:)/phip_real(i)
136
137 sj(i) = hs*(i-1.0_dp)
138 sqs = sqrt(sj(i)) ! sqrt(s) on full grid
139
140 rtf(:) = rt(i,:,0) + sqs*rt(i,:,1) ! dR/dTheta
141 ztf(:) = zt(i,:,0) + sqs*zt(i,:,1) ! dZ/dTheta
142 rzf(:) = rz(i,:,0) + sqs*rz(i,:,1) ! dR/dZeta
143 zzf(:) = zz(i,:,0) + sqs*zz(i,:,1) ! dZ/dZeta
144 r1f(:) = r1(i,:,0) + sqs*r1(i,:,1) ! R
145
146 gtt(:) = rtf(:)*rtf(:) + ztf(:)*ztf(:) ! g_uu
147
148 gpp(i,:) = gsqrt_full(i,:)**2.0_dp &
149 / ( gtt(:)*r1f(:)**2.0_dp &
150 + (rtf(:)*zzf(:) - rzf(:)*ztf(:))**2.0_dp) ! 1/gpp
151 END DO
152
153 ! COMPUTE SURFACE AVERAGES OVER dS/|grad-PHI|**3 => |Jac| du dv / |grad-PHI|**2
154 ! WHERE Jac = gsqrt/phip_real
155 DO i = 2,ns
156 b2(i,:) = two*(bsq(i,:) - pres(i))
157 END DO
158
159 DO i = 2,ns1
160 b2i(:) = p5*(b2(i+1,:) + b2(i,:))
161
162 ob2(:) = gsqrt_full(i,:)/b2i(:)
163 tpp(i) = sum(ob2(:)*wint(i:nrzt:ns)) ! <1/B**2>
164
165 ob2(:) = b2i(:) * gsqrt_full(i,:) * gpp(i,:)
166 tbb(i) = sum(ob2(:)*wint(i:nrzt:ns)) ! <b*b/|grad-phi|**3>
167
168 jdotb(:) = bdotj(i,:) * gpp(i,:) * gsqrt_full(i,:)
169 tjb(i) = sum(jdotb(:)*wint(i:nrzt:ns)) ! <j*b/|grad-phi|**3>
170
171 jdotb(:) = jdotb(:) * bdotj(i,:) / b2i(:)
172 tjj(i) = sum(jdotb(:)*wint(i:nrzt:ns)) ! <(j*b)2/b**2*|grad-phi|**3>
173 END DO
174
175! moved to end of routine for dbgout
176! DEALLOCATE (gpp, gsqrt_full, b2, stat=i)
177
178 ! REFERENCE: BAUER, BETANCOURT, GARABEDIAN, MHD Equilibrium and Stability of Stellarators
179 ! We break up the Omega-subs into a positive shear term (Dshear) and a net current term, Dcurr
180 ! Omega_subw == Dwell
181 ! Omega-subd == Dgeod (geodesic curvature, Pfirsch-Schluter term)
182 !
183 ! Include (eventually) Suydam for reference (cylindrical limit)
184
185 WRITE(nmerc,90)
18690 FORMAT(6x,'S',10x,'PHI',9x,'IOTA',8x,'SHEAR',7x,' VP ',8x,'WELL', &
187 8x,'ITOR',7x,'ITOR''',7x,'PRES',7x,'PRES''',/,120('-'))
188
189 DO i = 2,ns1
190 ! re-use sqs scalar for V' on full grid
191 sqs = p5*(vp_real(i) + vp_real(i+1))*sign_jac
192 IF (sqs .eq. zero) cycle
193
194 WRITE(nmerc,100) &
195 sj(i), & ! S
196 hs*sum(phip_real(2:i)), & ! PHI
197 p5*(iotas(i+1)+iotas(i)), & ! IOTA
198 shear(i)/sqs, & ! SHEAR
199 sqs, & ! VP
200 -vpp(i)*sign_jac, & ! WELL
201 p5*(torcur(i) + torcur(i+1)), & ! ITOR
202 ip(i)/sqs, & ! ITOR'
203 p5*(pres(i) + pres(i+1)), & ! PRES
204 presp(i)/sqs ! PRES'
205 END DO
206100 FORMAT(1p,10e12.4)
207
208 WRITE(nmerc,190)
209190 FORMAT(/,6x,'S',8x,'DMerc',8x,'DShear',7x,'DCurr',7x,'DWell',7x,'Dgeod',/,100('-'))
210
211 DO i = 2,ns1
212 tpp(i) = tpp(i) * (twopi*twopi)
213 tjb(i) = tjb(i) * (twopi*twopi)
214 tbb(i) = tbb(i) * (twopi*twopi)
215 tjj(i) = tjj(i) * (twopi*twopi)
216
217 dshear(i) = shear(i) * shear(i)/4.0_dp
218 dcurr(i) =-shear(i) * (tjb(i) - ip(i) *tbb(i))
219 dwell(i) = presp(i) * (vpp(i) - presp(i) *tpp(i))*tbb(i)
220 dgeod(i) = tjb(i) *tjb(i) - tbb(i) *tjj(i)
221 dmerc(i) = dshear(i) + dcurr(i) + dwell(i) + dgeod(i)
222
223 WRITE(nmerc,100) &
224 sj(i), &
225 dmerc(i), &
226 dshear(i), &
227 dcurr(i), &
228 dwell(i), &
229 dgeod(i)
230 END DO
231
232 CLOSE (nmerc)
233
234 ! fixup for comparison
235 sj(1) = 0.0_dp
236 sj(ns) = 1.0_dp
237
238 if (open_dbg_context("mercier", id=0)) then
239
240 call add_real("sign_jac", sign_jac)
241
242 call add_real_1d("phip_real", ns-1, phip_real(2:ns))
243 call add_real_1d("vp_real", ns-1, vp_real(2:ns))
244 call add_real_1d("torcur", ns-1, torcur(2:ns))
245
246 call add_real_1d("shear", ns-2, shear(2:ns1))
247 call add_real_1d("vpp", ns-2, vpp(2:ns1))
248 call add_real_1d("presp", ns-2, presp(2:ns1))
249 call add_real_1d("ip", ns-2, ip(2:ns1))
250
251 call add_real_3d("gsqrt_full", ns-2, nzeta, ntheta3, gsqrt_full(2:ns1,:))
252 call add_real_3d("bdotj", ns , nzeta, ntheta3, bdotj)
253 call add_real_3d("gpp", ns-2, nzeta, ntheta3, gpp(2:ns1,:))
254 call add_real_3d("b2", ns-1, nzeta, ntheta3, b2(2:ns,:))
255
256 call add_real_1d("tpp", ns-2, tpp(2:ns1))
257 call add_real_1d("tbb", ns-2, tbb(2:ns1))
258 call add_real_1d("tjb", ns-2, tjb(2:ns1))
259 call add_real_1d("tjj", ns-2, tjj(2:ns1))
260
261 call add_real_1d("sj", ns , sj)
262 call add_real_1d("Dshear", ns-2, dshear(2:ns1))
263 call add_real_1d("Dcurr", ns-2, dcurr(2:ns1))
264 call add_real_1d("Dwell", ns-2, dwell(2:ns1))
265 call add_real_1d("Dgeod", ns-2, dgeod(2:ns1))
266 call add_real_1d("DMerc", ns-2, dmerc(2:ns1))
267
268 call close_dbg_out()
269 end if
270
271 DEALLOCATE (gpp, gsqrt_full, b2, stat=i)
272
273END SUBROUTINE mercier
subroutine mercier(gsqrt, bsq, bdotj, iotas, wint, r1, rt, rz, zt, zz, bsubu, vp, phips, pres, ns, nznt)
Evaluate the Mercier stability criterion.
Definition mercier.f90:25
logical function open_dbg_context(context_name, repetition, id)
check if any output is desired for the current iteration check if the given context should be openend...
Definition dbgout.f90:17
fault-tolerant file opening routines
subroutine safe_open(iunit, istat, filename, filestat, fileform, record_in, access_in, delim_in)
integer, parameter dp
integer ntheta3
effective number of poloidal grid points
Definition vmec_dim.f90:11
integer nrzt
Definition vmec_dim.f90:13
character(len=100) input_extension
integer nzeta
real(rprec), dimension(nsd) dgeod
Definition vmercier.f90:12
real(rprec), dimension(nsd) dmerc
Definition vmercier.f90:11
real(rprec), dimension(nsd) dwell
Definition vmercier.f90:9
real(rprec), dimension(nsd) dcurr
Definition vmercier.f90:10
real(rprec), dimension(nsd) dshear
Definition vmercier.f90:8
integer, parameter nmercier0
Definition vparams.f90:27