Welcome to xemc3’s documentation!
xemc3
provides an interface for collecting the output data
from a EMC3
simulation into an xarray dataset, as well as accessor
methods for common EMC3 analysis and plotting tasks.
Currently only in alpha (until 1.0 released) so please report any bugs, and feel free to raise issues asking questions or making suggestions.
Getting started
Installing
To get the full install including plotting run:
pip install --user 'xemc3[full]'
If you want a minimal install without plotting capabilities you can run:
pip install --user xemc3
Note that without matplotlib plotting will not be available.
Basic tasks
Checking convergence
If you have run a simulations, typically the first thing you want to do, is
check the *_INFO
files to check whether the simulation is converged.
This can be done by reading in the info files, and plotting the traces, to have a look at the variation:
import xemc3
import matplotlib.pyplot as plt
ds = xemc3.load.file("path_to_simu/ENERGY_INFO")
for key in ds:
ds[key].plot()
plt.show()
See an example notebook for more info on reading these files.
Reading the output
xemc3
assumes that all files are called the way that also EMC3 is
expecting them to be named. If you use different names, ensure that
the files are linked to the EMC3 names, before loading the files with
xemc3
.
Once the simulation is sufficiently converged, further analysis can start.
For this xemc3
allows to read in all the files, and store the data as a
netcdf file. This has the advantage that successive reads are very fast, and
is especially convenient if the data is stored in the netcdf file after
running the simulation. There is a command line version, that can be
conveniently called from shell, xemc3-to-netcdf that is roughly equivalent to the
following python snippet:
import xemc3
ds = xemc3.load.all(path)
ds.to_netcdf(path + ".nc")
Besides faster loads, the netcdf also makes it easier to share the data for analysis, as all data is stored in a single file. This also allows to unlink the EMC3 names, or share the data with users that have a different naming convention for their files. The netcdf files can then be read again via
import xarray as xr
ds = xr.open_dataset(path + ".nc")
Post processing
xarray provides a wide variety of functionality for post processing. Good documentation and tutorials are available.
Plotting
xarray handles plotting already, but xemc3 extends this with some more
specific routines, for example to plot an \(R\times z\) slice. The
functionally is documented here and can be accessed via the
emc3
accessor of a dataset, for example the
xemc3.EMC3DatasetAccessor.plot_rz can be used by calling
ds.emc3.plot_rz(...)
with ds
an xr.Dataset.
Plotting in simulation coordinates can be done using xr.DataArray.plot as e.g.
ds["Te"].isel(r=-3).plot()
to plot the third outermost slice of the
electron temperature.
Exercises
To get an overview what is possible with xemc3, you can try the exercises.
You can find them in the docs/exercises/
folder or try them online.
Examples
Evaluate at
Some examples of the evaluate_at functionality
[1]:
import xarray as xr
import numpy as np
import xemc3
import matplotlib.pyplot as plt
# Matplotlib setup
import setup_plt
[2]:
# Use local helper function to get some data
from get_data import load_example_data
ds = load_example_data()
# If you want to use your own data use something like
# ds = xemc3.load.all("path/to/mydata/")
# or if you have converted it already to a netcdf file
# ds = xr.open_dataset("path/to/mydata.nc")
Evaluate along a line of sight
[3]:
nx = 1000
mapped = ds.emc3.evaluate_at_xyz(
np.linspace(4.8, 6.0, nx),
np.linspace(-0.1, 0.1, nx),
np.linspace(-0.5, 0.5, nx),
"ne",
periodicity=5,
updownsym=True,
delta_phi=np.pi / 1800,
)
# Add a coordinate
mapped.coords["dim_0"] = np.linspace(4.8, 6.0, nx)
mapped.dim_0.attrs = dict(units="m", long_name="x")
[4]:
plt.figure()
mapped["ne"].plot()
[4]:
[<matplotlib.lines.Line2D at 0x7fcd7b959970>]

Evaluate along a 2D array
Plotting an \(R\times z\) plane can be done with ds.emc3.plot_rz(key, phi)
this allows to plot abitrary cuts of the domain.
[5]:
# Evaluate an abitrary 2D slice
x = xr.DataArray(np.linspace(0, 7, 100), name="x", dims="x", attrs=dict(units="m"))
y = xr.DataArray(np.linspace(0, 7, 100), name="y", dims="y", attrs=dict(units="m"))
z = 0
# Add coordinates:
x.coords["x"] = x
y.coords["y"] = y
mapped = ds.emc3.evaluate_at_xyz(
x, y, z, ["ne", "Te"], periodicity=5, updownsym=True, delta_phi=np.pi / 180
)
[6]:
# Plot the pressure
plt.figure()
(mapped.ne * mapped.Te).plot(x="x", y="y")
[6]:
<matplotlib.collections.QuadMesh at 0x7fcd7976f520>

Precalculate the mapping
Rather then directly evaluating a quantity, we can also calculate the mapping first. This is usefull if the same mapping is needed for different simulations, as long as they use the identical grid.
[7]:
# Evaluate an abitrary 2D slice
x = xr.DataArray(np.linspace(0, 7, 100), name="x", dims="x", attrs=dict(units="m"))
y = xr.DataArray(np.linspace(0, 7, 100), name="y", dims="y", attrs=dict(units="m"))
z = 0
# Add coordinates:
x.coords["x"] = x
y.coords["y"] = y
# We don't pass in the key we want to evaluate
# In this case we get a dataset with indices
mapped = ds.emc3.evaluate_at_xyz(
x, y, z, key=None, periodicity=5, updownsym=True, delta_phi=np.pi / 180
)
[8]:
# This time we calculate the pressure
ds["Pe"] = ds.Te * ds.ne
# And then we evaluate with the pre-evaluated indices the pressure in the plane
plt.figure()
ds["Pe"].isel(**mapped).plot(x="x", y="y")
[8]:
<matplotlib.collections.QuadMesh at 0x7fcd7962a310>

