User Tools


Parametrized postprocessing with RIG-VM

The handling of large 3D POS files with GMSH can be cumbersome in some cases. For that purpose, a RIG-VM module module_ppp.so1) has been implemented. This module offers the following:

  • Read in binary post-processing (*.pos) - files generated during DSMC / PIC-MC runs
  • Fast generation of user specified averaged or interpolated views along cut planes or lines

Prerequisites / installation

The file module_ppp.so comes with the PIC-MC distribution and should be located under /…/path/to/picmc/lib. To use it within RIG-VM from command line, make sure that the following environment variables are set as follows:

  • LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/picmc/lib
  • PATH=$PATH:/path/to/picmc/bin

If PIC-MC is installed, it is very likely that the environment variables are already correctly set.

Summary of commands

In the following all postprocessing commands are summarized:

obj.read("input.pos", "variable_name");
obj.merge("input.pos", "variable_name");
obj.append("input.pos", "variable_name");
 
obj.xy_plane(z0, delta_z, "method");
obj.xz_plane(y0, delta_y, "method");
obj.yz_plane(x0, delta_x, "method");
obj.plane(x0, y0, z0,   x1, y1, z1,   x2, y2, z2,   delta, N, M, "method");
obj.line(x0, y0, z0,   x1, y1, z1,   delta, N, "method");
 
obj.cylinder(x0, y0, z0,   x1, y1, z1,  r_cyl,  delta,  nl, nphi, "method");
 
obj.write_pos_scalar("output.pos", "View title", expression);
obj.write_pos_vector("output.pos", "View title", expression_x, expression_y, expression_z);
obj.write_pos_scalar_binary("output.pos", "View title", expression);
obj.write_pos_vector_binary("output.pos", "View title", expression_x, expression_y, expression_z);
obj.write_pos_triangulate("output.pos", "View title", expression);
obj.write_tec_scalar("output.dat", "View title", expression);
obj.write_tec_vector("output.dat", "View title", expression_x, expression_y, expression_z);
obj.write_vtk_scalar("output.vtp", "View title", expression);
obj.write_vtk_vector("output.vtp", "View title", expression_x, expression_y, expression_z);
obj.write_txt("output.txt", ["col1", "col2", ...], [exp1, exp2, ...]);
obj.write_csv("output.csv", ["col1", "col2", ...], [exp1, exp2, ...]);
 
obj.write_pos_scalar_parametric("output.pos", "View title", x, y, z, expression);
obj.write_pos_scalar_binary_parametric("output.pos", "View title", x, y, z, expression);
obj.write_pos_vector_parametric("output.pos", "View title", x, y, z, expression_x, expression_y, expression_z);
obj.write_pos_vector_binary_parametric("output.pos", "View title", x, y, z, expression_x, expression_y, expression_z);
 
obj.write_pos_time_steps_scalar("output.pos", "View title", "variable_name");
obj.write_pos_time_steps_scalar_binary("output.pos", "View title", "variable_name");
obj.write_pos_time_steps_vector("output.pos", "View title", "variable_name");
obj.write_pos_time_steps_vector_binary("output.pos", "View title", "variable_name");
 
obj.create_scalar("variable_name", expression);
obj.create_vector("variable_name", expression_x, expression_y, expression_z);
 
result = obj.integrate(expression);
obj.identity();
obj.reduce();
obj.convolution(vx, vy, vz, T, interpolation);
obj.convolution_circular();

Usage of the module by examples

The module is to be used within RIG-VM scripts. We demonstrate the postprocessing capabilities on the DSMC gas flow example from this tutorial.

Simple scalar cut-plane

One of the easiest and most frequent tasks is to create a quick cut-plane aligned parallel to XY, XZ or YZ plane. Assumed, we have the file pressure_Ar_60.00ms.pos from a DSMC run of the tube example as output file, and would like to have a scalar pressure plot in the XY plane. The according script - let's call it extract.r looks like follows:

cutplane.r
compiletime load "module_ppp.so";
PPP obj = {};
 
