Skip to content

q-factor Objects

dexter.QfactorObject: TypeAlias = UnityQfactor | ParabolicQfactor | ArrayQfactor | NcQfactor module-attribute

Available Qfactor objects.

  • UnityQfactor: Analytical q-factor profile of $q=1$ and $\psi_t=\psi_p$.
  • ParabolicQfactor: Analytical parabolic q-factor profile.
  • ArrayQfactor: Numerical q-factor profile reconstructed from $\psi-q$ pairs.
  • NcQfactor: Numerical q-factor profile reconstructed from a netCDF file.

dexter.UnityQfactor(lcfs: MagneticFlux)

Bases: MachineObject, Qfactor

Analytical q-factor profile of $q=1$ and $\psi_t=\psi_p$.

Parameters:

  • lcfs (MagneticFlux) –

    The last closed flux surface. Only used for bounds checking.

Example
>>> lcfs = dex.MagneticFlux.Toroidal(0.05)
>>> qfactor = dex.UnityQfactor(lcfs)

dexter.ParabolicQfactor(qaxis: float, qlast: float, lcfs: MagneticFlux)

Bases: MachineObject, Qfactor

Analytical parabolic q-factor profile.

Parameters:

  • qaxis (float) –

    The q-factor's value at the magnetic axis, $q_{axis}$.

  • qlast (float) –

    The q-factor's value at the last closed flux surface, $q_{LCFS}$.

Example
>>> lcfs = dex.MagneticFlux.Toroidal(0.05)
>>> qfactor = dex.ParabolicQfactor(1.1, 3.9, lcfs)

The ParabolicQfactor's profile is described by the following formulas:

$$ q(\psi_t) = q_{axis} + (q_{LCFS} - q_{axis}) \bigg( \dfrac{\psi_t}{\psi_{t,LCFS}} \bigg)^2 $$

$$ \psi_p(\psi_t) = \dfrac{\psi_{t,LCFS}}{\sqrt{q_{axis}(q_{LCFS} - q_{axis})}} \arctan\bigg[ \dfrac{\psi_t\sqrt{q_{LCFS} - q_{axis}}}{\psi_{t,LCFS}\sqrt{q_{axis}}} \bigg] $$

$$ \psi_t(\psi_p) = \dfrac{\psi_{t,LCFS}\sqrt{q_{axis}}}{\sqrt{q_{LCFS} - q_{axis}}} \tan\bigg[ \dfrac{\sqrt{q_{axis}(q_{LCFS} - q_{axis})}}{\psi_{t,LCFS}}\psi_p \bigg] $$

$$ \dfrac{d\psi_p(\psi_t)}{d\psi_t} = ... = \dfrac{1}{q(\psi_t)} = \iota(\psi_t) $$

$$ \dfrac{d\psi_t(\psi_p)}{d\psi_p} = \dfrac{q_{axis}}{\cos^2 \bigg[ \dfrac{\sqrt{q_{axis}(q_{LCFS} - q_{axis})}}{\psi_{t,LCFS}}\psi_p \bigg]} \overset{*}{=} q(\psi_p) $$

$$ q(\psi_p) = q_{axis} + q_{axis} \tan^2 \bigg[ \dfrac{\sqrt{q_{axis}(q_{LCFS}-q_{axis})}}{\psi_{t,LCFS}} \psi_p \bigg] $$

$$ \dfrac{dq(\psi_t)}{d\psi_t} = 2(q_{last} - q_{axis})\dfrac{\psi_t}{\psi_{t,LCFS}} $$

$$ \dfrac{dq(\psi_p)}{d\psi_p} = \dfrac{2 q_{axis} \sqrt{q_{axis}(q_{last} - q_{axis})} \sin(\beta)} {\psi_{t,LCFS}\cos^3(\beta)}, \qquad \text{where} \qquad \beta= \dfrac{\sqrt{q_{axis} (q_{last} - q_{axis})}\psi_p}{\psi_{t,LCFS}} $$

