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

.. _spexapi_model:

Model related procedures
========================

A SPEX model is organised in sectors and components. Sectors are groups of components that model
a particular source of radiation. Below, the basic commands for building the model and change its
parameters are listed.

.. _spexapi_abun:

Abundance
---------

The solar abundance table used is set in SPEX using the ``abundance`` command. The desired
abundance table should be loaded by providing the
abbreviation for the table (``abunset``):

.. code-block:: fortran

    call spex%abundance(abun,ier)

The possible abundance tables are:

* ``reset``: Lodders et al. (2009)
* ``allen``: Allen (1973)
* ``ra``: Ross & Aller (1976)
* ``grevesse``: Grevesse et al. (1992)
* ``gs``: Grevesse & Sauval (1998)
* ``lodders``: Lodders proto-Solar (2003)
* ``solar``: Lodders Solar Photospheric (2003)
* ``ag``: Anders & Grevesse (1989)
* ``asplund``: Asplund et al. (2009)

.. code-block:: fortran

    module subroutine spex_model_abundance(this,abun,ier)
      class(sapi), intent(inout)      :: this
      character*(*), intent(in)       :: abun
      integer, intent(out)            :: ier
    end subroutine

For example:

.. code-block:: fortran

    call spex%abundance('asplund',ier)

Abundance show
""""""""""""""
To get the current abundance table, use the ``abundance_show`` procedure:

.. code-block:: fortran

    call spex%abundance_show(iabun,abun,ier)

Where ``iabun`` is the output number of the abundance set, and ``abun`` is
the output abundance reference:

.. code-block:: fortran

      module subroutine spex_model_abundance_show(this,iabun,abun,ier)
         class(sapi), intent(inout)      :: this
         integer, intent(out)            :: iabun
         character*256, intent(out)      :: abun
         integer, intent(out)            :: ier
      end subroutine

The procedure returns the reference as a text string:

.. code-block:: fortran

    call spex%abundance_show(iabun,abun)
    write(*,*) abun

    >>> Lodders et al. (2009)

.. _spexapi_aerror:

Aerror
------

Calculate the uncertainties for several parameters of a model component due to the uncertainties in the
atomic data:

.. code-block:: fortran

    call spex%aerror(isect,icomp,pname,ikl,pvalue,aerror,ier)

The Fortran interface:

.. code-block:: fortran

      module subroutine spex_model_atomic_error(this,isect,icomp,pname,ikl,pvalue,aerror,ier)
        class(sapi),  intent(inout)  :: this
        integer, intent(in)          :: isect    !! Sector number
        integer, intent(in)          :: icomp    !! Component number
        character*(*), intent(inout) :: pname    !! Parameter name
        integer, intent(in)          :: ikl      !! Shell number (0: full band, 1: L-shell, 2: K-shell)
        real(dp), intent(out)        :: pvalue   !! Parameter value
        real(dp), intent(out)        :: aerror   !! Atomic error
        integer, intent(inout)       :: ier
     end subroutine

Example:

.. code-block:: fortran

    call spex%aerror(1,1,'08',0,pvalue,aerror)
    write(*,*) pvalue, aerror

    >>> 1.00  0.0678

.. _spexapi_calc:

Calculate
---------

Once the model sectors and components are set-up and the parameters are set, the model spectrum
can be calculated using the SPEX ``calculate`` command. In the Fortran interface, we distinguish
two different calculate commands. One command only recalculates the model (``calc_model``) and
the other (``calc``) calculates the model and folds it with the response matrix:

.. code-block:: fortran

    call spex%calc_model(ier)

.. code-block:: fortran

     module subroutine spex_model_calc(this,ier)
         class(sapi),  intent(inout) :: this
         integer, intent(out)        :: ier               !! Error status
     end subroutine

The general ``calc`` command looks like this:

.. code-block:: fortran

    call spex%calc()

The general calculate command is actually a member of the fit library:

.. code-block:: fortran

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

Example:

.. code-block:: fortran

    call spex%calc_model(ier)
    call spex%calc()

.. _spexapi_com:

Component
---------

Spectral components, like power laws, thermal and absorption models are loaded using the
SPEX ``comp`` command. The Fortran procedure is also ``comp``:

.. code-block:: fortran

    call spex%comp(name, is1, is2, ier)

The requires the ``name`` of the spectral component and the range of sectors it needs to
apply to. This is slightly different from the command-line version of SPEX.

The Fortran interface looks like this:

.. code-block:: fortran

    module subroutine spex_model_comp(this, name, is1, is2, ier)
         class(sapi),  intent(inout)       :: this
         character*(*), intent(in)         :: name          !! input name of the component
         integer, intent(in)               :: is1           !! sectors [isect(1):isect(2)]
         integer, intent(in)               :: is2
         integer, intent(out)              :: ier           !! status error
    end subroutine

For example:

.. code-block:: fortran

    call spex%comp('cie',1,1,ier)

