Loading and storing IMAS data

IMAS data is grouped together in Data Entries. A Data Entry is a collection of IDSs and their (potentially) multiple occurrences, which groups and stores data over multiple IDSs as a single dataset. The Data Entry concept is used whether the collection of IDSs is stored in a database or only exists temporarily (for example for communication in an integrated workflow).

Loading and storing IMAS data happens through an IMAS Database Entry. A Database Entry tracks the information required for locating where the Data Entry is (or will be) stored on disk. In Fortran this object is modeled as an integer index.

You may Open an existing IMAS Database Entry, which you can use for loading data that was stored previously. Alternatively you can Create a new IMAS Database Entry to store IDS data.

Open an existing IMAS Database Entry

To open an IMAS Database Entry, you need to know the URI indicating where the Access Layer can find the data. IMAS URIs start with imas: and indicate the format and the location of the stored data. You can find a detailed description of the IMAS URI syntax on the IMAS Data Entry URIs page.

Fortran example: open an existing IMAS Database Entry
program open_data_entry
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer :: data_entry, data_entry2, status

    ! Open the database entry by providing an IMAS URI
    call imas_open( &
        "imas:mdsplus?user=public;pulse=131024;run=41;database=ITER;version=3", &
        OPEN_PULSE, data_entry, status)

    ! Alternatively, we can open an IDS providing legacy arguments
    call imas_open_env("ids", 131024, 41, data_entry2, "public", "ITER", "3", status)

end program open_data_entry

See also

API documentation for imas_open(), imas_open_env() (legacy).

Loading IMAS data

After you open a database entry, you can request to load data from disk.

Load an entire IDS

With ids_get() you can load (“get”) an entire IDS from the database entry.

Multiple occurrences of an IDS may be stored in a data entry. By default, if you don’t specify an occurrence number, occurrence 0 is loaded. By providing an occurrence number you can load a specific occurrence. How different occurrences are used depends on the experiment. They could, for example, correspond to:

  • different methods for computing the physical quantities of the IDS, or

  • different functionalities in a workflow (e.g. initial values, prescribed values, values at next time step, …), or

  • multiple subsystems (e.g. diagnostics) of the same type in an experiment, etc.

Fortran example: get an IDS from an IMAS Database Entry
program load_ids
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer :: data_entry, status
    type(ids_core_profiles) :: cp, cp1

    ! Open the database entry by providing an IMAS URI
    call imas_open( &
        "imas:mdsplus?user=public;pulse=131024;run=41;database=ITER;version=3", &
        OPEN_PULSE, data_entry, status)

    ! Load the core_profiles IDS, occurrence 0
    call ids_get(data_entry, "core_profiles", cp)

    ! Load the core_profiles IDS, occurrence 1
    call ids_get(data_entry, "core_profiles/1", cp1)

end program load_ids

See also

Load a single time slice of an IDS

Instead of loading a full IDS from disk, the Access Layer allows you to load a specific time slice. This is often useful when you’re not interested in the full time evolution, but instead want data of a specific time. You can use ids_get_slice() for this.

Most of the time there are no entries at that specific time, so you also need to indicate an interpolation method. This determines what values the access layer returns when your requested time is in between available time points in the data. Three interpolation methods currently exist:

PREVIOUS_INTERP

Returns the previous time slice if the requested time does not exactly exist in the original IDS.

For example, when data exists at \(t=\{1, 3, 4\}\), requesting \(t_r=2.1\) will give you the data at \(t=1\).

Edge case behaviour. \(\{t_i\}, i=1..N\) represents the time series stored in the IDS.

Case

Behaviour

\(t_r \lt t_1\)

Return data at \(t_1\).

\(t_r = t_i\) [1]

Return data at \(t_i\).

CLOSEST_INTERP

Returns the closest time slice in the original IDS. This can also be after the requested time.

For example, when data exists at \(t=\{1, 3, 4\}\), requesting \(t=2.1\) will give you the data at \(t=3\).

