The Pleiades Code

The Pleiades code is designed for computing axisymmetric magnetic fields from combinations of electromagnets and permanent magnets with a focus on modeling plasma physics experiments. The project originated at the Wisconsin Plasma Physics Laboratory during experiments on the novel confinement device known as the Big Red Ball. Pleiades is written entirely in Python for now and is intended to be an easily accessible toolkit for computing and visualizing magnetic confinement geometries and data simultaneously.

Aside from simple computations of magnetic fields, there is a plasma MHD equilibrium solver incorporated into the package as well, which has been used to generate magnetic mirror equilibria and input files suitable for being run with the GENRAY and CQL3D codes for the design of the new Wisconsin HTS Axisymmetric Mirror (WHAM) and its eventual upgrade to a neutron source (WHAM-NS).

The Pleiades code is available on github. Any and all feedback and contributions are welcome!

Contents

Quick Install Guide

To install the Pleiades code. Simply clone the github repository and install with pip. An example for how to do this on linux is shown below.

Installing on Linux

git clone https://github.com/eepeterson/pleiades
cd pleiades
pip install -e .

Examples

Here are some (1 right now) example Jupyter Notebooks for showing users how to get started with the Pleiades package.

General Usage

Introduction

intro_to_coil_sets

Introduction to Coil Sets, Devices, and Fields

The purpose of this tutorial is to develop familiarity with the fundamental objects we will need to create, manipulate, and analyze magnetic configurations.

The four objectives of this tutorial are enumerated below:

  1. Create coil sets and transform them through translations and rotations.
  2. Create suitable meshes and use them to create Green's functions for flux and magnetic field components
  3. Learn how to combine sets of coils into a Device object
  4. Understand and plot the results of goals 1, 2, and 3

Just like any other python notebook we start with importing standard libraries as well as our project's specific functionality

In [1]:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

from pleiades import RectangularCoil

Now we will create an instance of the RectangularCoil class as it is by far the most common coil set used for building electromagnets for plasma physics or fusion experiments. We create a RectangularCoil by specifying at a minimum the centroid (r0, z0) of the regular array of current filaments in cylindrical coordinates and with SI units.