See :ref:`chap:spexmodels` for a list of spectral components.

Component delete
""""""""""""""""

Deleting a component from the model is done using the sector and component number of the component:

.. code-block:: fortran

    call spex%comp_del(is1, is2, ic1, ic2, ier)

The command accepts a range of sectors and/or components to delete. ``is1`` and ``is2`` are the
low and high values of the sector range to consider. ``ic1`` and ``ic2`` are the low and high values
of the components to delete.

This is the Fortran interface:

.. code-block:: fortran

      module subroutine spex_model_comp_del(this, is1, is2, ic1, ic2, ier)
         !! Deletes the components with number from range #i2: for sector range (optional) #i1.
         class(sapi),  intent(inout)  :: this
         integer, intent(inout)           :: is1, is2
         integer, intent(inout)           :: ic1, ic2
         integer, intent(out)             :: ier
      end subroutine

For example:

.. code-block:: fortran

    call spex%comp_del(1,1,2,2,ier)

The command above deletes the second component in sector number 1.

Component relate
""""""""""""""""

The relation between the additive and multiplicative components is set with a ``com rel`` command
in SPEX. In the Fortran API this is:

.. code-block:: fortran

    call spex%comp_rel(is1, is2, ic1, ic2, irel, rel, ier)

The relations can be set by sector and component range and the related multiplicative
models should be entered (in the right order).

The Fortran interface:

.. code-block:: fortran

      module subroutine spex_model_comp_rel(this, is1, is2, ic1, ic2, irel, rel, ier)
         class(sapi),  intent(inout)         :: this
         integer, intent(in)                 :: is1
         integer, intent(in)                 :: is2
         integer, intent(in)                 :: ic1
         integer, intent(in)                 :: ic2
         integer, intent(in)                 :: irel
         integer, intent(in),dimension(irel) :: rel
         integer, intent(out)                :: ier
      end subroutine

For example:

.. code-block:: fortran

    call spex%comp('reds',1,1,ier)
    call spex%comp('hot',1,1,ier)
    call spex%comp('cie',1,1,ier)
    rel(1) = 1
    rel(2) = 2
    call spex%comp_rel(1,1,3,3,2,rel,ier)

.. _spexapi_dist:

Distance
--------

To calculate fluxes and luminosities, SPEX needs an assumed distance of the source. In SPEX this
is done with the ``distance`` command.

The distance can be set with the ``distance`` procedure:

.. code-block:: fortran

    call spex%distance(is1,is2,dist,unit)

where ``is1`` and ``is2`` are the sector number range, ``dist`` the distance and ``unit`` the unit of the
distance that is put in.

The Fortran interface:

.. code-block:: fortran

    module subroutine spex_model_dist(this, is1, is2, dist, uword, ier)
         class(sapi),  intent(inout)     :: this
         integer, intent(in)             :: is1    !! First of sector range
         integer, intent(in)             :: is2    !! Last of sector range
         real(dp), intent(in)            :: dist    !! Value
         character(len=8), intent(inout) :: uword
         integer, intent(out) :: ier
    end subroutine

If you do not want to set the distance, but just get the current parameters, the ``dist_get``
procedure can be used:

.. code-block:: fortran

    call spex%dist_get(isect, dis, ier)

This procedure returns the ``dis`` object which contains the distances in all available units:

.. code-block:: fortran

    real(dp)  :: h0 = 0.70_dp !! The Hubble constant in units of 100 km/s/Mpc
    real(dp)  :: om = 0.30_dp !! Omega_matter
    real(dp)  :: ov = 0.70_dp !! Omega_Lambda
    real(dp)  :: or = 0.00_dp !! Omega_radiation

    real(dp)  :: distance     !! Distance in SPEX units 1E22 m
    real(dp)  :: dm           !! Distance in meter
    real(dp)  :: dau          !! Distance in A.U.
    real(dp)  :: dly          !! Distance in lightyear
    real(dp)  :: dpc          !! Distance in parsec
    real(dp)  :: dkpc         !! Distance in kiloparsec
    real(dp)  :: dmpc         !! Distance in megaparsec
    real(dp)  :: dz           !! Distance in redshift
    real(dp)  :: dcz          !! Distance in cz
    real(dp)  :: age          !! Lookback time in Gyr
    real(dp)  :: dltt         !! Light travel time distance (Mpc)
    real(dp)  :: dl           !! Luminosity distance (Mpc)
    real(dp)  :: da           !! Angular size distance

One can write the desired distance to the terminal by:

.. code-block:: fortran

    call spex%dist_get(1,dis,ier)
    write(*,*) dis%dm      ! Write the distance in meter for sector 1


