module cubefit_function_spectral_absorption
  use fit_minuit
  !
  use cubefit_messaging
  use cubefit_spectral_parameters
  use cubefit_spectral_obs
  !
  real(kind=para_k), parameter ::  absorption_tau_min = 0.1
  real(kind=para_k), parameter ::  absorption_tau_max = 100.0
  !
  integer(kind=npar_k),parameter :: nparline = 3
  integer(kind=npar_k),parameter :: icont   = 1
  integer(kind=npar_k),parameter :: itau    = 2
  integer(kind=npar_k),parameter :: ivelo   = 3
  integer(kind=npar_k),parameter :: ifwhm   = 4
  !
  public cubefit_function_spectral_absorption_init, cubefit_function_spectral_absorption_minimize
  public cubefit_function_spectral_absorption_extract, cubefit_function_spectral_absorption_residuals
  public cubefit_function_spectral_absorption_npar, cubefit_function_spectral_absorption_user2par
  public cubefit_function_spectral_absorption_par2spec, cubefit_function_spectral_absorption_spec2par
  public cubefit_function_spectral_absorption_iterate,cubefit_function_spectral_absorption_wind2par
  public cubefit_function_spectral_absorption_flags, cubefit_function_spectral_absorption_doprofile
  public cubefit_function_spectral_absorption_units
  private
  !