In [2]:
rc1 = RectangularCoil(1, .5, nr=10, nz=10, dr=.01, dz=.01)
print(rc1)
CurrentFilamentSet
          Class:  <class 'pleiades.current_sets.RectangularCoil'>
        Current:  1.000000e+00 amps
    N Filaments:  100
  Total Current:  1.000000e+02 amps
        R, Z, W:  [9.550000e-01, 4.550000e-01, 1.000000e+00]
                  [9.550000e-01, 4.650000e-01, 1.000000e+00]
                  [9.550000e-01, 4.750000e-01, 1.000000e+00]
                  [9.550000e-01, 4.850000e-01, 1.000000e+00]
                  [9.550000e-01, 4.950000e-01, 1.000000e+00]
                  [9.550000e-01, 5.050000e-01, 1.000000e+00]
                  [9.550000e-01, 5.150000e-01, 1.000000e+00]
                  [9.550000e-01, 5.250000e-01, 1.000000e+00]
                  [9.550000e-01, 5.350000e-01, 1.000000e+00]
                  [9.550000e-01, 5.450000e-01, 1.000000e+00]
                  [9.650000e-01, 4.550000e-01, 1.000000e+00]
                  [9.650000e-01, 4.650000e-01, 1.000000e+00]
                  [9.650000e-01, 4.750000e-01, 1.000000e+00]
                  [9.650000e-01, 4.850000e-01, 1.000000e+00]
                  [9.650000e-01, 4.950000e-01, 1.000000e+00]
                  [9.650000e-01, 5.050000e-01, 1.000000e+00]
                  [9.650000e-01, 5.150000e-01, 1.000000e+00]
                  [9.650000e-01, 5.250000e-01, 1.000000e+00]
                  [9.650000e-01, 5.350000e-01, 1.000000e+00]
                  [9.650000e-01, 5.450000e-01, 1.000000e+00]
                  [9.750000e-01, 4.550000e-01, 1.000000e+00]
                  [9.750000e-01, 4.650000e-01, 1.000000e+00]
                  [9.750000e-01, 4.750000e-01, 1.000000e+00]
                  [9.750000e-01, 4.850000e-01, 1.000000e+00]
                  [9.750000e-01, 4.950000e-01, 1.000000e+00]
                  [9.750000e-01, 5.050000e-01, 1.000000e+00]
                  [9.750000e-01, 5.150000e-01, 1.000000e+00]
                  [9.750000e-01, 5.250000e-01, 1.000000e+00]
                  [9.750000e-01, 5.350000e-01, 1.000000e+00]
                  [9.750000e-01, 5.450000e-01, 1.000000e+00]
                  [9.850000e-01, 4.550000e-01, 1.000000e+00]
                  [9.850000e-01, 4.650000e-01, 1.000000e+00]
                  [9.850000e-01, 4.750000e-01, 1.000000e+00]
                  [9.850000e-01, 4.850000e-01, 1.000000e+00]
                  [9.850000e-01, 4.950000e-01, 1.000000e+00]
                  [9.850000e-01, 5.050000e-01, 1.000000e+00]
                  [9.850000e-01, 5.150000e-01, 1.000000e+00]
                  [9.850000e-01, 5.250000e-01, 1.000000e+00]
                  [9.850000e-01, 5.350000e-01, 1.000000e+00]
                  [9.850000e-01, 5.450000e-01, 1.000000e+00]
                  [9.950000e-01, 4.550000e-01, 1.000000e+00]
                  [9.950000e-01, 4.650000e-01, 1.000000e+00]
                  [9.950000e-01, 4.750000e-01, 1.000000e+00]
                  [9.950000e-01, 4.850000e-01, 1.000000e+00]
                  [9.950000e-01, 4.950000e-01, 1.000000e+00]
                  [9.950000e-01, 5.050000e-01, 1.000000e+00]
                  [9.950000e-01, 5.150000e-01, 1.000000e+00]
                  [9.950000e-01, 5.250000e-01, 1.000000e+00]
                  [9.950000e-01, 5.350000e-01, 1.000000e+00]
                  [9.950000e-01, 5.450000e-01, 1.000000e+00]
                  [1.005000e+00, 4.550000e-01, 1.000000e+00]
                  [1.005000e+00, 4.650000e-01, 1.000000e+00]
                  [1.005000e+00, 4.750000e-01, 1.000000e+00]
                  [1.005000e+00, 4.850000e-01, 1.000000e+00]
                  [1.005000e+00, 4.950000e-01, 1.000000e+00]
                  [1.005000e+00, 5.050000e-01, 1.000000e+00]
                  [1.005000e+00, 5.150000e-01, 1.000000e+00]
                  [1.005000e+00, 5.250000e-01, 1.000000e+00]
                  [1.005000e+00, 5.350000e-01, 1.000000e+00]
                  [1.005000e+00, 5.450000e-01, 1.000000e+00]
                  [1.015000e+00, 4.550000e-01, 1.000000e+00]
                  [1.015000e+00, 4.650000e-01, 1.000000e+00]
                  [1.015000e+00, 4.750000e-01, 1.000000e+00]
                  [1.015000e+00, 4.850000e-01, 1.000000e+00]
                  [1.015000e+00, 4.950000e-01, 1.000000e+00]
                  [1.015000e+00, 5.050000e-01, 1.000000e+00]
                  [1.015000e+00, 5.150000e-01, 1.000000e+00]
                  [1.015000e+00, 5.250000e-01, 1.000000e+00]
                  [1.015000e+00, 5.350000e-01, 1.000000e+00]
                  [1.015000e+00, 5.450000e-01, 1.000000e+00]
                  [1.025000e+00, 4.550000e-01, 1.000000e+00]
                  [1.025000e+00, 4.650000e-01, 1.000000e+00]
                  [1.025000e+00, 4.750000e-01, 1.000000e+00]
                  [1.025000e+00, 4.850000e-01, 1.000000e+00]
                  [1.025000e+00, 4.950000e-01, 1.000000e+00]
                  [1.025000e+00, 5.050000e-01, 1.000000e+00]
                  [1.025000e+00, 5.150000e-01, 1.000000e+00]
                  [1.025000e+00, 5.250000e-01, 1.000000e+00]
                  [1.025000e+00, 5.350000e-01, 1.000000e+00]
                  [1.025000e+00, 5.450000e-01, 1.000000e+00]
                  [1.035000e+00, 4.550000e-01, 1.000000e+00]
                  [1.035000e+00, 4.650000e-01, 1.000000e+00]
                  [1.035000e+00, 4.750000e-01, 1.000000e+00]
                  [1.035000e+00, 4.850000e-01, 1.000000e+00]
                  [1.035000e+00, 4.950000e-01, 1.000000e+00]
                  [1.035000e+00, 5.050000e-01, 1.000000e+00]
                  [1.035000e+00, 5.150000e-01, 1.000000e+00]
                  [1.035000e+00, 5.250000e-01, 1.000000e+00]
                  [1.035000e+00, 5.350000e-01, 1.000000e+00]
                  [1.035000e+00, 5.450000e-01, 1.000000e+00]
                  [1.045000e+00, 4.550000e-01, 1.000000e+00]
                  [1.045000e+00, 4.650000e-01, 1.000000e+00]
                  [1.045000e+00, 4.750000e-01, 1.000000e+00]
                  [1.045000e+00, 4.850000e-01, 1.000000e+00]
                  [1.045000e+00, 4.950000e-01, 1.000000e+00]
                  [1.045000e+00, 5.050000e-01, 1.000000e+00]
                  [1.045000e+00, 5.150000e-01, 1.000000e+00]
                  [1.045000e+00, 5.250000e-01, 1.000000e+00]
                  [1.045000e+00, 5.350000e-01, 1.000000e+00]
                  [1.045000e+00, 5.450000e-01, 1.000000e+00]