Cosmology
"""""""""

Next to the distance, the cosmology used by SPEX can also be specified:

.. code-block:: fortran

    call spex%dist_cosmo(cospar,value,ier)

The command can be used to set the values for the Hubble constant ``h0`` (70 km/s/Mpc),
Omega Matter ``omega_m`` (0.3), Omega Lambda ``omega_l`` (0.7) and Omega R ``omega_r`` (0.0).
The ``cospar`` variable contains the name of the parameter like a text string and the
``value`` variable contains the value for that parameter.

This is the Fortran interface:

.. code-block:: fortran

    module subroutine spex_model_dist_cosmo(this, cospar, value, ier)
         class(sapi),  intent(inout)      :: this
         character*(*), intent(in)        :: cospar     !!Cosmological parameter
         real(dp), intent(in)             :: value      !!Value of the cosmological parameter
         integer, intent(inout)           :: ier        !!error status (0 is okay)
    end subroutine

For example:

.. code-block:: fortran

    call spex%dist_cosmo('h0',75.0,ier)

.. _spexapi_egrid:

Energy grid
-----------

The model energy grid can be manipulated with the SPEX ``egrid`` command. In Fortran, this command
has been splitted into two varieties:

.. code-block:: fortran

    call spex%egrid(iopt, elow, ehigh, nbin, unit, ier)
    call spex%egrid_step(iopt, elow, ehigh, step, unit, ier)

For the first method, ``egrid``, the number of spectral bins ``nbin`` is known, while for
``egrid_step`` the step size (``step``) is an input value. The lowest and highest energy of
the grid needs to be provided using the ``elow`` and ``ehigh`` input values. The unit is a
text string and the grid can be logarithmic if the ``log`` parameter is set to ``log``.

The Fortran interface for egrid:

.. code-block:: fortran

    module subroutine spex_model_egrid(this, iopt, elow, ehigh, nbin, unit, ier)
         class(sapi),  intent(inout)         :: this
         character*(*), intent(in)           :: iopt     !! grid  options: lin or log
         real(dp), intent(in)                :: elow     !! elow  energy fbin=elow:ehigh
         real(dp), intent(in)                :: ehigh    !! ehigh energy fbin=elow:ehigh
         integer, optional, intent(inout)    :: nbin     !! nr of bins??
         character*(*), optional, intent(inout) :: unit  !! unit
         integer, intent(out)                :: ier      !! error status (0 is okay)
    end subroutine

The Fortran interface for egrid_step:

.. code-block:: fortran

    module subroutine spex_model_egrid_step(this, iopt, elow, ehigh, step, unit, ier)
         class(sapi),  intent(inout)         :: this
         character*(*), intent(in)           :: iopt     !! grid  options: lin or log
         real(dp), intent(in)                :: elow     !! elow  energy fbin=elow:ehigh
         real(dp), intent(in)                :: ehigh    !! ehigh energy fbin=elow:ehigh
         real(dp), optional, intent(inout)   :: step     !! step stepa
         character*(*), optional, intent(inout) :: unit  !! unit
         integer, intent(out)                :: ier      !! error status (0 is okay)
    end subroutine

Examples:

.. code-block:: fortran

    call spex%egrid('log',0.1,10.0,9990,'kev',ier)
    call spex%egrid_step('lin',0.1,10.0,0.01,'kev',ier)

Reading & saving grids
""""""""""""""""""""""

Grids can also be save and read from a text file. The two procedures below save and read a ``.egr``
file, respectively:

.. code-block:: fortran

    call spex%egrid_read(readfile,ier)
    call spex%egrid_write(savefile,ier)

The ``savefile`` or ``readfile`` parameter should provide the method with the filename to save
or read, including the ``.egr`` extension! If necessary, the full path to the file can be included.

The Fortran interface:

.. code-block:: fortran

    module subroutine spex_model_egrid_write(this, filenam, ier)
         !! Write and save the calculated model energy grids
         class(sapi),  intent(inout)      :: this
         character*(*), intent(in)        :: filenam    !! output file
         integer, intent(out)             :: ier        !! error status (0 is okay)
    end subroutine

    module subroutine spex_model_egrid_read(this, filenam, ier)
         !! Read model energy grids
         class(sapi),  intent(inout)      :: this
         character*(*), intent(in)        :: filenam    !! input file
         integer, intent(out)             :: ier        !! error status
    end subroutine


Examples:

.. code-block:: fortran

    call spex%egrid_write('mygrid.egr')
    call spex%egrid_read('mygrid.egr')

