
FUNCTION ch_setup_ion, ions, wvlmin=wvlmin, wvlmax=wvlmax, ioneq_file=ioneq_file, $
                       noprot=noprot, path=path, radtemp=radtemp, rphot=rphot, $
                       abund_file=abund_file, quiet=quiet, noionrec=noionrec, $
                       obs_only=obs_only, index_wgfa=index_wgfa, no_auto=no_auto,$
                       no_rrec=no_rrec, opt_lookup=opt_lookup, n_levels=n_levels


;+
; NAME:
;
;       CH_SETUP_ION
;
;
; PURPOSE:
;
;       A new version of setup_ion.pro that puts the atomic parameters
;       into a structure rather than common blocks. The structure can
;       be directly sent to the routine pop_solver for computing level
;       populations. 
;
;
; CATEGORY:
;
;       CHIANTI; data setup.
;
;
; CALLING SEQUENCE:
;
;	Result = CH_SETUP_ION( Ion_Name )
;
;
; INPUTS:
;
;	Ion_Name:  The name of an ion in CHIANTI format (e.g., 'o_6'
;	           for O VI).
;
;
; OPTIONAL INPUTS:
;
;	Wvlmin:  A wavelength in angstroms. If set, then the routine
;	         checks if the ion has any wavelengths above
;	         WVLMIN. If not, then the routine exits, and an empty
;	         output is returned.
;	Wvlmax:  A wavelength in angstroms. If set, then the routine
;	         checks if the ion has any wavelengths below
;	         WVLMAX. If not, then the routine exits, and an empty
;	         output is returned.
;       Ioneq_File: The name of an ionization equilibrium file, which
;                   is used to populate the level-resolved
;                   ionization/recombination data structure. If not
;                   specified then the file !ioneq_file is used.
;       Abund_File: The name of a CHIANTI format element abundance
;                   file. This would be used by pop_solver to compute
;                   the proton-to-electron ratio.
;       Radtemp: If photon excitation is included (by defining RPHOT),
;                then this input specifies the blackbody radiation
;                temperature in K. If not specified, then it is set to
;                6000 K.
;       Rphot:   Distance from the centre of the star in stellar radius units.
;                That is, RPHOT=1 corresponds to the star's
;                surface. If RPHOT is not specified, then photon
;                excitation will be switched off when pop_solver is
;                called.
;       Path:    This directly specifies the path where the
;                ion's data files are stored. If not set, then
;                the files are taken from the user's CHIANTI
;                distribution.
;       N_Levels: When computing INDEX_WGFA (see below), this restricts
;                 the transitions included to those for which the upper
;                 level is less than or equal to N_LEVELS. It does not
;                 affect the size of the arrays in the output structure.
;
;
; KEYWORD PARAMETERS:
;
;       QUIET:   If set, then information messages are not printed.
;       NOPROT:  If set, then proton rates are not read for the ion,
;                even if they exist.
;       NOIONREC: If set, then level-resolved ionization/recombination
;                rates are not read for the ion, even if they exist.
;       OBS_ONLY: If a wavelength check is performed (WVLMIN and/or
;                 WVLMAX), then the default is to consider observed
;                 and theoretical wavelengths. With /OBS_ONLY, only
;                 the observed wavelengths are considered.
;       NO_AUTO: If set, then the autoionization rates (contained in
;                the .auto file) are not read.
;       NO_RREC: If set, do not read the level-resolved radiative
;                recombination (RR) files.
;       OPT_LOOKUP: If set, then the routine will not load the electron
;                spline fits, recombination/ionization data for
;                autoionization rates. This is intended for calling
;                ch_setup_ion from ch_synthetic when using the /lookup
;                option and gives a significant time saving.
;
; OUTPUTS:
;
;       A structure with the tags:
;       .gname   Name of the ion in CHIANTI format.
;       .jj      Array containing J-values for all levels.
;       .ecm     Array containing level energies. Energies are
;                observed if available otherwise they are
;                theoretical. 
;       .ecmth   Array containing theoretical energies.
;       .wvl     2D array containing wavelengths. No negative values.
;       .a_value 2D array containing A-values.
;       .wgfastr Structure containing output from read_wgfa_str.
;       .splstr  Structure containing output from read_scups.
;       .ioneq_file The name of the ion balance file.
;       .ip      Ionization potential in cm^-1 units.
;       .two_photon A structure with two tags containing the
;                two-photon rate and upper level. If data not
;                available, then it is a scalar with value -1.
;
;       The following tags will be included if the relevant data-sets
;       exist:
;       .prot_struc  Structure containing proton rate data.
;       .ionrec  Structure containing level-resolved ionization and
;                recombination data.
;       .abund_file  The name of an element abundance file.
;       .dilute  The radiation dilution factor (derived from RPHOT).
;       .radtemp The value of RADTEMP.
;       .autostr Structure containing autoionization rates from the
;                .auto files (same format as returned by read_auto). 
;       .rrec    for the level-resolved RR
; 
;       If a problem is found, then the integer -1 is returned.
;
;
; OPTIONAL OUTPUTS:
;
;       Index_Wgfa:  This is an index of the WGFA structure that picks
;                    out those transitions that satisfy the WVLMIN
;                    and/or WVLMAX conditions, and the A_VALUE NE 0
;                    condition. This index is used by the routine
;                    CH_SYNTHETIC routine (see "anylines" in this
;                    routine). 
;
;
; EXAMPLE:
;
;       IDL> output=ch_setup_ion('o_6')
;       IDL> output=ch_setup_ion('fe_13',wvlmin=170,wvlmax=210)
;       IDL> output=ch_setup_ion('si_12',path='/mydata/si_12')
;
;
; CALLS:
;
;       READ_WGFA_STR, CONVERTNAME, READ_ELVLC, READ_SCUPS,
;       READ_IONREC, R2W, READ_SPLUPS, READ_AUTO, CH_IP, read_rrlvl
;
;
; PREVIOUS HISTORY:
;
;       Modified from setup_ion.pro by PRY
;
;
; WRITTEN:
;
;      Ver.1, 28-Jun-2017, Peter Young
;         Modified from setup_ion.pro.
;
;
; MODIFICATION HISTORY:
;
;      Ver.2, 9-Aug-2017, Peter Young
;         Added the elvlc structure to the output; implemented
;         /noionrec.
;      Ver.3, 25-Jan-2018, Peter Young
;         Added wgfastr to the output structure (needed this for
;         bb_rad_loss).
;      Ver.4, 31-Jan-2018, Peter Young
;         Fixed bug with wvlmax implementation; added /OBS_ONLY
;         keyword; removed call to read_wgfa2 (only call read_wgfa_str
;         now).
;      Ver.5, 4-Feb-2018, Peter Young
;         Added INDEX_WGFA optional output.
;      Ver.6, 3-Jun-2018, Peter Young
;         Reads the new .auto files (released with v.9) and puts them
;         in the structure.
;      Ver.7, 5-Jun-2018, Peter Young
;         Added ionization potential to output structure; added
;         information to header and comments.
;      Ver.8, 10 Oct 2018, Giulio Del Zanna (GDZ)
;         return -1 if no main data files (.wgfa, .elvlc, .scups) are
;         found.  
;      Ver.9, 12-Dec-2018, GDZ 
;         Added reading the new level-resolved RR files.
;      Ver.10, 12-Nov-2020, Peter Young
;         Added the tag 'two_photon' to the output. This is the
;         optional structure returned by read_wgfa_str.
;      Ver.11, 12-May-2023, Peter Young
;         Added /opt_lookup option, which prepares a reduced output structure
;         that is used by ch_synthetic and speeds up the synthetic spectrum
;         calculation; now calls ch_setup_index_wgfa to obtain index_wgfa;
;         index_wgfa is now modified if /no_auto is set (which usually
;         reduces number of levels); added n_levels optional input.
;      Ver.12, 12-Jun-2023, Peter Young
;         Populates the new diel tag of wgfastr.
;      v.13, 14 Sept 2023, GDZ, modified how the number of levels is set
;               so the keyword /no_auto (to exclude the autoionizing states)
;               can be used.
;      Ver.14, 17-Sep-2024, Peter Young
;         The method for flagging the dielectronic transitions was very slow,
;         so a faster method is used now.
;
; VERSION     : 14
;
; 
;-


IF n_params() LT 1 THEN BEGIN
  print,'Use:  IDL> output = ch_setup_ion ( ion_name [, wvlmin=, wvlmax=, ioneq_file=, '
  print,'                                   /noionrec, /noprot, /quiet, abund_file=, '
  print,'                                   rphot=, radtemp=, path=, index_wgfa= '
  print,'                                   /obs_only, /no_auto, /opt_lookup, n_levels= ])'
  return,-1
ENDIF

IF n_elements(ioneq_file) EQ 0 THEN BEGIN
  ioneq_file=!ioneq_file
ENDIF ELSE BEGIN
  chck=file_search(ioneq_file,count=count)
  IF count EQ 0 AND NOT keyword_set(quiet) THEN BEGIN
    print,'% CH_SETUP_ION: the specified IONEQ_FILE was not found. Returning...'
    return,-1
  ENDIF 
ENDELSE 


IF N_ELEMENTS(path) NE 0 THEN fname = concat_dir(path, ions) $
                         ELSE ion2filename,ions,fname
wname=fname+'.wgfa'
elvlcname=fname+'.elvlc'
upsname=fname+'.scups'
pname=fname+'.psplups'
autoname=fname+'.auto'

chck=file_search(wname,count=count1)
chck=file_search(elvlcname,count=count2)
chck=file_search(upsname,count=count3)