We can learn the basic properties of a coil set just by printing it, but to see what other attributes it has let's print some of those as well.

In [4]:
print(f'Centroid: {rc1.centroid}, Units: m')
print(f'Cross-sectional area: {rc1.area}, Units: m$^2$')
print(f'Current per filament: {rc1.current}, Units: A')
print(f'Total current: {rc1.total_current}, Units: A')
print(f'Current density: {rc1.current_density}, Units: A/m$^2$')
Centroid: [1.  0.5], Units: m
Cross-sectional area: 0.01, Units: m$^2$
Current per filament: 1.0, Units: A
Total current: 100.0, Units: A
Current density: 10000.0, Units: A/m$^2$

Now we will try our hand at plotting some of this data as well as the visulaizations. to access the fields like psi, B_R, and B_Z we simply call their attributes from the brb object as shown below.

In [5]:
from pleiades import RectMesh

mesh = RectMesh(rmin=0, rmax=3, zmin=0, zmax=3)
rc1.mesh = mesh
rc1.psi()
Out[5]:
array([0.00000000e+00, 2.02281630e-08, 8.09125551e-08, ...,
       4.59174341e-06, 4.60797399e-06, 4.62333460e-06])
In [5]:
fig, ax = plt.subplots()
rc1.plot(ax, color='k')
ax.contour(mesh.R, mesh.Z, rc1.psi().reshape(mesh.R.shape), 51)
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)
ax.set_aspect(1)
ax.set_xlabel('R (m)')
ax.set_ylabel('Z (m)')
plt.show()
fig, ax = plt.subplots()
rc1.plot(ax, color='k')
ax.contour(mesh.R, mesh.Z, rc1.psi().reshape(mesh.R.shape), 11)
ax.set_xlim(.9, 1.1)
ax.set_ylim(.4, .6)
ax.set_aspect(1)
ax.set_xlabel('R (m)')
ax.set_ylabel('Z (m)')
plt.show()

Big Red Ball (BRB) Basic Usage

BRB_intro

The Big Red Ball (BRB) Basic Functionality

In [1]:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

from pleiades import RectMesh
from pleiades.configurations import BRB
In [2]:
brb = BRB()
In [6]:
brb.hh_s.current = 50
brb.hh_n.current = 50
In [4]:
mesh = RectMesh(rmin=0, rmax=3, zmin=-2, zmax=2, nr=101, nz=101)
brb.mesh = mesh
In [7]:
fig, ax = plt.subplots(figsize=(4,6))

brb.plot(ax)
ax.set_xlim(0, 3)
ax.set_ylim(-2, 2)
ax.set_aspect(1)
ax.set_xlabel('R (m)')
ax.set_ylabel('Z (m)')
plt.show()
In [ ]:
 

Wisconsin HTS Axisymmetric Mirror (WHAM) Basic Usage

wham_intro
In [1]:
import numpy as np
import os
import matplotlib.pyplot as plt
from matplotlib import ticker
from matplotlib.collections import PatchCollection
import matplotlib.colors as colors