Evaluate along a field line
xemc3 doesn’t support field-line tracing
We can however use the webservices, if we are on the IPP network
Warning
Please note that if you don’t use the same magnetic configuration in the fieldlinetracer and the simulations, you might get slightly wrong results or completely wrong results.
Especially, conclusion drawn might be completely wrong!
Always double check the magnetic configurations!
[9]:
try:
from dave_utils import jafw
except ImportError:
print("Importing failed. Consider installing dave_utils with:")
print("pip install --user git+https://gitlab.mpcdf.mpg.de/dave/dave_utils.git")
jafw = None
raise
# This will fail outside of the IPP network
flt = jafw.getSrv()
config = jafw.setCurrents([-1.74e6] * 5 + [0] * 5)
pos = flt.types.Points3D()
pos.x1 = 5.7
pos.x2 = 0
pos.x3 = 0
lineTask = flt.types.LineTracing()
lineTask.numSteps = 3000
task = flt.types.Task()
task.step = 0.01
task.lines = lineTask
res = flt.service.trace(pos, config, task, None, None)
i = 0
dat = np.array(
[
res.lines[i].vertices.x1,
res.lines[i].vertices.x2,
res.lines[i].vertices.x3,
]
)
mapped = ds.emc3.evaluate_at_xyz(
dat[0],
dat[1],
dat[2],
"ne",
periodicity=5,
updownsym=True,
delta_phi=np.pi / 180,
)
Importing failed. Consider installing dave_utils with:
pip install --user git+https://gitlab.mpcdf.mpg.de/dave/dave_utils.git
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[9], line 2
1 try:
----> 2 from dave_utils import jafw
3 except ImportError:
4 print("Importing failed. Consider installing dave_utils with:")
ModuleNotFoundError: No module named 'dave_utils'
[10]:
if jafw is not None:
mapped["ne"].plot(figsize=(16, 6))
else:
print("Fieldlinetracer not available ...")
Fieldlinetracer not available ...
Heatflux post processing
Documenation of the EMC3-EIRENE is part of the EMC3-EIRENE documentation
map particle and density deposition
ENERGY_DEPO
andPARTICLE_DEPO
to the target structuresTarget heatflux contains contribution from potential energy
Neutrals are not included
plasma radiation not included
[ ]:
# Ensure the example data is present
from get_data import load_example_data
_ = load_example_data()
[ ]:
# Input file
! cat ../../example-data/emc3_example/fort.3
[ ]:
# Input file
! cat ../../example-data/emc3_example/fort.79
Run with e.g.
cd /raven/u/mp004/runs/casegroup/case_docs/
srun emc3_eirene
[4]:
# xemc3 can be used for plotting:
! xemc3-divertor ../../example-data/emc3_example/ -gls -t 'Example data'
Analysing the data
[ ]:
import xemc3
[ ]:
plates = xemc3.load.plates("../../example-data/emc3_example/")
[ ]:
plates
The data contains padding such that they can be combined into a numpy-data-block
[ ]:
plates["f_E"].isel(plate_ind=13).plot()
Using the ds.emc3[]
the padding can be removed, if it not not needed for the current data, i.e. if only a single plate is included:
[ ]:
nosym = list(plates.emc3.iter_plates())
nosym[13].emc3["f_E"].plot()
nosym[13]
It is possible to get all plates, rather then just the simulated half-module using the ds.emc3.iter_plates
function:
[ ]:
sym = list(plates.emc3.iter_plates(symmetry=True, segments=5))
len(sym)
Analysing traces
Often the first step after running the simulation is to ensure that the simulation is converged.
xemc3 can be used to do this.
[1]:
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import xemc3
import glob
# Matplotlib setup
import setup_plt
%matplotlib inline
[2]:
# Use local helper function to get some data
from get_data import load_example_data
path = load_example_data(get_path=True)
# If you want to use your own data use something like
# path = "path/to/mydata/"
Reading some files
The fastest way is to load just a single iteration trace *_INFO
file:
[3]:
ds = xemc3.load.file(path + "/ENERGY_INFO")
[4]:
plt.figure()
ds["Te_upstream"].plot()
[4]:
[<matplotlib.lines.Line2D at 0x7fdee46dcd30>]

However in most cases we are only interrested in the last points of the INFO file.
[5]:
plt.figure()
ds.Te_upstream[-50:].plot()
[5]:
[<matplotlib.lines.Line2D at 0x7fdee2363820>]

Reading all INFO files
Besides using xemc3.load.all(path)
it is also simple to read just the *_INFO
files:
[6]:
ds = xr.Dataset()
for file in glob.iglob(f"{path}/*_INFO"):
ds = xemc3.load.file(file, ds)
ds
[6]:
<xarray.Dataset> Dimensions: (iteration: 1000) Coordinates: * iteration (iteration) int64 -999 -998 -997 -996 ... -3 -2 -1 0 Data variables: (12/28) Te_change (iteration) float64 nan nan nan ... 0.012 0.012 0.012 Te_upstream (iteration) float64 nan nan nan ... 208.3 208.3 208.6 Te_down_back (iteration) float64 nan nan nan ... 17.59 17.58 17.56 Te_down_mean (iteration) float64 nan nan nan ... 16.56 16.55 16.53 Te_down_fwd (iteration) float64 nan nan nan ... 13.63 13.65 13.64 Ti_change (iteration) float64 nan nan nan ... 0.018 0.018 0.018 ... ... ionization_electron (iteration) float64 nan nan nan ... -26.6 -26.6 -26.6 ionization_ion (iteration) float64 nan nan nan ... 7.404 7.404 7.399 ionization_moment_fwd (iteration) float64 nan nan ... -6.633e-19 -6.671e-19 ionization_moment_bwk (iteration) float64 nan nan ... 1.144e-18 1.152e-18 TOTAL_FLX (iteration) float64 nan nan nan ... 83.96 84.17 84.36 TOTAL_RAD (iteration) float64 nan nan nan ... 5e+04 5e+04 5e+04 Attributes: title: EMC3-EIRENE Simulation data software_name: xemc3 software_version: 0.2.6.dev8+gf431467 date_created: 2023-05-30T11:24:34.208393 id: 8db5559c-fedc-11ed-9df3-0242ac110002 references: https://doi.org/10.5281/zenodo.5562265
Again, the different traces can be plotted, to get an estimate of whether the simulation is converged:
[7]:
plt.figure()
ds.dens_change[-60:].plot()
[7]:
[<matplotlib.lines.Line2D at 0x7fdee0af9640>]

