pyoculus.geo.interpolate_coordinates

class pyoculus.geo.interpolate_coordinates.SurfacesToroidal(nsurfaces=2, mpol=10, ntor=10, Nfp=1, stellar_sym=True)

Toroidal Surface respresented as a Fourier Harmonic Serie.

Define surfaces \(s(\vartheta, \zeta)\) and the angle transformations \(\theta (\vartheta, \zeta)\) written in terms of Fourier harmonics.

..math:

s(\vartheta, \zeta) &= \sum_{m=0}^{m_\text{pol}}\sum_{n=-n_\text{tor}}^{n_\text{tor}}
s_{c,m,n} \cos(m \vartheta - n n_\text{fp} \zeta) + s_{s,m,n} \sin(m \vartheta - n n_\text{fp} \zeta),
\theta(\vartheta, \zeta) = \vartheta + \sum_{m=0}^{m_\text{pol}}\sum_{n=-n_\text{tor}}^{n_\text{tor}}
\theta_{c,m,n} \cos(m \vartheta - n n_\text{fp} \zeta) +        heta_{s,m,n} \sin(m \vartheta - n n_\text{fp} \zeta).

If stellarator symmetry is assumed, then \(s\) will only have cosine components and \(\theta\) will only have sine components.

Each surface is assigned a radial label \(\rho\).

Now a new set of coordinates

$( ho, artheta, zeta) $ will be constructed

given the relationship

$s( ho, artheta, zeta), heta( ho, artheta, zeta) $.

If

$ ho $ is not on one of the known surfaces, an interpolation will be performed on all the Fourier harmonics radially using the method of the user’s choice.

remove_surface(i: int)

! Remove a surface @param i the id of the surface

add_surface(rho: float, scn, tsn, ssn=None, tcn=None)
! Adding a surface into the system with radial label rho

@param rho the new coordinate

$ ho $ for this new surface

@param scn the cosine components of

$s( ho, artheta, zeta) $

@param tsn the sine components of

$ heta( ho, artheta, zeta) $

@param ssn the sine componets of

$s( ho, artheta, zeta) $

@param tcn the cosine components of

$ heta( ho, artheta, zeta) $

replace_surface(idx: int, rho: float = None, scn=None, tsn=None, ssn=None, tcn=None)
! Replacing a surface by the new one

@param idx the index of the surface to be replaced @param rho the new coordinate

$ ho $ for this new surface. If this is None then keep same.

@param scn the cosine components of

$s( ho, artheta, zeta) $. If this is None then keep same.

@param tsn the sine components of

$ heta( ho, artheta, zeta) $. If this is None then keep same.

@param ssn the sine componets of

$s( ho, artheta, zeta) $. If this is None then keep same.

@param tcn the cosine components of

$ heta( ho, artheta, zeta) $. If this is None then keep same.

get_coords(sarr=[0], tarr=[0], zarr=[0], derivative=0, input1D=True)

! Compute the coordinates and their derivatives given $ ho, artheta, zeta $

@param sarr

$ ho $, an array

@param tarr

$ artheta $, an array

@param zarr

$zeta $, an array

@param derivative 0:only return

$s, heta $, 1: also return the derivatives, 2: also return the second derivatives

@param input1D if False, create a meshgrid with sarr, tarr and zarr, if True, treat them as a list of points @returns coords coords.s, coords.t contains the coordinates

$s, heta $. coords.ds and dt contains the first derivatives. coords.dds and ddt contains second derivates. coords.jacobi contains the jacobi matrix $J = partial(s, heta,zeta)/partial( ho, artheta,zeta) $, coords.djacobi contains the derivative.

jacobi_transform(jacobi, coords: CoordsOutput)
! Compute the derivatives wrt the new coordinate given the derivatives in the old coordinates

@param jacobi the Jacobi matrix

$partial(f_1, cdots, f_N) / partial (s, heta,zeta) $

@param coords the output of get_coords, should match the dimension of J @returns jacobi_output the transformed Jacobi matrix and the derivatives wrt

$( ho, artheta,zeta) $

Given