from pleiades import RectMesh, compute_equilibrium, write_eqdsk
from pleiades.configurations import WHAM
from pleiades.analysis import get_gpsi, locs_to_vals, get_fieldlines
In [2]:
# create the WHAM device
wham = WHAM()

# choose values for HTS mirror coils and central cell coils
wham.hts1.current = 160000
wham.hts2.current = 160000
wham.cc1.current = 60000
wham.cc2.current = 60000

mesh = RectMesh(rmin=0, rmax=0.75, zmin=-1, zmax=1, nr=76, nz=201)
R, Z = mesh.R, mesh.Z

# set the brb grid (does all greens functions calculations right here)
wham.mesh = mesh
In [3]:
# get desired field quantities from brb object and view coilset
B = np.sqrt(wham.BR()**2 + wham.BZ()**2).reshape(R.shape)
BR = wham.BR().reshape(R.shape)
BZ = wham.BZ().reshape(R.shape)
psi = wham.psi().reshape(R.shape)
psi_lim = locs_to_vals(R, Z, psi, [(1.25, 0)])[0]
psi_space = np.linspace(0, psi_lim, 11)
bspace = np.logspace(-3, 1, 41)

# Plotting Section
fig, ax = plt.subplots()
cf = ax.contourf(Z, R, BZ, bspace, cmap="rainbow", locator=ticker.LogLocator(), extend='min')
ax.contour(Z, R, psi, 21, colors="k", linewidths=0.5)
fig.colorbar(cf)
ax.set_xlim(-1., 1.)
ax.set_ylim(0, 0.75)
ax.set_xlabel("Z (m)")
ax.set_ylabel("R (m)")
ax.set_aspect(1)
plt.show()

# Zoomed in Plotting Section
fig,ax = plt.subplots()
cf = ax.contourf(Z,R,B,bspace,cmap="rainbow",locator=ticker.LogLocator(), extend='min')
ax.contour(Z,R,psi,21,colors="k", linewidths=0.5)
fig.colorbar(cf)
ax.set_xlim(-1., 1.)
ax.set_ylim(0, 0.75)
ax.set_xlabel("Z (m)")
ax.set_ylabel("R (m)")
ax.set_aspect(1)
plt.show()
/home/peterson/pkgs/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:12: UserWarning: Log scale: values of z <= 0 have been masked
  if sys.path[0] == '':

Now lets look at an equilibrium... A few parameters are important here: namely the plasma limiting radius, a, the peak pressure, P0, the exponent of the pressure profile, alpha, and the plasma beta at the origin with respect to the vacuum field. Once we build the pressure function and get the Green's function for plasma currents on the grid we are ready to run compute_equilibrium and write out an eqdsk. When this cell is run you should see the iteration number and error print to the screen and the error should decrease to a small value below 1E-10 by default to know the computation has converged.

In [4]:
# setup pressure profile and compute P0 for a given desired initial beta
a = .5
alpha = 2.0
beta0 = .1
B0 = locs_to_vals(R,Z,B,[(0,0)])[0]
P0 = beta0*B0**2/(2*4*np.pi*1E-7)
print("pressure ", P0)
# build pressure function of cylindrical radius
Pfunc = lambda x: P0*(1-(x/a)**2)**alpha if x < a else 0
# get greens function for plasma currents
gplas = get_gpsi(R,Z)
# compute equilibrium
psieq,plas_currents,pfit = compute_equilibrium(R,Z,Pfunc,psi,gplas,maxiter=400,plotit=False)
write_eqdsk(R,Z,psi,plas_currents,"wham_eqdsk.txt","WHAM_Mirror Equilib")
P = pfit(psi.flatten()).reshape(psi.shape)
jphi = plas_currents/((R[0,1]-R[0,0])*(Z[1,0]-Z[0,0]))
pressure  55831.54115016246
computing gpsi blocks...
creating reflected block matrix
building huge matrix...
returning...
0
1
1
0.8961154012763798
2
0.005589373519163506
3
0.00012944851537745114
4
2.9734097311246333e-06
5
2.7443691110110562e-08
6
2.657020365635984e-10
7
8.560402008105758e-12

Below we will plot some flux lines as well as the diamagnetic current density j_phi in A/m^2 in the colormap. We've also included a demonstration of tracing field lines which can be used to interpolate values along a field line for stability calculations as well as flux surface averaging.