Checking how much data
As numpy arrays need to be blocks, xemc3 uses nan-padding. np.isfinite
can be used to check how much data we have
[8]:
np.isfinite(ds).sum()
[8]:
<xarray.Dataset> Dimensions: () Data variables: (12/28) Te_change int64 221 Te_upstream int64 221 Te_down_back int64 221 Te_down_mean int64 221 Te_down_fwd int64 221 Ti_change int64 221 ... ... ionization_electron int64 206 ionization_ion int64 206 ionization_moment_fwd int64 206 ionization_moment_bwk int64 206 TOTAL_FLX int64 172 TOTAL_RAD int64 172
[ ]:
Introduction to xemc3
xemc3 is a library for reading the output fron EMC3 simulations into the xarray format.
[1]:
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
import xemc3
# Matplotlib setup
import setup_plt
Quick introduction to xarray
This is just to get a quick introduction of the structure of the xarray data type.
In the cell below we generate a xarray with dimensions \((3,3)\) for variable \(x\) with coordinates \((10,20)\) and \(y\) with coordinates \((1,2,3)\).
[2]:
data = xr.DataArray(
np.random.rand(2, 3), dims=("x", "y"), coords={"x": [10, 20], "y": [1, 2, 3]}
)
data
[2]:
<xarray.DataArray (x: 2, y: 3)> array([[0.70594585, 0.10235316, 0.80768602], [0.40721037, 0.71620189, 0.77389268]]) Coordinates: * x (x) int64 10 20 * y (y) int64 1 2 3
data.values
Returns the wrapped numpy array, in this case the return value of np.random.rand(2, 3)
[3]:
data.values
[3]:
array([[0.70594585, 0.10235316, 0.80768602],
[0.40721037, 0.71620189, 0.77389268]])
data.dims
Returns the name of the dimensions.
[4]:
data.dims
[4]:
('x', 'y')
data.coords
Returns the coordinates for all axis directions with coordinate names and datatype of the coordinates.
[5]:
data.coords
[5]:
Coordinates:
* x (x) int64 10 20
* y (y) int64 1 2 3
data.attrs
Returns other attributes in form of a dictionary with you can easily add by generating a new value associated with a new key.
[6]:
data.attrs["units"] = "m"
data.attrs
[6]:
{'units': 'm'}
More on xarray:
EMC3 data
The prerequisite for this example to work is to have downloaded the file emc3_example.nc and have the libraries specified in this script installed in your enviroment. We recommend using netCDF4 for opening .nc files. The emc3_example.nc can be found and downloaded here: https://gitlab.mpcdf.mpg.de/dave/xemc3-data given that you have acces.
The path specified in the string in the cell below is where you have stored the emc3_example.nc locally on your computer.
[7]:
# Use local helper function to get some data
from get_data import load_example_data
ds = load_example_data()
# If you want to use your own data use something like
# ds = xemc3.load.all("path/to/mydata/")
# or if you have converted it already to a netcdf file
# ds = xr.open_dataset("path/to/mydata.nc")
ds
[7]:
<xarray.Dataset> Dimensions: (r: 139, theta: 512, phi: 36, delta_r: 2, delta_theta: 2, delta_phi: 2, iteration: 1000, plate_ind: 22, plate_phi_plus1: 57, plate_x_plus1: 50, r_plus1: 140, theta_plus1: 513, phi_plus1: 37, plate_phi: 560, plate_x: 100, delta_plate_phi: 2, delta_plate_x: 2) Coordinates: R_bounds (r, theta, phi, delta_r, delta_theta, delta_phi) float64 ... z_bounds (r, theta, phi, delta_r, delta_theta, delta_phi) float64 ... phi_bounds (phi, delta_phi) float64 ... * iteration (iteration) int64 -999 -998 -997 ... -2 -1 0 plate_z (plate_ind, plate_phi_plus1, plate_x_plus1) float64 ... plate_phi (plate_ind, plate_phi_plus1) float64 ... plate_R (plate_ind, plate_phi_plus1, plate_x_plus1) float64 ... plate_z_bounds (plate_ind, plate_phi, plate_x, delta_plate_phi, delta_plate_x) float64 ... plate_phi_bounds (plate_ind, plate_phi, plate_x, delta_plate_phi, delta_plate_x) float64 ... plate_R_bounds (plate_ind, plate_phi, plate_x, delta_plate_phi, delta_plate_x) float64 ... Dimensions without coordinates: r, theta, phi, delta_r, delta_theta, delta_phi, plate_ind, plate_phi_plus1, plate_x_plus1, r_plus1, theta_plus1, phi_plus1, plate_x, delta_plate_phi, delta_plate_x Data variables: (12/93) _plasma_map (r, theta, phi) int64 ... ne (r, theta, phi) float64 ... nZ1 (r, theta, phi) float64 ... nZ2 (r, theta, phi) float64 ... nZ3 (r, theta, phi) float64 ... nZ4 (r, theta, phi) float64 ... ... ... f_E (plate_ind, plate_phi, plate_x) float64 ... avg_n (plate_ind, plate_phi, plate_x) float64 ... avg_Te (plate_ind, plate_phi, plate_x) float64 ... avg_Ti (plate_ind, plate_phi, plate_x) float64 ... tot_n (plate_ind) float64 ... tot_P (plate_ind) float64 ... Attributes: title: EMC3-EIRENE Simulation data software_name: xemc3 software_version: 0.2.2.dev5+g7311151 date_created: 2022-10-27T13:16:46.836400 id: 9bda9808-55f9-11ed-9228-00001029fe80 references: https://doi.org/10.5281/zenodo.5562265
dataset (ds) explanation
When running the codeline ds on the last line of a cell you get an overview of what the xarray object consist of.
ds.coords[‘R_bounds’]
R_bounds
represents the coordinates of the vertices at the gridcells in the radial direction in the \(xy\)-plane. with \(R = \sqrt{x^2 + y^2}\).
ds.coords[‘z_bounds’]
z_bounds
represents the coordinates of the vertices of the gridcells in the \(z\)-direction.
ds.coords[‘phi_bounds’]
phi_bounds
represents the coordinates of the vertices of the gridcells in the \(\phi\)-direction.
[8]:
# The shape is 6 dimensional, as we include 3 dimensions for the vertices
ds.coords["R_bounds"].shape
[8]:
(139, 512, 36, 2, 2, 2)
[9]:
# Note that units are m - xemc3 prefers SI, with only eV as exception
ds.coords["z_bounds"]
[9]:
<xarray.DataArray 'z_bounds' (r: 139, theta: 512, phi: 36, delta_r: 2, delta_theta: 2, delta_phi: 2)> [20496384 values with dtype=float64] Coordinates: R_bounds (r, theta, phi, delta_r, delta_theta, delta_phi) float64 ... z_bounds (r, theta, phi, delta_r, delta_theta, delta_phi) float64 ... phi_bounds (phi, delta_phi) float64 ... Dimensions without coordinates: r, theta, phi, delta_r, delta_theta, delta_phi Attributes: xemc3_type: geom units: m
Toroidal slice
A toroidal slice is defined as the grid of \((R,z)\)-values at a fixed angle \(\phi\). The values of the \(\phi\)-angles used in the W7X grid can be found in the paragraph below and demonstrated in the next cell.
ds.coords[‘phi_bounds’]
Running the cell below gives you an array of the \(\phi\) angles.
[10]:
ds.coords["phi_bounds"]
[10]:
<xarray.DataArray 'phi_bounds' (phi: 36, delta_phi: 2)> [72 values with dtype=float64] Coordinates: phi_bounds (phi, delta_phi) float64 ... Dimensions without coordinates: phi, delta_phi Attributes: xemc3_type: geom units: radian
ds.emc3.plot_rz(key, phi = \(\phi\))
The key is given as a string, None
can be passed as a key if you want to plot the mesh. An example is the angle phi
\(= \phi\) which is the angle given in radians as float.
For this particular example(.nc file) the floats of the angle \(\phi\) can be found in the dictionary defined by ds.coords[‘phi_bounds’] which has 2 dimensions; one for either side of the cell for a given angle \(\phi\). There are 37 different values for \(\phi\) since W7-X has a five-fold symmetry which is divided in two up-down symmetric parts and we use a resolution \(1^{\circ}\).
In the cells below are some examples of the parameter electron temperature \(T_e\) plotted in toroidal slices for phi index \(n_{\phi} = [0,18,35]\).
[11]:
# the parameter can be plotted by a one-liner
plt.figure()
ds.emc3.plot_rz("Te", phi=0)
[11]:
<matplotlib.collections.QuadMesh at 0x7fa3ac814190>