obj.read("pressure_Ar_60.00ms.pos", "pAr");
obj.xy_plane(0, 10, "nearest_neighbour");
obj.write_pos_scalar("cutplane_xy.pos", "Ar pressure [Pa]", pAr);

The script can be executed with rvm as shown in the following console output.

ap@sim037:~/sim/Tube/sample$ rvm extract.r 
Created View {pAr}
File pressure_Ar_60.00ms.pos has 109368 scalar and 0 vector points
XY-Plane extract, z0 = 0 +/- 10, method = 'nearest_neighbour'
ap@sim037:~/sim/Tube/sample$ 

As result, the output file cutplane_xy.pos is created and can be viewed in GMSH.

The meaning of the commands in the last three lines is as follows:

  • obj.read(“input.pos”, “variable_name”);
    • The first parameter is the filename of the binary POS file
    • The second parameter is a variable name used for referencing these data in a later step
  • obj.xy_plane(z0, delta_z, “method”);
    • z0 is the center coordinate of the XY plane
    • delta_z is the range, where data points will be considered
    • method is the interpolation method with following options:
      • “nearest_neighbour”: Within the given range delta_z, the points which are closest to z0 are projected onto the cut plane
      • “average”: Within the given range z0 +/- delta_z, all points belonging to the same (X,Y) coordinate are projected onto the cut-plane and averaged.
      • “interpolate”: Points from the given range z0 +/- delta_z are interpolated on the cut-plane. The interpolation weighting function is 1.0 at z==z0 and decreases linearly to 0.0 for z–>z0 +/l delta_z.
      • “sum”: Values from the given coordinate range z0 +/- delta_z are summed up.
  • obj.write_pos_scalar(“output.pos”, “plot_title”, expression);
    • This writes a scalar output file named “output.pos”.
    • The “plot_title” will be shown as view title in GMSH.
    • With expression the scalar output value can be specified. In the above example, it is just the pressure value named pAr.

Besides of obj.xy_plane(z0, delta_z, “method”) there are also obj.xz_plane(y0, delta_y, “method”); and obj.yz_plane(x0, delta_x, “method”); available.

Scalar cut-plane with arithmetics

Within the expression parameter it is possible to do some arithmetics. If e.g. the pressure should be plotted in mTorr instead of Pa (1 mTorr = 133 mPa), the following script will do it:

compiletime load "module_ppp.so";
PPP obj = {};
 
obj.read("pressure_Ar_60.00ms.pos", "pAr");
obj.xy_plane(0, 10, "nearest_neighbour");
obj.write_pos_scalar("cutplane_xy.pos", "Ar pressure [mTorr]", pAr*1000/133);

Scalar cut-plane with triangulation

By replacing the command write_pos_scalar by write_pos_triangulate it is possible to create a triangulated scalar view:

compiletime load "module_ppp.so";
PPP obj = {};
 
obj.read("pressure_Ar_60.00ms.pos", "pAr");
obj.xy_plane(0, 10, "nearest_neighbour");
obj.write_pos_triangulate("cutplane_xy.pos", "Ar pressure [mTorr]", pAr*1000/133);

Scalar cut-plane from multiple input files

Let's assume we would like to plot the flux (=density*velocity) in positive X direction but have forgotten to activate the density plot in the parameter file. Instead we have only the number and the velocity plots available.

The postprocessing module allows us to plot the density and the flux anyway as shown in the following:

compiletime load "module_ppp.so";
PPP obj = {};
 
obj.read("number_Ar_60.00ms.pos", "N");
obj.read("cell_volumes.pos", "vol");
obj.read("velocity_Ar_60.00ms.pos", "v");
obj.xy_plane(0, 20, "nearest_neighbour");
obj.write_pos_scalar("cutplane_xy.pos", "X-flux", vol>0 ? N/vol*v_x ! 0);