In [5]:
fig,ax = plt.subplots()
cf = ax.contourf(R,Z,jphi,101)
cs = ax.contour(R,Z,psieq,psi_space,colors="k")
for clev in psi_space[1:]:
    flpts = get_fieldlines(cs,clev,start_coord=(0.05,.98),end_coord=(0.05,-0.98),clockwise=True)
    ax.plot(flpts[:,0],flpts[:,1],"bo")
    ax.plot(flpts[0,0],flpts[0,1],"go")
    ax.plot(flpts[-1,0],flpts[-1,1],"ro")
ax.set_xlabel("R (m)")
ax.set_ylabel("Z (m)")
cbar = fig.colorbar(cf)
cbar.set_label("J_phi (A/m^2)")
plt.show()

Here let's plot a slice of B along the axis of symmetry.

In [6]:
#### Plotting 
# plot slice of B as function of Z at R=0
ridx = np.abs(R[0,:]).argmin()
plt.plot(Z[:,ridx],B[:,ridx])
plt.xlabel("Z @ R=0 (m)")
plt.ylabel("B (T)")
plt.show()

Equilibrium Solver Basics

pleiades_intro

Welcome to Pleiades! the python package for Plasma Experiment Iteratively Advanced Design and Simulation

The goal of this package is to make designing magnetic confinement experiments simple, intuitive and fast for the purpose of interfacing with advanced plasma physics codes like NIMROD, GENRAY, and CQL3D and optimization of design considerations.

This notebook is broken after the core functionality was reworked

There are 4 main sections to the code base that focus on the fundamental stages of experimental design. They are as follows:

  1. An object oriented coil design architecture with built in Green's functions for axisymmetric field calculations and incorporated visulization
  2. A simple MHD equilibrium solver
  3. Preliminary stability calculations (m=1 flute mode and m=\inf ballooning) for magnetic mirrors
  4. Integrated IO module for generating input files to other codes like eqdsks for GENRAY and CQL3D

This example notebook demonstrates how to use an existing experiment known as the Big Red Ball (BRB) located at the Wisconsin Plasma Physics Laboratory (WIPPL) and add an additional coil set to produce a non-paraxial magnetic mirror. A vacuum field geometry is selected and an equilibrium is computed for a given pressure profile. Finally an eqdsk compatible with GENRAY and CQL3D is generated. There are plots and explanations along the way to help you get your feet wet!

Necessary import section is shown below. Pleiades is designed to be compatible with an out-of-the-box installation of Anaconda2/3 and the primary dependencies are numpy/scipy and matplotlib. If part of this notebook doesn't work for you please let us know and we will try to get a fix in place as soon as possible!

In [1]:
import numpy as np
import os
import matplotlib.pyplot as plt
from matplotlib import ticker
from matplotlib.collections import PatchCollection
import matplotlib.colors as colors

from pleiades import RectMesh, compute_equilibrium, write_eqdsk
from pleiades.configurations import BRB
from pleiades.analysis import get_gpsi, locs_to_vals, get_fieldlines

Now we will start by creating our Experiment which is done by instantiating an object of the BRB class. We will add an extra coil set, set some coil current values and create a grid in the R-Z plane for the ensuing computations.

In [2]:
# create the BRB object
brb = BRB()

# choose values for TREX HH coil (2kA) and current in mirror coils to (100kA) and assign them
hh_cur = 200
mir_cur = 100000
brb.hh_n.current = hh_cur
brb.hh_s.current = hh_cur
brb.ltrx_n.currents = mir_cur
brb.ltrx_s.currents = mir_cur

# turn off current in ltrx coils and in the vessel magnets so they don't mess with the flux plot
# also hide the visualization for the ltrx coils with brb.ltrx.patch_mask
#brb.vessel_mags.currents = [0,0,0]

# build the grid for field computation - creates a structured R-Z grid from R=[0,1] with 101 points and Z=[-.5,5] with 151 points
mesh = RectMesh(rmin=0, rmax=2, zmin=-2, zmax=2, nr=201, nz=201)
R, Z = mesh.R, mesh.Z

# set the brb grid (does all greens functions calculations right here)
brb.mesh = mesh

Here we will force the shape of the limiting flux surface by specifying the flux line through the mirror throat at (R,Z) = (0.08m,0.5m) to also pass through the point (R,Z) = (0.6m,0.0m). The mirror coil current is fixed and the TREX coil current is solved in order to enforce this criterion.

