Specification of magnetic fields and geometries

Magnetic fields and machine geometry parts form the most important inputs for calculations driven by fusionsc. During computation, the descriptions for these information pieces undergo multiple states, which are useful to understand in order to sequence calls successfully while conserving computation resources.

Magnetic fields

The description of a magnetic field can be in one of 3 forms:

Abstract

Upon initial specification, the magnetic field will often take the form of a very high-level description. This description is minimal and human-readable, but can not be used directly by the underlying calculation machinery, since it references device-specific information that is not included in the message tree.

from fusionsc.devices import w7x
w7x.standard()
w7x:
  coilsAndCurrents:
    nonplanar: [15000, 15000, 15000, 15000, 15000]
    planar: [0, 0]
    trim: [0, 0, 0, 0, 0]
    control: [0, 0]
    coils:
      coils:
        invertMainCoils: true
        biotSavartSettings:
          width: 0.01
          stepSize: 0.01
        nWindMain: [108, 108, 108, 108, 108, 36, 36]
        nWindTrim: [48, 72, 48, 48, 48]
        nWindControl: [8, 8, 8, 8, 8, 8, 8, 8, 8, 8]
        invertControlCoils: [false, true, false, true, false, true, false, true, false, true]
        coilsDbSet:
          mainCoilOffset: 160
          trimCoilIDs: [350, 241, 351, 352, 353]
          controlCoilOffset: 230

Resolved

A resolved field is a magnetic field structure that has device-specific nodes replaced by their generic equivalents. For example, references to coil IDs or high-level field specifications will be replaced with data for the specific coils. To minimize data footprint and prevent redundant data transfer, the concrete coil shapes will usually be linked into the message tree by reference. Resolved fields hold all required data to compute the field at specific points without additional data.

Once set up with proper data providers, the resolution machinery does not require any further inputs to drive the resolution process, since all nodes are completely self-describing without extra context (however, they might link to information presently not available).

The resolution process is invoked by calling the MagneticConfig.resolve method to acquire the resolved equivalent of a field.

import fusionsc as fsc
fsc.resolve.importOfflineData('../w7x-op21.fsc')

lines = str(w7x.standard().resolve()).split("\n")
for i in range(20):
    print(lines[i])
print()
print('... {} lines follow ...'.format(len(lines) - 20))
sum:
  - scaleBy:
      field:
        sum:
          - filamentField:
              current: -1
              biotSavartSettings:
                width: 0.01
                stepSize: 0.01
              filament:
                ref: <capability>
              windingNo: 108
          - filamentField:
              current: -1
              biotSavartSettings:
                width: 0.01
                stepSize: 0.01
              filament:
                ref: <capability>
              windingNo: 108

... 736 lines follow ...

Computed

The field line tracer requires the magnetic field to be calculated on a slab grid, so that its values can be quickly interpolated. In contrast to the resolution process, the field computation depends on extra information in the form of the grid description.

The computed field is obtained through the MagneticField.compute(grid) method (which will also kick off the resolution process if needed). As a convenience feature, most tracing methods will optionally call this method for you if you specify the grid = ... keyword argument. However, keep in mind that the intermediate result will not be stored in this case, and you should only do this if you do not intend to run multiple calculations on the same field.

Note: For obvious reasons, computed fields are also resolved.

import fusionsc as fsc
fsc.resolve.importOfflineData('../w7x-op21.fsc')

grid = w7x.defaultGrid()
grid.nZ = 4
grid.nR = 4
grid.nPhi = 4

print(w7x.standard().compute(grid))
computedField:
  grid:
    rMin: 4
    rMax: 7
    zMin: -1.5
    zMax: 1.5
    nSym: 5
    nR: 4
    nZ: 4
    nPhi: 4

Machine geometries

Abstract & resolved

In a similar way to magnetic field descriptions, geometry descriptions can be either abstract or resolved. In this state, geometries contain nested nodes of grouping, transformation, tagging, and reference linking nodes that describe how to place and label meshes in the final geometry.

from fusionsc.devices import w7x

print(w7x.divertor("OP21"))
tags:
  - name: name
    value:
      text: OP2.1 Divertor
w7x: op21Divertor
from fusionsc.devices import w7x
import fusionsc as fsc

fsc.resolve.importOfflineData('../w7x-op21.fsc')
lines = str(w7x.divertor("OP21").resolve()).split("\n")
for i in range(20):
    print(lines[i])
print()
print('... {} lines follow ...'.format(len(lines) - 20))
tags:
  []
combined:
  - tags:
      - name: w7x-component-id
        value:
          uInt64: 14698
    mesh: <capability>
  - tags:
      - name: w7x-component-id
        value:
          uInt64: 14699
    mesh: <capability>
  - tags:
      - name: w7x-component-id
        value:
          uInt64: 14700
    mesh: <capability>
  - tags:
      - name: w7x-component-id

... 8083 lines follow ...

Merged

In its initial representation, a geometry description does not just include meshes, but also tagging and transformation operations that can allow the same mesh to be placed in multiple locations / orientations at different scales (and to receive different labels). Additionally, the mesh data do not have to be co-localized in memory, but can reference distributed data trees (with some nodes even representing yet to be loaded or computed information).

The merging process (accessible through geometry.Geometry.merge()) evaluates this initial description and produces a monolithic collection of individual meshes laid out together, each with the correct tagging and transformation options applied. This representation is well suited for transfer to computation devices (such as GPUs) and for monolithic storage and loading.

from fusionsc.devices import w7x
import fusionsc as fsc

fsc.resolve.importOfflineData('../w7x-op21.fsc')

print(w7x.divertor("OP21").merge())
tags:
  []
merged: <capability>

Indexed

While the merged representation can be accessed efficiently, many routines require further speedups for the frequently performed intersection tests. For this purpose, geometry has to be indexed along a cartesian grid. Similarly to the magnetic field computation, this process can be either requested ahead of time with the geometry.Geometry.index(grid) method, or by passing the geometryGrid = ... keyword argument to the tracing functions.

from fusionsc.devices import w7x
import fusionsc as fsc

fsc.resolve.importOfflineData('../w7x-op21.fsc')

grid = w7x.defaultGeometryGrid()

print(w7x.divertor("OP21").index(grid))
tags:
  []
indexed:
  grid:
    xMin: -7
    xMax: 7
    yMin: -7
    yMax: 7
    zMin: -1.5
    zMax: 1.5
    nX: 280
    nY: 280
    nZ: 60