Important: Please note, that the velocity is a vector plot. In the read command we have assigned the variable name v to the velocity. In the expression below, the three components v_x, v_y and v_z must be used instead. The appendixes _x, _y and _z are attached automatically to each variable name in case of a vector plot!.

Depending on the statistics, the cut-plane of the flux plot may look a bit noisy. In this case, it helps to use the “interpolate” option rather than “nearest_neighbour”:

obj.xy_plane(0, 20, "nearest_neighbour");
obj.xy_plane(0, 20, "interpolate");

The expression vol>0 ? N/vol*v_x ! 0 contains an implicit if statement: The computation density = number/volume is not possible for all cells, since in some cases the volume may be zero. Thus, first it is checked, if vol>0. If that condition evaluates to true, the expression behind the ? applies, otherwise the expression branches behind the ! symbol.

It may look a bit ugly to have these arithmetics in one line. Another possibility is to define the density within the PPP object:

compiletime load "module_ppp.so";
 
PPP obj = {
  float NREAL=1e11;
  float density: vol>0 ? NREAL*N/vol ! 0;
};
 
obj.read("number_Ar_60.00ms.pos", "N");
obj.read("cell_volumes.pos", "vol");
obj.read("velocity_Ar_60.00ms.pos", "v");
obj.xy_plane(0, 20, "interpolate");
obj.write_pos_scalar("cutplane_xy.pos", "X-flux", density*v_x);

Since the variables N and vol are not known during definition of density within the PPP object, it is important to use the alias method (i. e. the “:”) instead of the assign method (i. e. “=”) for definining density. The result of this modified script is the same as for the script above but now, it looks more clearly arranged.

Time averaging

If you would like to perform averaging of the same quantity over several time steps, it is sufficient to call obj.read(…) several times but with the same variable name. An example is given in the following:

time_averaging.r
compiletime load "module_ppp.so";
 
case = "sample2";
 
PPP obj={};
 
for (time=100; time<=200; time=time+20) {
  obj.read("$case/density_Ar_$(time).00ms.pos", "d");
  obj.read("$case/velocity_Ar_$(time).00ms.pos", "v");
}
 
obj.xy_plane(0, 10, "nearest_neighbour");
obj.write_pos_vector_binary("flux-cutplane.pos", "Ar flux [1e18/(m²s)]", 1e-18*d*v_x, 1e-18*d*v_y, 1e-18*d*v_z);

The read command of the PPP object automatically recognizes if one variable is used several times and performs an averaging of the data in this case.

Development over time

If you would like to illustrate the development of the same quantity over several time steps, you need to call obj.append(…) several times but with the same variable name and then one of the write_pos_time_steps functions. An example is given in the following:

append_cutplanes.r
compiletime load "module_ppp.so";
compiletime load "module_system_linux.so";
 
# defining parameters
case   = "sample2";
start  = 100;
end    = 200;
dtime  = 20;
unit   = "us";
z      = 0;
folder = "density";
files  = "density_e";
 
# create ppp subfolder if it does not exist
System FS = { rootpath = "$case"; };
if (FS.folder_exists("ppp") == 0) then {
  out "Creating folder $case/ppp\n";
  FS.create_folder("ppp", 493)
};
 
# applying a cut plane to each time step
for (time=$start; time<=$end+0.001; time=time+dtime) {
  PPP obj1 = {};
 
  obj1.read("$case/$folder/$(files)_$(time% 4,2f)$(unit).pos", "X");
  obj1.xy_plane($z, 10, "nearest_neighbour");
  obj1.write_pos_scalar_binary("$case/ppp/cut_$(files)_$(time% 4,2f)$(unit).pos", "$files", X);
}
 
# appending the cut planes to each other
PPP obj2 = {};
for (time=$start; time<=$end+0.001; time=time+dtime) {
  obj2.append("$case/ppp/cut_$(files)_$(time% 4,2f)$(unit).pos", "Y");
}
obj2.write_pos_time_steps_scalar_binary("$case/timestep_cut_$(z)mm_$(files)_$(start)-$(end)$(unit).pos", "$files", "Y");

