The Open FUSION Toolkit 1.0.0-8905cc5
Modeling tools for plasma and fusion research and engineering
Loading...
Searching...
No Matches
ThinCurr Python Example: Time-domain simulation of a cylinder with jumpers

In this example we demonstrate how to run a time-domain simulation with current diagnostics for a simple ThinCurr model of a cylinder.

Note
Running this example requires the h5py and pyvista python packages, which are installable using pip or other standard methods.
import struct
import sys
import os
import h5py
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import pyvista
pyvista.set_jupyter_backend('static') # Comment to enable interactive PyVista plots
plt.rcParams['figure.figsize']=(6,6)
plt.rcParams['font.weight']='bold'
plt.rcParams['axes.labelweight']='bold'
plt.rcParams['lines.linewidth']=2
plt.rcParams['lines.markeredgewidth']=2
%matplotlib inline
%config InlineBackend.figure_format = "retina"

Load ThinCurr library

To load the ThinCurr python module we need to tell python where to the module is located. This can be done either through the PYTHONPATH environment variable or within a script using sys.path.append() as below, where we look for the environement variable OFT_ROOTPATH to provide the path to where the OpenFUSIONToolkit is installed (/Applications/OFT for binaries on macOS).

thincurr_python_path = os.getenv('OFT_ROOTPATH')
if thincurr_python_path is not None:
sys.path.append(os.path.join(thincurr_python_path,'python'))
from OpenFUSIONToolkit import OFT_env
from OpenFUSIONToolkit.ThinCurr import ThinCurr
from OpenFUSIONToolkit.io import histfile
Python interface for ThinCurr thin-wall E-M functionality.
Definition __init__.py:1
General I/O functionality for Open FUSION Toolkit (OFT) Python interfaces.
Definition io.py:1

Setup ThinCurr model

We now create a OFT_env instance for execution using two threads and a ThinCurr instance that utilizes that execution environment.

Once created, we setup the model from an existing HDF5 and XML mesh definition using setup_model(). For this model we have defined current jumpers using additional nodesets, which must be identified using the jumper_start argument. Here we use python-style reverse array indexing to indicate the jumpers start at the last entry in the nodeset list.

Finally, we initialize I/O for this model using setup_io() to enable output of plotting files for 3D visualization in VisIt, Paraview, or using pyvista below.

myOFT = OFT_env(nthreads=2)
tw_cyl = ThinCurr(myOFT)
tw_cyl.setup_model(mesh_file='thincurr_ex-cyl.h5',xml_filename='oft_in-jumper.xml',jumper_start=-1)
tw_cyl.setup_io()
#----------------------------------------------
Open FUSION Toolkit Initialized
Development branch:   v1_beta6
Revision id:          681e857
Parallelization Info:
  # of MPI tasks      =    1
  # of NUMA nodes     =    1
  # of OpenMP threads =    2
Fortran input file    = /var/folders/52/n5qxh27n4w19qxzqygz2btbw0000gn/T/oft_64877/oftpyin
XML input file        = none
Integer Precisions    =    4   8
Float Precisions      =    4   8  16
Complex Precisions    =    4   8
LA backend            = native
#----------------------------------------------


Creating thin-wall model
 Orientation depth =         895
  Loading V(t) driver coils
  Loading I(t) driver coils

  # of points    =          795
  # of edges     =         2259
  # of cells     =         1464
  # of holes     =            1
  # of Vcoils    =            0
  # of closures  =            0
  # of Icoils    =            1

  Building holes

  Loading region resistivity:
     1  1.2570E-05

Setup sensor object

Unlike in ThinCurr Python Example: Time-domain simulation of a cylinder we are not definining any magnetic sensors. However, we must still call compute_Msensor() to setup the jumpers. During this time the orientation of positive current for each jumper is saved to a jumpers_orient.dat file in the current directory.

