.. SPDX-FileCopyrightText: 1992-2026 NWO-I/SRON Space Research Organisation Netherlands
..
.. SPDX-License-Identifier: CC-BY-4.0

Data related procedures
=======================

The commands in this section are all related to the data structures within SPEX. They deal with
spectral data and responses, simulation of data, etc.

.. _spexapi_data:

Data
----

Loading spectral data and responses in SPEX is done with the ``data`` command. Data can be added
by loading a .res and .spo file that contain the response and spectral information for an
instrument. The Fortran API procedure is:

.. code-block:: fortran

    call spex%data(resfile,spofile,ier)

where ``resfile`` is the response file, ``spofile`` the spectrum file, and ``ier`` the output
error condition.

.. code-block:: fortran

     module subroutine spex_io_data(this,resname,sponame,ier)
        class(sapi),  intent(inout)   :: this
        character*(*), intent(in)     :: resname   !! Input .res file name
        character*(*), intent(in)     :: sponame   !! Input .spo file name
        integer, intent(out)          :: ier       !! Error status
     end subroutine

Note that this command needs the full filename (and if necessary the path to the file) including
the extension. This is the only difference with the SPEX syntax. Example:

.. code-block:: fortran

    call spex%data('xifu.res','xifu.spo',ier)

.. _spexapi_data_del:

Data delete
"""""""""""

Deleting a loaded spectrum and response is done in SPEX with the ``data delete instrument #i``
command. In the Fortran API, the procedure is:

.. code-block:: fortran

    call spex%data_del(ins,ier)

where ``ins`` is the instrument number to be deleted and ``ier`` the output error condition.

.. code-block:: fortran

    module subroutine spex_io_data_del(this, ins, status)
        class(sapi),  intent(inout) :: this
        integer, intent(in)        :: ins         !! Instrument number
        integer, intent(inout)     :: status      !! Error status
    end subroutine

Example:

.. code-block:: fortran

    call spex%data_del(1,ier)

.. _spexapi_data_save:

Data save
"""""""""

Simulated spectra can be saved as a .spo file. In SPEX, this is done using the
``data save #i <spofile>`` command. In the Fortran API, the command is:

.. code-block:: fortran

    call spex%data_save(ins,sponame,overwrite,ier)

where ``ins`` is the instrument number of the data to be saved, ``spofile`` is the name of
the output .spo file. Again note that you should give the full file name, including the .spo
extension. The ``overwrite`` logical overwrites an existing file if true.

.. code-block:: fortran

    module subroutine spex_io_data_save_spo(this,ins,sponame,overwrite,status)
        class(sapi),  intent(inout)      :: this
        integer, intent(in)              :: ins        !! Instrument number
        character*(*), intent(in)        :: sponame    !! Output spectrum file name (.spo)
        logical, intent(in)              :: overwrite  !! Overwrite existing file (true/false)
        integer, intent(inout)           :: status     !! Error status
    end subroutine

For example:

.. code-block:: fortran

    call spex%data_save(1,'sim.spo',.true.,ier)


Data statistics
---------------

.. _spexapi_cnts:

Counts
""""""

This command shows counts statistics for a given energy, wavelength
or channel interval. The output shows the net number of source counts
in the range, the net source count rate, the background counts and
the background count rate. All numbers include the errors.  

The range to calculate the statistics for can be specified either as a channel range (no
units required) or in eiter any of the following units: keV, eV,
Rydberg, Joule, Hertz, :math:`$\AA$`, nanometer, with the following abbrevations:
kev, ev, ryd, j, hz, ang, nm. 

.. code-block:: fortran

   call spex%cnts(in,ir,r1,r2,unit,cnts,ier)

.. code-block:: fortran

      module subroutine spex_io_counts(this,in,ir,r1,r2,unit,cnts,ier)
        class (sapi), intent(inout)      :: this
        integer, intent(in)              :: in      !!Instrument number 
        integer, intent(in)              :: ir      !!Region number 
        real(dp), intent(in)             :: r1      !!Lower range boundary
        real(dp), intent(in)             :: r2      !!Upper range boundary
        character*(*), intent(in)        :: unit    !!unit of provided boundaries
        type(counts), intent(out)        :: cnts    !!Output counts structure       
        integer, intent(out)             :: ier     ! status error
      end subroutine
      
The returned counts type is defined in dataset:

.. code-block:: fortran    

      type counts
        integer      :: nchan           !! Number of used channels
        real(dp)     :: eband           !! Energy band (keV)
        real(dp)     :: elow            !! Lower limit of energy range (keV)
        real(dp)     :: ehigh           !! Upper limit of energy range (keV)
        real(dp)     :: src_count       !! Number of source counts in energy band (background subtracted)
        real(dp)     :: src_count_err   !! Error on the number of source counts (background subtracted)
        real(dp)     :: src_count_rate  !! Count rate in energy band (counts/s)
        real(dp)     :: src_crate_err   !! Count rate error
        real(dp)     :: bkg_count       !! Number of background counts in energy band
        real(dp)     :: bkg_count_err   !! Error on the number of background counts
        real(dp)     :: bkg_count_rate  !! Background count rate in energy band
        real(dp)     :: bkg_crate_err   !! Background count rate error
      end type counts

The values from the counts type can be accessed like this:

.. code-block:: fortran 
 
   cnts%src_count

.. _spexapi_bin:
      
Binning and data selection
--------------------------

Unlike some other fitting packages, SPEX rebins the spectra within the program. The easiest binning
method is to just rebin with a certain integer factor. This is the ``bin`` command in SPEX. In
addition, SPEX contains a binning method based on the bin statistics (``vbin``) and an optimal
binning algorithm (``obin``) which takes the instrument resolution into account.

**Binning with factor**

Binning with an integer factor is done with the bin command:

.. code-block:: fortran

    call spex%bin(in,ir,r1reb,r2reb,freb,unit,status)

The ``in`` parameter is the instrument number, the ``ir`` parameter the
region number, ``r1reb`` and ``r2reb`` are the lower and upper boundary of the
energy interval to be rebinned, ``freb`` is the rebinning factor, ``unit``
the physical unit of the energy range, and ``status`` is the output error condition.

.. code-block:: fortran

    module subroutine spex_io_bin(this,in,ir,r1reb,r2reb,freb,unit,status)
        class(sapi),  intent(inout)      :: this
        integer, intent(in)              :: in     !! Instrument number
        integer, intent(in)              :: ir     !! Region number
        real(dp), intent(in)             :: r1reb  !! Lower rebinning boundary
        real(dp), intent(in)             :: r2reb  !! Upper rebinning boundary
        integer, intent(in)              :: freb   !! Factor binning
        character*(*), intent(in)        :: unit   !! Unit of provided boundaries
        integer, intent(out)             :: status !! Error status
    end subroutine

In this example, we bin the spectrum with a factor of 5 between 0.3 and 10 keV:

.. code-block:: fortran

    call spex%bin(1,1,0.3,10.,5,'kev',status)

**Variable binning**

Variable binning is done with the vbin command:

.. code-block:: fortran

    call spex%vbin(in,ir,r1,r2,unit,ivarbin,snlim,status)

The ``in`` parameter is the instrument number, the ``ir`` parameter the
region number, ``r1`` and ``r2`` are the lower and upper boundary of the
energy interval to be rebinned, ``unit`` the physical unit of the energy range,
``ivarbin`` is the minimum number of bins to be added, ``snlim`` is the
minimum signal to noise and ``status`` is the output error condition.

.. code-block:: fortran

    module subroutine spex_io_vbin(this,in,ir,r1,r2,unit,ivarbin,snlim,status)
        class(sapi),  intent(inout)      :: this
        integer, intent(in)              :: in      !! Instrument number
        integer, intent(in)              :: ir      !! Region number
        real(dp), intent(in)             :: r1      !! Lower rebinning boundary
        real(dp), intent(in)             :: r2      !! Upper rebinning boundary
        character*(*), intent(in)        :: unit    !! Unit of provided boundaries
        integer, intent(in)              :: ivarbin !! Minimum number of bins to be added
        real(dp), intent(in)             :: snlim   !! Minimum S/N ratio to be achieved
        integer, intent(out)             :: status  !! Error status
    end subroutine

In this example, we bin the spectrum with a minimum factor of 3 and a minimum signal-to-noise
ratio of 25 between 0.3 and 10 keV:

.. code-block:: fortran

    call spex%vbin(1,1,0.3,10.,3,25.,'kev',status)

**Optimal binning**

The optimal binning algorithm bins based on the instrument resolution and statistics:

.. code-block:: fortran

    call spex%obin(in,ir,r1reb,r2reb,unit,status)

The ``in`` parameter is the instrument number, the ``ir`` parameter the
region number, ``r1reb`` and ``r2reb`` are the lower and upper boundary of the
energy interval to be rebinned, ``unit`` the physical unit of the energy range,
and ``status`` is the output error condition.

.. code-block:: fortran

    module subroutine spex_io_obin(this,in,ir,r1reb,r2reb,unit,status)
        class(sapi),  intent(inout)      :: this
        integer, intent(in)              :: in      !! Instrument number
        integer, intent(in)              :: ir      !! Region number
        real(dp), intent(in)             :: r1reb   !! Lower rebinning boundary
        real(dp), intent(in)             :: r2reb   !! Upper rebinning boundary
        character*(*), intent(in)        :: unit    !! Unit of provided boundaries
        integer, intent(out)             :: status  !! Error status
    end subroutine

In this example, we optimally bin the spectrum between 0.3 and 10 keV:

.. code-block:: fortran

    call spex%obin(1,1,0.3,10.,'kev',status)

.. _spexapi_use:

Data selection
""""""""""""""

Selecting data is done using the ``use`` and ``ignore`` commands. By default, the bin selection
in the ``.spo`` file is loaded, which should use all the good bins.

**Use function**

.. code-block:: fortran

    call spex%use(in,ir,r1,r2,unit,status)

The ``in`` parameter is the instrument number, the ``ir`` parameter the
region number, ``r1`` and ``r2`` are the lower and upper boundary of the
energy interval to be used, ``unit`` the physical unit of the energy range,
and ``status`` is the output error condition.

.. code-block:: fortran

    module subroutine spex_io_use(this,in,ir,r1,r2,unit,status)
        class(sapi),  intent(inout)       :: this
        integer, intent(in)              :: in     !! Instrument number
        integer, intent(in)              :: ir     !! Region number
        real(dp), intent(in)             :: r1     !! Lower range boundary
        real(dp), intent(in)             :: r2     !! Upper range boundary
        character*(*), intent(in)        :: unit   !! Unit of provided boundaries
        integer, intent(out)             :: status !! Status error
    end subroutine

Example:

.. code-block:: fortran

    call spex%use(1,1,0.3,10.,'kev')

**Ignore function**

.. code-block:: fortran

    call spex%ignore(in,ir,r1,r2,unit,status)

The ``in`` parameter is the instrument number, the ``ir`` parameter the
region number, ``r1`` and ``r2`` are the lower and upper boundary of the
energy interval to be ignored, ``unit`` the physical unit of the energy range,
and ``status`` is the output error condition.

Example:

.. code-block:: fortran

    call spex%ignore(1,1,0.0,0.3,'kev')
    call spex%ignore(1,1,10.,100.,'kev')

.. _spexapi_syserr:

Systematic errors
-----------------

If needed, the error bars on the source and background spectrum can be enlarged by the ``syserr``
command:

.. code-block:: fortran

    call spex%syserr(in,ir,r1,r2,unit,serr,berr,status)

The ``in`` parameter is the instrument number, the ``ir`` parameter the
region number, ``r1`` and ``r2`` are the lower and upper boundary of the
energy interval to be changed, ``unit`` the physical unit of the energy range,
``serr`` is the systematic error on the source to be added, ``berr`` is the
systematic error on the background, and ``status`` is the output error condition.

.. code-block:: fortran

    module subroutine spex_io_syserr(this,in,ir,r1,r2,unit,serr,berr,status)
        class(sapi),  intent(inout)       :: this
        integer, intent(in)              :: in     !! Instrument number
        integer, intent(in)              :: ir     !! Region number
        real(dp), intent(in)             :: r1     !! Lower range boundary
        real(dp), intent(in)             :: r2     !! Upper range boundary
        character*(*), intent(in)        :: unit   !! Unit of provided boundaries
        real(dp), intent(in)             :: serr   !! Systematic error source spectrum
        real(dp), intent(in)             :: berr   !! Systematic error background spectrum
        integer, intent(out)             :: status !! Error status
    end subroutine

Example:

.. code-block:: fortran

    call spex%syserr(1,1,0.1,10.,'kev',0.1,0.1,status)

.. warning::

    To use this function, you need to know what you are doing statistically. In many cases, this
    function would produce wrong results.

Other commands
--------------

Setting instrument normalisations
"""""""""""""""""""""""""""""""""

During spectral fitting, instrument normalisations can be set
and freed to account for cross-calibration issues between instruments:

.. code-block:: fortran

    call spex%set_inst_norm(is,ir,norm,ier)

Where ``is`` is the instrument number, ``ir`` the region number,
``norm`` the instrument normalisation, and ``ier`` the error code.

The Fortran interface:

.. code-block:: fortran

      module subroutine spex_io_set_inst_norm(this, is, ir, norm, ier)
        class(sapi), intent(inout) :: this
        integer, intent(in)       :: is     !instrument number
        integer, intent(in)       :: ir     !region number
        real, intent(in)          :: norm   !instrument normalisation
        integer, intent(out)      :: ier    !error status
      end subroutine

The normalisation can also be fixed or freed:

.. code-block:: fortran

    call spex%set_inst_free(is,ir,free,ier)

Where ``is`` is the instrument number, ``ir`` the region number,
``free`` is a logical (.true. is free), and ``ier`` is the error code.

The Fortran interface:

.. code-block:: fortran

    module subroutine spex_io_set_inst_norm_free(this, is, ir, free, ier)
      class(sapi), intent(inout) :: this
      integer, intent(in)       :: is     !instrument number
      integer, intent(in)       :: ir     !region number
      logical, intent(in)       :: free   !instrument normalisation free=.true./frozen=.false.
      integer, intent(out)      :: ier    !error status
    end subroutine

Delete all instruments (reset)
""""""""""""""""""""""""""""""

Example:

.. code-block:: fortran

    call spex%reset_data()

The Fortran interface:

.. code-block:: fortran

    module subroutine spex_io_reset(this)
      class(sapi), intent(inout) :: this
    end subroutine