In [3]:
# force psi_lim through rlim1,zlim1 and rlim2,zlim2
#rlim1,zlim1 = 0.08,0.5
#rlim2,zlim2 = 0.6,0.0
#r1idx,z1idx = np.abs(R[0,:]-rlim1).argmin(), np.abs(Z[:,0]-zlim1).argmin()
#r2idx,z2idx = np.abs(R[0,:]-rlim2).argmin(), np.abs(Z[:,0]-zlim2).argmin()
#gm1,gm2 = np.sum(brb.mirrors.gpsi,axis=-1).reshape(R.shape)[z1idx,r1idx], np.sum(brb.mirrors.gpsi,axis=-1).reshape(R.shape)[z2idx,r2idx]
#gc1,gc2 = np.sum(brb.trex.gpsi,axis=-1).reshape(R.shape)[z1idx,r1idx], np.sum(brb.trex.gpsi,axis=-1).reshape(R.shape)[z2idx,r2idx]
#gp1,gp2 = 0, 0
#iplas = 0
#new_trex_cur = -((gm1-gm2)*brb.mirrors.currents[0] + (gp1-gp2)*iplas)/(gc1 - gc2)
#print(new_trex_cur)
#brb.trex.currents = [new_trex_cur,new_trex_cur]

Now we will try our hand at plotting some of this data as well as the visulaizations. to access the fields like psi, B_R, and B_Z we simply call their attributes from the brb object as shown below.

In [4]:
# get desired field quantities from brb object and view coilset
B = np.sqrt(brb.BR()**2 + brb.BZ()**2).reshape(R.shape)
BR = brb.BR().reshape(R.shape)
BZ = brb.BZ().reshape(R.shape)
psi = brb.psi().reshape(R.shape)
psi_lim = locs_to_vals(R, Z, psi, [(1.25, 0)])[0]
psi_space = np.linspace(0, psi_lim, 11)

# Plotting Section
fig, ax = plt.subplots()
cf = ax.contourf(R, Z, B, 101, cmap="rainbow", locator=ticker.LogLocator())
ax.contour(R, Z, psi, 51, colors="k")
plt.colorbar(cf)
ax.set_xlim(0, 2.5)
ax.set_ylim(-1.75, 1.75)
ax.set_xlabel("R (m)")
ax.set_ylabel("Z (m)")
ax.set_aspect(1)
plt.show()

# Zoomed in Plotting Section
fig,ax = plt.subplots()
cf = ax.contourf(R,Z,B,101,cmap="rainbow",locator=ticker.LogLocator())
ax.contour(R,Z,psi,51,colors="k")
plt.colorbar(cf)
ax.set_xlim(0,1.2)
ax.set_ylim(-.6,.6)
ax.set_xlabel("R (m)")
ax.set_ylabel("Z (m)")
ax.set_aspect(1)
plt.show()
/home/peterson/pkgs/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:11: UserWarning: Log scale: values of z <= 0 have been masked
  # This is added back by InteractiveShellApp.init_path()
/home/peterson/pkgs/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:23: UserWarning: Log scale: values of z <= 0 have been masked

Now lets look at an equilibrium... A few parameters are important here: namely the plasma limiting radius, a, the peak pressure, P0, the exponent of the pressure profile, alpha, and the plasma beta at the origin with respect to the vacuum field. Once we build the pressure function and get the Green's function for plasma currents on the grid we are ready to run compute_equilibrium and write out an eqdsk. When this cell is run you should see the iteration number and error print to the screen and the error should decrease to a small value below 1E-10 by default to know the computation has converged.

In [ ]:
# setup pressure profile and compute P0 for a given desired initial beta
a = .6
alpha = 2.0
beta0 = .1
B0 = locs_to_vals(R,Z,B,[(0,0)])[0]
P0 = beta0*B0**2/(2*4*np.pi*1E-7)
print("pressure ", P0)
# build pressure function of cylindrical radius
Pfunc = lambda x: P0*(1-(x/a)**2)**alpha if x < a else 0
# get greens function for plasma currents
gplas = get_gpsi(R,Z)
# compute equilibrium
psieq,plas_currents,pfit = compute_equilibrium(R,Z,Pfunc,psi,gplas,maxiter=400,plotit=False)
write_eqdsk(R,Z,psi,plas_currents,"mfnpeqdsk1.txt","MFNP_Mirror Equilib")
P = pfit(psi.flatten()).reshape(psi.shape)
jphi = plas_currents/((R[0,1]-R[0,0])*(Z[1,0]-Z[0,0]))
pressure  0.0
computing gpsi blocks...
creating reflected block matrix
building huge matrix...