[12]:
# for several angles and control
import ipywidgets as widgets
fig = plt.figure()
def plot_Te(ip):
fig.clear()
ds.emc3.plot_rz("Te", phi=ip)
ip = widgets.FloatSlider(min=0, max=np.pi / 5, value=0, step=0.01)
widgets.interact(plot_Te, ip=ip)
<Figure size 1000x1000 with 0 Axes>
[12]:
<function __main__.plot_Te(ip)>
Simplifying of grid structure
The parameter values are defined in each grid cell, but the center or the mean of the vertices of the gridcell: \(\mathbf{r}_{param} = \langle \mathbf{r}_{vertex} \rangle\). A simplified analogy is the centerpoint of a 3D cube.
You can get the center coordinates ds.<direction>_bounds.mean(dim=("delta_r", "delta_theta", "delta_phi"), ignore_missing=True)
by averaging over the delta_*
dimensions of the variable, as that contains the cell extend in each direction.
[13]:
R = ds.R_bounds.mean(dim=("delta_r", "delta_theta", "delta_phi"))
z = ds.z_bounds.mean(dim=("delta_r", "delta_theta", "delta_phi"))
phi = ds.phi_bounds.mean(dim="delta_phi")
x = R * np.cos(phi)
y = R * np.sin(phi)
x, ds.Te
[13]:
(<xarray.DataArray (r: 139, theta: 512, phi: 36)>
array([[[5.90687195, 5.90143282, 5.89057613, ..., 4.27387008,
4.21925933, 4.16544375],
[5.90692225, 5.90157835, 5.89081476, ..., 4.27443602,
4.21980501, 4.16596973],
[5.90697495, 5.90172636, 5.89105602, ..., 4.27501305,
4.22036168, 4.16650662],
...,
[5.90673533, 5.90101113, 5.88987631, ..., 4.27223898,
4.2176884 , 4.1639313 ],
[5.90677846, 5.90114916, 5.89010684, ..., 4.27277148,
4.21820095, 4.16442445],
[5.90682402, 5.90128975, 5.89034014, ..., 4.27331521,
4.21872462, 4.16492863]],
[[5.88039947, 5.87478717, 5.86353702, ..., 4.22636359,
4.17204559, 4.11853643],
[5.88048206, 5.87503477, 5.86394578, ..., 4.22725027,
4.17290028, 4.11936057],
[5.88056438, 5.87528231, 5.86435491, ..., 4.22815464,
4.17377244, 4.12020195],
...
[5.64755073, 5.63505485, 5.61354696, ..., 3.81421366,
3.76543708, 3.71779086],
[5.64922161, 5.63819013, 5.61807245, ..., 3.81753395,
3.76870465, 3.7210289 ],
[5.65069075, 5.64113616, 5.6224387 , ..., 3.82111107,
3.77222496, 3.7245137 ]],
[[5.59414493, 5.58958172, 5.57387846, ..., 3.75650956,
3.70552132, 3.65647075],
[5.59422224, 5.59117652, 5.57709579, ..., 3.76073063,
3.70983664, 3.66088151],
[5.59408138, 5.5925006 , 5.5799317 , ..., 3.76497918,
3.71417615, 3.66525302],
...,
[5.59027424, 5.58100907, 5.56053413, ..., 3.7435559 ,
3.69303175, 3.64369176],
[5.59192682, 5.58422348, 5.56529866, ..., 3.74788951,
3.69714638, 3.64790418],
[5.59337562, 5.58724114, 5.56988255, ..., 3.75223099,
3.701277 , 3.65213093]]])
Dimensions without coordinates: r, theta, phi,
<xarray.DataArray 'Te' (r: 139, theta: 512, phi: 36)>
[2562048 values with dtype=float64]
Dimensions without coordinates: r, theta, phi
Attributes:
xemc3_type: mapped
long_name: Electron temperature
units: eV)
Use of NaN values in xEMC3
Not all gridcells have a defined parameter value attached to it. This is mostly the outer and inner region of the grid where the values of many parameter has been ignored because in the specific regions the quantity is not evolved. This is illustrated in the above plot example of the electron temperature \(T_e\). In the cell below you can see how large a fraction of the total number of gridpoints the mesh for the electron temperature that has NaN as a value.
[14]:
n_nans_Te = np.sum(np.isnan(ds.Te)).data
print("How many nans in Te field?", n_nans_Te)
print(f"Fraction of nans with respect to gridcells: {n_nans_Te/ds.Te.size*100:.2f} %")
How many nans in Te field? 584972
Fraction of nans with respect to gridcells: 22.83 %
Grid
In the cell below there is an interactive plot of the grid. You can use the slider to iterate through all toroidal slices(all \(\phi\) angles).
[15]:
import ipywidgets
fig = plt.figure()
def plot_emc3grid(ip):
fig.clear()
ds.emc3.plot_rz(None, phi=ip)
ip = ipywidgets.FloatSlider(min=0, max=np.pi / 5, value=0, step=0.01)
ipywidgets.interact(plot_emc3grid, ip=ip)
<Figure size 1000x1000 with 0 Axes>
[15]:
<function __main__.plot_emc3grid(ip)>
Grid with boundaries
Interactive plot of the grid, here you can use the ipywidget slider to iterate through all toroidal slices, the rmin and rmax to set the boundaries in r direction, and the zmin and zmax to set the boundaries in z direction.
[16]:
plt.figure()
ds.emc3.plot_rz("Te", phi=np.pi / 5, Rmin=5.8, Rmax=6.1, zmin=0, zmax=0.3)
[16]:
<matplotlib.collections.QuadMesh at 0x7fa3ac4d26d0>

Periodic boundary conditions for plotting
Naturally the data does not have periodic boundary conditions, which means that the last dataframe would be equal to the first. In the case of emc3 data the periodicity is in the theta direction. For plotting the dimension of the theta grid is increased by one and set to the first values in the theta direction. This is for tha plot to “complete the orbit” in the theta direction for it to be closed.
[17]:
fig = plt.figure()
def plot_Te_zoomed(ip):
fig.clear()
c = plt.pcolormesh(
ds.emc3["R_corners"][:, :, ip],
ds.emc3["z_corners"][:, :, ip],
ds.Te[:, :, ip],
cmap=plt.cm.jet,
shading="auto",
)
plt.colorbar(c)
phislider = ipywidgets.IntSlider(min=0, max=35)
ipywidgets.interact(plot_Te_zoomed, ip=phislider)
<Figure size 1000x1000 with 0 Axes>
[17]:
<function __main__.plot_Te_zoomed(ip)>
Loading the raw data is slow
As several text files have to be read and parsed, this is rather time consuming.
See Loading of data for a more convienent way to work with EMC3 data.
[1]:
import xemc3
[2]:
%%time
ds = xemc3.load.all("../../example-data/emc3_example/")
CPU times: user 1min 1s, sys: 301 ms, total: 1min 1s
Wall time: 1min 2s
Loading of data
The most convinient way of working with EMC3 data is from netcdf files. This way all metadata and all available fields are known, and are loaded into memory as needed.
Netcdf files are also beneficial for sharing simulation results, as they contain metadata and are thus easy to use. While xemc3 only exists for python, netcdf library implementation are present in most languages, so you can easily do your analysis in your preferred language.
[1]:
import xemc3
import xarray as xr
import numpy as np
# Matplotlib setup
import setup_plt
[2]:
%%time
ds = xr.open_dataset("../../example-data/emc3_example.nc")
CPU times: user 43.4 ms, sys: 23.8 ms, total: 67.1 ms
Wall time: 68.3 ms
Checking the present data
xarray is nicely integrated in jupyter notebook and many IDEs, so it is easy to check what quantities are included as well as units and so on.
Note that for this view the data does not need to recide in memory, which is especially beneficial for high-resolution grids or large parameter scans.
[3]:
ds
[3]:
<xarray.Dataset> Dimensions: (r: 139, theta: 512, phi: 36, delta_r: 2, delta_theta: 2, delta_phi: 2, iteration: 1000, plate_ind: 22, plate_phi_plus1: 57, plate_x_plus1: 50, r_plus1: 140, theta_plus1: 513, phi_plus1: 37, plate_phi: 560, plate_x: 100, delta_plate_phi: 2, delta_plate_x: 2) Coordinates: R_bounds (r, theta, phi, delta_r, delta_theta, delta_phi) float64 ... z_bounds (r, theta, phi, delta_r, delta_theta, delta_phi) float64 ... phi_bounds (phi, delta_phi) float64 ... * iteration (iteration) int64 -999 -998 -997 ... -2 -1 0 plate_z (plate_ind, plate_phi_plus1, plate_x_plus1) float64 ... plate_phi (plate_ind, plate_phi_plus1) float64 ... plate_R (plate_ind, plate_phi_plus1, plate_x_plus1) float64 ... plate_z_bounds (plate_ind, plate_phi, plate_x, delta_plate_phi, delta_plate_x) float64 ... plate_phi_bounds (plate_ind, plate_phi, plate_x, delta_plate_phi, delta_plate_x) float64 ... plate_R_bounds (plate_ind, plate_phi, plate_x, delta_plate_phi, delta_plate_x) float64 ... Dimensions without coordinates: r, theta, phi, delta_r, delta_theta, delta_phi, plate_ind, plate_phi_plus1, plate_x_plus1, r_plus1, theta_plus1, phi_plus1, plate_x, delta_plate_phi, delta_plate_x Data variables: (12/93) _plasma_map (r, theta, phi) int64 ... ne (r, theta, phi) float64 ... nZ1 (r, theta, phi) float64 ... nZ2 (r, theta, phi) float64 ... nZ3 (r, theta, phi) float64 ... nZ4 (r, theta, phi) float64 ... ... ... f_E (plate_ind, plate_phi, plate_x) float64 ... avg_n (plate_ind, plate_phi, plate_x) float64 ... avg_Te (plate_ind, plate_phi, plate_x) float64 ... avg_Ti (plate_ind, plate_phi, plate_x) float64 ... tot_n (plate_ind) float64 ... tot_P (plate_ind) float64 ... Attributes: title: EMC3-EIRENE Simulation data software_name: xemc3 software_version: 0.2.2.dev5+g7311151 date_created: 2022-10-27T13:16:46.836400 id: 9bda9808-55f9-11ed-9228-00001029fe80 references: https://doi.org/10.5281/zenodo.5562265
Quantities can be accessed either by the []
operator like ["nZ3"]
or by .nZ3
. Note that assigning new quantities is only possible with the []
notation.
[4]:
ds.nZ3
[4]:
<xarray.DataArray 'nZ3' (r: 139, theta: 512, phi: 36)> [2562048 values with dtype=float64] Dimensions without coordinates: r, theta, phi Attributes: print_before: 4\n xemc3_type: mapped units: m$^{-3}$
[5]:
ds.nZ3.isel(r=-2).plot()
[5]:
<matplotlib.collections.QuadMesh at 0x7f6e5a190a30>