First of all module_system_linux.so is loaded, which is needed for creating the ppp subfolder for storing the intermediate cut planes. Next some parameters are set including the time interval, the z position of the cut plane and the name of the plot. Then the ppp subfolder is created, where the cut planes generated in the next part are stored. In the last part these cut planes are appeded to each other and written to the output file.

The order of first creating the cut planes and then appending them to each other is important, because it is (at the moment) not possible to make cut planes of variables with multiple time steps. Additionally the write_pos_time_steps functions can only be called on the variable name and not on an expression, so that calculations have to be done before.

Generating vector plots as cut plane

Finally we would like to plot the flux as vector plot. All we have to do is to replace the obj.write_pos_scalar(…) by obj.write_pos_vector(…). Since a vector plot has three components, we have three instead of one expression arguments:

compiletime load "module_ppp.so";
 
PPP obj = {
  float NREAL=1e11;
  float density: vol>0 ? NREAL*N/vol ! 0;
};
 
obj.read("number_Ar_60.00ms.pos", "N");
obj.read("cell_volumes.pos", "vol");
obj.read("velocity_Ar_60.00ms.pos", "v");
obj.xy_plane(0, 20, "interpolate");
obj.write_pos_vector("cutplane_xy.pos", "X-flux", density*v_x, density*v_y, density*v_z);

Revert POS file data to original state

In case you have applied cut planes to your plots and you want to revert them to their original state, you can use the ​identity​ function. This is shown in the following example, where the complete density distribution is created after a cut plane of the number plot was written:

compiletime load "​module_ppp.so";
 
PPP obj = {};
 
obj.read("​number_Ar_60.00ms.pos","​N");
obj.xy_plane(0, 10, "nearest_neighbour");
obj.write_pos_scalar("cutplane_xy.pos", "Ar number [#]", N);
obj.identity();
obj.read("​cell_volumes.pos", "vol");
obj.write_pos_scalar("density_Ar.pos", "Ar density [#/m³]", vol>0 ? N/vol ! 0);

General (oblique) interpolation views

It is possible to generate cut planes in a more general way. For this purpose, a general description of a parametric plane looks as follows:

$\vec{r}(s, t) = \vec{x}_0 + s\left(\vec{x}_1-\vec{x}_0\right) + t\left(\vec{x}_2-\vec{x}_0\right)$.

The accordant post processing command is written as:

obj.plane(x0, y0, z0,   x1, y1, z1,    x2, y2, z2,  delta, N, M, "method");

First, the three points of the above equation are given coordinate-wise (i.e. the first 9 parameters). The next parameter delta is the vertical distance to the plane, wherin points are considered for interpolation. The integer numbers N, M give the resolution of the cut plane in direction of the first and second vector. Update: It is possible to specify 0, 0 for N, M; in this case the actual resolution of the imported POS file will be taken.

An example of an oblique plane with respect to our tube DSMC case is shown in the following:

oblique_cutplane.r
compiletime load "module_ppp.so";
 
PPP obj={
  float density: vol>0 ? N/vol ! 0;
};
 
obj.read("number_Ar_60.00ms.pos", "N");
obj.read("cell_volumes.pos", "vol");
obj.read("velocity_Ar_60.00ms.pos", "v");
 
obj.plane(-110, -210, -210,   200, 210, 0,    200, -210, 0,    15,   40, 40, "interpolate");
 
obj.write_pos_scalar("obliqueplane.pos", "Density [1/m²s]", density);

Analogously, it is possible to extract data along a straight line:

$\vec{r}(s) = \vec{x}_0 + s\left(\vec{x}_1-\vec{x}_0\right)$

with the postprocessing command:

obj.line(x0, y0, z0,   x1, y1, z1,    delta, N, "method");

Additional output formats

Visualization Toolkit (VTK)

Besides the POS output format of GMSH, it is possible to write the 3D data in the Visualization Toolkit format. The two functions for scalar and vector data are:

obj.write_vtk_scalar("output.vtp", "View title", expression);
obj.write_vtk_vector("output.vtp", "View title", expression_x, expression_y, expression_z);

The syntax is identical to that of the POS format.

Tecplot

Also the Tecplot format is supported for both scalar and vector data:

obj.write_tec_scalar("output.dat", "View title", expression);
obj.write_tec_vector("output.dat", "View title", expression_x, expression_y, expression_z);

Again the syntax is identical to that of the POS format.

ASCII export

TXT output

It is convenient to directly export certain results as ASCII TXT file, which can be imported e.g. in Origin or other graph presentation software. For that purpose, the following method is implemented:

obj.write_txt("file_name.txt", ["col1", "col2", ...], [exp1, exp2, ...]);

The arguments have the following meaning:

  • The first argument is the name of the TXT file, which should be created
  • The second argument is a list containing all column title strings. They will appear in the first line of the ASCII file.
  • The third argument is a list of expressions, which should be computed for each column.
    • Within the expressions, all variable names originating from imported POS files as well as the variables “x, y, z” for the respective coordinates can be used.
    • Additionally, the maximum and minimum values of all variables are computed and can be accessed via variablename_max, variablename_min.

We demonstrate this in a short example:

export_ascii.r
compiletime load "module_ppp.so";
 
PPP obj1 = {};
 
obj1.read("absorption.pos", "A");
obj1.xy_plane(-141.75, 10, "nearest_neighbour");
obj1.write_pos_scalar_binary("absorption-2D.pos", "SiH4 absorption [1/m²s]", A);
 
PPP obj2 = {};
 
obj2.read("absorption-2D.pos", "A");
obj2.line(420, -380, -141.75,   607.5, -380, -141.75,  40, 19, "average");
obj2.write_txt("profile_avg.txt", ["r [mm]", "abs [1/m²s]", "abs [%]"], [x-420, A, 100*A/A_max]);

This script reads a 3D posfile “absorption.pos” of an absorption plot into obj1, creates a 2D cutplane of it and stores the cutplane into “absorption-2D.pos”. This created file is then read into a second PPP object obj2. Here, the absorption profile is averaged along a line in X direction. The averaged profile is finally dumped into an ASCII file. The ASCII file finally looks like follows:

         r [mm]   abs [1/m^2s]        abs [%]

        4.93421    3.80416e+20        99.9454
        14.8026    3.77636e+20        99.2152
        24.6711    3.80623e+20            100
        34.5395     3.7374e+20        98.1916
        44.4079    3.75455e+20         98.642
        54.2763    3.73143e+20        98.0347
        64.1447    3.68571e+20        96.8336
        74.0132    3.63662e+20        95.5439
        83.8816    3.58961e+20        94.3087
          93.75    3.54935e+20         93.251
        103.618    3.47403e+20         91.272
        113.487    3.43792e+20        90.3235
        123.355    3.31429e+20        87.0752
        133.224    3.24909e+20        85.3624
        143.092    3.18416e+20        83.6563
        152.961    3.03792e+20        79.8144
        162.829    2.88857e+20        75.8905
        172.697    2.66338e+20        69.9741
        182.566    2.29174e+20        60.2102

CSV output

Instead of TXT files, writing the output in the CSV format is also possible. The syntax of write_csv is identical to that of write_txt:

obj.write_csv("file_name.csv", ["col1", "col2", ...], [exp1, exp2, ...]);

The column separator is a colon, which is explicitely mentioned in the file and the decimal separator is a point.

Numerical integration

The numerical integration feature is currently available for cut-planes only. In our tube example we might want to integrate the particle flux through the tube. This can be accomplished as follows:

integrate_flux.r
compiletime load "module_ppp.so";
 
mesh_unit=1e-3;
Nsccm = 4.478e17;
 
file "integration.txt" << 
for (time=20; time<=400; time=time+20) {
  PPP obj={};
 
  obj.read("density_Ar_$(time).00ms.pos", "density");
  obj.read("velocity_Ar_$(time).00ms.pos", "v");
  obj.plane(200, -60, -60,   200, 60, -60,   200, -60, 60,  10, 0, 0, "interpolate");
 
  flow = obj.integrate(density*v_x)*mesh_unit^2/Nsccm;
  out "$(time)  $(flow)\n";
}
Tube model with cross section of interest Script for obtaining integrated flow through cross section

In the script shown above, the density and velocity of Argon are loaded for a complete time series t=20…400 ms. For each time step, a PPP object is created, and the flux is generated via the expression density*v_x within the cut plane. With the integrate function, the total flux through the tube cross section can be computed.

The numerical integration is internally performed over triangles of adjacent points. For each triangle, the average of the three point values is multiplied with the area of the triangle element. If the mesh unit is not 1 m, the integration result should be multiplied with mesh_unit^2 in order to get the correct unit.

In the example script, the result is additionally divided by the particle number per second for 1 sccm in order to get the flow in sccm.

Merging of (position) plots

In the normal output files (e.g. density) each point corresponds to a single cell with its position and its value. A second density plot has the same cells at the same position but with different values. Therefore it makes no sense to merge these files into a single one, this is only the case for position plots, where the positions of particles are stored independent of the cell resolution. By merging multiple of these files the illustration of the particle movement is possible:

merge_position.r
compiletime load "module_ppp.so";
 
PPP obj = {};
 
for (float time=0; time<=100; time=time+0.5) {
  obj.merge("position/position_e_$(time %,2f)ns.pos", "pos");
 }
obj.write_pos_scalar_binary("position_e_0-100ns.pos", "e position", pos);

In this example a position plot each 0.5 ns for a time interval between 0 and 100 ns is merged and written to a single file. The syntax of the merge function is identical to that of the read function.

Manipulation of data

General concept

The RVM postprocessing module generally uses two different data sets, namely the “input” and the “view” data as shown in the figure below.

  • The “input” data consists of the raw data obtained from the simulation output files (GMSH POS format). With every read command, a new entry in the input data is created. Each input data can be assigned a short name which will be later used as variable name (e.g. “n” for density in the figure below).
  • The “view” data are obtained from the “input” data by some geometric transformation, e.g. a 2D cut plane or a line scan. They have the same names as the input data with some exceptions:
    • If the input data are vectors (e.g. the velocity field “v” in the example below) three different “view” variables v_x, v_y, v_z are generated in order to allow a component-wise access to the vector field.
    • Additional variables such as the coordinates x, y, z as well as maximum and minimum values for each “view” variable (n_min, n_max, v_x_min, v_x_max etc.) are automatically generated and appended to the “view” dataset.
  • If another geometrical operation is applied, the former “view” data is being deleted and replaced by data generated from the “input” in connection with the geometrical operation.
  • If no geometrical operation is applied at all, the “view” data will be a copy of all input data (in former versions, the command obj.identity(); had to be explicitely invoked for that purpose, nowadays this is automatically done internally).

After applying the geometrical operations, in the obj.write_XXX commands all variable names in the “view” data can be used for forming mathematical expressions defining what to extract into the output files. For the above example it is e.g. possible to extract the particle flux, i.e. the product of density and velocity, in direction of the Z coordinate as well as the relative density distribution in %:

create-fz.r
compiletime load "module_ppp.so";
PPP obj = {};
 
obj.read("density.pos",  "n");
obj.read("velocity.pos", "v");
obj.xy_plane(0, 10, "nearest_neighbour");
 
obj.write_pos_scalar_binary("flux-Z-2D.pos", "Vertical flux [1/m^2 s]", n*v_z);
obj.write_pos_scalar_binary("density-relative.pos", "Relative density [1/m^2 s]", 
                            100*n/n_max);

Creation of new input data

Usually, the input data is obtained directly via the command obj.read(“filename”, “label”);. In some cases it may be useful to create additional input data via an algebraic formula from the existing data. For this purpose, the following commands can be used:

  • obj.create_scalar(“new_label”, expression); –> Create new scalar input data entry
  • obj.create_vector(“new_label”, exp_x, exp_y, exp_z); –> Create new vector input data entry

In the example above, it is possible to plot the relative density distribution in % because of the existence of the view variable n_max. However it is not clear how to plot e.g. the relative flux distribution, since there is no obvious way to obtain the maximum value of n*v_z. By creation of a “flux” variable as new input entry, this problem can be solved (see example listing and data structure diagram below).

create-fz.r
compiletime load "module_ppp.so";
PPP obj = {};
 
obj.read("density.pos",  "n");
obj.read("velocity.pos", "v");
 
obj.create_vector("flux", n*v_x, n*v_y, n*v_z);
 
obj.xy_plane(0, 10, "nearest_neighbour");
 
obj.write_pos_scalar_binary("flux-Z.pos", "Vertical flux [1/m^2 s]", flux_z);
obj.write_pos_scalar_binary("flux-Z-relative.pos", "Relative Vertical flux [%]", 
                            100*flux_z/flux_z_max);

Reduction of input data

Let us assume we have 3D data of the density and velocity of evaporated atoms. In the XY-plane at Z=100 there is a substrate where the evaporated atoms can condensate.

First, in order to get the thickness profile on the substrate, the flux has to be extracted by multiplying density and z component of velocity in a cut-plane just below the substrate surface.

Second, let us assume the substrate is moving into Y direction, i.e. we have a dynamic deposition. In such cases, only the linescan of the profile in X direction averaged over Y is of relevance.

In order to do this, in prior versions of the post processing module it was required to make the 2D cut plane first, save it into a temporary POS file, load this POS file into a second post processing object and finally extract the line profile from there.

From version 2.4.3 on, there is the possibility of reducing the input data according to the last executed geometric operation, the command syntax is:

  • obj.reduce(); –> Reduction of input data space according to current arrangement of view data.

This allows for obtaining a line profile from 3D data in a much easier way:

line-profile.r
compiletime load "module_ppp.so";
PPP obj = {};
 
aperture_width = 100;   # Aperture width on substrate in transport direction
 
obj.read("density.pos", "n");
obj.read("velocity.pos", "v");
 
# Define vertical flux component
obj.create_scalar("Fz", n*v_z);
 
# Make cut plane of data just below the substrate surface
obj.xy_plane(99.5, 10, "nearest_neighbour");  
 
# Reduce input data to cut-plane (this is the important command)
obj.reduce(); 
 
# Make another cut plane averaging over the aperture_width
# in order to get the line profile
obj.xz_plane(0, aperture_width/2, "average");
 
# Write line profile into TXT file
obj.write_txt("absorption_line.txt", 
              ["x", "avg_absorption", "rel_absorption"],
              [x, Fz, 100*Fz/Fz_max]);

Control of written data

Regarding the format of written data, there are the following possibilities:

  • The coordinates, where the written data get plotted in POS files, can be explicitely specified
  • It is possible to set a flag deciding whether to write data at a given coordinate point or not

The first option involves the following output methods:

  • obj.write_pos_scalar_parametric(“filename.pos”, “Legend”, x, y, z, expression);
  • obj.write_pos_scalar_binary_parametric(“filename.pos”, “Legend”, x, y, z, expression);
  • obj.write_pos_vector_parametric(“filename.pos”, “Legend”, x, y, z, exp_x, exp_y, exp_z);
  • obj.write_pos_vector_binary_parametric(“filename.pos”, “Legend”, x, y, z, exp_x, exp_y, exp_z);

The first two commands are for parametrized scalar plots (in ASCII and binary format, respectively), the second two commands are for vector plots. An example is given in the following listing.

plot_n_flux.r
compiletime load "module_ppp.so";
 
PPP obj = {};
obj.read("density.pos",  "n");
obj.read("velocity.pos", "v");
obj.xy_plane(100, 10, "nearest_neighbour");
 