Below we will plot some flux lines as well as the diamagnetic current density j_phi in A/m^2 in the colormap. We've also included a demonstration of tracing field lines which can be used to interpolate values along a field line for stability calculations as well as flux surface averaging.

In [ ]:
fig,ax = plt.subplots()
cf = ax.contourf(R,Z,jphi,101)
cs = ax.contour(R,Z,psieq,psi_space,colors="k")
for clev in psi_space[1:]:
    flpts = get_fieldlines(cs,clev,start_coord=(0.05,.5),end_coord=(0.05,-0.5),clockwise=True)
    ax.plot(flpts[:,0],flpts[:,1],"bo")
    ax.plot(flpts[0,0],flpts[0,1],"go")
    ax.plot(flpts[-1,0],flpts[-1,1],"ro")
ax.set_xlabel("R (m)")
ax.set_xlabel("Z (m)")
cbar = fig.colorbar(cf)
cbar.set_label("J_phi (A/m^2)")
plt.show()

Here let's plot a slice of B along the axis of symmetry.

In [ ]:
#### Plotting 
# plot slice of B as function of Z at R=0
ridx = np.abs(R[0,:]).argmin()
plt.plot(Z[:,ridx],B[:,ridx])
plt.xlabel("Z @ R=0 (m)")
plt.ylabel("B (T)")
plt.show()

Now let's look at how the flux lines change with finite plasma beta. the black lines are the vacuum field lines and green are the equilibrium field line values for the given beta and pressure profile. The red line shows the limiting flux surface.

In [ ]:
# plot up close flux lines and J_phi
fig,ax = plt.subplots(figsize=(10,8))
#cf = ax.contourf(R,Z,B,101,cmap="rainbow",locator=ticker.LogLocator(),zorder=0)
cf = ax.contourf(R,Z,jphi,101,zorder=0)
cs = ax.contour(R,Z,psi,51,colors='k',zorder=1)
cs1 = ax.contour(R,Z,psieq,51,colors='g',zorder=1)
cs = ax.contour(R,Z,psieq,(psi_lim,),colors='r',zorder=1)
cbar = fig.colorbar(cf)
cbar.set_label("J_phi (A/m^2)",fontsize=16)
ax.set_xlim(0,1.0)
ax.set_ylabel("Z (m)")
ax.set_xlabel("R (m)")
ax.set_ylim(-.5,.5)
ax.set_aspect('equal')
ax.add_collection(PatchCollection(brb.patches,match_original=True))
plt.show()
In [ ]:
 

Python API

This page contains documentation on the Python modules, classes, and functions that comprise the pleiades package.

Modules

pleiades – Basic Functionality

Building Magnetic Configurations

pleiades.RectangularCoil A rectangular cross section coil in the R-Z plane
pleiades.Device A container for a full configuration of magnets for an experiment
pleiades.CurrentFilamentSet Set of locations that have the same current value.
pleiades.FieldsOperator Mixin class for computing fields on meshes
pleiades.compute_greens Compute axisymmetric Green’s functions for magnetic fields

Building Meshes

pleiades.Mesh A mesh in the R-Z plane for calculating magnetic fields and flux.
pleiades.RectMesh A regular linearly spaced 2D R-Z mesh.
pleiades.PointsMesh An unstructured mesh specified by two lists of coordinate points.
pleiades.RChord A 1D chord of cylindrical radii at fixed height z.
pleiades.ZChord A 1D chord of Z values at fixed cylindrical radius.
pleiades.SphericalRChord A 1D chord of spherical radius at fixed polar angle theta.
pleiades.ThetaChord A 1D chord of polar angles at fixed spherical radius r.

WIPPL Magnetic Configurations

pleiades.configurations.BRB The Device object representing the Big Red Ball at UW-Madison.
pleiades.configurations.PCX_magCage
pleiades.configurations.PCX_HH

Computing Equilibria

pleiades.compute_equilibrium Compute jxB=gradP equilibrium for given P as function of R
pleiades.mirror_equilibrium Compute jxB=gradP equilibrium for given P as function of psi/psi_lim given by P(psi) = p0*(1-(psi/psi_lim)**alpha1)**alpha2

IO Functionality

