Use Interface Data Structures¶
The Interface Data Structures (IDSs) are the main way to interact with IMAS data. An IDS is a tree-like structure with one root element (the IDS) and several branches with data at the leave nodes.
Many types of IDSs exist: check out the documentation of the Data Dictionary for a complete overview.
Creating IDSs¶
IDSs can be created in multiple ways:
Create an empty IDS¶
You can create an empty instance of an IDS by creating a new class object for that IDS, for example new imas.core_profiles() creates an empty
core_profiles IDS. This initializes all items in the IDS to their
Default values.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class create_ids {
public static void main(String[] args) {
// Create an empty core_profiles IDS
imas.core_profiles core_profiles = new imas.core_profiles();
// -> -999999999 (default INT_0D value)
System.out.println(core_profiles.ids_properties.homogeneous_time);
// Create an empty equilibrium IDS
imas.equilibrium equilibrium = new imas.equilibrium();
System.out.println(equilibrium.time_slice); // -> null
}
}
Create a copy of an IDS¶
You can create a copy of another IDS by putting it to the memory backend and getting it again.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class copy_ids {
public static void main(String[] args) throws ALException {
// Create an empty core_profiles IDS, and fill with some data
imas.core_profiles core_profiles = new imas.core_profiles();
core_profiles.ids_properties.homogeneous_time =
LowLevel.IDS_TIME_MODE_HOMOGENEOUS;
double[] time = {1.0, 2.0};
core_profiles.time = new Vect1DDouble(time);
core_profiles.profiles_1d = new imas.core_profiles.profiles_1dClass[2];
for (int i = 0; i < 2; i++ ) {
core_profiles.profiles_1d[i] =
new imas.core_profiles.profiles_1dClass();
double[] rtn = {0, 0.25, 0.5, 0.75, 1.0};
core_profiles.profiles_1d[i].grid.rho_tor_norm =
new Vect1DDouble(rtn);
double[] jt = {1+i, 1.25+i, 1.5+i, 1.75+i, 2.0+i};
core_profiles.profiles_1d[i].j_total = new Vect1DDouble(jt);
}
// Create memory backend Data Entry
int data_entry = imas.open(
"imas:memory?path=/", LowLevel.FORCE_CREATE_PULSE);
// Copy the IDS through the memory backend
core_profiles.put(data_entry, "core_profiles", core_profiles);
imas.core_profiles core_profiles_copy =
imas.core_profiles.get(data_entry, "core_profiles");
// Adjust some data
for (int i=0; i<5; i++)
core_profiles_copy.profiles_1d[0].j_total.setElementAt(
i, core_profiles_copy.profiles_1d[0].j_total.getElementAt(i)*2);
// Verify that we didn't change the original IDS
// -> [2.0,2.5,3.0,3.5,4.0]
System.out.println(core_profiles_copy.profiles_1d[0].j_total);
// -> [1.0,1.25,1.5,1.75,2.0]
System.out.println(core_profiles.profiles_1d[0].j_total);
}
}
Deallocate an IDS¶
If you no longer need an IDS, you can deallocate it so it releases the (memory) resources in use by the data. In Java this is automatically handled by the garbage collector of the JVM.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class deallocate_ids {
public static void main(String[] args) {
// Create an empty magnetics IDS
imas.magnetics magnetics = new imas.magnetics();
// Use the IDS ...
// Unset the magnetics IDS:
magnetics = null;
// Now the JVM garbage collector can free up the associated resources
}
}
Mandatory and recommended IDS attributes¶
Some attributes in an IDS are mandatory or recommended to always fill. Below list provides a short overview:
ids_properties/homogeneous_time[mandatory]: see Time coordinates and time handling.ids_properties/comment[recommended]: a comment describing the content of this IDS.ids_properties/provider[recommended]: name of the person in charge of producing this data.ids_properties/creation_date[recommended]: date at which this data has been produced, recommended to use the ISO 8601YYYY-MM-DDformat.
Note
ids_properties/version_put is filled by the access layer when you
put an IDS.
Understanding the IDS structure¶
An IDS is a tree structure. You can think of it similar as a directory structure with files: the IDS is the root “directory”, and inside it you can find “subdirectories” and “files” with data.
We will use the general Computer Science terminology for tree structures and call these “files” and “directories” of our IDSs nodes. IDSs can have a limited number of different types of nodes:
Structure: think of these as the directories of your file system. Structures contain one or more child nodes (files and subdirectories). Child nodes can be of any node type again.
Array of structures: this is an array of structures (see previous point).
Data: this is a data element. Like files on your file system these nodes contain the actual data stored in the IDS.
Structure¶
Structure nodes in an IDS are a container for other nodes. In Java they are implemented as a class. You can address child nodes as instance fields, see the code sample below.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class ids_structure {
public static void main(String[] args) {
// Create an empty core_profiles IDS
imas.core_profiles core_profiles = new imas.core_profiles();
// core_profiles/ids_properties is a structure, which has multiple child
// nodes. One of the child nodes is homogeneous time:
System.out.println(core_profiles.ids_properties.homogeneous_time);
// Since we created an empty IDS, this will output "-999999999": the
// default value for integers.
}
}
Array of structures¶
Array of structure nodes in an IDS are one-dimensional arrays, containing structure
nodes. In Java they are implemented as a an array of a class. The default value (for
example, when creating a new IDS) for these nodes is null.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class ids_aos {
public static void main(String[] args) {
// Create an empty core_profiles IDS
imas.core_profiles core_profiles = new imas.core_profiles();
// core_profiles/profiles_1d is an array of structures
System.out.println(core_profiles.profiles_1d); // -> null
// It currently has no elements, so let's create one
// 1. Create array:
core_profiles.profiles_1d = new imas.core_profiles.profiles_1dClass[1];
// 2. Initialize array element:
core_profiles.profiles_1d[0] = new imas.core_profiles.profiles_1dClass();
System.out.println(core_profiles.profiles_1d.length); // -> 1
// Use array indexation to get a specific structure from the array,
// then we can address child nodes as usual (for example, the average
// ion temperature, which is a null data array):
System.out.println(core_profiles.profiles_1d[0].t_i_average); // -> null
}
}
Resizing an array of structures¶
You can resize an array of structures with node = new nodeClass[n]. After calling
this, the array of structures will have n elements.
Caution
Resizing an array of structures with node = new nodeClass[n] will clear all data
inside the array of structure! Use a temporary variable (as in below example) to keep existing
data.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class ids_aos_resize {
public static void main(String[] args) {
// Create an empty interferometer IDS
imas.interferometer ids = new imas.interferometer();
// interferometer/channel is an array of structures, add some data
ids.channel = new imas.interferometer.channelClass[1];
System.out.println(ids.channel.length); // -> 1
ids.channel[0] = new imas.interferometer.channelClass();
ids.channel[0].name = "test";
// resize, this will destroy the data that we just stored
ids.channel = new imas.interferometer.channelClass[2];
ids.channel[0] = new imas.interferometer.channelClass();
ids.channel[1] = new imas.interferometer.channelClass();
System.out.println(ids.channel[0].name); // null
ids.channel[0].name = "test";
ids.channel[1].name = "test2";
// Resize, preserving existing data
imas.interferometer.channelClass[] tmp_channel =
new imas.interferometer.channelClass[3];
for (int i = 0; i < ids.channel.length; i++)
tmp_channel[i] = ids.channel[i];
ids.channel = tmp_channel;
System.out.println(ids.channel[0].name); // test
System.out.println(ids.channel[1].name); // test2
System.out.println(ids.channel[2]); // null
}
}
Data¶
Data nodes in an IDS contain numerical or textual data. The data type and dimensions of a node are defined in the Data Dictionary.
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class ids_data {
public static void main(String[] args) {
// Create an empty core_profiles IDS
imas.core_profiles core_profiles = new imas.core_profiles();
// core_profiles/time is a FLT_1D data node
// We can assign values to it:
core_profiles.time = new Vect1DDouble(new double[]{0.5, 1.0, 1.5});
System.out.println(core_profiles.time); // -> [0.5,1.0,1.5]
}
}
Data types¶
The following data types exist:
Textual data (
String)Whole numbers (
int)Floating point numbers (
double)Complex floating point numbers (
Complex)
Data nodes can be 0-dimensional, which means that the node accepts a single value of the specified type. Multi-dimensional data nodes also exist:
Textual data: at most 1 dimension (
Vect1DString)Whole numbers: 1-3 dimensions (
Vect1DInt,Vect2DInt,Vect3DInt)Floating point numbers: 1-6 dimensions (
Vect1DDouble,Vect2DDouble,Vect3DDouble,Vect4DDouble,Vect5DDouble,Vect6DDouble)Complex floating point numbers: 1-6 dimensions (
Vect1DComplex,Vect2DComplex,Vect3DComplex,Vect4DComplex,Vect5DComplex,Vect6DComplex)
Default values¶
The default values for data fields (for example when creating an empty IDS) are indicated in the following table. .. ids_is_valid is not implemented in java
0D |
1+ dimension |
|
|---|---|---|
Textual data |
|
|
Whole numbers |
|
|
Floating point numbers |
|
|
Complex numbers |
|
|
Time coordinates and time handling¶
Some quantities (and array of structures) are time dependent. In the Data Dictionary documentation this is indicated by a coordinate that refers to a time quantity.
This time-dependent coordinate is treated specially in the access layer, and it
depends on the value of ids_properties/homogeneous_time. There are three
valid values for this property:
IDS_TIME_MODE_HETEROGENEOUS(=0): time-dependent quantities in the IDS may have different time coordinates. The time coordinates are stored as indicated by the path in the documentation. This is known as heterogeneous time.IDS_TIME_MODE_HOMOGENEOUS(=1): All time-dependent quantities in this IDS use the same time coordinate. This is known as homogeneous time. This time coordinate is located in the root of the IDS, for examplecore_profiles/time. The paths time paths indicated in the documentation are unused in this case.IDS_TIME_MODE_INDEPENDENT(=2): The IDS stores no time-dependent data.
IDS validation¶
The IDSs you fill should be consistent. To help you in validating that, the
Access Layer provides a validation method (ids_validate) that executes the
following checks.
Validation checks
If you call this method and your IDS fails validation, the Access Layer throws an error explaining the problem. See the following example:
// Import the Access Layer
import imasjava.*;
import imasjava.wrapper.LowLevel;
public class validate_ids {
public static void main(String[] args) {
// Create an empty equilibrium IDS
imas.equilibrium equilibrium = new imas.equilibrium();
// |---> equilibrium is empty, -> raise of the ValidationException:
// "ids_properties.homogeneous_time wrong value (-999999999)"
try {
equilibrium.validate()
}
catch (ValidationException e) {
System.err.println("Validation exception:");
System.err.println(e.getMessage());
}
// Minimal equilibrium ids (homogeneous_time = 0 + time not empty)
equilibrium.ids_properties.homogeneous_time = 0; // (0 -> IDS_TIME_MODE_HOMOGENEOUS)
equilibrium.time= new Vect1DDouble(20);
try {
equilibrium.validate()
}
catch (ValidationException e) { // not occured here
System.err.println("Validation exception:");
System.err.println(e.getMessage());
}
}
}
The Access Layer automatically validates an IDS every time you do a
put or put_slice. To disable this feature, you must set the environment
variable IMAS_AL_DISABLE_VALIDATE to 1.
See also
API documentation: ids_validate
Validate the time mode¶
The time mode of an IDS is stored in ids_properties.homogeneous_time. This
property must be filled with a valid time mode (IDS_TIME_MODE_HOMOGENEOUS,
IDS_TIME_MODE_HETEROGENEOUS or IDS_TIME_MODE_INDEPENDENT). When the time
mode is IDS_TIME_MODE_INDEPENDENT, all time-dependent quantities must be empty.
Validate coordinates¶
If a quantity in your IDS has coordinates, then these coordinates must be filled. The size of your data must match the size of the coordinates:
Some dimensions must have a fixed size. This is indicated by the Data Dictionary as, for example,
1...3.For example, in the
magneticsIDS,b_field_pol_probe(i1)/bandwidth_3dbhas1...2as coordinate 1. This means that, if you fill this data field, the first (and only) dimension of this field must be of size 2.If the coordinate is another quantity in the IDS, then that coordinate must be filled and have the same size as your data.
For example, in the
pf_activeIDS,coil(i1)/current_limit_maxis a two-dimensional quantity with coordinatescoil(i1)/b_field_maxandcoil(i1)/temperature. This means that, if you fill this data field, their coordinate fields must be filled as well. The first dimension ofcurrent_limit_maxmust have the same size asb_field_maxand the second dimension the same size astemperature.Time coordinates are handled depending on the value of
ids_properties/homogeneous_time:When using
IDS_TIME_MODE_HOMOGENEOUS, all time coordinates look at the roottimenode of the IDS.When using
IDS_TIME_MODE_HETEROGENEOUS, all time coordinates look at the time path specified as coordinate by the Data Dictionary.For dynamic array of structures, the time coordinates is a
FLT_0Dinside the AoS (see, for example,profiles_1din thecore_profilesIDS). In such cases the time node must be set to something different thanEMPTY_FLOAT. This is the only case in which values of the coordinates are verified, in all other cases only the sizes of coordinates are validated.
Alternative coordinates
Version 4 of the Data Dictionary introduces alternative coordinates. An example of this can be found in the
core_profilesIDS inprofiles_1d(itime)/grid/rho_tor_norm. Alternatives for this coordinate are:profiles_1d(itime)/grid/rho_torprofiles_1d(itime)/grid/psiprofiles_1d(itime)/grid/volumeprofiles_1d(itime)/grid/areaprofiles_1d(itime)/grid/surfaceprofiles_1d(itime)/grid/rho_pol_norm
Multiple alternative coordinates may be filled (for example, an IDS might fill both the normalized and non-normalized toroidal flux coordinate). In that case, the size must be the same.
When a quantity refers to this set of alternatives (for example
profiles_1d(itime)/electrons/temperature), at least one of the alternative coordinates must be set and its size match the size of the quantity.The Data Dictionary can indicate exclusive alternative coordinates. See for example the
distribution(i1)/profiles_2d(itime)/density(:,:)quantity in thedistributionsIDS, which has as first coordinatedistribution(i1)/profiles_2d(itime)/grid/r OR distribution(i1)/profiles_2d(itime)/grid/rho_tor_norm. This means that eitherrorrho_tor_normcan be used as coordinate.Validation works the same as explained in the previous point, except that exactly one of the alternative coordinate must be filled. Its size must, of course, still match the size of the data in the specified dimension.
Some quantites indicate a coordinate must be the same size as another quantity through the property
coordinateX_same_as. In this case, the other quantity is not a coordinate, but their data is related and must be of the same size.An example can be found in the
edge_profilesIDS, quantityggd(itime)/neutral(i1)/velocity(i2)/diamagnetic. This is a two-dimensional field for which the first coordinate must be the same asggd(itime)/neutral(i1)/velocity(i2)/radial. When the diamagnetic velocity component is filled, the radial component must be filled as well, and have a matching size.