Edge case behaviour. \(\{t_i\}, i=1..N\) represents the time series stored in the IDS.

Case

Behaviour

\(t_r \lt t_1\)

Return data at \(t_1\).

\(t_r = t_i\) [1]

Return data at \(t_i\).

\(t_r - t_i = t_{i+1} - t_r\) [1]

Return data at \(t_{i+1}\).

LINEAR_INTERP

Returns a linear interpolation between the existing slices before and after the requested time.

For example, when data exists at \(t=\{1, 3, 4\}\), requesting \(t=2.1\) will give you a linear interpolation of the data at \(t=1\) and the data at \(t=3\).

Note that the linear interpolation will be successful only if, between the two time slices of an interpolated dynamic array of structure, the same leaves are populated and they have the same size. Otherwise ids_get_slice() will interpolate all fields with a compatible size and leave others empty.

Edge case behaviour. \(\{t_i\}, i=1..N\) represents the time series stored in the IDS.

Case

Behaviour

\(t_r \lt t_1\)

Return data at \(t_1\).

\(t_r \gt t_N\)

Return data at \(t_N\).

Fortran example: get a time slice from an IMAS Database Entry
program load_ids_slice
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer(ids_int) :: data_entry, status
    real(ids_real) :: twant
    type(ids_core_profiles) :: cp

    ! Open the database entry by providing an IMAS URI
    call imas_open( &
        "imas:mdsplus?user=public;pulse=131024;run=41;database=ITER;version=3", &
        OPEN_PULSE, data_entry, status)

    ! Retrieve the time slice just before t=370 of the core_profiles IDS
    twant = 370
    call ids_get_slice(data_entry, "core_profiles", cp, twant, PREVIOUS_INTERP)

end program load_ids_slice

Note

The access layer assumes that all time arrays are stored in increasing order. ids_get_slice() may return unexpected results if your data does not adhere to this assumption.

See also

API documentation for ids_get_slice().

Create a new IMAS Database Entry

To create a new IMAS Database Entry, you need to provide the URI to indicate the format and the location where you want to store the data. You can find a detailed description of the IMAS URI syntax and the options available on the IMAS Data Entry URIs page.

Caution

This function erases any existing database entry on the specified URI!

Fortran example: create a new IMAS Database Entry
program create_data_entry
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer :: data_entry, status, pulse, run
    character(len=132) :: user

    ! Create the database entry by providing an IMAS URI
    call imas_open("imas:hdf5?path=my-data", FORCE_CREATE_PULSE, data_entry, status)
    
    ! Alternatively, we can open an IDS providing legacy arguments
    pulse = 1
    run = 1
    call get_environment_variable("USER", user)
    call imas_create_env("ids", pulse, run, 0, 0, data_entry, user, "TEST", "3", status)

end program create_data_entry

See also

API documentation for imas_open(), imas_create_env() (legacy).

Store IMAS data

After you have created an IMAS Database Entry, you can use it for storing IDS data. There are two ways to do this:

Store an entire IDS

With ids_put() you can store (“put”) an entire IDS in a database entry. First you need to have an IDS with data: you can create a new one or load an IDS which you modify. See Use Interface Data Structures for more information on using and manipulating IDSs.

Caution

This function erases the existing IDS in the data entry if any was already stored previously.

Multiple occurrences of an IDS may be stored in a data entry. By default, if you don’t specify an occurrence number, the IDS is stored as occurrence 0. By providing an occurrence number you can store the IDS as a specific occurrence.

Note

The MDS+ backend has a limitation on the number of occurrences of a given IDS. This number is indicated in the Data Dictionary documentation in the “Max. occurrence number” column of the list of IDSs. This limitation doesn’t apply to other backends.

Fortran example: put an IDS to an IMAS Database Entry
program store_ids
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer :: data_entry, status
    type(ids_pf_active) :: pf_active

    ! Create the database entry by providing an IMAS URI
    call imas_open("imas:hdf5?path=my-data", FORCE_CREATE_PULSE, data_entry, status)

    ! Set the mandatory ids_properties.homogeneous_time field
    pf_active%ids_properties%homogeneous_time = IDS_TIME_MODE_HOMOGENEOUS
    ! Continue filling the pf_active IDS here
    ! ...

    ! Store the pf_active IDS
    call ids_put(data_entry, "pf_active", pf_active)

    ! Alternatively, store the pf_active IDS as occurrence 1
    call ids_put(data_entry, "pf_active/1", pf_active)

end program store_ids

See also

API documentation for ids_put().

Append a time slice to an already-stored IDS

With ids_put_slice() you can append a time slice to an existing database entry. This is useful when you generate data inside a time loop (for example in simulations, or when taking measurements of an experiment).

It means you can put a time slice with every iteration of your loop such that you don’t have to keep track of the complete time evolution in memory. Instead, the Access Layer will keep appending the data to the Database Entry in the storage backend.

Note

Although being put progressively time slice by time slice, the final IDS must be compliant with the data dictionary. A typical error when constructing IDS variables time slice by time slice is to change the size of the IDS fields during the time loop, which is not allowed but for the children of an array of structure which has time as its coordinate.

Fortran example: iteratively put time slices to an IMAS Database Entry
program store_ids_slice
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer :: data_entry, status
    real(ids_real) :: t, t_stop, dt
    type(ids_core_profiles) :: core_profiles

    ! Create the database entry by providing an IMAS URI
    call imas_open("imas:hdf5?path=my-data", FORCE_CREATE_PULSE, data_entry, status)

    ! Set the mandatory ids_properties.homogeneous_time field
    core_profiles%ids_properties%homogeneous_time = IDS_TIME_MODE_HOMOGENEOUS
    ! Continue filling the core_profiles IDS with static values here
    ! ...

    ! Allocate time array
    allocate(core_profiles%time(1))

    ! Example time integration loop:
    t = 0.0
    t_stop = 60.0
    dt = 0.1

    do while (t .lt. t_stop)
        core_profiles%time = (/ t /)
        ! Fill your time-dependent data in the core_profiles IDS here
        ! ...

        ! Append the current time slice to the data stored on disk
        call ids_put_slice(data_entry, "core_profiles", core_profiles)

        t = t + dt
    end do

end program store_ids_slice

See also

API documentation for ids_put_slice().

Listing all occurrences of an IDS from a backend

With list_all_occurrences() you can List all non-empty occurrences of an IDS using its name in the dataset, and optionnally return the content of a descriptive node path.

Note

The MDS+ backend is storing IDS occurrences infos (pulse file metadata) for AL version > 5.0.0. Pulse files created with AL version <= 5.0.0. do not provide these informations (an exception will occur for such pulse files when calling list_all_occurrences()).

Fortran example: listing all occurrences of a magnetics IDS from an IMAS Database Entry
program prog_list_all_occurrences
    ! Make the Access Layer available
    use ids_routines
    implicit none

    integer :: data_entry, status
    character(len=:), allocatable :: node_content_list(:)
    integer, allocatable          :: occurrence_list(:)
    ! Open the database entry by providing an IMAS URI
    call imas_open( &
        "imas:mdsplus?user=public;pulse=131024;run=41;database=ITER;version=3", &
        OPEN_PULSE, data_entry, status)

    call list_all_occurrences(data_entry, "core_profiles", "ids_properties/comment", node_content_list, occurrence_list)

    if(allocated(occurrence_list)) then 
        write(*,*) "i     occurrence_list(i)      node_content_list(i)"
            do i = 1, size(occurrence_list)
                write(*,*) i,occurrence_list(i),"'"//node_content_list(i)//"'"
            end do
    else 
        write(*,*) "node_content_list, occurrence_list are empty"
    end if 

end program prog_list_all_occurrences