_, _, sensor_obj = tw_cyl.compute_Msensor()
 Setting jumper information:
       1  -4.981645644143631E-02  -9.987583895355365E-01  -0.000000000000000E+00
 Building element->sensor inductance matrix
 Building coil->sensor inductance matrix

Compute self-inductance and resistivity matrices

With the model setup, we can now compute the self-inductance and resistivity matrices. A numpy version of the self-inductance matrix will be stored at tw_plate.Lmat. By default the resistivity matrix is not moved to python as it is sparse and converting to dense representation would require an increase in memory. These matrices correspond to the \(\textrm{L}\) and \(\textrm{R}\) matrices for the physical system

\(\textrm{L} \frac{\partial I}{\partial t} + \textrm{R} I = V\)

Note
For larger models calculating the self-inductance may take some time due to the \(N^2\) interaction of the elements (see ThinCurr Python Example: Using HODLR approximation for more information).
Mc = tw_cyl.compute_Mcoil()
tw_cyl.compute_Lmat()
tw_cyl.compute_Rmat()
 Building coil<->element inductance matrices
     Time =  0s          
 Building coil<->coil inductance matrix
 Building element<->element self inductance matrix
     Time =  1s          
 Building resistivity matrix

Run time-domain simulation

With the model fully defined we can now use run_td() to perform a time-domain simulation. In this case we simulate 80 ms using a timestep of 0.2 ms (400 steps). We also specify using a direct solver for the time-advance (direct=True) and set the current in the single I-coil defined in the XML input file as a function of time (coil_currs), where the first column specifies time points in ascending order and the remaining columns specify coil currents at each time point.

dt = 2.E-4
nsteps = 400
coil_currs = np.array([
[0.0, 0.0],
[2.E-2, 1.E3],
[1.0, 1.E3]
])
tw_cyl.run_td(dt,nsteps,status_freq=10,coil_currs=coil_currs,sensor_obj=sensor_obj,direct=True)
 Starting simulation
 Starting factorization
 Inverting real matrix
   Time =    1.2357999999999999E-002
 Timestep           10   2.00000009E-03   3.21358885E-03           1
 Timestep           20   4.00000019E-03   6.27122633E-03           1
 Timestep           30   6.00000005E-03   9.10636503E-03           1
 Timestep           40   8.00000038E-03   1.17357541E-02           1
 Timestep           50   9.99999978E-03   1.41745238E-02           1
 Timestep           60   1.20000001E-02   1.64365564E-02           1
 Timestep           70   1.40000004E-02   1.85346715E-02           1
 Timestep           80   1.60000008E-02   2.04807445E-02           1
 Timestep           90   1.79999992E-02   2.22857799E-02           1
 Timestep          100   1.99999996E-02   2.38748975E-02           1
 Timestep          110   2.19999999E-02   2.22232491E-02           1
 Timestep          120   2.40000002E-02   2.06126291E-02           1
 Timestep          130   2.60000005E-02   1.91188417E-02           1
 Timestep          140   2.80000009E-02   1.77332275E-02           1
 Timestep          150   2.99999993E-02   1.64479148E-02           1
 Timestep          160   3.20000015E-02   1.52556524E-02           1
 Timestep          170   3.40000018E-02   1.41497208E-02           1
 Timestep          180   3.59999985E-02   1.31238885E-02           1
 Timestep          190   3.79999988E-02   1.21723702E-02           1
 Timestep          200   3.99999991E-02   1.12897959E-02           1
 Timestep          210   4.19999994E-02   1.04711801E-02           1
 Timestep          220   4.39999998E-02   9.71189607E-03           1
 Timestep          230   4.60000001E-02   9.00765043E-03           1
 Timestep          240   4.80000004E-02   8.35445710E-03           1
 Timestep          250   5.00000007E-02   7.74862012E-03           1
 Timestep          260   5.20000011E-02   7.18670804E-03           1
 Timestep          270   5.40000014E-02   6.66553853E-03           1
 Timestep          280   5.60000017E-02   6.18215930E-03           1
 Timestep          290   5.79999983E-02   5.73383039E-03           1
 Timestep          300   5.99999987E-02   5.31801209E-03           1
 Timestep          310   6.19999990E-02   4.93234675E-03           1
 Timestep          320   6.40000030E-02   4.57464904E-03           1
 Timestep          330   6.59999996E-02   4.24289098E-03           1
 Timestep          340   6.80000037E-02   3.93519131E-03           1
 Timestep          350   7.00000003E-02   3.64980591E-03           1
 Timestep          360   7.19999969E-02   3.38511681E-03           1
 Timestep          370   7.40000010E-02   3.13962274E-03           1
 Timestep          380   7.59999976E-02   2.91193230E-03           1
 Timestep          390   7.80000016E-02   2.70075398E-03           1
 Timestep          400   7.99999982E-02   2.50489078E-03           1

