% -*- mode: SLang; mode: fold -*-
%; Time-stamp: <2008-09-02 09:51:55 dph>
%; Directory:  ~dph/h3/Analysis/He_like_emis/
%; File:       he_modifier.sl
%; Author:     David P. Huenemoerder <dph@space.mit.edu>
%; Orig. version: 2007.10.07
%;========================================

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#iffalse
 
 This file is part of he_modifier.sl, 
 Copyright (C) 2008 Massachusetts Institute of Technology
 
 This software was developed by the MIT Kavli Institute for
 Astrophysics and Space Research under contract SV3-73016 from the
 Smithsonian Institution.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either Version 2 of the
 License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA

#endif 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% 2008.08.30 version 1.1.0 - mods by J.Houck to hide the He_Data
% argument, since that is the only data we have.

% 2008.08.15
% version:  1.0.0   - for release
%
% Purpose: provide He-like triplet line emissivity density dependence
%          
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% example model usage:
%
% plasma( aped ) ;           % load the plasma database
% require( "he_modifier" ) ; 
% create_aped_fun( "xaped", default_plasma_state ) ; 
% fit_fun( "xaped( 1, He_triplets(1, He_Data ) )" );
% set_par( "He_triplets(1).density", 1.e12, 0, 1.e10, 1.e14 ) ; 
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%          

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#iffalse

 He-like line modifier, for use with APED 1.3.1, which has no density
 dependence.

 The required data table, He_like_xyz_coef.fits, has coefficients for
 C, N, O, Ne, Mg, Si, and S.  The tabulated coefficients encode the
 density dependence of the triplet line emissivity normalized by the
 triplet sum.

    Type              Label    APED_upper_level   APED_lower_level
   resonance           w       7                  1
   intercombination    x       6                  1
   intercombination    y       5                  1
   forbidden           z       2                  1

 The table has columns: elem  type  temp  a0 a1 a2 a3 a4; 
 The functional form is:  

  emis(k) / emis(w+x+y+z)  =  a0 + a1 * exp( -n/a2 ) + a3 * exp( -n/a4 )

 where k refers to one of the components, x, y, or z.

 These were derived from "apec_d", a test density dependent database,
 which has coarser temperature grid than APED, but which has a
 density grid.

 This file implements a model function which reads the coefficients
 table and applies an emissivity modifier to scale the APED
 emissivities to the density dependent values using the above function
 and a model parameter giving the density.

 Requirements: APED must be installed and initialized before this
               file is evaluated.
               The ancillary table, He_like_xyz_coef.fits should be
               in the same directory as this file (he_modifier.sl)

#endif
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

private variable version = [1, 1, 0 ]   ; % major, minor, patch

variable he_modifier_version = sum( version * [ 10000, 100, 1 ] ) ; 
variable he_modifier_version_string =
            sprintf("%d.%d.%d", version[0], version[1], version[2] ) ; 

public variable He_Data = struct
{
    line_id,  % line id ( from ATOMDB )
    triplet_ids,
    temp,     % temperature (from coef file)
    elem,     % atomic number (from coef file)
    ltype,    % line type: "x", "y", or "z" (from coef file)
    par       % 5 coefs ( from coef file )
} ;


variable Default_Table =  "./He_like_xyz_norm_coef.fits" ;

%
% read table, fill in He_Data
%
private define rd_he_table() %   ( ftbl )
{
    variable ftbl = NULL ; 
    switch( _NARGS )
    {
	case 0: ftbl = Default_Table ;
    }
    {
	case 1: ftbl = () ;
    }
    {
	error("%% bad args\n" ) ;
    }

    return( fits_read_table( ftbl ) ) ; 
}

private variable Trans_level = Assoc_Type[ Integer_Type ] ;

Trans_level[ "x" ] = 6 ;
Trans_level[ "y" ] = 5 ;
Trans_level[ "z" ] = 2 ; 