Get & set custom grids
""""""""""""""""""""""
If the grid needs to be transferred from or to Fortran memory, then the ``get`` and ``ingrid`` methods
can be used:

.. code-block:: fortran

    call spex%egrid_get(neg,eg,egb,deg,sener,wener,ier)
    call spex%egrid_ingrid()

The Fortran interface for ingrid:

.. code-block:: fortran

    module subroutine spex_model_egrid_ingrid(this, ingrid, ning, ier)
         class(sapi),  intent(inout)       :: this
         real(dp), dimension(ning), intent(in) :: ingrid
         integer, intent(in)                   :: ning
         integer, intent(out)                  :: ier
      end subroutine

The Fortran interface for get:

.. code-block:: fortran

    module subroutine spex_model_egrid_get(this,neg,eg,egb,deg,sener,wener,ier)
        class(sapi), intent(inout)         :: this
        integer, intent(out)               :: neg      !! Number of model bins
        real(dp), allocatable, intent(out) :: eg(:)    !! Bin centers (keV)
        real(dp), allocatable, intent(out) :: egb(:)   !! Bin upper boundaries (keV)
        real(dp), allocatable, intent(out) :: deg(:)   !! Bin width (keV)
        real(dp), allocatable, intent(out) :: sener(:) !! Spectrum ph/s/bin
        real(dp), allocatable, intent(out) :: wener(:) !! Weights
        integer, intent(out) :: ier
    end subroutine


The ``get`` routine returns the fortran egrid arrays. The ``ingrid`` routine requires
an array containing the energies of the bin boundaries. Note that the number
of elements of this array would be of length n + 1, where n is the number of bins in the array.

.. _spexapi_elim:

Flux & Luminosity
-----------------

For each component, the fluxes and luminosities are calculated using the set distance and energy
boundaries. These energy limits for the flux and luminosity can be set using the ``elim`` procedure:

.. code-block:: fortran

    call spex%elim(elow, ehigh, unit, ier)

where ``elow`` is the lower boundary of the flux and ``ehigh`` the higher boundary. The ``unit``
determines the units of the input values, for example 'kev' for keV.

The Fortran interface is:

.. code-block:: fortran

    module subroutine spex_model_elim(this, r1, r2, unit, ier)
         !! Calculates the model flux in a given energy interval for the current spectral model
         class(sapi),  intent(inout)   :: this
         real, intent(in) :: r1                         !! lower value of the energy range
         real, intent(in) :: r2                         !! upper value of the energy range
         character*(*), intent(inout), optional :: unit  !! given unit: keV, eV, Ryd, J, Hz, A, nm
         integer, intent(inout)  :: ier                     !! Error status
    end subroutine

Examples:

.. code-block:: fortran

    call spex%elim(13.6E-3,13.6,unit='kev',ier)

Get flux
""""""""
The fluxes and luminosities calculated in SPEX can be extracted using the ``flux_get`` procedure:

.. code-block:: fortran

    call spex%flux_get(isect, icomp, eflow, efhigh, cname, &
                       flux_photflux, flux_enerflux, flux_fotlum, flux_enelum, status)

The Fortran interface is:

.. code-block:: fortran

    module subroutine spex_model_flux_get(this, isect, icomp, eflow, efhigh, cname, &
                       flux_photflux, flux_enerflux, flux_fotlum, flux_enelum, status)
         !! Get the model flux in a given energy interval for the current spectral model
         class(sapi),  intent(inout) :: this
         integer, intent(inout)   :: isect   !! Input sector
         integer, intent(inout)   :: icomp   !! Input component
         real(dp), intent(inout)  :: eflow   !! lower energy boundary
         real(dp), intent(inout)  :: efhigh  !! upper energy boundary
         character*8, intent(out) :: cname   !! name component
         real(dp),  intent(out)   :: flux_photflux, flux_enerflux, flux_fotlum, flux_enelum !! output fluxes
         integer, intent(inout)   :: status  !! Error status
    end subroutine

.. _spexapi_ibal:

Ionisation balance
------------------

There are several ionisation balances available in SPEX. The Urdampilleta ionisation balance
is the current default set.

The ionisation balance can be set using the ``ibal`` method:

.. code-block:: fortran

    call spex%ibal(ref,ier)

The ``ref`` is the short text string describing the paper reference for the ionisation balance:

* ``ar92``: Arnaud & Raymond (1992) for Fe, Arnaud & Rothenflug (1985) for other elements.
* ``ar85``: Arnaud & Rothenflug (1985).
* ``oldbryans``: Old Bryans et al. data (NOT recommended).
* ``bryans09``: Bryans et al. (2009).
* ``u17``: Urdampilleta et al. (2017).

The Fortran interface is (with cname for ref):

.. code-block:: fortran

    module subroutine spex_model_ibal(this,cname,ier)
         !! Set type of ionisation balance
         class(sapi),  intent(inout)   :: this
         character*(*), intent(inout)  :: cname !! ionisation balance abbreviation
         integer, intent(inout)        :: ier   !! Error
      end subroutine

Examples:

.. code-block:: fortran

    call spex%ibal('u17',ier)

Show
""""

To show the current ionisation balance, the ``ibal_show`` procedure can be used:

.. code-block:: fortran

    call spex%ibal_show(ier)

This method returns the reference of the ionisation balance as a string to the terminal.

The Fortran interface looks like this:

.. code-block:: fortran

    module subroutine spex_model_ibal_show(this, ier)
       !! Show the type of ionisation balance
       class(sapi),  intent(inout)    :: this
       integer, intent(in)            :: ier  !!Error status
    end subroutine

Example:

.. code-block:: fortran

    call spex%ibal_show(ier)

    >>> Urdampilleta et al. (2017)

Get
"""
To get the current ionisation balance in your program, the ``ibal_get`` procedure can be used:

.. code-block:: fortran

    call spex%ibal_get(ibal_ref,ibal_abbr,ib,ier)

This method returns the reference of the ionisation balance, the abbreviation of the reference, 
and the ionisation balance number.

The Fortran interface looks like this:

.. code-block:: fortran

    module subroutine spex_model_ibal_get(this, ibal_ref, ibal_abbr, ib, ier)
         !! get type of ionisation balance
         class(sapi),  intent(inout)       :: this
         character(len=32), intent(out)    :: ibal_ref  !! name of the reference of ionisation balance
         character(len=32), intent(out)    :: ibal_abbr !! Ionisation balance abbreviation
         integer, intent(out)              :: ib        !! matching the numeric label to ibal_ref
         integer, intent(inout)            :: ier       !! Error status
      end subroutine

Example:

.. code-block:: fortran

    call spex%ibal_get(ibal_ref, ibal_abbr, ib, ier)


.. _spexapi_ion:

Ion selection
-------------

In original SPEX models that use the SPEX atomic data, ions can be turned on or off, or can be
calculated using the old SPEX version 2 or the new SPEX version 3. In addition, the maximum
principle quantum number (nmax) and the maximum angular momentum (lmax) can be set.

The functions have been created such that each function selects the ions either by atomic number (z),
iso-electronic sequence (iso) or ion (ion):

.. code-block:: fortran

    call spex%ions_ignore_all(ier)
    call spex%ions_ignore_iso(iso,ier)
    call spex%ions_ignore_z(z,ier)
    call spex%ions_ignore_ion(z,ion,ier)
    call spex%ions_use_all(ier)
    call spex%ions_use_iso(iso,ier)
    call spex%ions_use_z(z,ier)
    call spex%ions_use_ion(z,ion,ier)
    call spex%ions_mute_all(ier)
    call spex%ions_mute_iso(iso,ier)
    call spex%ions_mute_z(z,ier)
    call spex%ions_mute_ion(z,ion,ier)
    call spex%ions_unmute_all(ier)
    call spex%ions_unmute_iso(iso,ier)
    call spex%ions_unmute_z(z,ier)
    call spex%ions_unmute_ion(z,ion,ier)
    call spex%ions_old_all(ier)
    call spex%ions_old_iso(iso,ier)
    call spex%ions_old_z(z,ier)
    call spex%ions_old_ion(z,ion,ier)
    call spex%ions_ql_all(ier)
    call spex%ions_ql_iso(iso,ier)
    call spex%ions_ql_z(z,ier)
    call spex%ions_ql_ion(z,ion,ier)
    call spex%ions_new_all(ier)
    call spex%ions_new_iso(iso,ier)
    call spex%ions_new_z(z,ier)
    call spex%ions_new_ion(z,ion,ier)

Examples:

.. code-block:: fortran

    call spex%ions_ignore_ion(26,25,ier) ! Ignore Fe XXV
    call spex%ions_new_z(8,ier)          ! Use new line database for Oxygen

Show
""""

The ion selections can be shown in the terminal by calling the ``ions_show`` procedure below:

.. code-block:: fortran

    call spex%ions_show(ier)

Line selection
""""""""""""""

Up to 10 specific lines can be 'muted' using the ``ions_line`` procedure below:

.. code-block:: fortran

    call spex%ions_line(mute,lid,z,ion,ier)

The Fortran interface is:

.. code-block:: fortran

    module subroutine spex_model_ions_line(this, mute, lid, z, ion, ier)
         class(sapi),  intent(inout) :: this
         logical, intent(in)             :: mute   !! Mute line .true. / Unmute .false.
         integer, intent(in)             :: lid    !! Line number to mute/unmute
         integer, intent(in)             :: z      !! Atomic number
         integer, intent(in)             :: ion    !! Ionisation stage
         integer, intent(inout)          :: ier
    end subroutine

Example:

.. code-block:: fortran

    call spex%ions_line(.true.,1,26,25,ier)

In addition to the line mute procedure, there are a couple of convenience functions:

.. code-block:: fortran

    call spex%ions_line_show()       ! Shows list of muted lines in terminal
    call spex%ions_line_unmute_all() ! Unmutes all lines (resets to normal)

.. _spexapi_par:

Setting parameters
------------------

Model parameters in SPEX are set using the ``par`` command. Since this command has subcommands,
there are a number of methods to cover most of the functionality in Fortran.

The most basic function is to set a parameter value:

.. code-block:: fortran

    call spex%par_set_value(isect,icomp,pname,val,ier)      ! For numeric parameters
    call spex%par_set_avalue(isect,icomp,pname,avalue,ier)  ! For text string parameters (like filenames)

These are their Fortran interfaces:

.. code-block:: fortran

    module subroutine spex_model_par_set_value(this, isect, icomp, pname, val, ier)
         class(sapi),  intent(inout)      :: this
         integer, intent(in)              :: isect     !! Sector number of parameter
         integer, intent(in)              :: icomp     !! Component number of parameter
         character*(*), intent(in)        :: pname     !! Parameter name
         real, intent(in)                 :: val       !! New parameter value
         integer, intent(out)             :: ier       !! error status
    end subroutine

    module subroutine spex_model_par_set_avalue(this, isect, icomp, pname, avalue, ier)
         class(sapi),  intent(inout)       :: this
         integer, intent(in)               :: isect     !! Sector number of parameter
         integer, intent(in)               :: icomp     !! Component number of parameter
         character*(*),  intent(in)        :: pname     !! Parameter name
         character*(*), intent(in)         :: avalue    !! Parameter ascii value
         integer, intent(out)              :: ier       !! error status
    end subroutine


The ``par_set_value`` method is used for setting numerical values. It needs the sector number (isect),
component number (icomp) and the name of the parameter (name) to set.

For text values, like filenames of model input files, the ``par_set_avalue`` method is used. The
usage is very similar to the ``par_set_value`` method, but just with the difference a text string is
passed instead of a value. Text parameters cannot be free parameters as well.

Examples:

.. code-block:: fortran

    call spex%par_set_value(1,1,'norm',1E+8,ier)
    call spex%par_set_avalue(1,1,'file','dist.dat',ier)

Fix & Free parameters
"""""""""""""""""""""
Many times, we want to fix and free parameters without changing the values. For this purpose,
there is one procedure:

.. code-block:: fortran

    call spex%par_status(isect, icomp, pname, free, ier)

Where ``isect`` is the sector number, ``icomp`` the component number and ``pname``
is the parameter name. If the ``free`` parameter is .true. the parameter is free,
and frozen when .false.

The Fortran interface looks like this:

.. code-block:: fortran

    module subroutine spex_model_par_status(this, isect, icomp, pname, free, ier)
         class(sapi),  intent(inout)       :: this
         integer, intent(in)               :: isect     !! Sector number of parameter
         integer, intent(in)               :: icomp     !! Component number of parameter
         character*(*), intent(in)         :: pname     !! Parameter name
         logical, intent(inout)            :: free      !! logical status
         integer, intent(out)              :: ier       !! error status
      end subroutine

Examples:

.. code-block:: fortran

    call spex%par_status(1,1,'26',.true.,ier)
    call spex%par_status(1,1,'t',.false.,ier)

Parameter range
"""""""""""""""
Parameters have ranges in which they can be safely varied without causing undesired errors or
unphysical results. These ranges can be set using the ``par_set_range`` method:

.. code-block:: fortran

    call spex%par_set_range(isect, icomp, pname, low, upp, ier)

In addition to the sector number (``isect``), component number (``icomp``), and the parameter
name (``pname``), this function needs the lower (``low``) and upper range (``upp``) limits of
the parameter.

The Fortran interface looks like this:

.. code-block:: fortran

    module subroutine spex_model_par_set_range(this, isect, icomp, pname, low, upp, ier)
         class(sapi),  intent(inout)   :: this
         integer, intent(in)               :: isect  !! Sector number of parameter
         integer, intent(in)               :: icomp  !! Component number of parameter
         character*(*), intent(in)         :: pname  !! Parameter name
         real, intent(in)                  :: low    !! Parameter lower value of range
         real, intent(in)                  :: upp    !! Parameter upper value of range
         integer, intent(out)              :: ier    !! Error status
    end subroutine

Example:

.. code-block:: fortran

    call spex%par_set_range(1,1,'t',1.0,10.0,ier)