# Plot density and flux side by side. A coordinate offset allows for
# loading both pos files into GMSH and have the cut planes non-overlapping
 
obj.write_pos_scalar_binary_parametric("density-2D.pos", "Density [1/m^3]", x, y, z, n);
obj.write_pos_scalar_binary_parametric("flux-2D.pos", "Flux [1/m^3]", x+200, y, z, n*v_z);

In order to control explicitely, which data point should be written and which should be omitted, there is the possibility to define a write_flag variable within the postprocessing object. This is demonstrated in the following:

write_flag.r
compiletime load "module_ppp.so";
 
PPP obj = {
  write_flag: A>0;
};
 
obj.read("absorption.pos", "A");
obj.write_pos_scalar_binary("absorption-noZeros.pos", "Absorption [1/m^2 s]", A);

In a large 3D absorption file, non-zero values only occur at the intersection between volume grid and mesh elements of walls. Thus a large fraction of the POS file consists of zeros, which is a waste in memory consumption. With the above script, the write_flag variable forces the write routine to only output those values with non-zero absorption, i.e. A>0.

It is important to define write_flag with a colon and not to write write_flag = (A>0);. In the rvm scripting language, the colon means that the value of write_flag will be only computed on request according to the currently defined parameters on right side. With the regular “=” assignment operator, the value of the variable A will be required at the moment of the definition of write_flag, which is not the case in the above script.

It is also possible to combine multiple conditions e.g. in order to specify a certain coordinate range for output:

write_flag2.r
compiletime load "module_ppp.so";
 
PPP obj = {
  write_flag: (abs(x)<100) and (y>-50) and (y<150);
};
 
# ...

Convolution function

A convolution function has been implemented to operate on target absorptions plots in an attempt to model erosion profiles in MoveMag aplications. The primary approach here is to assume plasma and ion bombardment, respectively do not change with magnetron position which should be valid for small displacements vectors. Thus, we simply integrate over time the convolution of a time deviant, i.e. moving absorption plot with a stationary step function.

Convolution and integration over time of moving source data with stationary step function. Absorption plot (top) and convolution result (bottom) for a periodic oscillation in y.
compiletime load "module_ppp.so";
 
PPP obj1 = {};
 
#Define velocity vector and integration time
float vx[4], vy[4], vz[4]; #[m/s]
float T = 4; #[s]
 
#Oscillation starting from center position
#Move 1s to left with 1 m/s, then 2 times to right accordantly, then left again.
vx[0] = -1;
vx[1] = +1;
vx[2] = +1;
vx[3] = -1;
 
obj1.read("absorption_Arplus_100.00us.pos", "val");
 
obj1.xz_plane(0.0088, 1, "nearest_neighbour");
 
obj1.reduce; #Operate on cut plane (2D) data set for better performance!
 
obj1.convolution(vx, vy, vz, T, 0);
 
obj1.write_pos_scalar_binary("absorption_Arplus_100.00us_xz-plane_convolution.pos", val);

Convolution with linear weighting

Entering '1' for the weighting flag enables a linear weighting scheme during convolution operation from 1 (no displacement) to 0 (maximum displacment). With this you can realize an interpolation between two or more states, i.e. magnetron positions, by merging the results from corresponding convolution operations together.

Convolution with constant weighting. Convolution with linear weighting.

Circular Convolution

For a 360° convolution over the plot center use the function 'convolution_circular()' as seen below. This function has been implemented to adress modeling of absorption profiles on rotating substrates.

compiletime load "module_ppp.so";
 
PPP obj1 = {};
 
obj1.read("absorption_Arplus_100.00us.pos", "val");
 
obj1.xz_plane(0.0088, 1, "nearest_neighbour");
 
obj1.reduce; #Operate on cut plane (2D) data set for better performance!
 
obj1.convolution_circular();
 
obj1.write_pos_scalar_binary("absorption_Arplus_100.00us_xz-plane_convolution.pos", val);
Source data. After circular convolution.
1)
The acronym PPP stands for PIC-MC Postprocessing ;-)