[6]:
ds.emc3.plot_rz("nZ3", phi=np.pi / 5)
[6]:
<matplotlib.collections.QuadMesh at 0x7f6e53f7b3d0>

API reference
xemc3 Dataset
All of the functions that are part of the EMC3DatasetAccessor can be
accessed via the emc3
dataset accessor. For a given dataset ds
they can be reached as ds.emc3.*
, e.g. ds.emc3.plot_rz(...)
.
Additional functions for working with EMC3 data.
- xemc3.EMC3DatasetAccessor.evaluate_at_diagnostic(self, diag, key=None, num_points=100)
- xemc3.EMC3DatasetAccessor.evaluate_at_rpz(self, r, phi, z, key=None, periodicity: int = 5, updownsym: bool = True, delta_phi: float | None = None, fill_value=None, lazy=False, progress=False)
Evaluate the field key in the dataset at the positions given by the array r, phi, z. If key is None, return the indices to access the 3D field and get the appropriate values.
- Parameters:
r (array-like) – The (major) radial coordinates to evaluate
phi (array-like) – The toroidal coordinate
z (array-like) – The z component
key (None or str or sequence of str) – If None return the index-coordinates otherwise evaluate the specified field in the dataset
periodicity (int) – The rotational symmetry in toroidal direction
updownsym (bool) – Whether the data is additionally up-down symmetric with half the periodicity.
delta_phi (None or float) – If not None, delta_phi gives the accuracy of the precision at which phi is evaluated. Giving a float enables caching of the phi slices, and can speed up computation. Note that it should be smaller then the toroidal resolution. For a grid with 1° resolution, delta_phi=2 * np.pi / 360 would be the upper bound. None disables caching.
fill_value (None or any) – If fill_value is None, missing data is initialised as np.nan, or as -1 for non-float datatypes. Otherwise fill_value is used to set missing data.
lazy (bool) – Force the loading of the data for key. Defaults to False. Can significantly decrease performance, but can decrease memory usage.
progress (bool) – Show the progress of the mapping. Defaults to False.
- xemc3.EMC3DatasetAccessor.evaluate_at_xyz(self, x, y, z, *args, **kwargs)
See evaluate_at_rpz for options. Unlike evaluate_at_rpz the coordinates are given here in cartesian coordinates.
- xemc3.EMC3DatasetAccessor.from_fort(self, fn, skip_first=0, ignore_broken=False, kinetic=False)
Read from an EMC3 file, using the mappings for plasma or kinetic data.
- Parameters:
fn (str) – The name of the file to be read
skip_first (int (optional)) – For each datablock the first
skip_first
lines are ignoredignore_broken (boolean (optional)) – Incomplete datasets are ignored. This indicates that something is wrong, but is the default for EMC3
kinetic (boolean (optional)) – If kinetic then the data is read using the mapping for neutal quantities, otherwise the mapping for plasma quantities is assumed
- Returns:
The list of the read quantities
- Return type:
list of xr.DataArray
- xemc3.EMC3DatasetAccessor.get(self, *args)
Get one or more variables of the dataset.
The shapes are cropped to include only valid data if data for the divertor modules is returened.
The code also transforms to
*_corner
cases, if*_bounds
is present. The bounds version is an array of shape \(n\times...\times m\times 2\times...\times 2\) while the corner version is a \(n+1\times...\times m+1\) dimensional array.- Parameters:
args (list of str) – The keys of the data to get
- Returns:
The requested arrays from the dataset
- Return type:
list of xr.DataArray
- xemc3.EMC3DatasetAccessor.isel(self, indexers: Mapping[str, Any] | None = None, drop: bool = False, missing_dims: Literal['raise'] | Literal['warn'] | Literal['ignore'] = 'raise', **indexers_kwargs: Any) Dataset
- xemc3.EMC3DatasetAccessor.iter_plates(self, *, symmetry=False, segments=1)
Iterate over all plates.
Repeat with stellerator symmetry if symmetry is given. If segments is given, repeat for all segments - assuming a 2*pi/n rotational transform symmetry.
- Parameters:
symmetry (boolean (optional)) – Iterate over mirrored setup
segments (int (optional)) – Iterate over all segments, assuming a symmetry of n=segments.
- Returns:
The plates of the divertor plates
- Return type:
iterator of xr.Dataset
- xemc3.EMC3DatasetAccessor.load(self, path)
Load EMC3 simulation data from path
- Parameters:
path (str) – The directory of the EMC3 simulation data
- Returns:
The xemc3 dataset with the simulation data
- Return type:
xr.Dataset
- xemc3.EMC3DatasetAccessor.mean_time(self) Dataset
Average in time.
Workaround for https://github.com/pydata/xarray/issues/4885
- xemc3.EMC3DatasetAccessor.plot(self, key, *args, **kw)
Plot some data.
In case of data from data from the plates, a 3D plot of the divertor is returned.
In case of 3D data a volume plot is returend.
Otherwise xarray is used for plotting. Axes information might be missing.
See also: plot_rz
- Parameters:
key (str) – The key used for plotting
- Returns:
Return value depends on the specific plotting routine used.
- Return type:
any
- xemc3.EMC3DatasetAccessor.plot_Rz(self, key, phi, **kwargs)
- xemc3.EMC3DatasetAccessor.plot_div(self, index, **kwargs)
Plot divertor data.
- xemc3.EMC3DatasetAccessor.plot_rz(self, key, phi, **kwargs)
Plot a R-z slice in lab coordinates.
- Parameters:
key (str or None) – Index of the data to plot. Select None to plot the mesh instead.
phi (float) – Angle at which to plot. As always in radian.
ax (Axis object (optional)) – Axis object to be used for plotting
Rmin (float (optional)) – left bound for plot
Rmax (float (option)) – right bound for plot
zmin (float (optional)) – lower bound for plot
zmax (float (optional)) – upper bound for plot
kwargs (Other arguments to be passed to matplotlib) –
- Returns:
The return value from matplotlib.pyplot.pcolormesh is returned.
- Return type:
matplotlib.collections.QuadMesh
- xemc3.EMC3DatasetAccessor.sel(self, indexers: Mapping[str, Any] | None = None, drop: bool = False, missing_dims: str = 'raise', **indexers_kwargs: Any) Dataset
- xemc3.EMC3DatasetAccessor.to_fort(self, keys, fn, kinetic=False)
Write to text file, using the mappings for plasma or kinetic data.
- xemc3.EMC3DatasetAccessor.to_netcdf(self, fn, complevel=1)
Write to netcdf file.
Enables fast compression by default, which helps a lot with the sparse data.
- fn: string
The path of the netcdf file to be written.
- complevel: integer (optional)
The compression level in the range 1 to 9
- xemc3.EMC3DatasetAccessor.unit(self, var, value=None)
Get or set the units of a quantity
xemc3.load module
Note
Please ensure that files are linked to their EMC3 names, if you use alternative file names.
Routines for reading EMC3 files.
- xemc3.load.all(path: str, ignore_missing: bool | None = None) Dataset
Load all data from a path and return as dataset
- Parameters:
path (str) – Directory that contains files to be read
ignore_missing (None or bool) – True: ignore missing files. False: raise exceptions if a file is not found. None: use default option for that file.
- Returns:
A dataset with all the simulation data. Unless
ignore_missing=False
has been set, some data might be missing because it was not found. Howeverignore_missing=False
has the disadvantage that files added in later versions will cause errors if the files are not present.- Return type:
xr.Dataset
- xemc3.load.any(path: str, *args, **kwargs) Dataset
Read a file or directory. For possible kwargs see the respective functions that are called:
For a directory xemc3.load.all is called.
xemc3.load.plates for the “TARGET_PROFILES” file.
xemc3.load.file otherwise.
- xemc3.load.file(fn: str, ds: None | Dataset | DataArray = None, **opts) Dataset
Read a EMC3 simulation file. The expected content is derived from the filename.
- Parameters:
fn (str) – The location of the file to read
ds (xr.DataArray or xr.Dataset or None) – The mapping or a dataset containing a mapping or None. If one is needed and none is provided, it is read from the folder.
opts (dict) – Additional options depending on the type of file that is read.
- Returns:
The read variable is set. If a Dataset was given, the newly read entries from the file are added. Otherwise a new Dataset is returned with the read variables added.
- Return type:
xr.Dataset
- xemc3.load.mapped_raw(fn: str, mapping: ~xarray.core.dataset.Dataset | ~xarray.core.dataarray.DataArray, skip_first: int = 0, ignore_broken: bool = False, kinetic: bool = False, unmapped: bool = False, dtype: ~numpy.dtype[~typing.Any] | None | ~typing.Type[~typing.Any] | ~numpy._typing._dtype_like._SupportsDType[~numpy.dtype[~typing.Any]] | str | ~typing.Tuple[~typing.Any, int] | ~typing.Tuple[~typing.Any, ~typing.SupportsIndex | ~typing.Sequence[~typing.SupportsIndex]] | ~typing.List[~typing.Any] | ~numpy._typing._dtype_like._DTypeDict | ~typing.Tuple[~typing.Any, ~typing.Any] = <class 'float'>, squeeze: bool = True) Sequence[DataArray]
Read a file with the EMC3 mapping. Note that this function does not add meta-data or does normalisation of the data, thus
xemc3.load.file
is generally preferred.- Parameters:
fn (str) – The full path of the file to read
mapping (xr.DataArray or xr.Dataset) – The dataarray of the mesh mapping or a dataset containing the mapping
skip_first (int (optional)) – Ignore the first n lines. Default 0
ignore_broken (bool (optional)) – if incomplete datasets at the end should be ignored. Default: False
kinetic (bool (optional)) – The file contains also data for cells that are only evolved by EIRENE, rather then EMC3. Default: False
unmapped (bool (optional)) – The file contains unmapped data, i.e. on value for each cell. Default: False
dtype (datatype (optional)) – The type of the data, e.g. float or int. Is passed to numpy. Default: float
squeeze (bool) – If True return a DataArray if only one field is read.
- Returns:
The data that has been read from the file. If squeeze is True and only one field is read only a single DataArray is returned.
- Return type:
xr.DataArray or list of xr.DataArray
- xemc3.load.plates(dir: str, cache: bool = True) Dataset
Read the target fluxes from the EMC3_post processing routine
- Parameters:
dir (str) – The directory in which the file is
cache (bool (optional)) – Whether to check whether a netcdf file is present, and if not create one. This can result in faster reading the next time.
- Returns:
The target profiles
- Return type:
xr.Dataset
- xemc3.load.plates_geom(filename: str | Sequence[str]) Dataset
Read Target structures from a file that is in the Kisslinger format as used by EMC3.
- Parameters:
filename (str or sequence of str) – The location of the file to read
- Returns:
The coordinates
- Return type:
xr.Dataset
- xemc3.load.var(dir: str, var: str, ds: None | Dataset | DataArray = None) Dataset
Read the variable var from the simulation in directory dir.
- Parameters:
dir (str) – The path of the simulation directory
var (str) – The name of the variable to be read
ds (xr.DataArray or xr.Dataset or None (optional)) – An xemc3 Dataset or an DataArray containing the mapping. If not given and a mapping is required to read the file, it is read from the directory.
- Returns:
A dataset in which at least
var
is set. If there are other variables in the read file, it is also added. Finally, mapping and other variables that are required to read are also added.- Return type:
xr.Dataset
xemc3.write module
Routines for writing EMC3 files.
- xemc3.write.fortran.all(ds, dir)
Write all files to directory dir
- Parameters:
ds (xr.Dataset) – The xemc3 dataset
dir (str) – The directory to write the files to
- xemc3.write.fortran.coordinates(ds: Dataset, fn: str) None
Write spatial positions of grid points in EMC3 format.
- Parameters:
ds (xr.Dataset) – The dataset with the EMC3 simulation data
fn (str) – The filename to write to
- xemc3.write.fortran.magnetic_field(path: str, ds: Dataset) None
Write the magnetic field to a file
- Parameters:
path (str) – The filename to write to
ds (xr.Dataset) – The xemc3 dataset with the magnetic field
- xemc3.write.fortran.mapped(ds: Dataset, dir: str, fn: str | None = None, **args) None
Write a file for EMC3 using the mapped format.
- Parameters:
ds (xr.Dataset) – A xemc3 dataset
dir (str) – The directory to which to write the file
fn (str or None (optional)) – In case of
None
all mapped files are written. In the case of a str only that file is written. Any missing data is ignored. Thus if a file is specified, but the data hasn’t been loaded this is ignored.args (dict (optional)) – Can be used to overwrite options for writing. Defaults to the options used for that file.
- xemc3.write.fortran.plates_mag(fn: str, ds: Dataset) None
Write the
PLATES_MAG
info to a file in the EMC3 format.- Parameters:
fn (str) – The file to write to
ds (xr.Dataset or xr.DataArray) – either the PLATES_MAG dataarray or a dataset containing PLATES_MAG
Using xarray
Printing the dataset
xarray by default only prints 12 rows by default.
This can be changed using xarray.set_options
, for example using a context
manager to preserve the default outside of the block:
with xr.set_options(display_max_rows=40):
print(ds)
An alternative way to get all the variables, is to convert the dataset to a list before printing. This however only prints the keys, no additional data:
print(list(ds))
Merging datasets
If you run a parameter study, it is convienient to have all runs in a single dataset.
dss = []
for n in ns:
dss.append([])
for d in Ds:
dir = name(n, d)
dss[-1].append(load(dir))
ds = xr.combine_nested(dss, ["N", "D"])
ds["N"] = [x * 1e19 for x in ns]
ds.N.attrs = dict(long_name="Separatrix density", units="m^{-3}")
ds["D"] = [x * 0.1 for x in Ds]
ds.D.attrs = dict(long_name="Diffusion coefficient", units="m^2/s")
This allows to easily share the simulation results. Note that variables that are the same for all runs, for example the grid data, will be automatically deduplicated.
Configure xemc3
Currently, only the filenames can be changed.
The default
profile is used by default.
[1]:
import xemc3
you can get the current settings using xemc3.config.get()
:
[2]:
xemc3.config.get()
[2]:
{'filenames': 'default'}
There are 3 ways to change the profile.
Using the with
context manager
[3]:
with xemc3.config.set(filenames="some_specifc_config"):
try:
xemc3.load.all("some_folder")
except FileNotFoundError as e:
print("some_specifc_config does not exist, so it fails:")
print(e)
pass
some_specifc_config does not exist, so it fails:
[Errno 2] Failed to find a config file for some_specifc_config - the following locations have been tried:
* '/home/docs/checkouts/readthedocs.org/user_builds/xemc3/envs/stable/lib/python3.8/site-packages/xemc3/data/some_specifc_config.yaml'
* '~/.local/xemc3/some_specifc_config.yaml'
outside of the with block, we still have the default config
[4]:
print(xemc3.config.get("filenames"))
default
Simply setting it in the code
[5]:
xemc3.config.set(filenames="some_specifc_config")
print(xemc3.config.get("filenames"))
some_specifc_config
you have to reset it afterwards, if you want the default back:
[6]:
print(xemc3.config.get("filenames"))
xemc3.config.set(filenames="default")
print(xemc3.config.get("filenames"))
some_specifc_config
default
Setting it in ~/.local/xemc3/config.yaml
This allows to permanently set the default.
Putting
filenames: some_specifc_config
into ~/.local/xemc3/config.yaml
will ensure that is from now on used, also by xemc3 command line tools.
Custom filenames
xemc3 allows to customise the file names used for reading and writing of the files.
The default profile is called default
.
Using a specific version
This can be used to use the naming and normalisation scheeme from a specific version:
[1]:
import xemc3
with xemc3.config.set(filenames="v0.2.4"):
# Now you can load using the 0.2.4 version-compatibility mode
# xemc3.load.all('.')
pass
Ways to customise
There are several ways to customise.
Extending existing profiles
After reading the configuration provided by xemc3, xemc3 looks for a user provided file, in ~/.local/xemc3/<name>.yaml
where <name>
is e.g. default
.
Extending all profiles
xemc3 also reads the file ~/.local/xemc3/files.yaml
. This file has lower priority then user provided <names>.yaml
, but higher than xemc3 provided <name>.yaml
. Thus it can be used to override things by xemc3.
Custom profiles
Similar to extending existing profiles, it is possible to create on profiles. Just like in the case of extending a profile, ~/.local/xemc3/<name>.yaml
is read. In that case all needed files need to be set.
Syntax
The file is using the yaml syntax. Note that while yaml does not is specified to preserve order, xemc3 requires the order for the vars
key, as that specifies in which order the blocks are in the file. While all tested yaml reader preserve the order when reading, care has to be taken if the file is written from python.
See xemc3/data/default.yaml
for an example file:
[2]:
with open(xemc3.__file__.rsplit("/", 1)[0] + "/data/default.yaml") as yaml:
print(yaml.read())
fort.70:
type: mapping
vars:
_plasma_map: {}
fort.31:
type: mapped
skip_first: 1
kinetic: false
vars:
ne:
scale: 1000000.0
units: m$^{-3}$
long_name: Electron density
nZ%d:
scale: 1000000.0
units: m$^{-3}$
fort.33:
type: mapped
vars:
M:
long_name: Mach number
fort.30:
type: mapped
vars:
Te:
units: eV
long_name: Electron temperature
Ti:
units: eV
long_name: Ion temperature
CONNECTION_LENGTH:
type: mapped
vars:
Lc:
scale: 0.01
units: m
long_name: Connection length
DENSITY_A:
type: mapped
kinetic: true
vars:
nH:
scale: 1000000.0
units: m$^{-3}$
long_name: Atomic deuterium density
DENSITY_M:
kinetic: true
vars:
nH2:
scale: 1000000.0
units: m$^{-3}$
long_name: D_2 density
DENSITY_I:
kinetic: true
vars:
nH2+:
scale: 1000000.0
units: m$^{-3}$
long_name: D_2^+ density
TEMPERATURE_A:
kinetic: true
vars:
TH:
units: eV
long_name: Atomic hydrogen temperature
TEMPERATURE_M:
kinetic: true
vars:
TH:
units: eV
long_name: Atomic hydrogen temperature
BFIELD_STRENGTH:
type: full
vars:
bf_bounds:
units: T
long_name: Magnetic field strength
PLATES_MAG:
type: plates_mag
vars:
PLATES_MAG:
long_name: Cells that are within or behind plates
TEMPERATURE_I:
type: mapped
kinetic: true
vars:
TEMPERATURE_I_%d: {}
DENSITY_E_A:
type: mapped
kinetic: true
vars:
DENSITY_E_A_%d: {}
DENSITY_E_I:
type: mapped
kinetic: true
vars:
DENSITY_E_I_%d: {}
DENSITY_E_M:
type: mapped
kinetic: true
vars:
DENSITY_E_M_%d: {}
NEUTRAL_DENSITY:
type: mapped
skip_first:
- 3
- 2
unmapped: true
vars:
NEUTRAL_DENSITY_%d: {}
fort.1:
type: raw
vars:
fort.1:
long_name: Geometry input file
fort.2:
type: raw
vars:
fort.2:
long_name: Plasma parameters, boundary and initial conditions input file
fort.3:
type: raw
vars:
fort.3:
long_name: Control flow input file
fort.4:
type: raw
vars:
fort.4:
long_name: Neutrals input file for EIRENE
fort.40:
type: mapped
vars:
fort.40_%d: {}
fort.42:
type: mapped
vars:
fort.42_%d: {}
fort.43:
type: mapped
vars:
fort.43_%d: {}
fort.46:
type: mapped
vars:
fort.46_%d: {}
fort.47:
type: mapped
vars:
fort.47_%d: {}
IMPURITY_IONIZATION_SOURCE:
type: mapped
vars:
IMPURITY_IONIZATION_SOURCE_%d: {}
IMPURITY_NEUTRAL:
type: mapped
vars:
IMPURITY_NEUTRAL_%d: {}
IMP_RADIATION:
type: mapped
vars:
IMP_RADIATION_%d: {}
FLUX_CONSERVATION:
type: mapped
vars:
FLUX_CONSERVATION_%d: {}
LG_CELL:
type: mapped
dtype: int
vars:
LG_CELL_%d: {}
STREAMING_INFO:
type: info
fmt: '%6.2f %5.3f %10.3E %10.3E %10.3E %10.3E %10.3E'
vars:
dens_change:
long_name: Relative change in density
scale: 0.01
units: ''
notes: Unlike in EMC3/pymc3 this is not percent.
flow_change:
long_name: Change in Flow
notes: Not scaled
part_balance:
long_name: Global particle balance
units: A
dens_upstream:
long_name: Upstream Density
scale: 1000000.0
units: m$^{-3}$
dens_down_back:
long_name: Downstream Density (backward direction)
scale: 1000000.0
units: m$^{-3}$
dens_down_mean:
long_name: Downstream Density (averaged)
scale: 1000000.0
units: m$^{-3}$
dens_down_fwd:
long_name: Downstream Density (forward direction)
scale: 1000000.0
units: m$^{-3}$
ENERGY_INFO:
type: info
fmt: "%6.1f %11.4E %11.4E %11.4E %11.4E\n%6.1f %11.4E %11.4E %11.4E %11.4E\n \
\ %11.4E %11.4E %11.4E"
vars:
Te_change:
long_name: Relative change in el. temperature
scale: 0.01
units: ''
notes: Unlike in EMC3/pymc3 this is not percent.
Te_upstream:
long_name: Upstream el. temperature
units: eV
Te_down_back:
long_name: Downstream el. temperature (backward direction)
units: eV
Te_down_mean:
long_name: Downstream el. temperature (averaged)
units: eV
Te_down_fwd:
long_name: Downstream el. temperature (forward direction)
units: eV
Ti_change:
long_name: Change in ion temperature
scale: 0.01
units: ''
notes: Unlike in EMC3/pymc3 this is not percent.
Ti_upstream:
long_name: Upstream ion temperature
units: eV
Ti_down_back:
long_name: Downstream ion temperature (backward direction)
units: eV
Ti_down_mean:
long_name: Downstream ion temperature (averaged)
units: eV
Ti_down_fwd:
long_name: Downstream ion temperature (forward direction)
units: eV
P_loss_gas:
long_name: Power losses (neutral gas)
units: W
P_loss_imp:
long_name: Power losses (impurities)
units: W
P_loss_target:
long_name: Power losses (target)
units: W
NEUTRAL_INFO:
type: info
fmt: '%12.4E %11.4E %11.4E %11.4E %11.4E %11.4E'
vars:
ionization_core:
long_name: Core ionization
ionization_edge:
long_name: Edge ionization
ionization_electron:
long_name: Electron energy source / ionization
units: eV
ionization_ion:
long_name: Ion energy source / ionization
units: eV
ionization_moment_fwd:
long_name: Forward momentum source/ ionization
ionization_moment_bwk:
long_name: Backward momentum source/ ionization
IMPURITY_INFO:
type: info
fmt: '%12.4E %11.4E'
vars:
TOTAL_FLX:
long_name: Total impurity flux
TOTAL_RAD:
long_name: Total radiation
units: W
ADD_SF_N0:
type: surfaces
vars:
plate_phi:
units: radian
plate_R:
units: m
plate_z:
units: m
GRID_3D_DATA:
type: geom
vars:
R_bounds:
units: m
z_bounds:
units: m
phi_bounds:
units: radian
PARTICLE_DEPO:
type: depo
vars:
surftype_ne:
description: True means +1, False means -1
flux_ne:
long_name: Outflux of particles
units: s^-1
PARTICLE_DEPO_%d: {}
ENERGY_DEPO:
type: depo
vars:
surftype_Te:
description: True means +1, False means -1
flux_P:
long_name: Outflux of energy
units: W
ENERGY_DEPO_%d: {}
TARGET_PROFILES:
type: target_flux
vars:
f_n:
long_name: Particle flux
f_E:
units: W/m²
scale: 10000.0
long_name: Energy flux
avg_n:
units: m^-3
scale: 1000000.0
long_name: Averge density
avg_Te:
units: eV
long_name: Average electron temperature
avg_Ti:
units: eV
long_name: Average ion temperature
The first key is the file name. The type of the file is defined by type
, and defaults to mapped
if not given.
The var
option must always be given. It defines what variables are to be found in the files. As some files contain a variable number of arguments, the last variable may contain %d
, in which case the remaining entries are enumerated starting from 0, with %d
replaced by the number. Options for the variables are: * scale
- by which the variable is multiplied on reading * units
- The units of the quantity (after scaling) * long_name
- a more descriptive name for the
variable * other dictionary items are added to the read variable
Given on the type several options are available.
mapped
:kinetic
: bool, whether the file contains values outside of the mapped regionunmapped
: bool, whether there is one number for each geometric cellskip_first
: int or list of int, how many lines to skip before each data blockdtype
: str, The data type, defaults to floatmapping
:full
:plates_mag
:geom
:info
:fmt
: the formatting of each linelength
: length for theiteration
dimensionsurfaces
:target_flux
:raw
:depo
CLI reference
xemc3-append-time - CLI interface
Load the data from EMC3 simulations and store as netcdf file. The data is appended for each simulation to the netcdf file, which will be created if it does not yet exists.
xemc3-append-time [-h] [-q] [-o NAME] [-V] path [path ...]
xemc3-append-time positional arguments
path
-- Path of the directory to load. The netcdf file will be
(default:called dir.nc if the folder was called dir.
None
)
xemc3-append-time optional arguments
xemc3-to-netcdf - CLI interface
Load the data from EMC3 simulations and store as netcdf file. The data is written for each simulation to a netcdf file.
xemc3-to-netcdf [-h] [-q] [-o NAME] [-V] path [path ...]
xemc3-to-netcdf positional arguments
path
-- Path of the directory to load. The netcdf file will be
(default:called dir.nc if the folder was called dir.
None
)
xemc3-to-netcdf optional arguments
xemc3-divertor - CLI interface
Plot the heatflux on the divertor
xemc3-divertor [-h] [-c] [-s] [-a] [-l] [-q] [-g] [-r RANGE] [-t TITLE] [--phi_slices]
[-k KEY] [-V]
path
xemc3-divertor positional arguments
path
- Path of data (default:None
)
xemc3-divertor optional arguments
-l
,--plotlower
- Plot only the lower half--phi_slices
- Show where phi in deg is an integer
xemc3-to-archive - CLI interface
Archive the data from EMC3 simulations as a netcdf file.
xemc3-to-archive [-h] [-q] [-o NAME] [-V] [-g] [-n] [-d] path [path ...]
xemc3-to-archive positional arguments
path
-- Path of the directory to load. The netcdf file will be
(default:called dir.arch.nc if the folder was called dir.
None
)
xemc3-to-archive optional arguments
-o
NAME
,--name
NAME
- Specify the name for the output file. Defaults todir.arch.nc
when not given. Otherwisename.arch.nc
is used. (default:None
)-g
,--geometry
- Also store geometry-n
,--no-mapping
- Do not include the mapping information.-d
,--delete
- Delete the uncompressed input file. Only used if the input was a netcdf file
Citing xemc3
If you are using xemc3 for a scientific publication, please cite the code, preferably using the DOI of the version you used.
Entries for e.g. bibtex are available from zenodo.
The DOI for all versions is: DOI: 10.5281/zenodo.5562265
Installation
With pip
:
pip install --user xemc3
You can run the tests by running pytest --pyargs xemc3
.
xemc3 will install the required python packages when you run one of the above install commands if they are not already installed on your system.