VMEC 8.52
3D Equilibrium solver with nested flux surfaces.
Loading...
Searching...
No Matches
fixaray.f90
Go to the documentation of this file.
1
3
6SUBROUTINE fixaray
7
8 USE vmec_main, p5 => cp5
10
11 use dbgout
12
13 IMPLICIT NONE
14
15 REAL(rprec), PARAMETER :: two=2.0_dp
16
17 REAL(rprec), PARAMETER :: pexp=4.0_dp ! for <M> spectral width screen diagnostic
18
19 INTEGER :: i, m, j, n, mn, mn1, nmin0, istat1, istat2
20 INTEGER :: mnyq0, nnyq0
21 REAL(rprec):: argi, arg, argj, dnorm, dnorm3
22 logical :: dbg_fixaray
23 real(rprec), allocatable :: arg_mu(:,:), arg_nv(:,:)
24
25 ! COMPUTE TRIGONOMETRIC FUNCTION ARRAYS
26 ! NOTE: ARRAYS ALLOCATED HERE ARE GLOBAL AND ARE DEALLOCATED IN FILEOUT
27 ! NOTE: NEED 2 X NYQUIST FOR FAST HESSIAN CALCULATIONS
28 mnyq0 = ntheta1/2 ! maximum mode numbers supported by grid
29 nnyq0 = nzeta/2
30
31 ! make sure that mnyq, nnyq are at least twice mpol-1, ntor
32 ! or large enough to fully represent the information held in realspace (mnyq0, nnyq0)
33 mnyq = max(0, 2*mnyq0, 2*mpol1)
34 nnyq = max(0, 2*nnyq0, 2*ntor)
35
36 mnmax_nyq = nnyq/2 + 1 + mnyq/2 * (2 * nnyq/2 + 1)
37
38 ALLOCATE(cosmu(ntheta3,0:mnyq), sinmu(ntheta3,0:mnyq), &
39 cosmum(ntheta3,0:mnyq), sinmum(ntheta3,0:mnyq), &
40 cosmui(ntheta3,0:mnyq), cosmumi(ntheta3,0:mnyq), &
41 cosmui3(ntheta3,0:mnyq),cosmumi3(ntheta3,0:mnyq), &
42 sinmui(ntheta3,0:mnyq), sinmumi(ntheta3,0:mnyq), &
43 cosnv(nzeta,0:nnyq), sinnv(nzeta,0:nnyq), &
44 cosnvn(nzeta,0:nnyq), sinnvn(nzeta,0:nnyq), &
45 cos01(nznt), sin01(nznt), stat=istat1 )
46 ALLOCATE(xm(mnmax), xn(mnmax), ixm(mnsize), jmin3(0:mnsize-1), &
47 xm_nyq(mnmax_nyq), xn_nyq(mnmax_nyq), &
48 mscale(0:mnyq), nscale(0:nnyq), stat=istat2)
49 allocate(arg_mu(ntheta3,0:mnyq), arg_nv(nzeta,0:nnyq))
50
51 IF (istat1.ne.0) stop 'allocation error in fixaray: istat1'
52 IF (istat2.ne.0) stop 'allocation error in fixaray: istat2'
53
54 ! Normalization factor for forward Fourier transforms (e.g. tomnsp*)
55 ! In any case (symmetric or asymmetric), the Fourier integrals in tomnsp* and alias
56 ! (the only place where dnorm is used through cosmui, sinmui, etc.)
57 ! are ever only taken over [0, pi] corresponding to 1, ..., ntheta2.
58 dnorm = one/(nzeta*(ntheta2-1.0_dp))
59
60 ! Normalization factor for surface integrals/averages (wint is based on cosmui3).
61 ! For the asymmetric case, the norm in wint thus has to be 1/(nzeta*ntheta3).
62 IF (lasym) then
63 dnorm3 = one/(nzeta*ntheta1)
64 else
65 dnorm3 = one/(nzeta*(ntheta2-1.0_dp))
66 end if
67
68 ! (from vmec_params)
69 ! array for norming theta-trig functions (internal use only)
70 ! so that the discrete SUM[cos(mu)*cos(m'u)] = .5 delta(m,m')
71 ! and analogously for zeta/v
72 mscale(0) = 1.0_dp
73 nscale(0) = 1.0_dp
74 mscale(1:mnyq) = mscale(0)/osqrt2 ! == sqrt(2)
75 nscale(1:nnyq) = nscale(0)/osqrt2 ! == sqrt(2)
76
77 r0scale = mscale(0)*nscale(0) ! == 1
78
79 ! GENERALLY, ONLY NEED THIS FROM 1, ntheta2 EXCEPT IN GETBRHO ROUTINE
80 DO i = 1, ntheta3
81 argi = twopi*(i-1)/ntheta1
82 DO m = 0, mnyq
83 arg = argi*m
84
85 arg_mu(i,m) = arg
86
87 cosmu(i,m) = cos(arg)*mscale(m)
88 sinmu(i,m) = sin(arg)*mscale(m)
89
90 cosmui(i,m) = dnorm*cosmu(i,m)
91 sinmui(i,m) = dnorm*sinmu(i,m)
92 IF (i.EQ.1 .OR. i.EQ.ntheta2) then
93 ! Trapezoidal integration requires a factor of 1/2 for the first and the last point.
94 ! Note that this is done also in the case of an asymmetric run!
95 ! There, cosmui is only used up to ntheta2 anyway...
96 cosmui(i,m)=cosmui(i,m)/2.0_dp
97 end if
98
99 ! Use this if integration over FULL 1,ntheta3 interval
100 cosmui3(i,m) = dnorm3*cosmu(i,m)
101 IF (.not.lasym .and. (i.eq.1 .or. i.eq.ntheta2)) then
102 ! Note that cosmui3 is only ever used to construct the wint array.
103 ! where only the m=0 component of cosmui3 enters.
104 cosmui3(i,m) = cosmui3(i,m)/2.0_dp
105 end if
106
107 cosmum(i,m) = cosmu(i,m)*(m)
108 sinmum(i,m) =-sinmu(i,m)*(m)
109 cosmumi(i,m) = cosmui(i,m)*(m)
110 sinmumi(i,m) =-sinmui(i,m)*(m)
111
112 cosmumi3(i,m) = cosmui3(i,m)*m ! not used... --> remove?
113 END DO
114 END DO
115
116 DO j = 1, nzeta
117 argj = twopi*(j-1)/nzeta
118 DO n = 0, nnyq
119 arg = argj*(n)
120
121 arg_nv(j,n) = arg
122
123 cosnv(j,n) = cos(arg)*nscale(n)
124 sinnv(j,n) = sin(arg)*nscale(n)
125 cosnvn(j,n) = cosnv(j,n)*(n*nfp)
126 sinnvn(j,n) = -sinnv(j,n)*(n*nfp)
127 END DO
128 END DO
129
130 ! R,Z,L / s**(m/2) ARE LINEAR NEAR ORIGIN
131 mn = 0
132 mn1 = 0
133 DO m = 0, mpol1
134 xmpq(m,1) = m*(m - 1.0_dp) ! used for spectral constraint force --> m^2-m
135
136 ! xmpq(m,2:3) are ONLY used for screen diagnostic <M> !!!
137 xmpq(m,2) = m**pexp ! m^p with p = pexp = 4
138 xmpq(m,3) = m**(pexp+1.0_dp) ! m^(p+q) with q = 1
139
140 ! compute ixm == _i_nteger version of xm
141 DO n = 0, ntor
142 jmin3(mn) = jmin2(m) ! vmec_params: starting js(m) values for which R,Z are evolved
143 mn = mn + 1
144 ixm(mn) = m
145 END DO
146
147 ! use this loop also to compute xm, xn array contents
148 nmin0 = -ntor
149 IF (m .eq. 0) nmin0 = 0
150 DO n = nmin0, ntor
151 mn1 = mn1 + 1
152 xm(mn1) = m
153 xn(mn1) = n*nfp
154 END DO
155 END DO
156
157 IF (mn1 .ne. mnmax) stop 'mn1 != mnmax'
158
159 dbg_fixaray = open_dbg_context("fixaray")
160 if (dbg_fixaray) then
161
162 call add_real_2d("arg_mu", ntheta3, mnyq+1, arg_mu(1:ntheta3, 0:mnyq))
163 call add_real_2d("arg_nv", nzeta, nnyq+1, arg_nv(1:nzeta, 0:nnyq))
164
165 call add_real_2d("cosmu", ntheta2, mnyq+1, cosmu(1:ntheta2,:))
166 call add_real_2d("sinmu", ntheta2, mnyq+1, sinmu(1:ntheta2,:))
167 call add_real_2d("cosmum", ntheta2, mnyq+1, cosmum(1:ntheta2,:))
168 call add_real_2d("sinmum", ntheta2, mnyq+1, sinmum(1:ntheta2,:))
169 call add_real_2d("cosmui", ntheta2, mnyq+1, cosmui(1:ntheta2,:))
170 call add_real_2d("sinmui", ntheta2, mnyq+1, sinmui(1:ntheta2,:))
171 call add_real_2d("cosmui3", ntheta2, mnyq+1, cosmui3(1:ntheta2,:))
172 call add_real_2d("cosmumi", ntheta2, mnyq+1, cosmumi(1:ntheta2,:))
173 call add_real_2d("sinmumi", ntheta2, mnyq+1, sinmumi(1:ntheta2,:))
174 call add_real_2d("cosmumi3", ntheta2, mnyq+1, cosmumi3(1:ntheta2,:))
175
176 call add_real_2d("cosnv", nzeta, nnyq+1, cosnv)
177 call add_real_2d("sinnv", nzeta, nnyq+1, sinnv)
178 call add_real_2d("cosnvn", nzeta, nnyq+1, cosnvn)
179 call add_real_2d("sinnvn", nzeta, nnyq+1, sinnvn)
180
181 call add_real_1d("mscale", mnyq+1, mscale)
182 call add_real_1d("nscale", nnyq+1, nscale)
183
184 ! TODO: add r0scale output (currently in residue...)
185
186 end if
187
188 ! COMPUTE NYQUIST-SIZED ARRAYS FOR OUTPUT.
189 ! RESTORE m,n Nyquist TO 1 X ... (USED IN WROUT, JXBFORCE)
190 ! mnyq = mnyq0; nnyq = nnyq0
191 mnyq = mnyq/2
192 nnyq = nnyq/2
193
194 mn1 = 0
195 DO m = 0, mnyq
196 nmin0 = -nnyq
197 IF (m .eq. 0) nmin0 = 0
198 DO n = nmin0, nnyq
199 mn1 = mn1 + 1
200 xm_nyq(mn1) = m
201 xn_nyq(mn1) = n*nfp
202 END DO
203 END DO
204
205 IF (mn1 .ne. mnmax_nyq) stop 'mn1 != mnmax_nyq'
206
207 ! cos01 and sin01 are used as trigmult for precondn in bcovar
208 ! cos01: m*cos(m u - n v) for m=1, n=0 --> d(sin(mu))/du
209 ! sin01: -m*sin(m u - n v) for m=1, n=0 --> d(cos(mu))/du
210 ! evaluated on (ntheta3 x nzeta) grid
211 mn = 0 ! mn is re-used as linear grid index here
212 m = 1
213 DO i = 1, ntheta3
214 argi = twopi*(i - 1)/ntheta1
215 DO j = 1, nzeta
216 mn = mn + 1
217 cos01(mn) = m*cos(m*argi)*mscale(m)
218 sin01(mn) =-m*sin(m*argi)*mscale(m)
219 END DO
220 END DO
221
222 ! _fac_tor for _con_straint
223 faccon(0) = zero
224 faccon(1:mpol1-1) = -0.25_dp*signgs/xmpq(2:mpol1,1)**2.0_dp
225 faccon(mpol1) = zero
226
227 if (dbg_fixaray) then
228
229 call add_int("ntheta3", ntheta3)
230 call add_int("mnyq", mnyq)
231 call add_int("nzeta", nzeta)
232 call add_int("nnyq", nnyq)
233 call add_int("nznt", nznt)
234 call add_int("mnmax", mnmax)
235 call add_int("mnsize", mnsize)
236 call add_int("mnmax_nyq", mnmax_nyq)
237
238 call add_real_1d("cos01", nznt, cos01)
239 call add_real_1d("sin01", nznt, sin01)
240
241 call add_real_1d("xm", mnmax, xm)
242 call add_real_1d("xn", mnmax, xn)
243
244 call add_real_1d("xm_nyq", mnmax_nyq, xm_nyq)
245 call add_real_1d("xn_nyq", mnmax_nyq, xn_nyq)
246
247 call add_int_1d("ixm", mnsize, ixm)
248
249 call close_dbg_out()
250 end if
251
252 if (open_dbg_context("spectral_constraint", num_eqsolve_retries)) then
253
254 ! xmpq is allocated statically, so need size here explicitly!
255 call add_real_2d("xmpq", mpol, 3, xmpq(0:mpol1,1:3))
256
257 call add_real_1d("faccon", mpol, faccon)
258
259 call close_dbg_out()
260 end if
261
262END SUBROUTINE fixaray
subroutine fixaray
allocate and fill some fixed-size arrays (only depending on Fourier resolution).
Definition fixaray.f90:7
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
integer num_eqsolve_retries
real(rprec), dimension(0:mpol1d, 3) xmpq
spectral condensation weighting factors
Definition vmec_main.f90:81
real(rprec) r0scale
Definition vmec_main.f90:90
real(rprec), dimension(0:mpol1d) faccon
factor for spectral constraint
Definition vmec_main.f90:82
integer, dimension(0:mpold), parameter jmin2
starting js(m) values for which R,Z are evolved
real(rprec) signgs
sign of Jacobian : must be =1 (right-handed) or =-1 (left-handed)
real(rprec), dimension(:), allocatable mscale
array for norming theta-trig functions (internal use only) so that the discrete SUM[cos(mu)*cos(m'u)]...
real(rprec), dimension(:), allocatable nscale
array for norming zeta -trig functions (internal use only)