Skip to content

fix: cosmui, sinmui have wrong normalization factors for lasym=T #21

Description

@jonathanschilling

This is an issue to track work related to the case of an (I think) incorrectly initialized dnorm factor in fixaray.f.
The code changes happen on the branch fix_cosmui.

I found this problem when trying to understand the Fourier transform of the forces in VMEC.
The problem appears only when running VMEC with lasym=T.

Any (periodic) function can be decomposed into an even-parity contribution and an odd-parity contribution.
Some force components (armn, brmn, ..., clmn, frcon, fzcon) have even parity, some have odd parity when symmetry is assumed.
In the asymmetric case, the forces are decomposed into definite-parity contributions
and then Fourier-transformed over the poloidal half-interval [0, pi] (1, ..., ntheta2).
The decomposition into definite-parity contributions is done in symforce.
Those-parity contributions that would be the only ones in symmetric mode are Fourier-transformed by tomnsps.
The other-parity contributions are Fourier-transformed using tomnspa.
Concluding, the Fourier integrals computed to get the Fourier coefficients of the forces are only ever computed
over the poloidal half-interval [0, pi] and never over the full-poloidal interval [0, 2pi[ .

The Fourier integrals in tomnsp* make use of the Fourier basis functions
stored in cosmui, sinmui, cosmumi and sinmumi.
These are filled in Sources/Initialization_Cleanup/fixaray.f.
Here, the normalization of the Fourier integrals is being done using the dnorm factor.
Note that this enters also in the flux-surface average weighting factor wint through cosmui3.

dnorm gets changed to 1/(nzeta*ntheta3) if lasym=T.
In that case, ntheta3 equals ntheta1, which refers to the full poloidal interval [0, 2 pi[.
The Fourier integrals done in tomnsp* use this dnorm factor to normalize the discrete integrals there,
which however are still only being computed over half the poloidal range.
I think this is incorrect.

Instead, dnorm as used for cosmui, ..., sinmumi should always be 1/(nzeta*ntheta2-1) as in the symmetric case.
Furthermore, cosmui3 and in turn the weighting factors wint still need to adjust to the changed poloidal interval.
Thus, I propose to introduce dnorm3 in fixaray, which shall get adjusted to reflect the actual number of poloidal grid points.

The actual errors resulting from this mixup seems to be that in the asymmetric case,
the Fourier coefficients of the forces are too low by a factor of ntheta3/(ntheta2-1) which is approx. 2.
VMEC seems to be able to cope with that by just using more iterations,
but I think this should be fixed anyway.

The expected results of fixing this issue are:

  • for originally symmetric cases, when switching to lasym=T, the number of iterations should approximately stay constant
  • for asymmetric cases, the number of iterations should go down

The SOLOVEV case from the DESC repository is used as a simple test case here:

&INDATA
  ! based on input.SOLOVEV from github.com/PlasmaControl/DESC/examples 
 
  LASYM = F
  
  NFP =    1
  MPOL =   16
  NTOR =   0
 
  NS_ARRAY    = 16    256
  NITER_ARRAY = 1000  20000 
  FTOL_ARRAY  = 1E-16 1E-16 
 
  DELT =   9.00E-01
  NSTEP =  250
 
  GAMMA =   0.000000E+00
  PHIEDGE =   1.00000000000000E+00
  CURTOR =   0.00000000000000E+00
  PMASS_TYPE = "power_series"
  AM =  0.125 -0.125
  NCURR =    0
  AI = 1.0
  
  RAXIS =   3.999
  ZAXIS =   0.00000000000000E+00
  
  RBC( 0,0) =  3.999     ZBS( 0,0) =  0.000
  RBC( 0,1) =  1.026     ZBS( 0,1) =  1.580
  RBC( 0,2) = -0.068     ZBS( 0,2) =  0.010
/
&END

Running this case with the current master branch, I get the following output:

  NS =   16 NO. FOURIER MODES =   16 FTOLV =  1.000E-16 NITER =   1000
  PROCESSOR COUNT - RADIAL:    1

  ITER    FSQR      FSQZ      FSQL    RAX(v=0)    DELT       WMHD

    1  9.56E-02  2.88E-03  3.25E-02  3.999E+00  9.00E-01  2.6381E+00
  250  2.15E-15  6.16E-16  4.15E-16  3.990E+00  9.00E-01  2.5484E+00
  272  8.89E-17  3.66E-17  2.15E-17  3.990E+00  9.00E-01  2.5484E+00

  NS =  256 NO. FOURIER MODES =   16 FTOLV =  1.000E-16 NITER =  20000
  PROCESSOR COUNT - RADIAL:    1

  ITER    FSQR      FSQZ      FSQL    RAX(v=0)    DELT       WMHD

    1  9.94E-04  1.52E-04  1.43E-07  3.990E+00  9.00E-01  2.5484E+00
  250  2.37E-10  5.61E-11  6.49E-13  3.989E+00  5.93E-01  2.5484E+00
  500  4.14E-12  2.00E-13  1.92E-15  3.989E+00  5.93E-01  2.5484E+00
  750  9.16E-13  2.13E-14  3.32E-18  3.989E+00  5.93E-01  2.5484E+00
 1000  1.81E-13  3.04E-15  3.45E-20  3.989E+00  5.93E-01  2.5484E+00
 1250  1.66E-14  2.26E-16  3.81E-22  3.989E+00  5.93E-01  2.5484E+00
 1500  8.72E-16  1.17E-17  1.30E-23  3.989E+00  5.93E-01  2.5484E+00
 1698  1.00E-16  1.41E-18  4.74E-24  3.989E+00  5.93E-01  2.5484E+00

Then lasym=T is put in the input file and the output now reads:

  NS =   16 NO. FOURIER MODES =   16 FTOLV =  1.000E-16 NITER =   1000
  PROCESSOR COUNT - RADIAL:    1

  ITER    FSQR      FSQZ      FSQL    RAX(v=0)   ZAX(v=0)    DELT       WMHD

    1  2.39E-02  7.20E-04  8.13E-03  3.999E+00  0.000E+00  9.00E-01  2.6381E+00
  250  8.19E-12  3.05E-13  4.01E-13  3.990E+00  2.528E-16  9.00E-01  2.5484E+00
  500  4.96E-16  6.97E-18  1.57E-18  3.990E+00 -5.771E-17  9.00E-01  2.5484E+00
  533  9.77E-17  1.49E-18  1.68E-19  3.990E+00 -8.564E-16  9.00E-01  2.5484E+00

  NS =  256 NO. FOURIER MODES =   16 FTOLV =  1.000E-16 NITER =  20000
  PROCESSOR COUNT - RADIAL:    1

  ITER    FSQR      FSQZ      FSQL    RAX(v=0)   ZAX(v=0)    DELT       WMHD

    1  2.59E-04  4.06E-05  4.06E-08  3.990E+00 -8.564E-16  9.00E-01  2.5484E+00
  250  5.93E-12  9.04E-13  1.48E-14  3.989E+00 -2.752E-16  9.00E-01  2.5484E+00
  500  8.62E-13  9.57E-14  3.46E-17  3.989E+00 -2.291E-15  9.00E-01  2.5484E+00
  750  2.95E-13  2.13E-14  3.01E-19  3.989E+00 -1.379E-15  9.00E-01  2.5484E+00
 1000  7.49E-14  2.57E-15  3.11E-20  3.989E+00 -1.565E-15  9.00E-01  2.5484E+00
 1250  1.84E-14  3.96E-16  3.08E-21  3.989E+00 -1.876E-15  9.00E-01  2.5484E+00
 1500  2.75E-15  4.85E-17  5.63E-22  3.989E+00 -4.524E-15  9.00E-01  2.5484E+00
 1750  3.40E-16  6.61E-18  8.85E-23  3.989E+00 -3.454E-15  9.00E-01  2.5484E+00
 1906  9.91E-17  2.29E-18  4.60E-23  3.989E+00 -1.048E-15  9.00E-01  2.5484E+00

The number of iterations has increased:

  • for ns=16 from 272 to 533
  • for ns=256 from 1698 to 1906

With the changes on this branch, the symmetric case runs in the same number of iterations
and the asymmetric case runs in 312 (for ns=16) and 1685 (for ns=256) iterations.

TODO:

  • find openly-available asymmetric test case and demonstrate that number of iterations goes down (has been tested locally, but I don't know if I am allowed to post the input file here)
  • see where else in the code cosmui, ... are used and see if this has implications there

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions