.. SPDX-FileCopyrightText: 1992-2026 NWO-I/SRON Space Research Organisation Netherlands .. .. SPDX-License-Identifier: CC-BY-4.0 QDP File Operations (qdp) ========================== **Module**: ``utils_qdp`` Overview -------- Provides utilities for working with QDP (Quick Data Plot) files, a simple ASCII format used for plotting data in SPEX and other astronomical software. Key Features ------------ - Simple ASCII-based data format - Easy to read and write - Human-readable format - Suitable for quick data exchange - Integration with SPEX plotting system Types ----- **qdp_set** Individual data set structure: .. list-table:: :widths: 20 30 50 :header-rows: 1 * - Component - Type - Description * - ``n`` - integer - Number of rows in set * - ``x, x1, x2`` - real(dp), allocatable - X-axis data arrays * - ``y, y1, y2`` - real(dp), allocatable - Y-axis data arrays * - ``z`` - real(dp), allocatable - Z-axis data array * - ``b`` - real(dp), allocatable - Additional data array **qdp** Main QDP container: .. list-table:: :widths: 20 30 50 :header-rows: 1 * - Component - Type - Description * - ``nset`` - integer - Number of data sets * - ``set`` - type(qdp_set), allocatable - Array of data sets * - ``type`` - character(5) - QDP type identifier API Reference ------------- Methods ~~~~~~~ **init** .. function:: qdp_handler%init(nset, type) Initialize QDP object :param nset: Number of data sets (integer, in) :param type: QDP type (character(5), in) :return: None Allocates memory for specified number of data sets. **init_col** .. function:: qdp_handler%init_col(iset, n, col) Initialize data set column :param iset: Data set index (integer, in) :param n: Number of data points (integer, in) :param col: Column identifier (character(1), in) :return: None Allocates memory for specified column in data set. **setcol** .. function:: qdp_handler%setcol(iset, n, col, a, a1, a2) Set data set column values :param iset: Data set index (integer, in) :param n: Number of data points (integer, in) :param col: Column identifier (character(1), in) :param a: Main data array (real(dp)(n), in) :param a1: Optional error array (real(dp)(n), in, optional) :param a2: Optional second error array (real(dp)(n), in, optional) :return: None Sets values for specified column in data set. **read** .. function:: qdp_handler%read(filename, type, status) Read QDP data from file :param filename: QDP filename (character(*), in) :param type: QDP type (character(5), in) :param status: Error status (integer, inout) :return: None Loads QDP data from file into object. **write** .. function:: qdp_handler%write(filename, type, status) Write QDP data to file :param filename: QDP filename (character(*), in) :param type: QDP type (character(5), in) :param status: Error status (integer, inout) :return: None Saves QDP data from object to file. **final** .. function:: qdp_handler%final() Clean up QDP object :return: None Deallocates all memory used by QDP object. Usage Examples -------------- **Basic QDP Initialization** .. code-block:: fortran use utils_qdp type(qdp) :: qdp_handler integer :: status ! Initialize QDP object for 2 data sets call qdp_handler%init(2, 'SPEX', status) if (status == 0) then write(*,*) 'QDP object initialized for', qdp_handler%nset, 'data sets' endif **Setting Up Data Columns** .. code-block:: fortran use utils_qdp use utils_cspex, only: dp type(qdp) :: qdp_handler integer :: status, n_points real(dp), dimension(100) :: energy, flux, error ! Initialize QDP object call qdp_handler%init(1, 'SPEX', status) if (status == 0) then ! Set up data set 1 with 100 points n_points = 100 call qdp_handler%init_col(1, n_points, 'x', status) call qdp_handler%init_col(1, n_points, 'y', status) ! Fill with sample data energy = [(0.1_dp + (i-1)*0.01_dp, i=1,100)] flux = exp(-energy) * sin(energy*10.0_dp) error = flux * 0.1_dp ! Set X and Y columns call qdp_handler%setcol(1, n_points, 'x', energy, status) call qdp_handler%setcol(1, n_points, 'y', flux, error, status) write(*,*) 'Data columns set successfully' endif **Reading QDP File** .. code-block:: fortran use utils_qdp type(qdp) :: qdp_handler integer :: status ! Read QDP data from file call qdp_handler%read('spectrum.qdp', 'SPEX', status) if (status == 0) then write(*,*) 'QDP file loaded successfully' write(*,*) 'Number of data sets:', qdp_handler%nset write(*,*) 'Data set 1 points:', qdp_handler%set(1)%n endif **Writing QDP File** .. code-block:: fortran use utils_qdp use utils_cspex, only: dp type(qdp) :: qdp_handler integer :: status, i, n_points real(dp), dimension(50) :: x, y ! Initialize and set up data call qdp_handler%init(1, 'SPEX', status) n_points = 50 ! Create sample data do i = 1, n_points x(i) = 0.1_dp + (i-1)*0.02_dp y(i) = exp(-x(i)) * cos(x(i)*5.0_dp) enddo ! Set up columns and data call qdp_handler%init_col(1, n_points, 'x', status) call qdp_handler%init_col(1, n_points, 'y', status) call qdp_handler%setcol(1, n_points, 'x', x, status) call qdp_handler%setcol(1, n_points, 'y', y, status) ! Write to file call qdp_handler%write('output.qdp', 'SPEX', status) if (status == 0) then write(*,*) 'QDP file written successfully' endif **Multiple Data Sets** .. code-block:: fortran use utils_qdp use utils_cspex, only: dp type(qdp) :: qdp_handler integer :: status, i, n_points real(dp), dimension(50) :: x, y1, y2 ! Initialize for 2 data sets call qdp_handler%init(2, 'SPEX', status) n_points = 50 ! Create sample data do i = 1, n_points x(i) = 0.1_dp + (i-1)*0.02_dp y1(i) = exp(-x(i)) y2(i) = x(i)**2 enddo ! Set up data set 1 call qdp_handler%init_col(1, n_points, 'x', status) call qdp_handler%init_col(1, n_points, 'y', status) call qdp_handler%setcol(1, n_points, 'x', x, status) call qdp_handler%setcol(1, n_points, 'y', y1, status) ! Set up data set 2 call qdp_handler%init_col(2, n_points, 'x', status) call qdp_handler%init_col(2, n_points, 'y', status) call qdp_handler%setcol(2, n_points, 'x', x, status) call qdp_handler%setcol(2, n_points, 'y', y2, status) ! Write to file call qdp_handler%write('multi_dataset.qdp', 'SPEX', status) if (status == 0) then write(*,*) 'Multi-dataset QDP file created' endif **Cleanup Example** .. code-block:: fortran use utils_qdp type(qdp) :: qdp_handler integer :: status ! Initialize and use QDP object call qdp_handler%init(1, 'SPEX', status) ! ... work with QDP data ... ! Clean up when done call qdp_handler%final() write(*,*) 'QDP object cleaned up' Notes ----- **QDP Data Structure** - ``qdp`` object contains multiple ``qdp_set`` objects - Each ``qdp_set`` represents one data series - Columns: x, y, z, b with optional error arrays - Type identifier specifies QDP format version **Column Identifiers** .. list-table:: :widths: 20 30 50 :header-rows: 1 * - Identifier - Description - Typical Usage * - ``'x'`` - X-axis data - Energy, wavelength, time * - ``'y'`` - Y-axis data - Flux, counts, intensity * - ``'z'`` - Z-axis data - Secondary values * - ``'b'`` - Background - Background data **Error Handling** - ``status`` parameter for error reporting - ``status = 0`` indicates success - Non-zero values indicate specific errors - Check status after each operation **Memory Management** - ``init()`` allocates memory - ``final()`` deallocates memory - Always call ``final()`` when done - Prevents memory leaks **Performance** - Fast data access - Efficient memory usage - Suitable for medium-sized datasets - ASCII format limits performance **Best Practices** 1. **Initialize before use** with ``init()`` 2. **Set up columns** before adding data 3. **Use appropriate column types** for data 4. **Clean up** with ``final()`` when done 5. **Check status** after each operation **Common QDP Types** .. list-table:: :widths: 20 30 50 :header-rows: 1 * - Type - Description - Usage * - ``'SPEX'`` - SPEX QDP format - Standard SPEX data * - ``'QDP'`` - Generic QDP format - Compatible with other tools * - ``'PLOT'`` - Plotting format - Simple XY plots See Also -------- - :doc:`files` - General file operations - :doc:`fits` - FITS format for complex data - :doc:`../core/messages` - Error handling - :doc:`../data/energy` - Energy unit conversions