Couple parameters
"""""""""""""""""
Parameters can be coupled to each other such they have the same values in the fit. Or, optionally,
remain coupled with a given multiplication factor. The Fortran procedure for this is ``par_couple``:

.. code-block:: fortran

    call spex%par_couple(isect, icomp, iname, csect, ccomp, cname, factor, ier)

This is the Fortran interface:

.. code-block:: fortran

    module subroutine spex_model_par_couple(this, isect, icomp, iname, csect, ccomp, cname, factor, ier)
        class(sapi),  intent(inout)   :: this
        integer, intent(in)               :: isect        !! Sector number of parameter to link
        integer, intent(in)               :: icomp        !! Component number of parameter to link
        character*(*), intent(inout)      :: iname        !! Parameter name to link
        integer, intent(in), optional     :: csect        !! Sector number of target parameter
        integer, intent(in), optional     :: ccomp        !! Component number target of parameter
        character*(*), intent(inout)      :: cname        !! Parameter name of target
        real, intent(in)                  :: factor       !! Coupling factor
        integer, intent(out)              :: ier          !! error status
      end subroutine

The parameter located in ``isect``, ``icomp`` and with ``iname`` will be coupled to the parameter
in ``csect``, ``ccomp``, and ``cname``. The ``factor`` sets the multiplication factor for the
coupling.

To decouple a parameter again, use:

.. code-block:: fortran

    call spex%par_decouple(isect, icomp, iname, ier)

This is the Fortran interface:

.. code-block:: fortran

    module subroutine spex_model_par_decouple(this, isect, icomp, iname, ier)
        class(sapi),  intent(inout)   :: this
        integer, intent(in)               :: isect  !! Sector number of parameter
        integer, intent(in)               :: icomp  !! Component number of parameter
        character*(*),  intent(inout)     :: iname  !! Parameter name
        integer, intent(out)              :: ier
    end subroutine

Examples:

.. code-block:: fortran

    call spex%par_couple(1, 2, 't', 1, 1, 't', 0.5, ier)  ! Couple the temperature in component 2 to 0.5 times the temperature in component 1
    call spex%par_decouple(1, 2, 't', ier)

Show parameters
"""""""""""""""
Show the model parameters in the terminal. One can specify a couple of options to
show more or less information:

.. code-block:: fortran

    call spex%par_show(free)

Example:

.. code-block:: fortran

    call spex%par_show(free=.true.)

Also individual components of the par show command can be shown:

.. code-block:: fortran

    call spex%par_show_flux()   ! Shows the flux overview for the model
    call spex%par_show_couple() ! Shows the coupled parameters

Get parameters
""""""""""""""
When doing an analysis it could be very helpful to get the value of a parameter:

.. code-block:: fortran

    call spex%par_get_value(in,is,ic,pval,free,low,upp,ier)

This is the Fortran interface:

.. code-block:: fortran

    module subroutine spex_model_par_get_value(this,in,is,ic,pval,free,low,upp,ier)
        class(sapi), intent(inout) :: this
        character*(*), intent(inout) :: in     !!parameter name
        integer, intent(in)          :: is     !!sector number
        integer, intent(in)          :: ic     !!component number
        real, intent(out)            :: pval   !!parameter value
        logical, intent(out)         :: free   !!true if parameter is free
        real, intent(out)            :: low    !!lower allowed limit
        real, intent(out)            :: upp    !!upper allowed limit
        integer, intent(out)         :: ier    !!error status
    end subroutine

This function returns for parameter ``in``, sector number ``is`` and component number ``ic``,
the parameter value ``pval``, the free status ``free``, and the lower and upper range ``low`` and
``upp``.

Example:

.. code-block:: fortran

    call spex%par_get_value('t',1,1,pval,free,low,upp,ier)

Write parameters to .com file
"""""""""""""""""""""""""""""
The current parameter settings can be saved to a command file (.com) and be loaded later by
the ``log_exe`` command. The ``par_write`` method in Fortran is called like this:

.. code-block:: fortran

    call spex%par_write(filename, overwrite)

The Fortran interface for this procedure looks like this:

.. code-block:: fortran

    module subroutine spex_model_par_write(this, filename, overwrite)
         class(sapi),  intent(inout)     :: this
         character*(*), intent(in)       :: filename     !name of the command file
         logical, intent(in)             :: overwrite    !overwrite flag
    end subroutine

Example:

.. code-block:: fortran

    call spex%par_write('myparam.com', .true.) ! overwrites the file 'myparam.com'

.. _spexapi_sector:

Sectors
-------

Sectors group spectral components to form the model for a particular source or phenomenon.
If the sectors need a different response, the sectors should also be defined in the .spo and
.res files. When starting SPEX, the number of sectors is 1 by default, even if loaded
data files contain more sectors. Sectors can be added to SPEX with the ``sector`` command.

In Fortran a new sector is created easily with the ``sector_new`` procedure:

.. code-block:: fortran

    call spex%sector_new(ier)

This creates an empty sector. Sometimes, the new sector needs to have the same components as
a previous one. In this case, the sector (with number ``isect``) can be copied:

.. code-block:: fortran

    call spex%sector_copy(isect,ier)

If a sector is no longer needed, it can be deleted:

.. code-block:: fortran

    call spex%sector_del(sect1,sect2,ier)  ! Delete sectors in the range from ``sect1`` to ``sect2``.

Examples:

.. code-block:: fortran

    call spex%sector_new(ier)
    call spex%sector_copy(1,ier)
    call spex%sector_delete(1,1,ier)

Since the sector contains the spectral model before convolving it with the response matrix, it
can be helpful to get the model spectrum for, for example, the file model or get the spectrum in
your program. There are two procedures for this:

.. code-block:: fortran

    call spex%sector_adump(isect, filenam, overwrite)
    call spex%sector_get_spectrum(isect, neg, eg, lum, ier)

These are their Fortran interfaces:

.. code-block:: fortran

    module subroutine spex_model_sector_adump(this, isect, filenam, overwrite)
         class(sapi),  intent(inout)       :: this
         integer, intent(in)               :: isect      !! Sector number to dump to file
         character(len=128), intent(in)    :: filenam    !! Filename of output file
         logical, intent(in)               :: overwrite  !! Overwrite existing file (true/false)
    end subroutine

    module subroutine spex_model_sector_get_spectrum(this, isect, neg, eg, lum, ier)
         class(sapi), intent(inout)         :: this
         integer, intent(in)                :: isect
         integer, intent(out)               :: neg   !! Number of bins
         real(dp), allocatable, intent(out) :: eg(:) !! Energy grid
         real(dp), allocatable, intent(out) :: lum(:)!! Luminosity in 10^44 ph/s/keV
         integer, intent(out)               :: ier
    end subroutine

.. _spexapi_var:

Plasma model parameters
-----------------------

There are a number of settings for the SPEX plasma models that can be changed by the user. In
SPEX these are done using the ``var`` command. The ``var`` commands have been implemented in
Fortran through the methods below.

Free-bound accuracy
"""""""""""""""""""
The free-bound accuracy (``fbacc``) can be set by:

.. code-block:: fortran

   call spex%var_gacc(fbacc, ier)
   call spex%var_gacc_reset(ier)

Example:

.. code-block:: fortran

    call spex%var_gacc(1.E-7,ier)

Line emission contributions
"""""""""""""""""""""""""""
The line processes can be switched off:

.. code-block:: fortran

    call spex%var_line(proc,stat,ier)

The Fortran interface looks like this:

.. code-block:: fortran

    module subroutine spex_model_var_line(this, proc, stat, ier)
         class(sapi),  intent(inout) :: this
         character*(*), intent(in)       :: proc   !! Process name
         logical, intent(in)             :: stat   !! Process on/off
         integer, intent(out)            :: ier
    end subroutine

Example:

.. code-block:: fortran

    call spex%var_line('ex',.false.,ier)

Doppler broadening
""""""""""""""""""
The Doppler broadening can be set by:

.. code-block:: fortran

    call spex%var_doppler(dopp,ier)

    module subroutine spex_model_var_doppler(this, dopp, ier)
         class(sapi),  intent(inout)     :: this
         integer, intent(in)             :: dopp  !! Type of broadening included
         integer, intent(out)            :: ier
    end subroutine

Example:

.. code-block:: fortran

    call spex%var_doppler(1,ier)

SPEXACT version 3 calculations
""""""""""""""""""""""""""""""

The use of SPEX version 3 atomic data can be set by:

.. code-block:: fortran

    call spex%var_calc(ivar,ier)

Where ``ivar`` is either ``old``, ``new``, or ``qc``.

Example:

.. code-block:: fortran

    call spex%var_calc('new',ier)

Occupation numbers starting values
""""""""""""""""""""""""""""""""""
Set the occupation number starting values:

.. code-block:: fortran

    call spex%var_occstart(levdist,ier)

Example:

.. code-block:: fortran

    call spex%var_occstart('ground',ier)

SPEXACT version 2 settings (MEKAL)
""""""""""""""""""""""""""""""""""
Specific SPEX version 2 MEKAL settings can be changed by (TBD):

.. code-block:: fortran

    call spex%var_mekal()

Examples:

.. code-block:: fortran

    call spex%var_mekal('fe17', .false.)

Multi-Maxwellians for the ionisation balance
""""""""""""""""""""""""""""""""""""""""""""

    call spex%var_ibalmaxw(maxw,ier)

Example:

.. code-block:: fortran

    call spex%var_ibalmaxw(.false.,ier)

SPEXACT version 3 cooling
"""""""""""""""""""""""""
Switch cooling methods on or off:

.. code-block:: fortran

   call spex%var_newcoolexc(cool, ier)

Example:

.. code-block:: fortran

    call spex%var_newcoolexc(.false.,ier)

And for the cooling by di-electronic recombination:

.. code-block:: fortran

   call spex%var_newcooldr(cool,ier)

Example:

.. code-block:: fortran

    call spex%var_newcooldr(.false.,ier)

Charge exchange recombination and ionization
""""""""""""""""""""""""""""""""""""""""""""
Set the origin of the charge exchange recombination and ionization rates:

.. code-block:: fortran

    call spex%var_cxcon(cxcon)

Example:

.. code-block:: fortran

    call spex%var_cxcon(1)

Set photo-ionisation cross-sections
"""""""""""""""""""""""""""""""""""
The photo-ionisation cross-sections can be set by:

.. code-block:: fortran

    call spex%var_pixsec(pixsec)

Example:

.. code-block:: fortran

    call spex%var_pixsec(2)