$$ \iota = \dfrac{1}{q} $$

$$ \iota' = -\dfrac{q'}{q^2} $$

$^*$ Identity: $\dfrac{1}{\cos^2\theta} = 1 + \tan^2\theta$


dexter.ArrayQfactor(flux_array: MagneticFluxArray, q_array: ArrayLike, interp_type: Interpolation1dType)

Bases: MachineObject, Qfactor

Numerical q-factor profile reconstructed from $\psi-q$ pairs.

Consistency of evaluations

Regardless of the kind of the passed flux_array, two interpolators are built: a $\iota(\psi_t)$ and a $q(\psi_p)$ interpolator, where the values of the other flux are obtained by integrating the corresponding interpolator.

When calling an evaluation method, the kind of the passed flux argument determines the interpolator that will be used; an evaluation with respect to a toroidal flux always uses the $\iota(\psi_t)$ interpolator while an evaluation with resepct to a poloidal flux always uses the $q(\psi_p)$ interpolator. For example, qfactor.eval_q(psi) returns 1/qfactor.eval_iota(psi) and qfactor.eval_iota_deriv(psip) returns -qfactor.eval_q_deriv(psip)/qfactor.eval_q(psip)^2.

The above rule guarantees that no evaluation will pass through both interpolators (with the exception of eval_deriv_wrt_other, which is a shorthand of eval_other -> eval_deriv_of_other). This is important as passing through both interpolators introduces numerical inconsistencies.

However, when flux_array.kind() == Toroidal, the very construction of the $q(\psi_p)$ interpolator is inconsistent , as the extra step of calculating $\psi_p$ from $\psi_t$ is needed before constructing the $q(\psi_p)$ interpolator (similarly when flux_array.kind() ==Poloidal for $\iota(\psi_t)$). Therefore, while the evaluation methods guarantee that the correct interpolator will be used, it is up to you to ensure you evaluate the qfactor with respect to the flux you constructed it with.

Tip

The defining_flux attribute stores the kind of flux w.r.t. which the ArrayQfactor was defined.

Parameters:

Attributes:

  • interp_type (Interpolation1dType) –

    The 1D interpolation type.

  • defining_flux (MagneticFluxKind) –

    The flux w.r.t. which the qfactor was defined.

  • psit_array (Array1) –

    The toroidal flux' values.

  • psip_array (Array1) –

    The poloidal flux' values.

  • q_array (Array1) –

    The q-factor's values.

Raises:

  • Exception –

    An exception is raised if:

    • Data arrays are empty,
    • Data arrays have different sizes,
    • The first element of flux_array is not 0.0.
    • flux_array values are non-rising.
    • The interpolators fail to build.
Example
>>> psip_values = [0, 0.1, 0.2, 0.3, 0.4]
>>> q_array = [0.9, 0.7, 0.5, 0.1, -0.2]
>>> qfactor = dex.ArrayQfactor(
...     flux_array=dex.MagneticFluxArray.Poloidal(psip_values),
...     q_array=q_array,
...     interp_type="Cubic",
...     )

dexter.NcQfactor(path: str, interp_type: Interpolation1dType)

Bases: MachineObject, Qfactor

Numerical q-factor profile reconstructed from a netCDF file.

This type is a wrapper type of ArrayQfactor where the $\psi$ and $q$ data are extracted from the netCDF file.

Parameters:

  • path (str) –

    The path to the netCDF file.

  • interp_type (Interpolation1dType) –

    The 1D interpolation type.

Attributes:

  • path (str) –

    The path to the netCDF file.

  • netcdf_version (NetCDFVersion) –

    The netCDF file's version (SemVer).

  • interp_type (Interpolation1dType) –

    The 1D interpolation type.

  • psit_array (Array1) –

    The toroidal flux' values.

  • psip_array (Array1) –

    The poloidal flux' values.

  • q_array (Array1) –

    The q-factor's values.

Example
>>> qfactor = dex.NcQfactor(path, "Cubic")