bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions

A collection of functions used to approximate toroidal harmonics.

Classes

ToroidalHarmonicsParams

A Dataclass holding necessary parameters for the toroidal harmonics approximation

TauLimit

TauLimit Enum

ToroidalHarmonicsSelectionResult

Toroidal harmonic selection result dataclass

Functions

f_hypergeometric(a, b, c, z[, n_max])

Evaluates the hypergeometric power series up to n_max.

legendre_p(lam, mu, x[, n_max])

Evaluates the associated Legendre function of the first kind as a function of x.

legendre_q(lam, mu, x[, n_max])

Evaluates Olver's definition of the associated Legendre function of the second

toroidal_harmonic_grid_and_coil_setup(...)

Set up the grid and coils to be used in toroidal harmonic approximation.

coil_toroidal_harmonic_amplitude_matrix(...)

Construct coefficient matrices from toroidal harmonic amplitudes at given coil

toroidal_harmonic_approximate_psi(...)

Approximate psi using toroidal harmonic amplitudes for the specified cos and sin

_separate_psi_contributions(→ tuple[numpy.ndarray, ...)

Separate the psi contributions from fixed sources (plasma + excluded coils) and from

_set_n_degrees_of_freedom(→ int)

Determine the number of degrees of freedom to use. This is limited by the number

_get_plasma_mask(→ int | numpy.ndarray)

Get a plasma mask to apply to the psi field.

toroidal_harmonics_to_positions(→ tuple[numpy.ndarray, ...)

Matrix for collocation psi fitting or psi calculation across

toroidal_harmonic_approximation(...)

Calculate the toroidal harmonic (TH) amplitudes/coefficients for a given

_approximation_direct_from_currents(eq, th_params, ...)

Approximate psi using the equation for toroidal harmonic amplitudes that

_approximation_from_psi_fitting(th_params, ...)

Approximate psi by fitting for psi at collocation points using

plot_toroidal_harmonic_approximation(eq, th_params, result)

Plot the toroidal harmonic approximation of the coilset psi and the bluemira

Module Contents

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.f_hypergeometric(a, b, c, z, n_max=20)

Evaluates the hypergeometric power series up to n_max. Valid for |z| < 1

\[F(a, b; c; z) = \sum_0^{n_max} \frac{(a)_{s} (b)_{s}}{Gamma(c + s) s!} z^{s}\]

See https://dlmf.nist.gov/15.2#E2 and https://dlmf.nist.gov/5.2#iii for more information.

Parameters:
  • a – hypergeometric function term, as defined in the equation above

  • b – hypergeometric function term, as defined in the equation above

  • c – hypergeometric function term, as defined in the equation above

  • z – hypergeometric function term, as defined in the equation above Require |z|<1.

  • n_max – upper limit of summation, default=20

Returns:

hypergeometric function result.

Return type:

F

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.legendre_p(lam, mu, x, n_max=20)

Evaluates the associated Legendre function of the first kind as a function of x. See https://dlmf.nist.gov/14.3#E18 for more information. Works for half integers.

Valid for 1<x<infinity, and real mu and lambda

\[ \begin{align}\begin{aligned}P_{\lambda}^{-\mu}(x) = 2^{-\mu} x^{\lambda - \mu} (x^2 - 1)^{\mu/2} F(\frac{1}{2}(\mu - \lambda), \frac{1}{2}(\mu - \lambda + 1); \mu + 1; 1 - \frac{1}{x^2})\\where F is the hypergeometric function defined above as f_hypergeometric.\end{aligned}\end{align} \]
Parameters:
  • lam – legendre function term, as defined in the equation above

  • mu – legendre function term, as defined in the equation above

  • x – points at which to evaluate legendreP

  • n_max – upper value for summation in f_hypergeometric

Returns:

value of legendreP.

Return type:

legP

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.legendre_q(lam, mu, x, n_max=20)

Evaluates Olver’s definition of the associated Legendre function of the second kind as a function of x. See https://dlmf.nist.gov/14, https://dlmf.nist.gov/14.3#E10, and https://dlmf.nist.gov/14.3#E7 for more information. Works for half integers.

Valid for 1<x<infinity, and real mu and lambda

\[ \begin{align}\begin{aligned}\textbf{Q}_{\lambda}^{\mu}(x) = \frac{\pi^{\frac{1}{2}} (x^2 - 1)^ {\frac{\mu}{2}}}{2^{\lambda + 1} x^{\lambda + \mu + 1}} F(\frac{1}{2}(\lambda + \mu)+1, \frac{1}{2}(\lambda + \mu); \lambda + \frac{3}{2}; \frac{1}{x^2})\\where F is the hypergeometric function defined above as f_hypergeometric.\end{aligned}\end{align} \]
Parameters:
  • lam – legendre function term, as defined in the equation above

  • mu – legendre function term, as defined in the equation above

  • x – points at which to evaluate legendreQ

  • n_max – upper value for summation in f_hypergeometric

Returns:

value of legendreQ.

Return type:

legQ

Raises:

EquilibriaError – If too many dimensions are input.

class bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.ToroidalHarmonicsParams

A Dataclass holding necessary parameters for the toroidal harmonics approximation

R_0: float

R coordinate of the focus point in cylindrical coordinates

Z_0: float

Z coordinate of the focus point in cylindrical coordinates

min_tau: float

The minimum tau for the toroidal coordinate approximation region

R: numpy.ndarray

R coordinates of the grid in cylindrical coordinates

Z: numpy.ndarray

Z coordinates of the grid in cylindrical coordinates

R_coils: numpy.ndarray

R coordinates of the coils in cylindrical coordinates

Z_coils: numpy.ndarray

Z coordinates of the coils in cylindrical coordinates

tau: numpy.ndarray

tau coordinates of the grid in toroidal coordinates

sigma: numpy.ndarray

sigma coordinates of the grid in toroidal coordinates

tau_c: numpy.ndarray

tau coordinates of the coils in toroidal coordinates

sigma_c: numpy.ndarray

sigma coordinates of the coils in toroidal coordinates

th_coil_names: list

names of coils to use with TH approximation (always outside the LCFS tau limit)

class bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.TauLimit(*args, **kwds)

Bases: enum.Enum

Inheritance diagram of bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.TauLimit

TauLimit Enum

LCFS
COIL
MANUAL
bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.toroidal_harmonic_grid_and_coil_setup(eq: bluemira.equilibria.equilibrium.Equilibrium, R_0: float, Z_0: float, tau_limit=TauLimit.LCFS, min_tau_value: float | None = None) ToroidalHarmonicsParams

Set up the grid and coils to be used in toroidal harmonic approximation.

Use the LCFS to find the region over which to approximate psi using TH. Find the coils located outside this region, which can be used in the TH approximation, and find the coils located inside this region which need to be held fixed.

Parameters:
  • eq (bluemira.equilibria.equilibrium.Equilibrium) – Starting equilibrium to use in our approximation

  • R_0 (float) – R coordinate of the toroidal focus point in cylindrical coordinates

  • Z_0 (float) – Z coordinate of the toroidal focus point in cylindrical coordinates

  • tau_limit

    How the maximum tau value is chosen. The three options are:
    • LCFS: use the maximum extent of the LCFS

    • COIL: use the maximum area within all the coils

    • MANUAL: use a specified limit

  • min_tau_value (float | None) – The minimum tau for the toroidal coordinate approximation region, lower min tau means a larger region of space (maximum tau is at focus)

Returns:

Dataclass holding necessary parameters for the TH approximation

Return type:

ToroidalHarmonicsParams

Raises:

EquilibriaError – If MANUAL tau_limit is specified but no min_tau_value chosen.

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.coil_toroidal_harmonic_amplitude_matrix(input_coils: bluemira.equilibria.coils._grouping.CoilSet, th_params: ToroidalHarmonicsParams, cos_m_chosen: numpy.ndarray | None = None, sin_m_chosen: numpy.ndarray | None = None, sig_figures: int = 15) tuple[numpy.ndarray, numpy.ndarray]

Construct coefficient matrices from toroidal harmonic amplitudes at given coil locations, for the specified cos and sin poloidal mode numbers (m).

To get the individual cos and sin arrays of toroidal harmonic amplitudes/coefficients (Am_cos, Am_sin) which can be used in a toroidal harmonic approximation of the vacuum/coil contribution to the poloidal flux (psi) do:

A_m = matrix harmonic amplitudes @ vector of coil currents

Am_cos and Am_sin can be used as constraints in optimisation, see ToroidalHarmonicsConstraint.

N.B. for a single filament (coil):

\[ \begin{align}\begin{aligned}A_{m} = \frac{\mu_{0} I_{c}}{2^{5/2}} \frac{(2m+1)!!}{2^m m!} \frac{\sinh{\tau_{c}}}{\Delta_{c}^{1/2}} P_{m-\frac{1}{2}}^{-1}(\cosh{\tau_c})\\A_{m}^{\sin} = A_m \sin(m \sigma_c) A_{m}^{\cos} = A_m \cos(m \sigma_c)\end{aligned}\end{align} \]

Where: m is the poloidal mode number, :math: P_{lambda}^{-mu} are the associated Legendre functions of the first kind, with lambda = m - 1/2 and mu = 1 (i.e., we use multiple poloidal mode numbers but toroidal mode number is fixed), :math: Delta_c = cosh{tau_c} - cos{sigma_c}.

Note: the factorial term frac{(2m+1)!!}{2^m m!} is equivalent to 1 if m = 0, otherwise prod_{i=0}^{m-1} left( 1 + frac{1}{2(m-i)}right)

Parameters:
  • input_coils (bluemira.equilibria.coils._grouping.CoilSet) – Bluemira CoilSet

  • th_params (ToroidalHarmonicsParams) – Dataclass holding necessary parameters for the TH approximation

  • cos_m_chosen (numpy.ndarray | None) – Poloidal mode numbers (m) chosen to be used for the cos components

  • sin_m_chosen (numpy.ndarray | None) – Poloidal mode numbers (m) chosen to be used for the sin components

  • sig_figures (int) – Number of significant figures for rounding currents2harmonics values

Returns:

  • Am_cos – Cos component of matrix of harmonic amplitudes,

  • Am_sin – Sin component of matrix of harmonic amplitudes

Return type:

tuple[numpy.ndarray, numpy.ndarray]

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.toroidal_harmonic_approximate_psi(eq: bluemira.equilibria.equilibrium.Equilibrium, th_params: ToroidalHarmonicsParams, cos_m_chosen: numpy.ndarray | None = None, sin_m_chosen: numpy.ndarray | None = None) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Approximate psi using toroidal harmonic amplitudes for the specified cos and sin poloidal mode numbers (m) as calculated in coil_toroidal_harmonic_amplitude_matrix.

coil_toroidal_harmonic_amplitude_matrix returns Am_cos and Am_sin, which we use here.

..math::

A_{m} = frac{mu_{0} I_{c}}{2^{5/2}} frac{(2m+1)!!}{2^m m!} frac{sinh{tau_{c}}}{Delta_{c}^{1/2}} P_{m-frac{1}{2}}^{-1}(cosh(tau_c))

A_{m}^{sin} = A_m sin(m sigma_c) A_{m}^{cos} = A_m cos(m sigma_c) A(tau, sigma) = sum_{m=0}^{infty} A_{m}^{cos} epsilon_{m} m! sqrt{frac{2}{pi}}Delta^{frac{1}{2}} textbf{Q}_{m-frac{1}{2}}^{1} (cosh tau) cos(m sigma) + A_{m}^{sin}epsilon_{m} m! sqrt{frac{2} {pi}} Delta^{frac{1}{2}} textbf{Q}_{m-frac{1}{2}}^{1}(cosh tau) sin(m sigma)

Parameters:
  • eq (bluemira.equilibria.equilibrium.Equilibrium) – Bluemira Equilibrium

  • th_params (ToroidalHarmonicsParams) – Dataclass holding necessary parameters for the TH approximation

  • cos_m_chosen (numpy.ndarray | None) – Poloidal mode numbers (m) chosen to be used for the cos components

  • sin_m_chosen (numpy.ndarray | None) – Poloidal mode numbers (m) chosen to be used for the sin components

Returns:

  • approx_coilset_psi – Matrix of coilset psi values approximated using TH

  • Am_cos @ currents – TH cos coefficients for required number of poloidal mode numbers (m)

  • Am_sin @ currents – TH sin coefficients for required number of poloidal mode numbers (m)

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions._separate_psi_contributions(eq: bluemira.equilibria.equilibrium.Equilibrium, th_params: ToroidalHarmonicsParams, collocation: bluemira.equilibria.optimisation.harmonics.harmonics_approx_functions.Collocation | None = None) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Separate the psi contributions from fixed sources (plasma + excluded coils) and from potentially variable sources (coilset).

Excluded coils are any coils not being used in the toroidal harmonic approximation, e.g. they fall within the region over which we are approximating and so their contribution must be fixed.

Parameters:
Returns:

  • coilset_psi - excluded_coil_psi – The psi contribution from coils for which we allow the currents to vary. These coils are outside of our approximation region

  • plasma_psi + excluded_coil_psi – The psi contribution from fixed sources

  • collocation_coilset_psi – The psi contribution from the coilset at the specified collocation points. If no collocation points are specified, this is None

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions._set_n_degrees_of_freedom(n_dof: int | None, max_harmonic_mode: int, max_n_dof: int) int

Determine the number of degrees of freedom to use. This is limited by the number of coils and by the maximum poloidal mode number (m) of the harmonic functions.

Parameters:
  • n_dof (int | None) – The number of harmonic functions (and amplitudes) to choose. If this is None, then n_dof is calculated as the minimum of max_n_dof and 2 * max_harmonic_mode

  • max_harmonic_mode (int) – The maximum poloidal mode number of the harmonic functions to use

  • max_n_dof (int) – The maximum number of degrees of freedom that could be used. This is equal to the number of coils used in the approximation

Returns:

The number of harmonic functions (and amplitudes) to choose. If None, will default to the number of “free” coils. Will warn if input n_dof is inappropriate and will instead select an appropriate n_dof to return

Return type:

n_dof

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions._get_plasma_mask(eq: bluemira.equilibria.equilibrium.Equilibrium, th_params: ToroidalHarmonicsParams, psi_norm: float, *, plasma_mask: bool) int | numpy.ndarray

Get a plasma mask to apply to the psi field.

Parameters:
  • eq (bluemira.equilibria.equilibrium.Equilibrium) – Bluemira Equilibrium

  • th_params (ToroidalHarmonicsParams) – Dataclass holding necessary parameters for the TH approximation

  • plasma_mask (bool) – Whether or not to apply a mask to the error metric (within the psi_norm flux surface)

  • psi_norm (float) – Normalised flux value of the surface of interest.

Returns:

The plasma mask to be applied to the psi field

Return type:

mask

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.toroidal_harmonics_to_positions(th_params: ToroidalHarmonicsParams, n_allowed: int, collocation: bluemira.equilibria.optimisation.harmonics.harmonics_approx_functions.Collocation | None = None) tuple[numpy.ndarray, numpy.ndarray]

Matrix for collocation psi fitting or psi calculation across grid with known amplitude values.

Parameters:
Returns:

  • harmonics2collocation_cos – collocation matrix for cos components

  • harmonics2collocation_sin – collocation matrix for sin components

Raises:

EquilibriaError – If there are too few collocation points used.

Return type:

tuple[numpy.ndarray, numpy.ndarray]

class bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.ToroidalHarmonicsSelectionResult

Toroidal harmonic selection result dataclass

cos_m: numpy.ndarray

Selected cosine poloidal mode numbers

sin_m: numpy.ndarray

Selected sine poloidal mode numbers

cos_amplitudes: numpy.ndarray

Selected cosine toroidal harmonic amplitudes

sin_amplitudes: numpy.ndarray

Selected sine toroidal harmonic amplitudes

error: float

Error of L2 norm when comparing approximated coilset psi to desired coilset psi

coilset_psi: numpy.ndarray

Approximated coilset psi

fixed_psi: numpy.ndarray

Background (fixed) psi

true_unfixed_psi: numpy.ndarray

Bluemira psi for toroidal harmonic coils

th_params: ToroidalHarmonicsParams

Set up info

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.toroidal_harmonic_approximation(eq: bluemira.equilibria.equilibrium.Equilibrium, th_params: ToroidalHarmonicsParams | None = None, psi_norm: float = 0.95, n_degrees_of_freedom: int | None = None, max_harmonic_mode: int = 5, *, plasma_mask: bool = False) ToroidalHarmonicsSelectionResult

Calculate the toroidal harmonic (TH) amplitudes/coefficients for a given number of degrees of freedom, using TH functions up to a given maximum poloidal mode number.

The optimal selection of harmonic functions is carried out by brute force for the different combinations, using an L2 norm of the error across the full psi map. If plasma_mask is specified the error is evaluated as the L2 norm of the psi map within the specified flux surface.

Parameters:
  • eq (bluemira.equilibria.equilibrium.Equilibrium) – Equilibrium to use as starting point for approximation. We will approximate psi using THs - the aim is to keep the core plasma contribution fixed (using TH amplitudes as constraints) while being able to vary the vacuum (coil) contribution, so that we do not need to re-solve for the equilibria during optimisation

  • th_params (ToroidalHarmonicsParams | None) – Dataclass containing necessary parameters for use in TH approximation. If th_params is None, then function defaults to finding the th_params by using toroidal_harmonic_grid_and_coil_setup with the focus point set to the effective centre.

  • psi_norm (float) – Normalised flux value of the surface of interest. None value will default to 0.95 flux surface.

  • n_degrees_of_freedom (int | None) – The number of harmonic functions (and amplitudes) to choose. If None, will default to the number of “free” coils

  • max_harmonic_mode (int) – The maximum poloidal mode number of the harmonic functions to use

  • plasma_mask (bool) – Whether or not to apply a mask to the error metric (within the psi_norm flux surface)

Returns:

ToroidalHarmonicsSelectionResult

Return type:

result

Raises:
  • EquilibriaError – Problem not setup for harmonics

  • ValueError – Number of degrees of freedom inappropriate

Notes

If no th_params are provided, the focus is set to the effective centre of the plasma, and the region defaults to using the maximum extent of the LCFS

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions._approximation_direct_from_currents(eq, th_params, cos_m_chosen, sin_m_chosen, true_coilset_psi, mask)

Approximate psi using the equation for toroidal harmonic amplitudes that is dependent on current

Returns:

  • error – Error of L2 norm when comparing approximated coilset psi to desired coilset psi

  • approximate_coilset_psi – Approximated coilset psi

  • cos_amps – Selected cosine toroidal harmonic amplitudes

  • sin_amps – Selected sine toroidal harmonic amplitudes

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions._approximation_from_psi_fitting(th_params, n_degrees_of_freedom, collocation, mode_id, max_harmonic_mode, collocation_psi, mask, true_coilset_psi)

Approximate psi by fitting for psi at collocation points using toroidal harmonic equations

Returns:

  • error – Error of L2 norm when comparing approximated coilset psi to desired coilset psi

  • approximate_coilset_psi – Approximated coilset psi

  • cos_amps – Selected cosine toroidal harmonic amplitudes

  • sin_amps – Selected sine toroidal harmonic amplitudes

Notes

The length of mode_id must be equal to n_degrees_of_freedom

bluemira.equilibria.optimisation.harmonics.toroidal_harmonics_approx_functions.plot_toroidal_harmonic_approximation(eq: bluemira.equilibria.equilibrium.Equilibrium, th_params: ToroidalHarmonicsParams, result: ToroidalHarmonicsSelectionResult, psi_norm: float = 0.95)

Plot the toroidal harmonic approximation of the coilset psi and the bluemira true coilset psi on the same graph to allow comparison. Also plot the psi_norm flux surfaces for the approximation psi and the equilibrium coilset psi

Parameters:
Returns:

The Matplotlib figure and axis

Return type:

f, ax