gvec.scripts.quasr#

The pyGVEC script for converting a QUASR configuration to a G-Frame for use with GVEC.

QUASR is the A QUAsi-symmetric Stellarator Repository: https://quasr.flatironinstitute.org/

The algorithm is described in the paper Hindenlang et al. DOI: 10.1088/1361-6587/adba11 and is as follows:

### STEP 1: Evaluate surface in cartesian space:

  1. using a json file from QUASR (from https://quasr.flatironinstitute.org/) with the simsopt interface

2. Evaluate the surface cartesian position \((x,y,z)( artheta_i,\zeta_j)\) at a meshgrid on the full torus:

$

artheta_i=2pi rac{i}{n_t},i=0dots,n_t-1,quad zeta_j=2pi rac{j}{n_z},j=0,dots,n_z-1$

where the angles $

artheta,zeta$ are just the parametrization of the given surface. (For the quasr surfaces, its a boozer angle parameterization!).

The number \(n_z\) is chosen as a multiple of the number of field periods \(n_{FP}\), to be able to reduce the discrete dataset exactly to one field period.

### STEP 2: Project to surface with elliptical cross-sections

Project \({m x}_m(\zeta)= rac{1}{2\pi}\int_{ artheta=0}^{2\pi}{m x}( artheta,\zeta)\sigma_m( artheta)d artheta\) with \(\sigma_0( artheta)=1,\sigma_{s}( artheta)=2\sin( artheta),\sigma_{c}( artheta)=2\cos( artheta)\), leading to a surface

\({m x}_m( artheta,\zeta)={m x}_0(\zeta) + {m x}_s(\zeta)\sin( artheta)+ {m x}_c(\zeta)\cos( artheta)\)

where cross-sections of \(\zeta= ext{const.}\) are planar elliptical curves.

### STEP 3: Compute the plane of the ellipse cross-sections

First choice is to set the first basis unit vector \(N\) from the center point of the ellipse to a point on the boundary at \( artheta=0\) position. Then use a first guess for the second basis unit vector \(B\) from the center point to $ heta= rac{\pi}{2}$ position to span the unit normal of the plane \(K=(N imes B)\), and then set the second unit vector \(B=K imes N\), such that \(N\) and \(B\) are orthonormal and describe the plane of the ellipse. ### STEP 4: compute fourier coefficients of the ellipse

The ellipse in a single \(N,B\) plane is defined as $ \(X^k( artheta)= x^k_c\cos( artheta)+x^k_s\sin( artheta),k=1,2\)

We can deduce from the four coefficients the shift \( artheta_0\) and the rotation angle \(\Gamma\).

### STEP 5: Final frame

The final frame is obtained by rotating the \(N\) and \(B\) vectors by \(-(\Gamma- artheta_0)\), which yields constant rotation speed along \(\zeta\). Thus, in the final frame, the rotating ellipse is represented by a single poloidal and a single toroidal Fourier mode.

### STEP 6: cut the original surface with the planes of the frame

For each discrete \(N,B\) plane, we compute the intersection of the all curves \(m x( artheta_i,\zeta)\) and compute its position \(X^1,X^2\) in the \(N,B\) plane. This gives the final surface.

gvec.scripts.quasr.check_args(parser, args)#
gvec.scripts.quasr.convert_quasr(xyz: ndarray, nfp: int, name: str, tolerance: float = 1e-08, format: Literal['yaml', 'toml'] = 'yaml')#
gvec.scripts.quasr.cut_surf(xyz, nfp, xyz0, N, B)#

given xyz(zeta,theta) on the full torus, find intersection point of lines of theta=const with all N-B planes with origin xyz0. then project these points to find x1,x2 coordinates in each N-B cross-section

gvec.scripts.quasr.eval_curve(zeta_in, xyz, dft_dict)#

evaluates the curve at a single point zeta_in given by cartesian positions of a periodic curve xyz[0:len(zeta1d)+1,0:2], evaluated zeta1d[0:2pi[,

gvec.scripts.quasr.eval_distance_to_curve(zeta_in, origin, normal, xyz, dft_dict)#
gvec.scripts.quasr.eval_distance_to_plane(xyz_in, origin, normal)#
gvec.scripts.quasr.find_zeta_cuts(zeta_in, origin, normal, xyz, dft_dict, zeta_bracket: float)#
gvec.scripts.quasr.get_B(x_out, deriv, nfp, modes)#
gvec.scripts.quasr.get_X0_N_B(xyz)#

Get guiding curve and two guiding vectors from the cartesian coordinates of a surface.

gvec.scripts.quasr.get_json_from_quasr(configuration: int, filename: str | Path | None = None)#

Retrieve a simsopt-compatible JSON for a given QUASR configuration.

gvec.scripts.quasr.get_surface_from_json_file(filename: Path | str)#

Get the boundary surface as a SIMSOPT Surface object from a QUASR JSON file.

gvec.scripts.quasr.get_xyz_cut(zeta_start, origins, normals, xyz_in, dft_dict, nfp)#
gvec.scripts.quasr.get_xyz_from_surface(nt: int, nz: int, surface)#

Sample a SIMSOPT Surface object in cartesian coordinates.

Sample surface at nt,nz*nfp point positions on the full torus. Gives cartesian positions xyz[0:nz*nfp,0:nt,0:2].

gvec.scripts.quasr.load_xyz(filename: Path | str)#
gvec.scripts.quasr.main(args: Sequence[str] | Namespace | None = None)#
gvec.scripts.quasr.minimal_modes(X, Y, tolerance)#

Find the minimal maximum mode numbers (M, N) such that the error is below the tolerance.

gvec.scripts.quasr.real_dft_mat(x_in, x_out, nfp=1, modes=None, deriv=0)#

Flexible Direct Fourier Transform for real data takes an input array of equidistant points in [0,2pi/nfp[ (exclude endpoint!), evaluate the discrete fourier transform with the given 1d mode vector (all >=0) using the input points x_in, then evaluate the inverse transform (or its derivative deriv>0) on the output points x_out anywhere… len(x_in) must be > 2*max(modes) output is the matrix that transforms real function to real function [derivative]:

f^deriv(x_out) = Mat f(x_in) (can then be used to do 2d transforms with matmul!)

nfp is the number of field periods, default 1 (int), all modes are multiples of nfp

gvec.scripts.quasr.save_xyz(xyz: ndarray, nfp: int, filename: Path | str, attrs: dict = {})#
gvec.scripts.quasr.write_Gframe_ncfile(filename: str | Path, dict_in)#

Write the G-Frame & boundary to a GVEC-compatible netCDF file.