Besides the data structure, the IMAS Data Dictionary also defines metadata associated with elements in the IDS, such as coordinate information, units, etc. IMAS-Python provides the :py:class:`~imas.ids_metadata.IDSMetadata` API for interacting with this metadata.
On this page you find several examples for querying and using the metadata of IDS elements.
.. seealso:: IMAS-Python advanced training: :ref:`Using metadata`
An overview of available metadata is given in the API documentation for
:py:class:`~imas.ids_metadata.IDSMetadata`.
The documented attributes are always available, but additional metadata from the data
dictionary may be available as well.
For example, the data dictionary indicates a lifecycle_last_change
on all IDS
toplevels (in which DD version was that IDS last updated). This is not listed in the
metadata documentation, but you can still access it. See the following code sample:
>>> import imas
>>> core_profiles = imas.IDSFactory().core_profiles()
>>> core_profiles.metadata.lifecycle_last_change
'3.39.0'
All multi-dimensional quantities in an IDS have coordinate information. These can be data nodes (for example 2D floating point data) or array of structure nodes.
Each data node and array of structures has a coordinates
attribute. By
indexing this attribute, you can retrieve the coordinate values for that
dimension. For example, coordinates[2]
attempts to retrieve the coordinate
values for the third dimension of the data.
When another quantity in the IDS is used as a coordinate, that quantity is looked up. See below example.
>>> core_profiles = imas.IDSFactory().core_profiles()
>>> core_profiles.profiles_1d.resize(1)
>>> profile = core_profiles.profiles_1d[0]
>>> profile.grid.rho_tor_norm = [0, 0.15, 0.3, 0.45, 0.6]
>>> # Electron temperature has rho_tor_norm as coordinate:
>>> profile.electrons.temperature.coordinates[0]
IDSNumericArray("/core_profiles/profiles_1d/1/grid/rho_tor_norm", array([0. , 0.15, 0.3 , 0.45, 0.6 ]))
When a coordinate is just an index, IMAS-Python generates a :external:py:func:`numpy.arange` with the same length as the data. See below example.
>>> pf_active = imas.IDSFactory().pf_active()
>>> pf_active.coil.resize(10)
>>> # Coordinate1 of coil is an index 1...N
>>> pf_active.coil.coordinates[0]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Time coordinates
Time coordinates are a special case: the coordinates depend on whether the IDS is in homogeneous time mode or not. IMAS-Python handles this transparently.
>>> core_profiles = imas.IDSFactory().core_profiles()
>>> # profiles_1d is a time-dependent array of structures:
>>> core_profiles.profiles_1d.coordinates[0]
[...]
ValueError: Invalid IDS time mode: ids_properties/homogeneous_time is <IDSInt0D (IDS:core_profiles, ids_properties/homogeneous_time, empty INT_0D)>, was expecting 0 or 1.
>>> core_profiles.ids_properties.homogeneous_time = \\
... imas.ids_defs.IDS_TIME_MODE_HOMOGENEOUS
>>> # In homogeneous time mode, the root /time array is used
>>> core_profiles.time = [0, 1]
>>> core_profiles.profiles_1d.resize(2)
>>> core_profiles.profiles_1d.coordinates[0]
IDSNumericArray("/core_profiles/time", array([0., 1.]))
>>> # But in heterogeneous time mode, profiles_1d/time is used instead
>>> core_profiles.ids_properties.homogeneous_time = \\
... imas.ids_defs.IDS_TIME_MODE_HETEROGENEOUS
>>> core_profiles.profiles_1d.coordinates[0]
array([-9.e+40, -9.e+40])
Alternative coordinates
Sometimes the Data Dictionary indicates that multiple other quantities could be
used as a coordinate. For example, the
distribution(i1)/profiles_2d(itime)/density(:,:)
quantity in the
distributions
IDS has as first coordinate
distribution(i1)/profiles_2d(itime)/grid/r OR
distribution(i1)/profiles_2d(itime)/grid/rho_tor_norm
. This means that either
r
or rho_tor_norm
can be used as coordinate. When requesting such a
coordinate from IMAS-Python, four things may happen:
- When
r
is empty andrho_tor_norm
not,coordinates[0]
will returnrho_tor_norm
. - When
rho_tor_norm
is empty andr
not,coordinates[0]
will returnr
. - When both
r
andrho_tor_norm
are not empty, IMAS-Python raises an error because it cannot determine which of the two coordinates should be used. - Similarly, an error is raised by IMAS-Python when neither
r
norrho_tor_norm
are set.
.. seealso:: API documentation for :py:class:`~imas.ids_coordinates.IDSCoordinates`
In IMAS-Python you can query coordinate information in two ways:
- Directly query the coordinate attribute on the metadata:
<quantity>.metadata.coordinate2
gives you the coordinate information for the second dimension of the quantity. - Use the :py:attr:`~imas.ids_metadata.IDSMetadata.coordinates` attribute:
<quantity>.metadata.coordinates
is a tuple containing all coordinate information for the quantity.
The coordinate information from the Data Dictionary is parsed and stored in an :py:class:`~imas.ids_coordinates.IDSCoordinate`. The Data Dictionary has several types of coordinate information:
When the coordinate is an index, the Data Dictionary indicates this via
1...N
. When a literalN
is given, no restrictions apply.It is also possible to have a specific value for
N
, for example1...3
. Then, this dimension can contain at most 3 items.When another quantity in the IDS is used as a coordinate, the coordinate indicates the path to that other quantity.
.. TODO:: Detailed coordinate descriptions should happen in the DD docs. Link to that when available.
>>> pf_active = imas.IDSFactory().pf_active()
>>> # coordinate1 of pf_active/coil is an index (the number of the coil)
>>> pf_active.coil.metadata.coordinate1
IDSCoordinate('1...N')
>>> pf_active.coil.resize(1)
>>> # pf_active/coil/current_limit_max is 2D, so has two coordinates
>>> # Both refer to another quantity in the IDS
>>> pf_active.coil[0].current_limit_max.metadata.coordinates
(IDSCoordinate('coil(i1)/b_field_max'), IDSCoordinate('coil(i1)/temperature'))
.. seealso:: API documentation for :py:class:`~imas.ids_coordinates.IDSCoordinate`.
Starting in Data Dictionary 4.0, a coordinate quantity may indicate alternatives for itself. These alternatives are stored in the metadata attribute :py:attr:`~imas.ids_metadata.IDSMetadata.alternative_coordinates`.
For example, most quantities in profiles_1d
of the core_profiles
IDS have
profiles_1d/grid/rho_tor_norm
as coordinate. However, there are alternatives
that may be used instead (e.g. rho_tor
, psi
, ...). This is then indicated in
the metadata of rho_tor_norm
:
>>> import imas
>>> import rich
>>> dd4 = imas.IDSFactory("4.0.0")
>>> core_profiles = dd4.core_profiles()
>>> rich.print(cp.profiles_1d[0].grid.rho_tor_norm.metadata.alternative_coordinates)
(
IDSPath('profiles_1d(itime)/grid/rho_tor'),
IDSPath('profiles_1d(itime)/grid/psi'),
IDSPath('profiles_1d(itime)/grid/volume'),
IDSPath('profiles_1d(itime)/grid/area'),
IDSPath('profiles_1d(itime)/grid/surface'),
IDSPath('profiles_1d(itime)/grid/rho_pol_norm')
)