;
; dielectronic is an output here and is used for the /opt_lookup option.
;
ion2spectroscopic,ions,snote, dielectronic=dielectronic


IF count1 eq 0 or count2 eq 0 or count3 eq 0 then begin 
if not keyword_set(quiet) then print, '% CH_SETUP_ION: no data files available for the ion ',ions
return, -1 
end 


convertname,ions,iz,ion

;
; Read .wgfa file.
; ---------------
read_wgfa_str,wname,wgfastr,two_photon=two_photon

index_wgfa=ch_setup_index_wgfa(wgfastr,wvlmin=wvlmin,wvlmax=wvlmax, $
                               obs_only=obs_only,count=count,levmax=n_levels)

IF count EQ 0 THEN BEGIN
  index_wgfa=-1
  IF NOT keyword_set(quiet) THEN print,'% CH_SETUP_ION: no transitions found for '+trim(ions)+'. Returning...'
  return,-1
ENDIF 



;
; Process radiative data to get WVL and A_VALUE 2D arrays. Note that
; the routine checks for transitions with zero wavelengths (2-photon
; and autoionization rates) and adds these rates to the A-values.
; Also note that WVL is forced to be positive.
;
lvl1=wgfastr.lvl1
lvl2=wgfastr.lvl2
nlvls=max([lvl1,lvl2])
wvl=fltarr(nlvls,nlvls)
gf=fltarr(nlvls,nlvls)
a_value=fltarr(nlvls,nlvls)
wvl[lvl1-1,lvl2-1]= abs(wgfastr.wvl)
gf[lvl1-1,lvl2-1]= wgfastr.gf
a_value[lvl1-1,lvl2-1]=wgfastr.aval+wgfastr.auto


;
; Read .elvlc file to get JJ, ECM and ECMTH arrays
; ----------------
read_elvlc,elvlcname,l1a,term,conf,ss,ll,jj,ecm,eryd,ecmth,erydth,eref,elvlcstr=elvlcstr
;
; The following sets the observed energy to be the theoretical energy
; in the case that the observed energy is missing (i.e., zero).
; 9-Nov-2015, PRY: I've added the line for eryd to be
; consistent, but eryd isn't used in the software.
;
; Recall that ecm=0 (not -1) for levels with no observed energy.
;
g=where(ecm EQ 0.)
IF max(g) GT 0 THEN BEGIN
  ecm[g]=ecmth[g]
  eryd[g]=erydth[g]
ENDIF 

;
; Read scups file to put collision data in SPLSTR
; ---------------
IF NOT keyword_set(opt_lookup) OR dielectronic THEN BEGIN 
  read_scups, upsname, splstr
  splstr_levmax=max(splstr.data.lvl2)
ENDIF ELSE BEGIN
  splstr=0
  splstr_levmax=-1
ENDELSE 


;
; Get ionization potential in cm^-1 units.
; ---------------------------------------
ip=ch_ip(ions,/cm)

;
; Populate the diel tag in wgfastr.
; 17-Sep-2026, PRY: This is a faster method of populating wgfastr.diel
;
k=where(ecm[wgfastr.lvl2-1] GT ip,nk)
IF nk GT 0 THEN wgfastr[k].diel=1b

;; Previous code
;;
;; k=where(ecm GT ip,ndr)
;; IF ndr GT 0 THEN BEGIN
;;   drlev=l1a[k]
;;   FOR i=0,ndr-1 DO BEGIN 
;;     j=where(wgfastr.lvl2 EQ drlev[i],nj)
;;     IF nj NE 0 THEN wgfastr[j].diel=1b
;;   ENDFOR 
;; ENDIF 

;
; Initially create the "core" structure, i.e., containing the
; essential elements required by pop_solver.
; 
output={ gname: ions, $
         jj:jj,  $
         ecm:ecm, $
         ecmth:ecmth, $
         elvlcstr: elvlcstr, $
         wvl:wvl,  $
         a_value:a_value, $
         wgfastr: wgfastr, $
         splstr:splstr, $
         ip: ip, $
         two_photon: two_photon, $
         ioneq_file: ioneq_file}

IF keyword_set(opt_lookup) THEN return,output

;
; Check for proton rates, and add structure to output
; ----------------------
chck=file_search(pname,count=count)
IF count NE 0 AND NOT keyword_set(noprot) THEN BEGIN
  IF NOT keyword_set(quiet) THEN BEGIN
    print,'% CH_SETUP_ION: proton rates added to output.'
  ENDIF 

  read_splups, pname, pstr, pref, /prot
  output=add_tag(output,pstr,'prot_struc')
ENDIF 


