pyrokinetics.normalisation

Contents

pyrokinetics.normalisation#

Classes for working with different conventions for normalisation.

Each piece of software that Pyrokinetics works with may use its own convention for normalising physical quantities, and each individual run or simulation may have different physical reference values. We want to be able to convert normalised quantities between these different conventions and simulations.

For example, GS2 normalises lengths to the minor radius, while GENE uses the major radius. We can convert lengths between the two codes if we know either the aspect ratio, or both the minor and major radius in physical units (for example, from a particular machine or equilibrium code). Both codes normalise speeds to the thermal velocity, except GS2’s definition includes an additional factor of the square root of two; this means we can always convert normalised speeds between the two codes simply by multipling or dividing by sqrt(2) as appropriate.

This module aims to make this process of conversion simpler by packaging up all the units and conventions together. Then, for example, we could convert a growth rate from GS2 to GENE’s normalisation like so:

growth_rate_gene = growth_rate_gs2.to(norms.gene)

where norms is a SimulationNormalisation instance.

We make a distinction between “simulation units” and “physical units”: simulation units are named something like lref_minor_radius, are always valid for a particular code, have a fictional dimension such as [lref], and usually need some extra information to convert them to a different unit. Physical units, on the other hand, are associated with a particular simulation and named something like lref_minor_radius_run1234, have a real reference value, for example 1.5 metres, and can be converted to other physical units without extra information.

Importantly, for a given convention, we can convert between its simulation and physical units. For example, a length of one in GS2 simulation units (that is, without a physical reference value) is always equivalent to one GS2 physical length unit. This is equivalent to defining the reference length for the simulation.

We define a “convention” to be a set of simulation units. These are:

  • bref: magnetic field

  • lref: Equilibrium length

  • mref: mass

  • nref: density

  • qref: charge

  • tref: temperature

  • vref: velocity

  • rhoref: gyroradius

For example, the GS2 convention has an lref of lref_minor_radius, and a vref of vref_most_probable; while GENE has lref_major_radius and vref_nrl respectively.

A unique SimulationNormalisation is created for each Pyro instance, and contains a dict of ConventionNormalisation instances. Each of these conventions contains a full set of units, that is lref, bref, and so on. The SimulationNormalisation has a default convention whose units can be accessed directly.

Initially, the convention’s units refer to the simulation units for that particular convention, but can be set to physical units:

>>> norm = SimulationNormalisation("run1234")
# We can access the units either through a particular convention:
>>> norm.gs2.lref
<Unit('lref_minor_radius')>
# Or through `norm` directly for the default convention
>>> norm.lref
<Unit('lref_minor_radius')>
# Providing a physical reference value changes simulation units
# to physical units
>>> norm.set_lref(minor_radius=1.5)
>>> norm.lref
<Unit('lref_minor_radius_run1234')>

We use [pint](https://pint.readthedocs.io) (with some local modifications) for the units. Read their docs to understand how to work with units generally.

The modifications here are to enable converting units to a convention. To convert growth rates between GS2 and GENE we could do:

growth_rate_gene = growth_rate_gs2.to(norms.gene.vref / norms.gene.lref)

Or more succinctly:

growth_rate_gene = growth_rate_gs2.to(norms.gene)

which converts all (simulation or physical) units to GENE’s normalisation convention.

Warning

bref is not a fundamental dimension, so converting magnetic fields needs to be done directly:

# Wrong! Units will be mref * vref / qref / lref
B0_cgyro = B0_gene.to(norms.cgyro)
# Right! Units will be bref
B0_cgyro = B0_gene.to(norms.cgyro.bref)

beta#

The magnetic \(\beta_N\) is a dimensionless quantity defined by:

\[\beta_N = \frac{2 \mu_0 n_{ref} T_{ref}}{B_{ref}^2}\]

When we have all of nref, tref, bref we can compute beta_ref, and we only need to track which convention it was defined in. We do so by giving it units in another arbitrary dimension, [beta_ref], with distinct units for each convention. We also don’t need to keep track of simulation and physical units separately, as these are always directly convertable. [beta_ref] is essentially just another name for “percent”, but gives us a mechanism to convert between normalisations.

Module Attributes

NORMALISATION_CONVENTIONS

Particular normalisation conventions

Functions

convert_dict(data, norm)

Copy data into a new dict, converting any quantities to other normalisation

Classes

Convention(name[, tref_species, ...])

A description of a normalisation convention, including what species reference values use, whether the velocity includes a sqrt(2) factor, what length scales are normalised to, and so on.

ConventionNormalisation(convention, parent)

A concrete set of reference values/normalisations.

SimulationNormalisation(name[, convention, ...])

Holds the normalisations for a given simulation for all the known conventions.