%[ 7 is "w" (resonance) , but we don't need it here.]

private define pop_he_struct( tbl )
{
    variable num =   length( tbl.elem ) ; 
    He_Data.elem    = tbl.elem ;
    He_Data.ltype   = tbl.type ;
    He_Data.temp    = tbl.temp ;
    He_Data.line_id = Integer_Type[ num ] ;
    He_Data.triplet_ids = Array_Type[num];
    He_Data.par     = Array_Type[ num ] ;

    variable l, a, t ;
    foreach a ( [C, N, O, Ne, Mg, Si, S] )
    {
	foreach t ( ["x", "y", "z"] )
	{
	    l = where( He_Data.elem == a and He_Data.ltype == t ) ;
	    He_Data.line_id[ l ] = where( trans( a, a-1, Trans_level[ t ], 1 ) )[0];
            He_Data.triplet_ids[l] = where( trans( a, a-1, [5,6,7,2], 1 ) ) ;
	}
    }
    for (l=0; l<num; l++)
    {
	He_Data.par[ l ] = [ tbl.a0[l], tbl.a1[l], tbl.a2[l], tbl.a3[l], tbl.a4[l] ] ;
    }
}


define init_he_data( )  % ( [ftbl] ) 
{
    variable ftbl = NULL ; 

    switch( _NARGS )
    {
	case 0: ftbl = Default_Table ;
    }
    {
	case 1: ftbl = () ;
    }
    {
	error("%% Bad args: init_he_data( [fit_coef_table] );\n" ) ;
    }
    pop_he_struct(  rd_he_table( ftbl ) ) ; 
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#iffalse

  Retrieve indices of fit coefficients from the table given a line_id
  and a temperature.  Retrieve for the nearest temperature (as opposed
  to returning the bracketing values to interpolate the functions).

  The apec_d database, used to fit the He lines, had a grid spacing of
  0.25 dex.  So there should be a temperature within 0.125 dex, or a
  factor of 10^0.125 = 1.3352

#endif
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

variable Temperature_Grid_Tolerance = 10^0.125 ; 


% %
% % What to do if the nearest temperature is further than the tolerance? 
% % Should we return the original emissivity?  0.0?  an error?
% % Define a variable for now:
% %
% %%variable He_Triplet_Default_Modifier = 1.0 ;   % *** UNUSED ***



define get_he_par_idx( line_id, temperature )
{
    variable l = where( He_Data.line_id == line_id ) ;

    % l maps to the He-triplet component (x,y, or z) for a list of
    % temperatures.  Find the nearest, to the specified tolerance.
    % He_Data.temp array is in ascending order.

% slang bug - following causes fatal error:
%
%    l = l[ where( feqs( temperature,
%                        He_Data.temp,
%                       1.0-1.0/Temperature_Grid_Tolerance) ) ] ;

    variable y = abs( temperature - He_Data.temp[l] ) ;
    variable i = where( y == min(y) ) ;
 
   return( l[i] ) ; 
}

define get_he_par( line_id, temperature )
{
    variable l = get_he_par_idx( line_id, temperature ) ;
    if ( length( l ) > 1 ) message("%% WARNING: >1 temperature found.");
    return ( He_Data.par[l[0]] ) ; 
}


%
% Two-exponential parameterization of the He-like lines.  In default
% table, the function is the emissivity normalized by the sum of
% (w+x+y+z)

define emis_vs_density( dens, par )
{
    return( par[0] + par[1] * exp( -dens / par[2] )
                   + par[3] * exp( -dens / par[4] ) ) ; 
}

define he_triplet_modifier( par, line_id, pstate, emis)
{
   variable Atomic_Data = He_Data;
   
    variable n_density = par[ 0 ] ;
    variable l = where( line_id == Atomic_Data.line_id ) ;

    if ( length( l ) == 0 ) return( emis );

    variable triplet_ids = Atomic_Data.triplet_ids[l[0]];

    % get emissivity for w+x+y+z:
    variable s = line_em( triplet_ids, pstate.temperature, pstate.ndensity )[0] ; 

    % retrieve the parameters for the line in question:
    %
    variable p = get_he_par( line_id, pstate.temperature ) ; 
    variable f = emis_vs_density( n_density, p ) ; % emis(id)/ emis(w+x+y+z)


% for debugging:
#if(0)
message("%% modifying emissivity....");
message("%% elem $a ......"$ );
message("%% normalization = $s ..........."$);
message("%% params: "); () = array_map(Integer_Type, &printf, "%S\n ", p );
message("%% normed emis = $f"$);
#endif

    return( s * f ) ; 
}

%create_aped_line_modifier( "He_triplets", &he_triplet_modifier, "density", 1 ) ;
create_aped_line_modifier( "He_triplets", &he_triplet_modifier, "density") ;


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Default_Table = path_concat( path_dirname( __FILE__ ),
                             "He_like_xyz_norm_coef.fits") ; 

init_he_data( Default_Table ) ; 