$N $ variables $f_1, cdots, f_N $, the jacobi matrix (derivatives wrt the new coordinate is given by the chain rule

[

rac{partial(f_1, cdots, f_N)}{partial( ho, artheta, zeta)} = rac{partial(f_1, cdots, f_N)}{partial(s, heta, zeta)}

cdot

rac{partial(s, heta, zeta)}{partial( ho, artheta, zeta)}

]

metric_transform(g, coords: CoordsOutput)

! Compute the coordinate transformation for a metric tensor $g_{ij} $ (lower!) (derivative computation under construction)

@param g the metric

$g_{ij} $ of the old coordinate $(s, heta,zeta) $ to be transformed, should have the dimension (3,3,…)

@param coords the output of get_coords, should match the dimension of g @returns goutput, [dgoutput], the transformed metric and the derivative wrt

$( ho, artheta,zeta) $

Let the input metric for

$(s, heta, zeta) $ being $mathbf{g} $,

the metric for

$( ho, artheta, zeta) $ is given by

[

mathbf{G} = mathbf{J}^T mathbf{g} mathbf{J},

]

where

[

mathbf{J} =

rac{partial(s, heta,zeta)}{partial( ho, artheta,zeta)}

]

is the Jacobi matrix.

contra_vector_transform(v, coords: CoordsOutput, has_jacobian=False, derivative=False, dv=None)

! Compute the coordinate transformation for a contravariant vector $v^i $ (upper!)

@param v the vector

$v^i $ to be transformed, should have the dimension (3,…)

@param coords the output of get_coords, should match the dimension of v @param has_jacobian if the given vector already contains the jacobian @param derivative if the derivatives are needed or not. If True, dv is required. @param dv the derivative of v wrt

$(s, heta,zeta) $, have the dimension (3 derivatives, 3 component,…)

@returns voutput, dvoutput, the transformed metric and the derivative wrt

$( ho, artheta,zeta) $

The vector transformation is given by

[

left(egin{array}{c} v^{

ho} v^{ artheta} v^{zeta}

end{array}

ight)

= mathbf{J}^{-1} left(egin{array}{c} v^{{s}} v^{ heta} v^{zeta} end{array}

ight),

]

where

[

mathbf{J} =

rac{partial(s, heta,zeta)}{partial( ho, artheta,zeta)}

]

is the Jacobi matrix. For computing the result we use matrix solve instead of matrix inversion.

When the derivative is needed, we note that

[

rac{partial }{partial ho}mathbf{J}

left(egin{array}{c} v^{

ho} v^{ artheta} v^{zeta}

end{array}

ight)

+mathbf{J}

rac{partial }{partial ho}left(egin{array}{c}

v^{

ho} v^{ artheta} v^{zeta}

end{array}

ight)

=

rac{partial }{partial ho}left(egin{array}{c}

v^{{s}} v^{ heta} v^{zeta} end{array}

ight),

]

therefore

[

rac{partial}{partial ho}left(egin{array}{c}

v^{

ho} v^{ artheta} v^{zeta}

end{array}

ight)

= mathbf{J}^{-1} left[

rac{partial }{partial ho}left(egin{array}{c}

v^{{s}} v^{ heta} v^{zeta} end{array}

ight)

rac{partial mathbf{J}}{partial ho}

left(egin{array}{c} v^{

ho} v^{ artheta} v^{zeta}

end{array}

ight) ight].

]

Similarly we can compute the

$ artheta $ and $zeta $ derivatives.

Now,

[

rac{partial (v^{{s}}, v^{ heta}, v^{zeta})}{partial( ho, artheta, zeta)} =

rac{partial (v^{{s}}, v^{ heta}, v^{zeta})}{partial(s, heta, zeta)}

imes

rac{partial(s, heta, zeta)}{partial( ho, artheta, zeta)}

]

Summing up

[

rac{partial ( v^{ ho}, v^{ artheta}, v^{zeta})}{partial( ho, artheta, zeta)} =

mathbf{J}^{-1} cdot

rac{partial (v^{{s}}, v^{ heta}, v^{zeta})}{partial(s, heta, zeta)} cdot mathbf{J}
  • mathbf{J}^{-1} cdot (mathbf{J}_

ho, mathbf{J}_ artheta, mathbf{J}_zeta) cdot left(egin{array}{c}

v^{

ho} v^{ artheta} v^{zeta}

end{array}

ight)

]

construct_interpolant(rhosurfs=None, method='cubic_spline', **kwargs)
! Construct the interpolant between surfaces for the given method

@param rhosurfs The

$ ho $ coordinates for each surface. If input is None, the default or internally stored will be used.

@param method The method being used, can be one of [‘cubic_spline’, ‘cubic_hermite’, ‘pchip’]. The corresponding scipy class is being used. @param **kwargs Other parameters passing onto the interpolant constructor

plot(zeta=0, npoints=129, **kwargs)

!Plot the surfaces @param zeta the toroidal plane @param npoints the number of points in the plot @param **kwargs all other parameters going into plt.plot

read_surfaces_from_file(filename='data.npz', Nfp=None)

! Read the surfaces into a numpy array on disk @param filename the filename to load from @param Nfp the toroidal periodicity, if known.

write_surfaces_to_file(filename=None)

! Save the surfaces into a numpy array on disk @param filename the filename to save to