pleiades.write_eqdsk

pleiades.configurations – Existing Experimental Facility Models

WIPPL Magnetic Configurations

pleiades.configurations.BRB The Device object representing the Big Red Ball at UW-Madison.
pleiades.configurations.PCX_magCage
pleiades.configurations.PCX_HH

pleiades.analysis – Analysis Functions

Mathematical Functions

pleiades.analysis.get_gpsi

Plotting Helper Functions

pleiades.analysis.get_mirror_patches Get mirror image patches across desired axis.
pleiades.analysis.scale_patches scale patches by desired factor.
pleiades.analysis.transpose_patches Transpose patches (reflect across line y=x).

Interpolation Helper Functions

pleiades.analysis.contour_points
pleiades.analysis.reflect_and_hstack Reflect and concatenate grid and quantities in args to plot both half planes (rho>=0 and rho<=0).
pleiades.analysis.regular_grid
pleiades.analysis.poly_fit
pleiades.analysis.transform_to_rtheta Transform Rho Z grid and rho,z components of vector field to polar coordinates
pleiades.analysis.transform_to_rhoz Transform R Theta grid and r theta components of vector field to cylindrical coordinates
pleiades.analysis.locs_to_vals Picks values of field Q at desired coordinates.
pleiades.analysis.locs_to_vals_griddata Picks values of field Q at desired coordinates.
pleiades.analysis.locs_to_vals1D Picks values of field Q at desired coordinates.

Fieldline Analysis Functions

pleiades.analysis.get_fieldlines Return coordinates for segments comprising a flux surface (Nx2 array).
pleiades.analysis.parse_segment
pleiades.analysis.get_fieldline_distance Return cumulative field line distance vector
pleiades.analysis.interp interpolate quantity Q on Rho, Z grid onto flpoints (Nx2 array of x,y pairs).
pleiades.analysis.flux_surface_avg Compute flux surface average of quantity Q or return dVdpsi (dl_B)
pleiades.analysis.diff_central

Miscellaneous Helper Functions

pleiades.analysis.get_deltapsi Returns contribution to psi from fast ion currents.
pleiades.analysis.get_concave_hull

Publications

Used Pleiades in Analysis

  • Ethan E. Peterson, Douglass A. Endrizzi, Matthew Beidler, Kyle J. Bunkers, Michael Clark, Jan Egedal, Ken Flanagan, Karsten J. McCollam, Jason Milhone, Joseph Olson, Carl R. Sovinec, Roger Waleffe, John Wallace, and Cary B. Forest, “A laboratory model for the Parker spiral and magnetized stellar winds,” Nature Physics, 15, 1095–1100 (2019).

Resources and Reference Material

Below is a collection of resources for specific computing and software development resources we’ve curated in an attempt to provide a quick reference page for anybody starting out on a project like this.

Linux

Just for reference, the Linux Kernel Documentation uses Read the Docs to host their official documentation. So, if it’s good enough for the kernel developers, it’s good enough for us.

The Bash Reference Manual is super helpful for learning how to write bash scripts for manipulating files, running programs, and in general for executing things on the commandline.

The Linux Filesystem Hierarchy Standard is a helpful reference for how files are organized and common conventional uses of each directory. Understanding the purpose of the handful of directories under the root directory can go a long way towards helping you get comfortable with Linux and give you more control over your system. Also, because what the hell is …AppDataRoaming

Python

Python is the most popular programming language and is still growing. There are many reasons for this including general purpose capabilites as well as visualization toolkits and scientific computing libraries that are all user-friendly (more or less). The current supported version of Python is Python 3 as Python 2 support was ended in January 2020.

Scientific Computing with Python

The recommended Python distribution for scientific computing and data analysis is Anaconda 3 as it provides a very simple way to access many of most popular packages like numpy, scipy, pandas, matplotlib, scikit-learn and tensorflow. In addition many third-party packages are available for install through the conda package manager, including OpenMC.

Data Visualization

Git and Software Development

Git is a powerful tool for software development and version control for projects of all sizes, big or small. There are a number of helpful tutorials out there on how to use git,

Often times using git at the command line can be daunting for beginners. However, having a good understanding of proper git workflows can go a long way towards easing that anxiety. A very helpful reference that clearly explains a good git workflow including branching, merging, and releases can be found here

Program Design

License Agreement

MIT License

Copyright (c) 2020 Ethan Peterson

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.