;
; Check for level-resolved ionization and recombination rates. 

if file_exist(expand_path(fname+'.rrlvl')) and $ 
   file_exist(expand_path(fname+'.reclvl')) then begin 
  print, 'ERROR ! both  .reclvl and .rrlvl files found ! '
  return, -1
end 

rrec_levmax=-1
IF NOT keyword_set(no_rrec) THEN BEGIN
  rrec=read_rrlvl(fname, status)
  if status then begin 
     IF NOT keyword_set(quiet) THEN BEGIN
      print,'% CH_SETUP_ION: level-resolved radiative recombination rates added to output.'
    ENDIF 
     rrec_levmax=max(rrec.final_level)
    output=add_tag(output,rrec,'rrec')
 endif 
endif 


;
; Check for level-resolved ionization and recombination rates. If they
; exist, then create ionrec structure and add it as a tag to output.
;
ionrec_levmax=-1
IF NOT keyword_set(noionrec) THEN BEGIN
  read_ionrec,fname,rec_rate,ci_rate,temp_ionrec,luprec,lupci,status
 ;
  IF status GT 0 THEN BEGIN
    IF NOT keyword_set(quiet) THEN BEGIN
      print,'% CH_SETUP_ION: level-resolved ionization & recombination rates added to output.'
    ENDIF 
   ;
    ionrec_levmax=max([luprec,lupci])
    read_ioneq,ioneq_file,temp_all,ioneq,ioneq_ref
    IF ion GT 1 THEN ioneq_ionrec=reform(ioneq[*,iz-1,ion-2:ion])
    IF ion EQ 1 THEN ioneq_ionrec=reform(ioneq[*,iz-1,ion-1:ion])
    ionrec = {rec:rec_rate, ci:ci_rate, temp:temp_ionrec, lev_up_rec:luprec, $
              lev_up_ci:lupci, status:status, ioneq:ioneq_ionrec, temp_ioneq:temp_all}
    output=add_tag(output,ionrec,'ionrec')
  ENDIF
ENDIF 


;
; Add the dilute and radtemp tags. Note that radtemp is ignored by
; pop_solver unless dilute is non-zero.
;
IF n_elements(rphot) NE 0 THEN BEGIN
  dilute=r2w(rphot)
  IF n_elements(radtemp) EQ 0 THEN radt=6d3 ELSE radt=double(radtemp)
  output=add_tag(output,dilute,'dilute')
  output=add_tag(output,radt,'radtemp')
ENDIF


;
; This adds the name of an abundance file. This will be used by
; pop_solver when computing the proton-to-electron ratio. If
; the tag doesn't exist, then pop_solver will just use the
; default abundance file. 
;
IF n_elements(abund_file) NE 0 THEN BEGIN
  chck=file_search(abund_file,count=count)
  IF count NE 0 THEN BEGIN
    output=add_tag(output,abund_file,'abund_file')
  ENDIF ELSE BEGIN
    IF NOT keyword_set(quiet) THEN print,'% CH_SETUP_ION: the specified abundance file was not found, so it has not been added'
    print,'                                              to the output structure.'
  ENDELSE 
ENDIF 


levmax=max([splstr_levmax,ionrec_levmax,rrec_levmax])

;
; Add autoionization rates stored in the .auto files (if available).
; GDZ - modified this part to remove the autoionizing states if required. 

IF file_exist(autoname)  THEN BEGIN 
;
  read_auto, autoname, autostr=autostr

  if keyword_set(no_auto) then begin

   IF NOT keyword_set(quiet) THEN $
    print,'% CH_SETUP_ION: autoionization rates were not added to output upon request and other data removed.'

; the last bound state must be one below the first autoionizing state:   
 autostr_levmax=min(autostr.lvl2)-1
 
endif else begin
  
  autostr_levmax=max(autostr.lvl2)
  output=add_tag(output,autostr,'autostr')

   IF NOT keyword_set(quiet) THEN $
    print,'% CH_SETUP_ION: autoionization rates added to output.'

endelse 
  
levmax=min([levmax, autostr_levmax])

ENDIF 

IF n_elements(n_levels) NE 0 THEN n_levels=min([n_levels,levmax]) else n_levels=levmax

; GDZ: note, n_levels gets modified inside this routine, then passed on.
; We could remove the autoionising states here but it is done in load_ion_rates

index_wgfa=ch_setup_index_wgfa(wgfastr,wvlmin=wvlmin,wvlmax=wvlmax, $
                               obs_only=obs_only,count=count, levmax=n_levels)

IF count EQ 0 THEN BEGIN
  index_wgfa=-1
  IF NOT keyword_set(quiet) THEN print,'% CH_SETUP_ION: no transitions found for '+trim(ions)+'. Returning...'
  return,-1
ENDIF 


return,output

END