Plot jumper signals

We can now plot the signals from the current jumper as a function of time. During the time-domain run this information is stored in OFT's binary history file format, which can be read using the histfile class. This class stores the resulting signals in a Python dict-like representation.

hist_file = histfile('jumpers.hist')
print(hist_file)
fig, ax = plt.subplots(1,1)
ax.plot(hist_file['time'],hist_file['JUMPER_0001'], label=r'$I_{Cyl}$ [A]')
ax.plot(coil_currs[:,0],2.0*coil_currs[:,1], label=r'$I_{Coil}$ [A]')
ax.legend()
ax.set_xlabel('Time [s]')
ax.set_ylabel('Jumper current [A]')
ax.set_ylim()
_ = ax.set_xlim(left=0.0,right=0.09)
OFT History file: jumpers.hist
  Number of fields = 3
  Number of entries = 401

  Fields:
    time: Simulation time [s] (d1)
    JUMPER_0001: No description (d1)
    HOLE_0001: No description (d1)

Generate plot files

After completing the simulation we can generate plot files using tw_torus.plot_td(). Plot files are saved at a fixed timestep interval, specified by the nplot argument to run_td() with a default value of 10.

Once all fields have been saved for plotting tw_torus.build_XDMF() to generate the XDMF descriptor files for plotting with VisIt of Paraview. This method also returns a XDMF_plot_file object, which can be used to read and interact with plot data in Python (see below).

tw_cyl.plot_td(nsteps,sensor_obj=sensor_obj)
plot_data = tw_cyl.build_XDMF()
 Post-processing simulation
Removing old Xdmf files
  Removed 43 files

Creating output files: oft_xdmf.XXXX.h5
  Found Group: thincurr
    Found Mesh: icoils
      # of blocks: 1
    Found Mesh: smesh
      # of blocks: 1

Plot current fields using pyvista

For demonstration purposes we now plot the the solution at the end of the driven phase using pyvista. We now use the plot_data object to generate a 3D plot of the current at t=2.E-2. For more information on the basic steps in this block see ThinCurr Python Example: Compute eigenstates in a plate

icoil_grid = plot_data['ThinCurr']['icoils'].get_pyvista_grid()
grid = plot_data['ThinCurr']['smesh'].get_pyvista_grid()
J = plot_data['ThinCurr']['smesh'].get_field('J_v',2.E-2)
# Plot mesh and current density using PyVista
p = pyvista.Plotter()
grid["vectors"] = J
grid.set_active_vectors("vectors")
scale = 0.2/(np.linalg.norm(J,axis=1)).max()
arrows = grid.glyph(scale="vectors", orient="vectors", factor=scale)
p.add_mesh(arrows, cmap="turbo", scalar_bar_args={'title': "Imag(J)", "vertical": True, "position_y":0.25, "position_x": 0.0})
p.add_mesh(grid, color="white", opacity=1.0, show_edges=False)
p.add_mesh(icoil_grid, color="blue", opacity=1.0, line_width=4)
p.show()