contains
    !
  subroutine cubefit_function_spectral_absorption_init(par,obs,minuit,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    type(spectral_pars_t), intent(inout) :: par
    type(spectral_obs_t),  intent(in)    :: obs
    type(fit_minuit_t),    intent(inout) :: minuit
    logical,               intent(inout) :: error
    !
    real(kind=coor_k) :: vsup,vinf
    real(kind=sign_k) :: val,area,ymin,ymax,cont
    character(len=mess_l) :: mess
    integer(kind=4) :: ipar,iline
    integer(kind=chan_k) :: ichan
    character(len=*), parameter :: rname='SPECTRAL>ABSORPTION>INIT'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')    
    !
    if (.not.obs%hfs%loaded) then
       call cubefit_message(seve%e,rname,'No HFS structure loaded')
       error = .true.
       return
    endif
    !
    ! Starting values
    if (par%nline.eq.0) then
       par%leaders(:) = 0
       par%flag(:,:) = 0
       par%errs(:) = 0
       ymax=0.
       ymin=0.
       area=0.
       vinf=minval(obs%spec%v)
       vsup=maxval(obs%spec%v)
       cont = 0.5*(obs%spec%t(obs%ifirst)+obs%spec%t(obs%ilast))
       do ichan=obs%ifirst+1,obs%ilast-1
          if (obs%wfit(ichan).ne.0) then
             val = ( obs%spec%t(ichan) + &
                  obs%wfit(ichan-1)*obs%spec%t(ichan-1) +   &
                  obs%wfit(ichan+1)*obs%spec%t(ichan+1) )   &
                  / (1+obs%wfit(ichan-1)+obs%wfit(ichan+1))
             val = val - cont
             if (val.ge.ymax) then
                ymax = val
                vsup = obs%spec%v(ichan)
             endif
             if (val.le.ymin) then
                ymin = val
                vinf = obs%spec%v(ichan)
             endif
             area=area+val*abs((obs%spec%v(ichan+1)-obs%spec%v(ichan-1)))
          endif
       enddo
       area = area*0.5
       if (abs(ymin).lt.abs(ymax)) then
          par%pars(ivelo)=vsup
          par%pars(itau)=ymax
       else
          par%pars(ivelo)=vinf
          par%pars(itau)=ymin
       endif
       par%pars(ifwhm) = abs(area/par%pars(itau)/1.064467)/2.    ! Take care of satellites
       par%pars(icont) = cont
       if (par%pars(ifwhm).lt.2*obs%deltav) par%pars(ifwhm) = 2*obs%deltav
       minuit%nu=cubefit_function_spectral_absorption_npar(1)
    else
       minuit%nu=cubefit_function_spectral_absorption_npar(par%nline)
    endif
    !
    ipar=0
    write(mess,'(a)') 'Input Parameters           Tau      Position         Width'
    call cubefit_message(fitseve%others,rname,mess)
    do iline=1,max(par%nline,1)
       write (mess,1002) par%pars(ipar+itau),par%pars(ipar+ivelo),par%pars(ipar+ifwhm)
       call cubefit_message(fitseve%others,rname,mess)
       ipar=ipar+nparline
    enddo
    !
    ! Set Up Parameters
    minuit%u(icont) = par%pars(icont)
    minuit%alim(icont) = absorption_tau_min*par%pars(icont)
    minuit%blim(icont) = absorption_tau_max*par%pars(icont)
    minuit%werr(icont) = obs%sigbase
    ipar=2
    do iline=1,max(par%nline,1)
       !
       ! Optical depth
       minuit%u(ipar)=par%pars(ipar)
       minuit%werr(ipar)=obs%sigbase
       if ( mod(par%flag(iline,itau),2).ne.0 .and. par%nline.ne.0) minuit%werr(ipar)=0.
       if (minuit%u(ipar).ne.0.) then
          minuit%alim(ipar)=min(0.d0,8.d0*minuit%u(ipar))
          minuit%blim(ipar)=max(0.d0,8.d0*minuit%u(ipar))
       else
          minuit%lcode(ipar)=1
       endif
       ipar=ipar+1
       !
       ! Velocity
       minuit%u(ipar)=par%pars(ipar)
       minuit%werr(ipar)=obs%deltav
       if ( mod(par%flag(iline,ivelo),2).ne.0 .and. par%nline.ne.0) minuit%werr(ipar)=0.
       minuit%alim(ipar)=minuit%u(ipar)-0.15*obs%spec%n*obs%deltav
       minuit%blim(ipar)=minuit%u(ipar)+0.15*obs%spec%n*obs%deltav
       ipar=ipar+1
       !
       ! Line Width
       minuit%u(ipar)=abs(par%pars(ipar))/1.665109
       minuit%werr(ipar)=2*obs%deltav
       if ( mod(par%flag(iline,ifwhm),2).ne.0 .and. par%nline.ne.0 ) minuit%werr(ipar)=0.
       minuit%alim(ipar)=0.2*obs%deltav
       minuit%blim(ipar)=0.5*obs%spec%n*obs%deltav
       ipar=ipar+1
    enddo
    !
1002 format((3(f14.3)))
  end subroutine cubefit_function_spectral_absorption_init
  !
  subroutine cubefit_function_spectral_absorption_minimize(npar,grad,chi2,pars,iflag,obs)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=npar_k),   intent(in)    :: npar        ! Number of parameters
    real(kind=grad_k),      intent(out)   :: grad(npar)  ! Gradientes
    real(kind=chi2_k),      intent(out)   :: chi2        ! chi squared
    real(kind=para_k),      intent(in)    :: pars(npar)  ! Parameter values
    integer(kind=4),        intent(in)    :: iflag       ! Code operation
    type(spectral_obs_t),   intent(inout) :: obs         ! Observation
    !
    integer(kind=chan_k) :: ichan
    real(kind=coor_k) :: xvel
    real(kind=sign_k) :: pred
    real(kind=grad_k), allocatable :: parsgrad(:)
    real(kind=8) :: arg,aux
    real(kind=chi2_k) :: chi2loc
    real(kind=para_k) :: tau,velo,fwhm,acoeff,bcoeff,ccoeff
    integer(kind=line_k) :: iline,ihfs
    integer(kind=npar_k) :: ipar,it,iv,id,ntot
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>MINIMIZE'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    if (iflag.eq.minuit_rms_flag) then
       call obs%sigma(cubefit_function_spectral_absorption_profile,.false.)
       return
    endif
    !
    grad(:) = 0.
    chi2 = 0.
    ntot = cubefit_function_spectral_absorption_npar(max(obs%par%nline,1))
    !
    allocate(parsgrad(ntot))
    do ichan=obs%ifirst,obs%ilast
       !
       ! Skip over masked area or bad channels
       if (obs%wfit(ichan).gt.0) then
          xvel = obs%spec%v(ichan)
          pred = 0
          do iline = 1, max(obs%par%nline,1)
             acoeff = 0
             bcoeff = 0
             ccoeff = 0
             it = (iline-1)*3+2
             iv = it+1
             id = iv+1
             tau  = pars(it)
             velo = pars(iv)
             fwhm = pars(id)
             do ihfs=1, obs%hfs%n
                arg = (xvel-obs%hfs%vel(ihfs)-velo)/fwhm
                if (abs(arg).lt.4.) then
                   aux = obs%hfs%rel(ihfs)*exp(-arg**2)
                   acoeff = acoeff + aux
                   aux = aux*2.*arg/fwhm
                   bcoeff = bcoeff + aux
                   ccoeff = ccoeff + aux*arg
                endif
             enddo
             pred = pred+acoeff*tau
             parsgrad(it) = acoeff
             parsgrad(iv) = bcoeff*tau
             parsgrad(id) = ccoeff*tau
          enddo
          pred = exp(-pred)
          chi2loc = obs%spec%t(ichan) - pred*pars(icont)
          chi2 = chi2 + chi2loc**2
          pred = 2*pred*chi2loc
          grad(icont) = grad(icont) - pred
          do ipar =2, ntot
             grad(ipar) = grad(ipar)+pred*parsgrad(ipar)
          enddo
       endif
    enddo
  end subroutine cubefit_function_spectral_absorption_minimize
  !
  subroutine cubefit_function_spectral_absorption_extract(minuit,obs,par,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    type(fit_minuit_t),    intent(inout) :: minuit
    type(spectral_obs_t),  intent(inout) :: obs
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=npar_k) :: ipar
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>EXTRACT'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    ! Update Parameters
    par%pars(icont) = minuit%u(icont)
    par%errs(icont) = minuit%werr(icont)
    ipar=0
    do iline=1,max(par%nline,1)
       par%pars(ipar+itau)  = minuit%u(ipar+itau)
       par%pars(ipar+ivelo) = minuit%u(ipar+ivelo)
       par%pars(ipar+ifwhm) = minuit%u(ipar+ifwhm)*1.665109
       par%errs(ipar+itau)  = minuit%werr(ipar+itau)
       par%errs(ipar+ivelo) = minuit%werr(ipar+ivelo)
       par%errs(ipar+ifwhm) = minuit%werr(ipar+ifwhm)*1.665109
       ipar=ipar+nparline
    enddo
  end subroutine cubefit_function_spectral_absorption_extract
  !
  subroutine cubefit_function_spectral_absorption_residuals(obs,spec,error)
    use cubemain_spectrum_real
    !------------------------------------------------------------------------
    ! Compute the residuals of a absorption fit
    !------------------------------------------------------------------------
    type(spectral_obs_t), intent(inout) :: obs          
    type(spectrum_t),     intent(inout) :: spec         
    logical,              intent(inout) :: error
    !
    integer(kind=chan_k) :: ichan
    real(kind=coor_k) :: xvel
    real(kind=sign_k) :: pred
    character(len=*), parameter :: rname = "SPECTRAL>ABSORPTION>RESIDUALS"
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    do ichan=1, spec%n
       xvel = obs%spec%v(ichan)
       pred = cubefit_function_spectral_absorption_profile(obs,xvel,0)
       spec%t(ichan) = obs%spec%t(ichan) - pred
    end do
  end subroutine cubefit_function_spectral_absorption_residuals
  !
  subroutine cubefit_function_spectral_absorption_doprofile(iline,obs,spec,error)
    use cubemain_spectrum_real
    !------------------------------------------------------------------------
    ! Compute the doprofile of a absorption fit
    !------------------------------------------------------------------------
    integer(kind=line_k), intent(in)    :: iline
    type(spectral_obs_t), intent(inout) :: obs          
    type(spectrum_t),     intent(inout) :: spec         
    logical,              intent(inout) :: error
    !
    integer(kind=chan_k) :: ichan
    real(kind=coor_k) :: xvel
    real(kind=sign_k) :: pred
    character(len=*), parameter :: rname = "SPECTRAL>ABSORPTION>DOPROFILE"
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    do ichan=1, spec%n
       xvel = obs%spec%v(ichan)
       pred = cubefit_function_spectral_absorption_profile(obs,xvel,iline)
       spec%t(ichan) = pred
    end do
  end subroutine cubefit_function_spectral_absorption_doprofile
  !
  subroutine cubefit_function_spectral_absorption_user2par(flag,pars,par,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=flag_k),  intent(in)    :: flag(:)
    real(kind=para_k),     intent(in)    :: pars(:)
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=npar_k) :: ipar,jpar
    integer(kind=line_k) :: iline
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>USER2PAR'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    par%leaders(:) = 0
    par%flag(:,:)  = 0
    par%errs(:)    = 0.
    !
    par%flag(1,icont) = flag(icont)
    par%pars(icont)   = pars(icont)
    jpar = 2
    do iline=1,par%nline
       do ipar=2,nparline+1
          par%flag(iline,ipar) = flag(jpar)
          par%pars(jpar)       = pars(jpar)
          jpar=jpar+1
       enddo ! ipar
    enddo ! iline
    !
    call par%check_line(itau,error)
    if (error) return
    call par%check_line(ivelo,error)
    if (error) return
    call par%check_line(ifwhm,error)
    if (error) return
  end subroutine cubefit_function_spectral_absorption_user2par
  !
  subroutine cubefit_function_spectral_absorption_par2spec(par,spec,error)
    use cubemain_spectrum_real
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectral_pars_t), intent(in)    :: par
    type(spectrum_t),      intent(inout) :: spec
    logical,               intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=chan_k) :: ichan
    integer(kind=npar_k) :: ipar
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>PAR2SPEC'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    ichan = spec_ndaps+1
    spec%t(ichan) = par%flag(1,icont)
    ichan = ichan+1
    spec%t(ichan) = par%pars(icont)
    ichan = ichan+1
    spec%t(ichan) = par%errs(icont)
    ipar = 0
    do iline=1,max(par%nline,1)
       ichan = ichan+1
       spec%t(ichan) = par%flag(iline,itau)
       ichan = ichan+1
       spec%t(ichan) = par%pars(ipar+itau)
       ichan = ichan+1
       spec%t(ichan) = par%errs(ipar+itau)
       ichan = ichan+1
       spec%t(ichan) = par%flag(iline,ivelo)
       ichan = ichan+1
       spec%t(ichan) = par%pars(ipar+ivelo)
       ichan = ichan+1
       spec%t(ichan) = par%errs(ipar+ivelo)
       ichan = ichan+1
       spec%t(ichan) = par%flag(iline,ifwhm)
       ichan = ichan+1
       spec%t(ichan) = par%pars(ipar+ifwhm)
       ichan = ichan+1
       spec%t(ichan) = par%errs(ipar+ifwhm)
       ipar = ipar + nparline
    enddo
  end subroutine cubefit_function_spectral_absorption_par2spec
  !
  subroutine cubefit_function_spectral_absorption_spec2par(spec,par,error)
    use cubemain_spectrum_real
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectrum_t),      intent(in)    :: spec
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=npar_k) :: ipar
    integer(kind=line_k) :: iline
    integer(kind=chan_k) :: ichan
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>SPEC2PAR'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    ichan = spec_ndaps+1
    par%flag(1,icont) = spec%t(ichan)  
    ichan = ichan+1
    par%pars(icont)   = spec%t(ichan)  
    ichan = ichan+1
    par%errs(icont)   = spec%t(ichan) 
    ipar = 0
    do iline=1,max(par%nline,1)
       ichan = ichan+1
       par%flag(iline,itau)  = spec%t(ichan)
       ichan = ichan+1
       par%pars(ipar+itau)   = spec%t(ichan)
       ichan = ichan+1
       par%errs(ipar+itau)   = spec%t(ichan)
       ichan = ichan+1
       par%flag(iline,ivelo) = spec%t(ichan)
       ichan = ichan+1
       par%pars(ipar+ivelo)  = spec%t(ichan)
       ichan = ichan+1
       par%errs(ipar+ivelo)  = spec%t(ichan)
       ichan = ichan+1
       par%flag(iline,ifwhm) = spec%t(ichan)
       ichan = ichan+1
       par%pars(ipar+ifwhm)  = spec%t(ichan)
       ichan = ichan+1
       par%errs(ipar+ifwhm)  = spec%t(ichan)
       ipar = ipar + nparline
    enddo
  end subroutine cubefit_function_spectral_absorption_spec2par
  !
  subroutine cubefit_function_spectral_absorption_iterate(par,error)
    !----------------------------------------------------------------------
    ! This routine is empty as no transformation is needed when we are
    ! iterating an ABSORPTION fit
    !----------------------------------------------------------------------
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>ITERATE'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
  end subroutine cubefit_function_spectral_absorption_iterate
  !
  subroutine cubefit_function_spectral_absorption_wind2par(obs,wind,par,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectral_obs_t),  intent(in)    :: obs
    integer(kind=chan_k),  intent(in)    :: wind(:)
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=chan_k) :: first,last
    integer(kind=line_k) :: iline
    integer(kind=npar_k) :: ipar
    real(kind=para_k) :: area,velo,fwhm
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>WIND2PAR'
    !
    par%errs(:) = 0
    par%leaders(:)  = 0
    par%pars(icont) = 1.0
    !
    ipar = 0
    do iline=1,par%nline
       first = wind(2*iline-1)
       last  = wind(2*iline)
       par%flag(iline,:)   = 0
       !
       call obs%est_gauss(first,last,area,velo,fwhm,error)
       if (error) return
       if (fwhm.lt.obs%deltav) fwhm = obs%deltav
       if (fwhm.gt.obs%deltav*obs%spec%n*0.5) fwhm = obs%deltav*obs%spec%n*0.5
       par%pars(ipar+itau)  = area 
       par%pars(ipar+ivelo) = velo 
       par%pars(ipar+ifwhm) = fwhm
       ipar = ipar + nparline
    end do
  end subroutine cubefit_function_spectral_absorption_wind2par
  !
  !----------------------------------------------------------------------
  !
  function cubefit_function_spectral_absorption_profile(obs,xvel,iline) result(absorption)
    !----------------------------------------------------------------------
    ! Compute the value of a hyperfine line or a sum of hyperfine
    ! lines at xvel
    !----------------------------------------------------------------------
    type(spectral_obs_t),   intent(in)    :: obs         ! Observation
    real(kind=coor_k),      intent(in)    :: xvel        ! Coordinate to compute value
    integer(kind=line_K),   intent(in)    :: iline       ! Which absorption is to be computed
    !
    real(kind=sign_k) :: absorption
    !
    integer(kind=line_k) :: jline
    !
    absorption=0.
    if (iline.eq.0) then
       do jline=1,max(obs%par%nline,1)
          absorption = absorption+absorption_single(jline)
       enddo
       absorption = exp(-absorption)
    else
       absorption = exp(-absorption_single(iline))
    endif
    ! multiply by continuum level
    absorption = absorption*obs%par%pars(icont)
  contains
    function absorption_single(iwhich)
      !----------------------------------------------------------------------
      !
      !----------------------------------------------------------------------
      integer(kind=line_K),   intent(in)    :: iwhich   ! Which absorption is to be computed
      real(kind=sign_k) :: absorption_single  
      ! Local
      real(kind=para_k) :: tau, velo, fwhm, arg
      integer(kind=line_k) :: ihfs
      !
      tau =obs%par%pars((iwhich-1)*nparline+itau)
      velo=obs%par%pars((iwhich-1)*nparline+ivelo)
      fwhm=obs%par%pars((iwhich-1)*nparline+ifwhm)/1.665109
      absorption_single = 0.
      if (tau.ne.0 .and. fwhm.ne.0) then
         do ihfs=1,obs%hfs%n
            arg = abs ((xvel-obs%hfs%vel(ihfs)-velo)/fwhm)
            if (arg.lt.4.) then
               absorption_single = absorption_single + obs%hfs%rel(ihfs)*exp(-(arg**2))
            endif
         enddo
      endif
      absorption_single = absorption_single*tau
    end function absorption_single
  end function cubefit_function_spectral_absorption_profile
  !
  subroutine cubefit_function_spectral_absorption_flags(ipar,lineflag,parflag,error)
    use cubedag_allflags
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=npar_k), intent(in)    :: ipar
    type(flag_t),         intent(out)   :: lineflag
    type(flag_t),         intent(out)   :: parflag
    logical,              intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=npar_k) :: mypar
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>FLAGS'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    if (ipar.eq.1) then
       parflag = flag_continuum
       lineflag = flag_one
       return
    else
       mypar = ipar-1
       iline  = mypar/nparline+1
       select case(mod(mypar,nparline))
       case(1)
          parflag = flag_tau
       case(2)
          parflag = flag_velocity
       case(0)
          parflag = flag_fwhm
          iline  = iline-1
       end select
       call cubefit_line2flag(iline,lineflag,error)
       if (error)  return
    endif
  end subroutine cubefit_function_spectral_absorption_flags
  !
  subroutine cubefit_function_spectral_absorption_units(ipar,unit,error)
    use cubetools_unit
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=npar_k), intent(in)    :: ipar
    character(len=*),     intent(out)   :: unit
    logical,              intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=npar_k) :: mypar
    character(len=*), parameter :: rname = 'SPECTRAL>ABSORPTION>UNITS'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    if (ipar.eq.1) then
       unit = strg_id
    else
       mypar = ipar-1
       iline  = mypar/nparline+1
       select case(mod(mypar,nparline))
       case(1)
          unit = '---' ! no unit for opacity
       case(2)
          unit = unit_velo_name(1) ! km/s
       case(0)
          unit = unit_velo_name(1) ! km/s
       end select
    endif
  end subroutine cubefit_function_spectral_absorption_units
  !
  function cubefit_function_spectral_absorption_npar(nline) result(npar)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=line_k), intent(in) :: nline
    !
    integer(kind=npar_k) :: npar
    !
    npar = nparline*nline+1
  end function cubefit_function_spectral_absorption_npar
end module cubefit_function_spectral_absorption
