def __calc_x4(*args):
    """
    This function calculates the x4 coeffiecient to the S(Q,E) Jacobian

    @param args: A list of parameters used to calculate the x4 coefficient

    The following is a list of the arguments needed in there expected order
      1. Initial Energy
      2. Initial Time-of-Flight
      3. Time-zero Slope Correction
      4. Vector of Zeros
    @type args: C{list}

    
    @return: The calculated x4 coefficient
    @rtype: (C{nessi_list.NessiList}, C{nessi_list.NessiList}) 
    """
    # Settle out the arguments to sensible names
    E_i = args[0]
    T_i = args[1]
    t_0_s_corr = args[2]
    z_vec = args[3]

    # E_i / T_i
    temp1 = array_manip.div_ncerr(E_i, z_vec, T_i, z_vec)
    # -2 * (E_i / T_i)
    temp2 = array_manip.mult_ncerr(temp1[0], temp1[1], -2.0, 0.0)
    # -2 * (E_i / T_i) * (1 / 1 + ((h / m) * (t0_s / L_i)))
    return array_manip.mult_ncerr(temp2[0], temp2[1], t_0_s_corr, 0.0)
def __calc_x4(*args):
    """
    This function calculates the x4 coeffiecient to the S(Q,E) Jacobian

    @param args: A list of parameters used to calculate the x4 coefficient

    The following is a list of the arguments needed in there expected order
      1. Initial Energy
      2. Initial Time-of-Flight
      3. Time-zero Slope Correction
      4. Vector of Zeros
    @type args: C{list}

    
    @return: The calculated x4 coefficient
    @rtype: (C{nessi_list.NessiList}, C{nessi_list.NessiList}) 
    """
    # Settle out the arguments to sensible names
    E_i = args[0]
    T_i = args[1]
    t_0_s_corr = args[2]
    z_vec = args[3]

    # E_i / T_i
    temp1 = array_manip.div_ncerr(E_i, z_vec, T_i, z_vec)
    # -2 * (E_i / T_i)
    temp2 = array_manip.mult_ncerr(temp1[0], temp1[1], -2.0, 0.0)
    # -2 * (E_i / T_i) * (1 / 1 + ((h / m) * (t0_s / L_i)))
    return array_manip.mult_ncerr(temp2[0], temp2[1], t_0_s_corr, 0.0)
Beispiel #3
0
def calc_deltat_over_t(axis, axis_err2=None):
    """
    This function takes a TOF axis and calculates the quantity Delta t / t
    for every element. 

    @param axis: The TOF axis from which Delta t / t will be calculated
    @type axis: C{nessi_list.NessiList}
    
    @param axis_err2: (OPTIONAL) The error^2 on the incoming TOF axis
    @type axis_err2: C{nessi_list.NessiList}


    @return: The calculated Delta t / t
    @rtype: C{SOM.SOM}
    """
    import nessi_list

    # Check to see if incoming is really a NessiList
    try:
        axis.__type__
    except AttributeError:
        raise RuntimeError("The object passed to this function needs to be a "\
                           +"NessiList. Do not understand how to deal with "\
                           +"%s" % type(axis))

    len_axis = len(axis)
    if axis_err2 is None:
        axis_err2 = nessi_list.NessiList(len_axis)

    deltat = nessi_list.NessiList()
    deltat_err2 = nessi_list.NessiList()

    # Calculate bin deltas, assume axis in ascending order
    for i in xrange(len_axis - 1):
        deltat.append(axis[i + 1] - axis[i])
        deltat_err2.append(axis_err2[i + 1] - axis_err2[i])

    # Calculate bin centers
    import utils
    (binc, binc_err2) = utils.calc_bin_centers(axis, axis_err2)

    # Calculate delta t / t
    import array_manip
    dtot = array_manip.div_ncerr(deltat, deltat_err2, binc, binc_err2)

    import SOM
    som = SOM.SOM()
    so = SOM.SO()
    so.y = dtot[0]
    so.var_y = dtot[1]
    som.append(so)

    som.setDataSetType("density")
    som.setYLabel("deltat_over_t")

    return som
def calc_deltat_over_t(axis, axis_err2=None):
    """
    This function takes a TOF axis and calculates the quantity Delta t / t
    for every element. 

    @param axis: The TOF axis from which Delta t / t will be calculated
    @type axis: C{nessi_list.NessiList}
    
    @param axis_err2: (OPTIONAL) The error^2 on the incoming TOF axis
    @type axis_err2: C{nessi_list.NessiList}


    @return: The calculated Delta t / t
    @rtype: C{SOM.SOM}
    """
    import nessi_list

    # Check to see if incoming is really a NessiList
    try:
        axis.__type__
    except AttributeError:
        raise RuntimeError("The object passed to this function needs to be a "\
                           +"NessiList. Do not understand how to deal with "\
                           +"%s" % type(axis))

    len_axis = len(axis)
    if axis_err2 is None:
        axis_err2 = nessi_list.NessiList(len_axis)

    deltat = nessi_list.NessiList()
    deltat_err2 = nessi_list.NessiList()
    
    # Calculate bin deltas, assume axis in ascending order
    for i in xrange(len_axis - 1):
        deltat.append(axis[i+1] - axis[i])
        deltat_err2.append(axis_err2[i+1] - axis_err2[i])        

    # Calculate bin centers
    import utils
    (binc, binc_err2) = utils.calc_bin_centers(axis, axis_err2)

    # Calculate delta t / t
    import array_manip
    dtot = array_manip.div_ncerr(deltat, deltat_err2, binc, binc_err2)

    import SOM
    som = SOM.SOM()
    so = SOM.SO()
    so.y = dtot[0]
    so.var_y = dtot[1]
    som.append(so)

    som.setDataSetType("density")
    som.setYLabel("deltat_over_t")

    return som
def __calc_x2(*args):
    """
    This function calculates the x2 coeffiecient to the S(Q,E) Jacobian

    @param args: A list of parameters used to calculate the x2 coefficient

    The following is a list of the arguments needed in there expected order
      1. Initial Wavevector
      2. Momentum Transfer
      3. Initial Time-of-Flight
      4. Wavevector Final x Cos(polar)
      5. Time-zero Slope Correction
      6. Vector of Zeros
    @type args: C{list}

    
    @return: The calculated x2 coefficient
    @rtype: (C{nessi_list.NessiList}, C{nessi_list.NessiList})
    """
    # Settle out the arguments to sensible names
    k_i = args[0]
    Q = args[1]
    T_i = args[2]
    k_f_cos_pol = args[3]
    t_0_s_corr = args[4]
    z_vec = args[5]

    # k_f * cos(pol) - k_i
    temp1 = array_manip.sub_ncerr(k_f_cos_pol, 0.0, k_i, z_vec)
    # (k_f * cos(pol) - k_i) / Q
    temp2 = array_manip.div_ncerr(temp1[0], temp1[1], Q, z_vec)
    # k_i / T_i
    temp3 = array_manip.div_ncerr(k_i, z_vec, T_i, z_vec)
    # (k_i / T_i) * ((k_f * cos(pol) - k_i) / Q)
    temp4 = array_manip.mult_ncerr(temp2[0], temp2[1], temp3[0], temp3[1])
    # (k_i / T_i) * ((k_f * cos(pol) - k_i) / Q) *
    # (1 / 1 + ((h / m) * (t0_s / L_i)))
    return array_manip.mult_ncerr(temp4[0], temp4[1], t_0_s_corr, 0.0)
def __calc_x2(*args):
    """
    This function calculates the x2 coeffiecient to the S(Q,E) Jacobian

    @param args: A list of parameters used to calculate the x2 coefficient

    The following is a list of the arguments needed in there expected order
      1. Initial Wavevector
      2. Momentum Transfer
      3. Initial Time-of-Flight
      4. Wavevector Final x Cos(polar)
      5. Time-zero Slope Correction
      6. Vector of Zeros
    @type args: C{list}

    
    @return: The calculated x2 coefficient
    @rtype: (C{nessi_list.NessiList}, C{nessi_list.NessiList})
    """
    # Settle out the arguments to sensible names
    k_i = args[0]
    Q = args[1]
    T_i = args[2]
    k_f_cos_pol = args[3]
    t_0_s_corr = args[4]
    z_vec = args[5]

    # k_f * cos(pol) - k_i
    temp1 = array_manip.sub_ncerr(k_f_cos_pol, 0.0, k_i, z_vec)
    # (k_f * cos(pol) - k_i) / Q
    temp2 = array_manip.div_ncerr(temp1[0], temp1[1], Q, z_vec)
    # k_i / T_i
    temp3 = array_manip.div_ncerr(k_i, z_vec, T_i, z_vec)
    # (k_i / T_i) * ((k_f * cos(pol) - k_i) / Q)
    temp4 = array_manip.mult_ncerr(temp2[0], temp2[1], temp3[0], temp3[1])
    # (k_i / T_i) * ((k_f * cos(pol) - k_i) / Q) *
    # (1 / 1 + ((h / m) * (t0_s / L_i)))
    return array_manip.mult_ncerr(temp4[0], temp4[1], t_0_s_corr, 0.0)
Beispiel #7
0
def fix_bin_contents(obj, **kwargs):
    """
    This function takes a SOM or SO and goes through the individual spectra
    adjusting the bin contents by either multiplying or dividing by the
    bin widths or the bin centers taken from the individual spectra.

    @param obj: The data object to be scaled
    @type obj: C{SOM.SOM} or C{SOM.SO}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword scale: A flag that signals multiplication by the required bin
                    quantity. The default is I{False} (divide).
    @type scale: C{bool}

    @keyword width: A flag that signals that the adjusting quantity is the
                    bin width. The default is I{True}. If I{False}, the bin
                    center is used.
    @type width: C{bool}

    @keyword units: The expected units for this function. The default for this
                    function is I{microsecond}.
    @type units: C{string}


    @return: The object with the individual spectrum scaled
    @rtype: C{SOM.SOM} or C{SOM.SO}
    """
    import hlr_utils

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    # Setup keyword arguments
    try:
        scale = kwargs["scale"]
    except KeyError:
        scale = False

    try:
        width = kwargs["width"]
    except KeyError:
        width = True

    try:
        units = kwargs["units"]
    except KeyError:
        units = "microsecond"

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis_pos = hlr_utils.one_d_units(obj, units)
    else:
        axis_pos = 0

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    # iterate through the values
    import array_manip
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "y")
        err2 = hlr_utils.get_err2(obj, i, o_descr, "y")
        axis = hlr_utils.get_value(obj, i, o_descr, "x", axis_pos)
        axis_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis_pos)

        map_so = hlr_utils.get_map_so(obj, None, i)

        if width:
            (bin_const,
             bin_const_err2) = utils.calc_bin_widths(axis, axis_err2)
        else:
            (bin_const,
             bin_const_err2) = utils.calc_bin_centers(axis, axis_err2)

        if scale:
            value = array_manip.mult_ncerr(val, err2, bin_const,
                                           bin_const_err2)
        else:
            value = array_manip.div_ncerr(val, err2, bin_const, bin_const_err2)

        hlr_utils.result_insert(result, res_descr, value, map_so, "y")

    return result
Beispiel #8
0
def div_ncerr(left, right, **kwargs):
    """
    This function divides two objects (C{SOM}, C{SO} or {tuple(val,val_err2)})
    and returns the result of the division in an C{SOM}, C{SO} or C{tuple}.

    @param left: Object on the left of the division sign
    @type left: C{SOM.SOM} or C{SOM.SO} or C{tuple}
    
    @param right: Object on the right of the division sign
    @type right: C{SOM.SOM} or C{SOM.SO} or C{tuple}
    
    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword axis: This is the axis one wishes to manipulate. If no argument
                   is given the default value is y
    @type axis: C{string}=<y or x>
    
    @keyword axis_pos: This is position of the axis in the axis array. If no
                       argument is given, the default value is 0
    @type axis_pos: C{int}
    
    @keyword length_one_som: This is a flag that lets the function know it is
                             dealing with a length 1 C{SOM} so that attributes
                             may be passed along. The length 1 C{SOM} will be
                             turned into a C{SO}. The default value is False.
    @type length_one_som: C{boolean}
    
    @keyword length_one_som_pos: This is the argument position of the length 1
                                 C{SOM} since division order is not
                                 commutative. The default value is 2.
    @type length_one_som_pos: C{int}=<1 or 2> 


    @return: Object containing the results of the division
    @rtype: C{SOM.SOM}, C{SOM.SO} or C{tuple}


    @raise IndexError: The two C{SOM}s do not contain the same number of
                       spectra
                       
    @raise RunTimeError: The x-axis units of the C{SOM}s do not match
    
    @raise RunTimeError: The y-axis units of the C{SOM}s do not match
    
    @raise RunTimeError: The x-axes of the two C{SO}s are not equal
    """

    # import the helper functions
    import hlr_utils

    # Check to see if we are working with a length 1 SOM
    try:
        length_one_som = kwargs["length_one_som"]
    except KeyError:
        length_one_som = False

    try:
        length_one_som_pos = kwargs["length_one_som_pos"]
        if length_one_som_pos != 1 or length_one_som_pos != 2:
            raise RuntimeError("length_one_som_pos must be either 1 or 2 and "\
                               +"%d" % length_one_som_pos)
    except KeyError:
        length_one_som_pos = 2

    if length_one_som:
        if length_one_som_pos == 1:
            som_copy = left
            left = left[0]
        else:
            som_copy = right
            right = right[0]
    else:
        # Not working with a length 1 SOM, do nothing
        pass

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(left, right)
    (l_descr, r_descr) = hlr_utils.get_descr(left, right)

    is_number = False

    # error check information
    if r_descr == "SOM" and l_descr == "SOM":
        hlr_utils.math_compatible(left, l_descr, right, r_descr)
    elif l_descr == "number" and r_descr == "number":
        is_number = True
    else:
        pass

    # Check for axis keyword argument
    try:
        axis = kwargs["axis"]
    except KeyError:
        axis = "y"

    # Check for axis_pos keyword argument
    try:
        axis_pos = kwargs["axis_pos"]
    except KeyError:
        axis_pos = 0

    if length_one_som:
        if length_one_som_pos == 1:
            result = hlr_utils.copy_som_attr(result, res_descr, som_copy,
                                             "SOM", right, r_descr)
        else:
            result = hlr_utils.copy_som_attr(result, res_descr, left, l_descr,
                                             som_copy, "SOM")
    else:
        result = hlr_utils.copy_som_attr(result, res_descr, left, l_descr,
                                         right, r_descr)

    # iterate through the values
    import array_manip

    for i in xrange(hlr_utils.get_length(left, right)):
        val1 = hlr_utils.get_value(left, i, l_descr, axis, axis_pos)
        err2_1 = hlr_utils.get_err2(left, i, l_descr, axis, axis_pos)

        val2 = hlr_utils.get_value(right, i, r_descr, axis, axis_pos)
        err2_2 = hlr_utils.get_err2(right, i, r_descr, axis, axis_pos)

        (descr_1, descr_2) = hlr_utils.get_descr(val1, val2)

        hlr_utils.math_compatible(val1, descr_1, val2, descr_2)

        value = array_manip.div_ncerr(val1, err2_1, val2, err2_2)

        map_so = hlr_utils.get_map_so(left, right, i)
        hlr_utils.result_insert(result, res_descr, value, map_so, axis,
                                axis_pos)

    if is_number:
        return tuple(result)
    else:
        return result
Beispiel #9
0
def div_ncerr(left, right, **kwargs):
    """
    This function divides two objects (C{SOM}, C{SO} or {tuple(val,val_err2)})
    and returns the result of the division in an C{SOM}, C{SO} or C{tuple}.

    @param left: Object on the left of the division sign
    @type left: C{SOM.SOM} or C{SOM.SO} or C{tuple}
    
    @param right: Object on the right of the division sign
    @type right: C{SOM.SOM} or C{SOM.SO} or C{tuple}
    
    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword axis: This is the axis one wishes to manipulate. If no argument
                   is given the default value is y
    @type axis: C{string}=<y or x>
    
    @keyword axis_pos: This is position of the axis in the axis array. If no
                       argument is given, the default value is 0
    @type axis_pos: C{int}
    
    @keyword length_one_som: This is a flag that lets the function know it is
                             dealing with a length 1 C{SOM} so that attributes
                             may be passed along. The length 1 C{SOM} will be
                             turned into a C{SO}. The default value is False.
    @type length_one_som: C{boolean}
    
    @keyword length_one_som_pos: This is the argument position of the length 1
                                 C{SOM} since division order is not
                                 commutative. The default value is 2.
    @type length_one_som_pos: C{int}=<1 or 2> 


    @return: Object containing the results of the division
    @rtype: C{SOM.SOM}, C{SOM.SO} or C{tuple}


    @raise IndexError: The two C{SOM}s do not contain the same number of
                       spectra
                       
    @raise RunTimeError: The x-axis units of the C{SOM}s do not match
    
    @raise RunTimeError: The y-axis units of the C{SOM}s do not match
    
    @raise RunTimeError: The x-axes of the two C{SO}s are not equal
    """

    # import the helper functions
    import hlr_utils

    # Check to see if we are working with a length 1 SOM
    try:
        length_one_som = kwargs["length_one_som"]
    except KeyError:
        length_one_som = False

    try:
        length_one_som_pos = kwargs["length_one_som_pos"]
        if length_one_som_pos != 1 or length_one_som_pos != 2:
            raise RuntimeError("length_one_som_pos must be either 1 or 2 and "\
                               +"%d" % length_one_som_pos)
    except KeyError:
        length_one_som_pos = 2

    if length_one_som:
        if length_one_som_pos == 1:
            som_copy = left
            left = left[0]
        else:
            som_copy = right
            right = right[0]
    else:
        # Not working with a length 1 SOM, do nothing
        pass

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(left, right)
    (l_descr, r_descr) = hlr_utils.get_descr(left, right)

    is_number = False
    
    # error check information
    if r_descr == "SOM" and l_descr == "SOM":
        hlr_utils.math_compatible(left, l_descr, right, r_descr)
    elif l_descr == "number" and r_descr == "number":
        is_number = True
    else:
        pass

    # Check for axis keyword argument
    try:
        axis = kwargs["axis"]
    except KeyError:
        axis = "y"
        
    # Check for axis_pos keyword argument
    try:
        axis_pos = kwargs["axis_pos"]
    except KeyError:
        axis_pos = 0

    if length_one_som:
        if length_one_som_pos == 1:
            result = hlr_utils.copy_som_attr(result, res_descr,
                                             som_copy, "SOM",
                                             right, r_descr)
        else:
            result = hlr_utils.copy_som_attr(result, res_descr, left, l_descr,
                                             som_copy, "SOM")            
    else:
        result = hlr_utils.copy_som_attr(result, res_descr, left, l_descr,
                                         right, r_descr)

    # iterate through the values
    import array_manip
    
    for i in xrange(hlr_utils.get_length(left, right)):
        val1 = hlr_utils.get_value(left, i, l_descr, axis, axis_pos)
        err2_1 = hlr_utils.get_err2(left, i, l_descr, axis, axis_pos)

        val2 = hlr_utils.get_value(right, i, r_descr, axis, axis_pos)
        err2_2 = hlr_utils.get_err2(right, i, r_descr, axis, axis_pos)

        (descr_1, descr_2)=hlr_utils.get_descr(val1, val2)

        hlr_utils.math_compatible(val1, descr_1, val2, descr_2)

        value = array_manip.div_ncerr(val1, err2_1, val2, err2_2)

        map_so = hlr_utils.get_map_so(left, right, i)
        hlr_utils.result_insert(result, res_descr, value, map_so, axis,
                                axis_pos)

    if is_number:
        return tuple(result)
    else:
        return result
def fix_bin_contents(obj, **kwargs):
    """
    This function takes a SOM or SO and goes through the individual spectra
    adjusting the bin contents by either multiplying or dividing by the
    bin widths or the bin centers taken from the individual spectra.

    @param obj: The data object to be scaled
    @type obj: C{SOM.SOM} or C{SOM.SO}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword scale: A flag that signals multiplication by the required bin
                    quantity. The default is I{False} (divide).
    @type scale: C{bool}

    @keyword width: A flag that signals that the adjusting quantity is the
                    bin width. The default is I{True}. If I{False}, the bin
                    center is used.
    @type width: C{bool}

    @keyword units: The expected units for this function. The default for this
                    function is I{microsecond}.
    @type units: C{string}


    @return: The object with the individual spectrum scaled
    @rtype: C{SOM.SOM} or C{SOM.SO}
    """
    import hlr_utils

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    # Setup keyword arguments
    try:
        scale = kwargs["scale"]
    except KeyError:
        scale = False

    try:
        width = kwargs["width"]
    except KeyError:
        width = True
    
    try:
        units = kwargs["units"]
    except KeyError:
        units = "microsecond"

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis_pos = hlr_utils.one_d_units(obj, units)
    else:
        axis_pos = 0

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)
        
    # iterate through the values
    import array_manip
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "y")
        err2 = hlr_utils.get_err2(obj, i, o_descr, "y")
        axis = hlr_utils.get_value(obj, i, o_descr, "x", axis_pos)
        axis_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis_pos)
        
        map_so = hlr_utils.get_map_so(obj, None, i)

        if width:
            (bin_const, bin_const_err2) = utils.calc_bin_widths(axis,
                                                                axis_err2)
        else:
            (bin_const, bin_const_err2) = utils.calc_bin_centers(axis,
                                                                 axis_err2)

        if scale:
            value = array_manip.mult_ncerr(val, err2, bin_const,
                                           bin_const_err2)
        else:
            value = array_manip.div_ncerr(val, err2, bin_const, bin_const_err2)

        hlr_utils.result_insert(result, res_descr, value, map_so, "y")

    return result
        # Replace counts data with fractional area. The axes remain the same
        comb_som[0].y = area_sum
        comb_som[0].var_y = area_sum_err2

        # Write out summed counts into file
        hlr_utils.write_file(configure.output, "text/Dave2d", comb_som,
                             output_ext="fra",
                             verbose=configure.verbose,
                             data_ext=configure.ext_replacement,         
                             path_replacement=configure.path_replacement,
                             message="fractional area")        

    else:
        # Divide summed fractional counts by the sum of the fractional areas
        (so_dim.y, so_dim.var_y) = array_manip.div_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         area_sum,
                                                         area_sum_err2)
        
        if configure.scale_sqe:
            (so_dim.y, so_dim.var_y) = array_manip.div_ncerr(so_dim.y,
                                                             so_dim.var_y,
                                                             bin_count,
                                                             bin_count_err2)

        comb_som.append(so_dim)

    del so_dim
        
    return comb_som

def __set_som_attributes(tsom, inst_name, **kwargs):
def create_E_vs_Q_dgs(som, E_i, Q_final, **kwargs):
    """
    This function starts with the rebinned energy transfer and turns this
    into a 2D spectra with E and Q axes for DGS instruments.

    @param som: The input object with initial IGS wavelength axis
    @type som: C{SOM.SOM}

    @param E_i: The initial energy for the given data.
    @type E_i: C{tuple}

    @param Q_final: The momentum transfer axis to rebin the data to
    @type Q_final: C{nessi_list.NessiList}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword corner_angles: The object that contains the corner geometry
                            information.
    @type corner_angles: C{dict}

    @keyword so_id: The identifier represents a number, string, tuple or other
                    object that describes the resulting C{SO}
    @type so_id: C{int}, C{string}, C{tuple}, C{pixel ID}
    
    @keyword y_label: The y axis label
    @type y_label: C{string}
    
    @keyword y_units: The y axis units
    @type y_units: C{string}
    
    @keyword x_labels: This is a list of names that sets the individual x axis
    labels
    @type x_labels: C{list} of C{string}s
    
    @keyword x_units: This is a list of names that sets the individual x axis
    units
    @type x_units: C{list} of C{string}s

    @keyword split: This flag causes the counts and the fractional area to
                    be written out into separate files.
    @type split: C{boolean}

    @keyword configure: This is the object containing the driver configuration.
    @type configure: C{Configure}


    @return: Object containing a 2D C{SO} with E and Q axes
    @rtype: C{SOM.SOM}    
    """
    import array_manip
    import axis_manip
    import common_lib
    import hlr_utils
    import nessi_list
    import SOM
    import utils

    # Check for keywords
    corner_angles = kwargs["corner_angles"]
    configure = kwargs.get("configure")
    split = kwargs.get("split", False)

    # Setup output object
    so_dim = SOM.SO(2)

    so_dim.axis[0].val = Q_final
    so_dim.axis[1].val = som[0].axis[0].val # E_t

    # Calculate total 2D array size
    N_tot = (len(so_dim.axis[0].val) - 1) * (len(so_dim.axis[1].val) - 1)

    # Create y and var_y lists from total 2D size
    so_dim.y = nessi_list.NessiList(N_tot)
    so_dim.var_y = nessi_list.NessiList(N_tot)

    # Create area sum and errors for the area sum lists from total 2D size
    area_sum = nessi_list.NessiList(N_tot)
    area_sum_err2 = nessi_list.NessiList(N_tot)

    # Convert initial energy to initial wavevector
    l_i = common_lib.energy_to_wavelength(E_i)
    k_i = common_lib.wavelength_to_scalar_k(l_i)

    # Since all the data is rebinned to the same energy transfer axis, we can
    # calculate the final energy axis once
    E_t = som[0].axis[0].val
    if som[0].axis[0].var is not None:
        E_t_err2 = som[0].axis[0].var
    else:
        E_t_err2 = nessi_list.NessiList(len(E_t))        

    # Get the bin width arrays from E_t
    (E_t_bw, E_t_bw_err2) = utils.calc_bin_widths(E_t)

    E_f = array_manip.sub_ncerr(E_i[0], E_i[1], E_t, E_t_err2)
    
    # Now we can get the final wavevector
    l_f = axis_manip.energy_to_wavelength(E_f[0], E_f[1])
    k_f = axis_manip.wavelength_to_scalar_k(l_f[0], l_f[1])

    # Output position for Q
    X = 0

    # Iterate though the data
    len_som = hlr_utils.get_length(som)
    for i in xrange(len_som):
        map_so = hlr_utils.get_map_so(som, None, i)

        yval = hlr_utils.get_value(som, i, "SOM", "y")
        yerr2 = hlr_utils.get_err2(som, i, "SOM", "y")

        cangles = corner_angles[str(map_so.id)]

        avg_theta1 = (cangles.getPolar(0) + cangles.getPolar(1)) / 2.0
        avg_theta2 = (cangles.getPolar(2) + cangles.getPolar(3)) / 2.0
        
        Q1 = axis_manip.init_scatt_wavevector_to_scalar_Q(k_i[0],
                                                          k_i[1],
                                                          k_f[0][:-1],
                                                          k_f[1][:-1],
                                                          avg_theta2,
                                                          0.0)
        
        Q2 = axis_manip.init_scatt_wavevector_to_scalar_Q(k_i[0],
                                                          k_i[1],
                                                          k_f[0][:-1],
                                                          k_f[1][:-1],
                                                          avg_theta1,
                                                          0.0)
        
        Q3 = axis_manip.init_scatt_wavevector_to_scalar_Q(k_i[0],
                                                          k_i[1],
                                                          k_f[0][1:],
                                                          k_f[1][1:],
                                                          avg_theta1,
                                                          0.0)

        Q4 = axis_manip.init_scatt_wavevector_to_scalar_Q(k_i[0],
                                                          k_i[1],
                                                          k_f[0][1:],
                                                          k_f[1][1:],
                                                          avg_theta2,
                                                          0.0)

        # Calculate the area of the E,Q polygons
        (A, A_err2) = utils.calc_eq_jacobian_dgs(E_t[:-1], E_t[:-1], 
                                                 E_t[1:], E_t[1:],
                                                 Q1[X], Q2[X], Q3[X], Q4[X])

        # Apply the Jacobian: C/dE_t * dE_t / A(EQ) = C/A(EQ)
        (jac_ratio, jac_ratio_err2) = array_manip.div_ncerr(E_t_bw,
                                                            E_t_bw_err2,
                                                            A, A_err2)
        (counts, counts_err2) = array_manip.mult_ncerr(yval, yerr2,
                                                       jac_ratio,
                                                       jac_ratio_err2)
        
        try:
            (y_2d, y_2d_err2,
             area_new,
             bin_count) = axis_manip.rebin_2D_quad_to_rectlin(Q1[X], E_t[:-1],
                                                           Q2[X], E_t[:-1],
                                                           Q3[X], E_t[1:],
                                                           Q4[X], E_t[1:],
                                                           counts,
                                                           counts_err2,
                                                           so_dim.axis[0].val,
                                                           so_dim.axis[1].val)
            
            del bin_count
            
        except IndexError, e:
            # Get the offending index from the error message
            index = int(str(e).split()[1].split('index')[-1].strip('[]'))
            print "Id:", map_so.id
            print "Index:", index
            print "Verticies: %f, %f, %f, %f, %f, %f, %f, %f" % (Q1[X][index],
                                                              E_t[:-1][index],
                                                                 Q2[X][index],
                                                              E_t[:-1][index],
                                                                 Q3[X][index],
                                                              E_t[1:][index],
                                                                 Q4[X][index],
                                                              E_t[1:][index])
            raise IndexError(str(e))

        # Add in together with previous results
        (so_dim.y, so_dim.var_y) = array_manip.add_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         y_2d, y_2d_err2)
        
        (area_sum, area_sum_err2) = array_manip.add_ncerr(area_sum,
                                                          area_sum_err2,
                                                          area_new,
                                                          area_sum_err2)
Beispiel #13
0
def rebin_axis_1D_frac(obj, axis_out):
    """
    This function rebins the primary axis for a C{SOM} or a C{SO} based on the
    given C{NessiList} axis.

    @param obj: Object to be rebinned
    @type obj: C{SOM.SOM} or C{SOM.SO}
    
    @param axis_out: The axis to rebin the C{SOM} or C{SO} to
    @type axis_out: C{NessiList}


    @return: Object that has been rebinned according to the provided axis
    @rtype: C{SOM.SOM} or C{SOM.SO}


    @raise TypeError: The rebinning axis given is not a C{NessiList}
    
    @raise TypeError: The object being rebinned is not a C{SOM} or a C{SO}
    """
    # import the helper functions
    import hlr_utils

    # set up for working through data
    try:
        axis_out.__type__
    except AttributeError:
        raise TypeError("Rebinning axis must be a NessiList!")

    o_descr = hlr_utils.get_descr(obj)

    if o_descr == "number" or o_descr == "list":
        raise TypeError("Do not know how to handle given type: %s" % \
                        o_descr)
    else:
        pass

    (result, res_descr) = hlr_utils.empty_result(obj)

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    # iterate through the values
    import array_manip
    import axis_manip

    for i in xrange(hlr_utils.get_length(obj)):
        axis_in = hlr_utils.get_value(obj, i, o_descr, "x", 0)
        val = hlr_utils.get_value(obj, i, o_descr)
        err2 = hlr_utils.get_err2(obj, i, o_descr)

        value = axis_manip.rebin_axis_1D_frac(axis_in, val, err2, axis_out)

        frac_err = nessi_list.NessiList(len(value[2]))

        value1 = array_manip.div_ncerr(value[0], value[1], value[2], frac_err)

        xvals = []
        xvals.append(axis_out)

        map_so = hlr_utils.get_map_so(obj, None, i)

        hlr_utils.result_insert(result, res_descr, value1, map_so, "all", 0,
                                xvals)

    return result
def __calc_x1(*args):
    """
    This function calculates the x1 coeffiecient to the S(Q,E) Jacobian

    @param args: A list of parameters used to calculate the x1 coefficient

    The following is a list of the arguments needed in there expected order
      1. Initial Wavevector
      2. Momentum Transfer
      3. Length Ratio (L_f / L_i)
      4. Final Wavevector
      5. Wavevector Final x Cos(polar)
      6. Wavevector Final x Sin(polar)
      7. Lambda Constant (2*pi/l_f^2)(dlf/dh)
      8. Derivative dazi_dh
      9. Derivative dpol_dh
      10. Derivative dpol_dtd
      11. Derivative dazi_dtd
      12. Cos(polar)
      13. Time-zero Slope Correction
      14. Vector of Zeros
    @type args: C{list}

    
    @return: The calculated x1 coefficient
    @rtype: (C{nessi_list.NessiList}, C{nessi_list.NessiList})
    """
    # Settle out the arguments to sensible names
    k_i = args[0]
    Q = args[1]
    len_ratio = args[2]
    k_f = args[3]
    k_f_cos_pol = args[4]
    k_f_sin_pol = args[5]
    lam_const = args[6]
    dpol_dh = args[7]
    dpol_dtd = args[8]
    dtd_over_dh = args[9]
    cos_pol = args[10]
    t_0_s_corr = args[11]
    z_vec = args[12]

    # (L_f / L_i) * (1 / k_f)^2 * (1 / 1 + ((h / m) * (t0_s / L_i)))
    const1 = (len_ratio * t_0_s_corr) / (k_f * k_f)
    # (dpol/dh + (dpol/dtd * dtd/dh)) * k_f * sin(pol)
    const2 = (dpol_dh + (dpol_dtd * dtd_over_dh)) * k_f_sin_pol

    # k_i^2
    temp1 = array_manip.mult_ncerr(k_i, z_vec, k_i, z_vec)
    # (L_f / L_i) * (k_i / k_f)^2 * (1 / 1 + ((h / m) * (t0_s / L_i)))
    temp2 = array_manip.mult_ncerr(temp1[0], temp1[1], const1, 0.0)
    # k_i - k_f * cos(pol)
    temp3 = array_manip.sub_ncerr(k_i, z_vec, k_f_cos_pol, 0.0)
    # (k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2
    temp4 = array_manip.mult_ncerr(temp2[0], temp2[1], temp3[0], temp3[1])

    # k_i * cos(pol)
    temp5 = array_manip.mult_ncerr(k_i, z_vec, cos_pol, 0.0)
    # k_f - k_i * cos(pol)
    temp6 = array_manip.sub_ncerr(k_f, 0.0, temp5[0], temp5[1])

    # (k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))
    temp7 = array_manip.sub_ncerr(temp4[0], temp4[1], temp6[0], temp6[1])
    # ((k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))) * (2 * pi / l_f^2) * dlf/dh
    temp8 = array_manip.mult_ncerr(temp7[0], temp7[1], lam_const, 0.0)

    # k_i * k_f * sin(pol) * (dpol/dh + (dpol/dtd * dtd/dh))
    temp9 = array_manip.mult_ncerr(k_i, z_vec, const2, 0.0)

    # ((k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))) * (2 * pi / l_f^2) * dlf/dh +
    # k_i * k_f * sin(pol) * (dpol/dh + (dpol/dtd * dtd/dh))
    temp10 = array_manip.add_ncerr(temp8[0], temp8[1], temp9[0], temp9[1])

    # (((k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))) * (2 * pi / l_f^2) * dlf/dh +
    # k_i * k_f * sin(pol) * (dpol/dh - (dpol/dtd dazi/dh / dazi/dtd))
    return array_manip.div_ncerr(temp10[0], temp10[1], Q, z_vec)
def energy_transfer(obj, itype, axis_const, **kwargs):
    """
    This function takes a SOM with a wavelength axis (initial for IGS and
    final for DGS) and calculates the energy transfer.  

    @param obj: The object containing the wavelength axis
    @type obj: C{SOM.SOM}

    @param itype: The instrument class type. The choices are either I{IGS} or
                  I{DGS}.
    @type itype: C{string}

    @param axis_const: The attribute name for the axis constant which is the 
                         final wavelength for I{IGS} and the initial energy for
                         I{DGS}.
    @type axis_const: C{string}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword units: The units for the incoming axis. The default is
                    I{Angstroms}.
    @type units: C{string}

    @keyword change_units: A flag that signals the function to convert from
                           I{meV} to I{ueV}. The default is I{False}.
    @type change_units: C{boolean}

    @keyword scale: A flag to scale the y-axis by lambda_f/lambda_i for I{IGS}
                    and lambda_i/lambda_f for I{DGS}. The default is I{False}.
    @type scale: C{boolean}

    @keyword lojac: A flag that turns on the calculation and application of
                    the linear-order Jacobian. The default is I{False}.
    @type lojac: C{boolean}

    @keyword sa_norm: A flag to turn on solid angle normlaization.
    @type sa_norm: C{boolean}

    @return: Object with the energy transfer calculated in units of I{meV} or
             I{ueV}. The default is I{meV}.
    @rtype: C{SOM.SOM}


    @raise RuntimeError: The instrument class type is not recognized
    @raise RuntimeError: The x-axis units are not Angstroms
    @raise RuntimeError: A SOM is not given to the function
    """
    # Check the instrument class type to make sure its allowed
    allowed_types = ["DGS", "IGS"]

    if itype not in allowed_types:
        raise RuntimeError("The instrument class type %s is not known. "\
                           +"Please use DGS or IGS" % itype)

    # import the helper functions
    import hlr_utils

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    if o_descr != "SOM":
        raise RuntimeError("Must provide a SOM to the function.")
    # Go on
    else:
        pass

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "Angstroms"

    try:
        change_units = kwargs["change_units"]
    except KeyError:
        change_units = False       

    try:
        scale = kwargs["scale"]
    except KeyError:
        scale = False

    try:
        sa_norm = kwargs["sa_norm"]
    except KeyError:
        sa_norm = False

    if sa_norm:
        inst = obj.attr_list.instrument

    try:
        lojac = kwargs["lojac"]
    except KeyError:
        lojac = False
    
    # Primary axis for transformation. 
    axis = hlr_utils.one_d_units(obj, units)

    # Get the subtraction constant
    try:
        axis_c = obj.attr_list[axis_const]
    except KeyError:
        raise RuntimeError("Must provide a final wavelength (IGS) or initial "\
                           +"energy (DGS) via the incoming SOM")
    
    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)
    if change_units:
        unit_str = "ueV"
    else:
        unit_str = "meV"
    result = hlr_utils.force_units(result, unit_str, axis)
    result.setAxisLabel(axis, "energy_transfer")
    result.setYUnits("Counts/" + unit_str)
    result.setYLabel("Intensity")

    # iterate through the values
    import array_manip
    import axis_manip
    import dr_lib
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        if itype == "IGS":
            l_i = hlr_utils.get_value(obj, i, o_descr, "x", axis)
            l_i_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)
        else:
            l_f = hlr_utils.get_value(obj, i, o_descr, "x", axis)
            l_f_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)
            
        y_val = hlr_utils.get_value(obj, i, o_descr, "y", axis)
        y_err2 = hlr_utils.get_err2(obj, i, o_descr, "y", axis)
        
        map_so = hlr_utils.get_map_so(obj, None, i)

        if itype == "IGS":
            (E_i, E_i_err2) = axis_manip.wavelength_to_energy(l_i, l_i_err2)
            l_f = hlr_utils.get_special(axis_c, map_so)[:2]
            (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f[0], l_f[1])
            if lojac:
                (y_val, y_err2) = utils.linear_order_jacobian(l_i, E_i, 
                                                              y_val, y_err2)  
        else:
            (E_i, E_i_err2) = axis_c.toValErrTuple()
            (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f, l_f_err2)
            if lojac:
                (y_val, y_err2) = utils.linear_order_jacobian(l_f, E_f, 
                                                              y_val, y_err2)

        if scale:
            # Scale counts by lambda_f / lambda_i
            if itype == "IGS":
                (l_n, l_n_err2) = l_f
                (l_d, l_d_err2) = utils.calc_bin_centers(l_i, l_i_err2)
            else:
                (l_n, l_n_err2) = utils.calc_bin_centers(l_f, l_f_err2)
                (l_d, l_d_err2) = axis_manip.energy_to_wavelength(E_i,
                                                                  E_i_err2)
                
            ratio = array_manip.div_ncerr(l_n, l_n_err2, l_d, l_d_err2)
            scale_y = array_manip.mult_ncerr(y_val, y_err2, ratio[0], ratio[1])
        else:
            scale_y = (y_val, y_err2)

        value = array_manip.sub_ncerr(E_i, E_i_err2, E_f, E_f_err2)

        if change_units:
            # Convert from meV to ueV
            value2 = array_manip.mult_ncerr(value[0], value[1], 1000.0, 0.0)
            scale_y = array_manip.mult_ncerr(scale_y[0], scale_y[1],
                                             1.0/1000.0, 0.0)
        else:
            value2 = value

        if sa_norm:
            if inst.get_name() == "BSS":
                dOmega = dr_lib.calc_BSS_solid_angle(map_so, inst)
                scale_y = array_manip.div_ncerr(scale_y[0], scale_y[1],
                                                dOmega, 0.0)
            else:
                raise RuntimeError("Do not know how to get solid angle from "\
                                   +"%s" % inst.get_name())
            
        if itype == "IGS":
            # Reverse the values due to the conversion
            value_y = axis_manip.reverse_array_cp(scale_y[0])
            value_var_y = axis_manip.reverse_array_cp(scale_y[1])
            value_x = axis_manip.reverse_array_cp(value2[0])
        else:
            value_y = scale_y[0]
            value_var_y = scale_y[1]
            value_x = value2[0]

        hlr_utils.result_insert(result, res_descr, (value_y, value_var_y),
                                map_so, "all", 0, [value_x])

    return result
def igs_energy_transfer(obj, **kwargs):
    """
    @depricated: This function will eventually disappear when the full S(Q,E)
                 transformation for IGS detectors is completed and verified.
                 
    This function takes a SOM or a SO and calculates the energy transfer for
    the IGS class of instruments. It is different from
    common_lib.energy_transfer in that the final wavelength is provided in a
    SOM.Information, SOM.CompositeInformation or a tuple, then converted to
    energy in place before being given to the common_lib.energy_transfer
    function.

    Parameters:
    ----------
    -> obj
    -> kwargs is a list of key word arguments that the function accepts:
          units= a string containing the expected units for this function.
                 The default for this function is meV
          lambda_f= a SOM.Information, SOM.CompositeInformation or a tuple
                    containing the final wavelength information
          offset= a SOM.Information or SOM.CompositeInformation containing
                  the final energy offsets
          scale=<boolean> is a flag that determines if the energy transfer
                          results are scaled by the ratio of lambda_f/lambda_i.
                          The default is False

    Returns:
    -------
    <- A SOM or SO with the energy transfer calculated in units of THz

    Exceptions:
    ----------
    <- RuntimeError is raised if the x-axis units are not meV
    <- RuntimeError is raised if a SOM or SO is not given to the function
    <- RuntimeError is raised if the final wavelength is not provided to the
       function
    """

    # import the helper functions
    import hlr_utils

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    if o_descr == "number" or o_descr == "list":
        raise RuntimeError, "Must provide a SOM of a SO to the function."
    # Go on
    else:
        pass

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "meV"

    try:
        lambda_f = kwargs["lambda_f"]
    except KeyError:
        lambda_f = None

    try:
        offset = kwargs["offset"]
    except KeyError:
        offset = None

    try:
        scale = kwargs["scale"]
    except KeyError:
        scale = False

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis = hlr_utils.one_d_units(obj, units)
    else:
        axis = 0

    if lambda_f is None:
        if o_descr == "SOM":
            try:
                lambda_f = obj.attr_list["Wavelength_final"]
            except KeyError:
                raise RuntimeError("Must provide a final wavelength via the "\
                                   +"incoming SOM or the lambda_f keyword")
        else:
            raise RuntimeError("Must provide a final wavelength via the "\
                                   +"lambda_f keyword")
    else:
        pass

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)
    if res_descr == "SOM":
        result = hlr_utils.force_units(result, "ueV", axis)
        result.setAxisLabel(axis, "energy_transfer")
        result.setYUnits("Counts/ueV")
        result.setYLabel("Intensity")
    else:
        pass

    # iterate through the values
    import array_manip
    import axis_manip
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "x", axis)
        err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)

        y_val = hlr_utils.get_value(obj, i, o_descr, "y", axis)
        y_err2 = hlr_utils.get_err2(obj, i, o_descr, "y", axis)

        map_so = hlr_utils.get_map_so(obj, None, i)

        l_f = hlr_utils.get_special(lambda_f, map_so)

        (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f[0], l_f[1])

        if offset is not None:
            info = hlr_utils.get_special(offset, map_so)
            try:
                E_f_new = array_manip.add_ncerr(E_f, E_f_err2, info[0],
                                                info[1])
            except TypeError:
                # Have to do this since add_ncerr does not support
                # scalar-scalar operations
                value1 = E_f + info[0]
                value2 = E_f_err2 + info[1]
                E_f_new = (value1, value2)
        else:
            E_f_new = (E_f, E_f_err2)

        # Scale counts by lambda_f / lambda_i
        if scale:
            l_i = axis_manip.energy_to_wavelength(val, err2)

            l_i_bc = utils.calc_bin_centers(l_i[0], l_i[1])

            ratio = array_manip.div_ncerr(l_f[0], l_f[1], l_i_bc[0], l_i_bc[1])

            scale_y = array_manip.mult_ncerr(y_val, y_err2, ratio[0], ratio[1])
        else:
            scale_y = (y_val, y_err2)

        value = array_manip.sub_ncerr(val, err2, E_f_new[0], E_f_new[1])

        # Convert from meV to ueV
        value2 = array_manip.mult_ncerr(value[0], value[1], 1000.0, 0.0)
        value3 = array_manip.mult_ncerr(scale_y[0], scale_y[1], 1.0 / 1000.0,
                                        0.0)

        hlr_utils.result_insert(result, res_descr, value3, map_so, "all", 0,
                                [value2[0]])

    return result
def create_E_vs_Q_dgs(som, E_i, Q_final, **kwargs):
    """
    This function starts with the rebinned energy transfer and turns this
    into a 2D spectra with E and Q axes for DGS instruments.

    @param som: The input object with initial IGS wavelength axis
    @type som: C{SOM.SOM}

    @param E_i: The initial energy for the given data.
    @type E_i: C{tuple}

    @param Q_final: The momentum transfer axis to rebin the data to
    @type Q_final: C{nessi_list.NessiList}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword corner_angles: The object that contains the corner geometry
                            information.
    @type corner_angles: C{dict}

    @keyword so_id: The identifier represents a number, string, tuple or other
                    object that describes the resulting C{SO}
    @type so_id: C{int}, C{string}, C{tuple}, C{pixel ID}
    
    @keyword y_label: The y axis label
    @type y_label: C{string}
    
    @keyword y_units: The y axis units
    @type y_units: C{string}
    
    @keyword x_labels: This is a list of names that sets the individual x axis
    labels
    @type x_labels: C{list} of C{string}s
    
    @keyword x_units: This is a list of names that sets the individual x axis
    units
    @type x_units: C{list} of C{string}s

    @keyword split: This flag causes the counts and the fractional area to
                    be written out into separate files.
    @type split: C{boolean}

    @keyword configure: This is the object containing the driver configuration.
    @type configure: C{Configure}


    @return: Object containing a 2D C{SO} with E and Q axes
    @rtype: C{SOM.SOM}    
    """
    import array_manip
    import axis_manip
    import common_lib
    import hlr_utils
    import nessi_list
    import SOM
    import utils

    # Check for keywords
    corner_angles = kwargs["corner_angles"]
    configure = kwargs.get("configure")
    split = kwargs.get("split", False)

    # Setup output object
    so_dim = SOM.SO(2)

    so_dim.axis[0].val = Q_final
    so_dim.axis[1].val = som[0].axis[0].val  # E_t

    # Calculate total 2D array size
    N_tot = (len(so_dim.axis[0].val) - 1) * (len(so_dim.axis[1].val) - 1)

    # Create y and var_y lists from total 2D size
    so_dim.y = nessi_list.NessiList(N_tot)
    so_dim.var_y = nessi_list.NessiList(N_tot)

    # Create area sum and errors for the area sum lists from total 2D size
    area_sum = nessi_list.NessiList(N_tot)
    area_sum_err2 = nessi_list.NessiList(N_tot)

    # Convert initial energy to initial wavevector
    l_i = common_lib.energy_to_wavelength(E_i)
    k_i = common_lib.wavelength_to_scalar_k(l_i)

    # Since all the data is rebinned to the same energy transfer axis, we can
    # calculate the final energy axis once
    E_t = som[0].axis[0].val
    if som[0].axis[0].var is not None:
        E_t_err2 = som[0].axis[0].var
    else:
        E_t_err2 = nessi_list.NessiList(len(E_t))

    # Get the bin width arrays from E_t
    (E_t_bw, E_t_bw_err2) = utils.calc_bin_widths(E_t)

    E_f = array_manip.sub_ncerr(E_i[0], E_i[1], E_t, E_t_err2)

    # Now we can get the final wavevector
    l_f = axis_manip.energy_to_wavelength(E_f[0], E_f[1])
    k_f = axis_manip.wavelength_to_scalar_k(l_f[0], l_f[1])

    # Output position for Q
    X = 0

    # Iterate though the data
    len_som = hlr_utils.get_length(som)
    for i in xrange(len_som):
        map_so = hlr_utils.get_map_so(som, None, i)

        yval = hlr_utils.get_value(som, i, "SOM", "y")
        yerr2 = hlr_utils.get_err2(som, i, "SOM", "y")

        cangles = corner_angles[str(map_so.id)]

        avg_theta1 = (cangles.getPolar(0) + cangles.getPolar(1)) / 2.0
        avg_theta2 = (cangles.getPolar(2) + cangles.getPolar(3)) / 2.0

        Q1 = axis_manip.init_scatt_wavevector_to_scalar_Q(
            k_i[0], k_i[1], k_f[0][:-1], k_f[1][:-1], avg_theta2, 0.0)

        Q2 = axis_manip.init_scatt_wavevector_to_scalar_Q(
            k_i[0], k_i[1], k_f[0][:-1], k_f[1][:-1], avg_theta1, 0.0)

        Q3 = axis_manip.init_scatt_wavevector_to_scalar_Q(
            k_i[0], k_i[1], k_f[0][1:], k_f[1][1:], avg_theta1, 0.0)

        Q4 = axis_manip.init_scatt_wavevector_to_scalar_Q(
            k_i[0], k_i[1], k_f[0][1:], k_f[1][1:], avg_theta2, 0.0)

        # Calculate the area of the E,Q polygons
        (A, A_err2) = utils.calc_eq_jacobian_dgs(E_t[:-1], E_t[:-1], E_t[1:],
                                                 E_t[1:], Q1[X], Q2[X], Q3[X],
                                                 Q4[X])

        # Apply the Jacobian: C/dE_t * dE_t / A(EQ) = C/A(EQ)
        (jac_ratio,
         jac_ratio_err2) = array_manip.div_ncerr(E_t_bw, E_t_bw_err2, A,
                                                 A_err2)
        (counts, counts_err2) = array_manip.mult_ncerr(yval, yerr2, jac_ratio,
                                                       jac_ratio_err2)

        try:
            (y_2d, y_2d_err2, area_new,
             bin_count) = axis_manip.rebin_2D_quad_to_rectlin(
                 Q1[X], E_t[:-1], Q2[X], E_t[:-1], Q3[X], E_t[1:], Q4[X],
                 E_t[1:], counts, counts_err2, so_dim.axis[0].val,
                 so_dim.axis[1].val)

            del bin_count

        except IndexError, e:
            # Get the offending index from the error message
            index = int(str(e).split()[1].split('index')[-1].strip('[]'))
            print "Id:", map_so.id
            print "Index:", index
            print "Verticies: %f, %f, %f, %f, %f, %f, %f, %f" % (
                Q1[X][index], E_t[:-1][index], Q2[X][index], E_t[:-1][index],
                Q3[X][index], E_t[1:][index], Q4[X][index], E_t[1:][index])
            raise IndexError(str(e))

        # Add in together with previous results
        (so_dim.y,
         so_dim.var_y) = array_manip.add_ncerr(so_dim.y, so_dim.var_y, y_2d,
                                               y_2d_err2)

        (area_sum,
         area_sum_err2) = array_manip.add_ncerr(area_sum, area_sum_err2,
                                                area_new, area_sum_err2)
        comb_som[0].var_y = area_sum_err2

        # Write out summed counts into file
        hlr_utils.write_file(configure.output,
                             "text/Dave2d",
                             comb_som,
                             output_ext="fra",
                             verbose=configure.verbose,
                             data_ext=configure.ext_replacement,
                             path_replacement=configure.path_replacement,
                             message="fractional area")

    else:
        # Divide summed fractional counts by the sum of the fractional areas
        (so_dim.y,
         so_dim.var_y) = array_manip.div_ncerr(so_dim.y, so_dim.var_y,
                                               area_sum, area_sum_err2)

        comb_som.append(so_dim)

    del so_dim

    return comb_som


def __set_som_attributes(tsom, **kwargs):
    """
    This is a helper function that sets attributes for the final S(Q,E)
    C{SOM.SOM}.

    @param tsom: The input object for attribute setting
    @type tsom: C{SOM.SOM}
def dimensionless_mon(obj, min_ext, max_ext, **kwargs):
    """
    This function takes monitor spectra and converts them to dimensionless
    spectra by dividing each spectrum by the total number of counts within the
    range [min_ext, max_ext]. Then, each spectrum is multiplied by the quantity
    max_ext - min_ext. The units of min_ext and max_ext are assumed to be the
    same as the monitor spectra axis.

    @param obj: Object containing monitor spectra
    @type obj: C{SOM.SOM} or C{SOM.SO}

    @param min_ext: Minimium range and associated error^2 for integrating total
                    counts.
    @type min_ext: C{tuple}

    @param max_ext: Maximium range and associated error^2 for integrating total
                    counts.
    @type max_ext: C{tuple}

    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword units: The expected units for this function. The default for this
                    function is I{Angstroms}.
    @type units: C{string}


    @return: Dimensionless monitor spectra
    @rtype: C{SOM.SOM} or C{SOM.SO}
    """

    # import the helper functions
    import hlr_utils

    if obj is None:
        return obj

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "Angstroms"

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis = hlr_utils.one_d_units(obj, units)
    else:
        axis = 0

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    import array_manip
    import dr_lib
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "y")
        err2 = hlr_utils.get_err2(obj, i, o_descr, "y")
        x_axis = hlr_utils.get_value(obj, i, o_descr, "x", axis)
        x_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)
        map_so = hlr_utils.get_map_so(obj, None, i)

        bin_widths = utils.calc_bin_widths(x_axis, x_err2)

        # Scale bin contents by bin width
        value0 = array_manip.mult_ncerr(val, err2, bin_widths[0],
                                        bin_widths[1])

        # Find bin range for extents
        min_index = utils.bisect_helper(x_axis, min_ext[0])
        max_index = utils.bisect_helper(x_axis, max_ext[0])

        # Integrate axis using bin width multiplication
        (asum, asum_err2) = dr_lib.integrate_axis_py(map_so,
                                                     start=min_index,
                                                     end=max_index,
                                                     width=True)

        # Get the number of bins in the integration range
        num_bins = max_index - min_index + 1

        asum /= num_bins
        asum_err2 /= (num_bins * num_bins)

        # Divide by sum
        value1 = array_manip.div_ncerr(value0[0], value0[1], asum, asum_err2)

        hlr_utils.result_insert(result, res_descr, value1, map_so, "y")

    return result
Beispiel #20
0
def run(config, tim):
    """
    This method is where the data reduction process gets done.

    @param config: Object containing the data reduction configuration
                   information.
    @type config: L{hlr_utils.Configure}

    @param tim: Object that will allow the method to perform timing
                evaluations.
    @type tim: C{sns_time.DiffTime}
    """
    import array_manip
    import common_lib
    import dr_lib
    import DST
    import SOM

    import math

    if tim is not None:
        tim.getTime(False)
        old_time = tim.getOldTime()

    if config.data is None:
        raise RuntimeError("Need to pass a data filename to the driver "\
                           +"script.")

    # Read in sample data geometry if one is provided
    if config.data_inst_geom is not None:
        if config.verbose:
            print "Reading in sample data instrument geometry file"

        data_inst_geom_dst = DST.getInstance("application/x-NxsGeom",
                                             config.data_inst_geom)
    else:
        data_inst_geom_dst = None

    # Read in normalization data geometry if one is provided
    if config.norm_inst_geom is not None:
        if config.verbose:
            print "Reading in normalization instrument geometry file"
            
        norm_inst_geom_dst = DST.getInstance("application/x-NxsGeom",
                                             config.norm_inst_geom)
    else:
        norm_inst_geom_dst = None        
    
    # Perform Steps 1-2 on sample data
    d_som1 = dr_lib.process_reflp_data(config.data, config, None,
                                       config.dbkg_roi_file,
                                       config.no_bkg,
                                       inst_geom_dst=data_inst_geom_dst,
                                       timer=tim)

    # Get the detector angle
    if config.omega is None:
        # Make a fake SO
        so = SOM.SO()
        try: 
            theta = hlr_utils.get_special(d_som1.attr_list["Theta"], so)
        except KeyError: 
            theta = (float('nan'), float('nan'))
    else:
        theta = config.omega.toFullTuple()
        
    if theta[0] is not None: 
        if theta[2] == "degrees" or theta[2] == "degree": 
            theta_rads = (theta[0] * (math.pi / 180.0), 0.0)
        else: 
            theta_rads = (theta[0], 0.0)
    else: 
        theta_rads = (float('nan'), float('nan'))

    d_som1.attr_list["data-theta"] = (theta_rads[0], theta_rads[1], "radians")

    # Perform Steps 1-3 on normalization data
    if config.norm is not None:
        n_som1 = dr_lib.process_reflp_data(config.norm, config,
                                           config.norm_roi_file,
                                           config.nbkg_roi_file,
                                           config.no_norm_bkg,
                                           inst_geom_dst=norm_inst_geom_dst,
                                           timer=tim)
    else:
        n_som1 = None

    # Closing sample data instrument geometry file
    if data_inst_geom_dst is not None:
        data_inst_geom_dst.release_resource()

    # Closing normalization data instrument geometry file
    if norm_inst_geom_dst is not None:
        norm_inst_geom_dst.release_resource()        

    # Step 4: Divide data by normalization
    if config.verbose and config.norm is not None:
        print "Scale data by normalization"

    if tim is not None:
        tim.getTime(False)

    if config.norm is not None:
        # Need to rebin the normalization spectra to the data pixel spectra
        n_som2 = dr_lib.rebin_monitor(n_som1, d_som1, rtype="frac")
        # Now divide the spectra
        d_som2 = common_lib.div_ncerr(d_som1, n_som2)
        del n_som2
    else:
        d_som2 = d_som1

    if tim is not None and config.norm is not None:
        tim.getTime(msg="After normalizing signal spectra")

    del d_som1, n_som1

    sin_theta_rads = (math.sin(theta_rads[0]), math.sin(theta_rads[1]))
    if sin_theta_rads[0] < 0.0:
        sin_theta_rads = (math.fabs(sin_theta_rads[0]),
                          math.fabs(sin_theta_rads[1]))

    # Step 6: Scale wavelength axis by sin(theta) to make lambda_T
    if config.verbose:
        print "Scaling wavelength axis by sin(theta)"
    
    if tim is not None:
        tim.getTime(False)
        
    d_som3 = common_lib.div_ncerr(d_som2, sin_theta_rads, axis="x")

    if tim is not None:
        tim.getTime(msg="After scaling wavelength axis ")

    del d_som2

    d_som3.setAxisLabel(0, "lambda_T")

    # Step 7: Rebin to lambda_T axis
    if config.verbose:
        print "Rebinning spectra"

    if config.lambdap_bins is None:
        # Create a binning scheme
        pathlength = d_som3.attr_list.instrument.get_total_path(
            det_secondary=True)

        delta_lambda = common_lib.tof_to_wavelength((config.delta_TOF, 0.0),
                                                    pathlength=pathlength)
 
        delta_lambdap = array_manip.div_ncerr(delta_lambda[0], delta_lambda[1],
                                              sin_theta_rads[0], 0.0)

        config.lambdap_bins = dr_lib.create_axis_from_data(d_som3,
                                                       width=delta_lambdap[0])
    else:
        # Do nothing, got the binning scheme
        pass

    if tim is not None:
        tim.getTime(False)

    d_som4 = common_lib.rebin_axis_1D_frac(d_som3,
                                           config.lambdap_bins.toNessiList())

    if tim is not None:
        tim.getTime(msg="After rebinning spectra ")

    del d_som3

    if config.inst == "REF_M":
        # Clean up spectrum
        if config.tof_cut_min is not None:
            tof_cut_min = float(config.tof_cut_min)
        else:
            tof_cut_min = config.TOF_min

        if config.tof_cut_max is not None:
            tof_cut_max = float(config.tof_cut_max)
        else:
            tof_cut_max = config.TOF_max

        pathlength = d_som4.attr_list.instrument.get_total_path(
            det_secondary=True)

        lambda_min = common_lib.tof_to_wavelength((tof_cut_min, 0.0),
                                                  pathlength=pathlength)

        lambda_T_min = common_lib.div_ncerr(lambda_min, sin_theta_rads)
        
        lambda_max = common_lib.tof_to_wavelength((tof_cut_max, 0.0),
                                                  pathlength=pathlength)

        lambda_T_max = common_lib.div_ncerr(lambda_max, sin_theta_rads)

        nz_list = []
        for i in xrange(hlr_utils.get_length(d_som4)):
            nz_list.append((lambda_T_min[0], lambda_T_max[0]))
        
        d_som4A = dr_lib.zero_spectra(d_som4, nz_list)
    else:
        d_som4A = d_som4

    del d_som4

    # Step 8: Write out all spectra to a file
    hlr_utils.write_file(config.output, "text/Spec", d_som4A,
                         replace_ext=False,
                         replace_path=False,
                         verbose=config.verbose,
                         message="Reflectivity information")

    if config.dump_twod:
        d_som5 = dr_lib.create_X_vs_pixpos(d_som4A,
                                           config.lambdap_bins.toNessiList(),
                                           rebin=False,
                                           y_label="R",
                                           y_units="",
                                           x_label="$\lambda_T$",
                                           x_units="$\AA$")

        hlr_utils.write_file(config.output, "text/Dave2d", d_som5,
                             output_ext="plp", verbose=config.verbose,
                             data_ext=config.ext_replacement,
                             path_replacement=config.path_replacement,
                             message="2D Reflectivity information")

    d_som4A.attr_list["config"] = config

    hlr_utils.write_file(config.output, "text/rmd", d_som4A,
                         output_ext="rmd", verbose=config.verbose,
                         data_ext=config.ext_replacement,
                         path_replacement=config.path_replacement,
                         message="metadata")

    if tim is not None:
        tim.setOldTime(old_time)
        tim.getTime(msg="Total Running Time")    
Beispiel #21
0
def run(config, tim):
    """
    This method is where the data reduction process gets done.

    @param config: Object containing the data reduction configuration
                   information.
    @type config: L{hlr_utils.Configure}

    @param tim: Object that will allow the method to perform timing
                evaluations.
    @type tim: C{sns_time.DiffTime}
    """
    import array_manip
    import common_lib
    import dr_lib
    import DST
    import SOM

    import math

    if tim is not None:
        tim.getTime(False)
        old_time = tim.getOldTime()

    if config.data is None:
        raise RuntimeError("Need to pass a data filename to the driver "\
                           +"script.")

    # Read in sample data geometry if one is provided
    if config.data_inst_geom is not None:
        if config.verbose:
            print "Reading in sample data instrument geometry file"

        data_inst_geom_dst = DST.getInstance("application/x-NxsGeom",
                                             config.data_inst_geom)
    else:
        data_inst_geom_dst = None

    # Read in normalization data geometry if one is provided
    if config.norm_inst_geom is not None:
        if config.verbose:
            print "Reading in normalization instrument geometry file"

        norm_inst_geom_dst = DST.getInstance("application/x-NxsGeom",
                                             config.norm_inst_geom)
    else:
        norm_inst_geom_dst = None

    # Perform Steps 1-2 on sample data
    d_som1 = dr_lib.process_reflp_data(config.data,
                                       config,
                                       None,
                                       config.dbkg_roi_file,
                                       config.no_bkg,
                                       inst_geom_dst=data_inst_geom_dst,
                                       timer=tim)

    # Get the detector angle
    if config.omega is None:
        # Make a fake SO
        so = SOM.SO()
        try:
            theta = hlr_utils.get_special(d_som1.attr_list["Theta"], so)
        except KeyError:
            theta = (float('nan'), float('nan'))
    else:
        theta = config.omega.toFullTuple()

    if theta[0] is not None:
        if theta[2] == "degrees" or theta[2] == "degree":
            theta_rads = (theta[0] * (math.pi / 180.0), 0.0)
        else:
            theta_rads = (theta[0], 0.0)
    else:
        theta_rads = (float('nan'), float('nan'))

    d_som1.attr_list["data-theta"] = (theta_rads[0], theta_rads[1], "radians")

    # Perform Steps 1-3 on normalization data
    if config.norm is not None:
        n_som1 = dr_lib.process_reflp_data(config.norm,
                                           config,
                                           config.norm_roi_file,
                                           config.nbkg_roi_file,
                                           config.no_norm_bkg,
                                           inst_geom_dst=norm_inst_geom_dst,
                                           timer=tim)
    else:
        n_som1 = None

    # Closing sample data instrument geometry file
    if data_inst_geom_dst is not None:
        data_inst_geom_dst.release_resource()

    # Closing normalization data instrument geometry file
    if norm_inst_geom_dst is not None:
        norm_inst_geom_dst.release_resource()

    # Step 4: Divide data by normalization
    if config.verbose and config.norm is not None:
        print "Scale data by normalization"

    if tim is not None:
        tim.getTime(False)

    if config.norm is not None:
        # Need to rebin the normalization spectra to the data pixel spectra
        n_som2 = dr_lib.rebin_monitor(n_som1, d_som1, rtype="frac")
        # Now divide the spectra
        d_som2 = common_lib.div_ncerr(d_som1, n_som2)
        del n_som2
    else:
        d_som2 = d_som1

    if tim is not None and config.norm is not None:
        tim.getTime(msg="After normalizing signal spectra")

    del d_som1, n_som1

    sin_theta_rads = (math.sin(theta_rads[0]), math.sin(theta_rads[1]))
    if sin_theta_rads[0] < 0.0:
        sin_theta_rads = (math.fabs(sin_theta_rads[0]),
                          math.fabs(sin_theta_rads[1]))

    # Step 6: Scale wavelength axis by sin(theta) to make lambda_T
    if config.verbose:
        print "Scaling wavelength axis by sin(theta)"

    if tim is not None:
        tim.getTime(False)

    d_som3 = common_lib.div_ncerr(d_som2, sin_theta_rads, axis="x")

    if tim is not None:
        tim.getTime(msg="After scaling wavelength axis ")

    del d_som2

    d_som3.setAxisLabel(0, "lambda_T")

    # Step 7: Rebin to lambda_T axis
    if config.verbose:
        print "Rebinning spectra"

    if config.lambdap_bins is None:
        # Create a binning scheme
        pathlength = d_som3.attr_list.instrument.get_total_path(
            det_secondary=True)

        delta_lambda = common_lib.tof_to_wavelength((config.delta_TOF, 0.0),
                                                    pathlength=pathlength)

        delta_lambdap = array_manip.div_ncerr(delta_lambda[0], delta_lambda[1],
                                              sin_theta_rads[0], 0.0)

        config.lambdap_bins = dr_lib.create_axis_from_data(
            d_som3, width=delta_lambdap[0])
    else:
        # Do nothing, got the binning scheme
        pass

    if tim is not None:
        tim.getTime(False)

    d_som4 = common_lib.rebin_axis_1D_frac(d_som3,
                                           config.lambdap_bins.toNessiList())

    if tim is not None:
        tim.getTime(msg="After rebinning spectra ")

    del d_som3

    if config.inst == "REF_M":
        # Clean up spectrum
        if config.tof_cut_min is not None:
            tof_cut_min = float(config.tof_cut_min)
        else:
            tof_cut_min = config.TOF_min

        if config.tof_cut_max is not None:
            tof_cut_max = float(config.tof_cut_max)
        else:
            tof_cut_max = config.TOF_max

        pathlength = d_som4.attr_list.instrument.get_total_path(
            det_secondary=True)

        lambda_min = common_lib.tof_to_wavelength((tof_cut_min, 0.0),
                                                  pathlength=pathlength)

        lambda_T_min = common_lib.div_ncerr(lambda_min, sin_theta_rads)

        lambda_max = common_lib.tof_to_wavelength((tof_cut_max, 0.0),
                                                  pathlength=pathlength)

        lambda_T_max = common_lib.div_ncerr(lambda_max, sin_theta_rads)

        nz_list = []
        for i in xrange(hlr_utils.get_length(d_som4)):
            nz_list.append((lambda_T_min[0], lambda_T_max[0]))

        d_som4A = dr_lib.zero_spectra(d_som4, nz_list)
    else:
        d_som4A = d_som4

    del d_som4

    # Step 8: Write out all spectra to a file
    hlr_utils.write_file(config.output,
                         "text/Spec",
                         d_som4A,
                         replace_ext=False,
                         replace_path=False,
                         verbose=config.verbose,
                         message="Reflectivity information")

    if config.dump_twod:
        d_som5 = dr_lib.create_X_vs_pixpos(d_som4A,
                                           config.lambdap_bins.toNessiList(),
                                           rebin=False,
                                           y_label="R",
                                           y_units="",
                                           x_label="$\lambda_T$",
                                           x_units="$\AA$")

        hlr_utils.write_file(config.output,
                             "text/Dave2d",
                             d_som5,
                             output_ext="plp",
                             verbose=config.verbose,
                             data_ext=config.ext_replacement,
                             path_replacement=config.path_replacement,
                             message="2D Reflectivity information")

    d_som4A.attr_list["config"] = config

    hlr_utils.write_file(config.output,
                         "text/rmd",
                         d_som4A,
                         output_ext="rmd",
                         verbose=config.verbose,
                         data_ext=config.ext_replacement,
                         path_replacement=config.path_replacement,
                         message="metadata")

    if tim is not None:
        tim.setOldTime(old_time)
        tim.getTime(msg="Total Running Time")
def sum_by_rebin_frac(obj, axis_out, **kwargs):
    """
    This function uses the C{axis_manip.rebin_axis_1D_frac} function from the
    SCL to perform the rebinning. The function tracks the counts and fractional
    area from all spectra separately. The counts and fractional area are
    divided after all spectra have been parsed. 
    
    @param obj: Object to be rebinned and summed
    @type obj: C{SOM.SOM} or C{SOM.SO}
    
    @param axis_out: The axis to rebin the C{SOM} or C{SO} to
    @type axis_out: C{NessiList}

    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword configure: This is the object containing the driver configuration.
                        This will signal the function to write out the counts
                        and fractional area to files.
    @type configure: C{Configure}


    @return: Object that has been rebinned and summed according to the
             provided axis
    @rtype: C{SOM.SOM} or C{SOM.SO}


    @raise TypeError: The rebinning axis given is not a C{NessiList}
    @raise TypeError: The object being rebinned is not a C{SOM} or a C{SO}
    @raise TypeError: The dimension of the input object is not 1D
    """
    # import the helper functions
    import hlr_utils

    # set up for working through data
    try:
        axis_out.__type__
    except AttributeError:
        raise TypeError("Rebinning axis must be a NessiList!")
        
    o_descr = hlr_utils.get_descr(obj)

    if o_descr == "number" or o_descr == "list":
        raise TypeError("Do not know how to handle given type: %s" % \
                        o_descr)
    else:
        pass

    try:
        if obj.getDimension() != 1:
            raise TypeError("The input object must be 1D!. This one is "\
                            +"%dD." % obj.getDimension())
    except AttributeError:
        # obj is a SO
        if obj.dim() != 1:
            raise TypeError("The input object must be 1D!. This one is "\
                            +"%dD." % obj.dim())

    # Check for keywords
    try:
        config = kwargs["configure"]
    except KeyError:
        config = None

    (result, res_descr) = hlr_utils.empty_result(obj)

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    import array_manip
    import axis_manip

    len_data = len(axis_out) - 1

    counts = nessi_list.NessiList(len_data)
    counts_err2 = nessi_list.NessiList(len_data)
    frac_area = nessi_list.NessiList(len_data)
    frac_area_err2 = nessi_list.NessiList(len_data)

    for i in xrange(hlr_utils.get_length(obj)):
        axis_in = hlr_utils.get_value(obj, i, o_descr, "x", 0)
        val = hlr_utils.get_value(obj, i, o_descr)
        err2 = hlr_utils.get_err2(obj, i, o_descr)
        
        value = axis_manip.rebin_axis_1D_frac(axis_in, val, err2, axis_out)

        (counts, counts_err2) = array_manip.add_ncerr(counts, counts_err2,
                                                      value[0], value[1])
        
        (frac_area, frac_area_err2) = array_manip.add_ncerr(frac_area,
                                                            frac_area_err2,
                                                            value[2],
                                                            frac_area_err2)

    # Divide the total counts by the total fractional area
    value1 = array_manip.div_ncerr(counts, counts_err2, frac_area,
                                   frac_area_err2)
    xvals = []
    xvals.append(axis_out)
    
    map_so = hlr_utils.get_map_so(obj, None, 0)
        
    hlr_utils.result_insert(result, res_descr, value1, map_so, "all",
                            0, xvals)

    if config is not None:
        if o_descr == "SOM":
            import SOM
            o_som = SOM.SOM()
            o_som.copyAttributes(obj)

            so = hlr_utils.get_map_so(obj, None, 0)
            so.axis[0].val = axis_out
            so.y = counts
            so.var_y = counts_err2
            o_som.append(so)

            # Write out summed counts into file
            hlr_utils.write_file(config.output, "text/Spec", o_som,
                                 output_ext="cnt",
                                 verbose=config.verbose,
                                 data_ext=config.ext_replacement,         
                                 path_replacement=config.path_replacement,
                                 message="summed counts")
            
            # Replace counts data with fractional area. The axes remain the
            # same
            o_som[0].y = frac_area
            o_som[0].var_y = frac_area_err2
            
            # Write out summed fractional area into file
            hlr_utils.write_file(config.output, "text/Spec", o_som,
                                 output_ext="fra",
                                 verbose=config.verbose,
                                 data_ext=config.ext_replacement,         
                                 path_replacement=config.path_replacement,
                                 message="fractional area")

    return result
        # Replace counts data with fractional area. The axes remain the same
        comb_som[0].y = area_sum
        comb_som[0].var_y = area_sum_err2

        # Write out summed counts into file
        hlr_utils.write_file(configure.output, "text/Dave2d", comb_som,
                             output_ext="fra",
                             verbose=configure.verbose,
                             data_ext=configure.ext_replacement,         
                             path_replacement=configure.path_replacement,
                             message="fractional area")        

    else:
        # Divide summed fractional counts by the sum of the fractional areas
        (so_dim.y, so_dim.var_y) = array_manip.div_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         area_sum,
                                                         area_sum_err2)


        comb_som.append(so_dim)

    del so_dim
        
    return comb_som

def __set_som_attributes(tsom, **kwargs):
    """
    This is a helper function that sets attributes for the final S(Q,E)
    C{SOM.SOM}.

    @param tsom: The input object for attribute setting
def feff_correct_mon(obj, **kwargs):
    """
    This function takes in a monitor spectra, calculates efficiencies based on
    the montior's wavelength axis and divides the monitor counts by the
    calculated efficiencies. The function is a M{constant * wavelength}.

    @param obj: Object containing monitor spectra
    @type obj: C{SOM.SOM} or C{SOM.SO}
    
    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword units: The expected units for this function. The default for this
                    function is I{Angstroms}.
    @type units: C{string}

    @keyword eff_const: Use this provided effieciency constant. The default is
                        (0.00000085 / 1.8) Angstroms^-1.
    @type eff_const: L{hlr_utils.DrParameter}

    @keyword inst_name: The short name of an instrument.
    @type inst_name: C{string}
    
    
    @return: Efficiency corrected monitor spectra
    @rtype: C{SOM.SOM} or C{SOM.SO}
    """
    
    # import the helper functions
    import hlr_utils

    if obj is None:
        return obj

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "Angstroms"

    try:
        eff_const = kwargs["eff_const"]
    except KeyError:
        # This is for SNS (specifically BASIS) monitors
        eff_const = hlr_utils.DrParameter((0.00000085 / 1.8), 0.0,
                                          "Angstroms^-1") # A^-1

    inst_name = kwargs.get("inst_name")

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis = hlr_utils.one_d_units(obj, units)
    else:
        axis = 0

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    # iterate through the values
    import array_manip
    import nessi_list
    import dr_lib
    
    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "x", axis)
        map_so = hlr_utils.get_map_so(obj, None, i)

        if inst_name is None:
            eff = nessi_list.NessiList()
        
            for j in xrange(len(val)-1):
                bin_center = (val[j+1] + val[j]) / 2.0
                eff.append(eff_const.getValue() * bin_center)

            eff_err2 = nessi_list.NessiList(len(eff))
        else:
            if inst_name == "SANS":
                (eff, eff_err2) = dr_lib.subexp_eff(eff_const, val)
            else:
                raise RuntimeError("Do not know how to handle %s instrument" \
                                   % inst_name)
                
        y_val = hlr_utils.get_value(obj, i, o_descr, "y")
        y_err2 = hlr_utils.get_err2(obj, i, o_descr, "y")

        value = array_manip.div_ncerr(y_val, y_err2, eff, eff_err2)

        hlr_utils.result_insert(result, res_descr, value, map_so, "y")

    return result
def rebin_axis_1D_frac(obj, axis_out):
    """
    This function rebins the primary axis for a C{SOM} or a C{SO} based on the
    given C{NessiList} axis.

    @param obj: Object to be rebinned
    @type obj: C{SOM.SOM} or C{SOM.SO}
    
    @param axis_out: The axis to rebin the C{SOM} or C{SO} to
    @type axis_out: C{NessiList}


    @return: Object that has been rebinned according to the provided axis
    @rtype: C{SOM.SOM} or C{SOM.SO}


    @raise TypeError: The rebinning axis given is not a C{NessiList}
    
    @raise TypeError: The object being rebinned is not a C{SOM} or a C{SO}
    """
    # import the helper functions
    import hlr_utils

    # set up for working through data
    try:
        axis_out.__type__
    except AttributeError:
        raise TypeError("Rebinning axis must be a NessiList!")
        
    o_descr = hlr_utils.get_descr(obj)

    if o_descr == "number" or o_descr == "list":
        raise TypeError("Do not know how to handle given type: %s" % \
                        o_descr)
    else:
        pass
    
    (result, res_descr) = hlr_utils.empty_result(obj)

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    # iterate through the values
    import array_manip
    import axis_manip
    
    for i in xrange(hlr_utils.get_length(obj)):
        axis_in = hlr_utils.get_value(obj, i, o_descr, "x", 0)
        val = hlr_utils.get_value(obj, i, o_descr)
        err2 = hlr_utils.get_err2(obj, i, o_descr)

        value = axis_manip.rebin_axis_1D_frac(axis_in, val, err2, axis_out)

        frac_err = nessi_list.NessiList(len(value[2]))

        value1 = array_manip.div_ncerr(value[0], value[1], value[2], frac_err)
        
        xvals = []
        xvals.append(axis_out)

        map_so = hlr_utils.get_map_so(obj, None, i)

        hlr_utils.result_insert(result, res_descr, value1, map_so, "all",
                                0, xvals)

    return result
def dimensionless_mon(obj, min_ext, max_ext, **kwargs):
    """
    This function takes monitor spectra and converts them to dimensionless
    spectra by dividing each spectrum by the total number of counts within the
    range [min_ext, max_ext]. Then, each spectrum is multiplied by the quantity
    max_ext - min_ext. The units of min_ext and max_ext are assumed to be the
    same as the monitor spectra axis.

    @param obj: Object containing monitor spectra
    @type obj: C{SOM.SOM} or C{SOM.SO}

    @param min_ext: Minimium range and associated error^2 for integrating total
                    counts.
    @type min_ext: C{tuple}

    @param max_ext: Maximium range and associated error^2 for integrating total
                    counts.
    @type max_ext: C{tuple}

    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword units: The expected units for this function. The default for this
                    function is I{Angstroms}.
    @type units: C{string}


    @return: Dimensionless monitor spectra
    @rtype: C{SOM.SOM} or C{SOM.SO}
    """
    
    # import the helper functions
    import hlr_utils

    if obj is None:
        return obj

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "Angstroms"

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis = hlr_utils.one_d_units(obj, units)
    else:
        axis = 0

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    import array_manip
    import dr_lib
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "y")
        err2 = hlr_utils.get_err2(obj, i, o_descr, "y")
        x_axis = hlr_utils.get_value(obj, i, o_descr, "x", axis)
        x_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)
        map_so = hlr_utils.get_map_so(obj, None, i)

        bin_widths = utils.calc_bin_widths(x_axis, x_err2)

        # Scale bin contents by bin width
        value0 = array_manip.mult_ncerr(val, err2,
                                        bin_widths[0], bin_widths[1])

        # Find bin range for extents
        min_index = utils.bisect_helper(x_axis, min_ext[0])
        max_index = utils.bisect_helper(x_axis, max_ext[0])

        # Integrate axis using bin width multiplication
        (asum, asum_err2) = dr_lib.integrate_axis_py(map_so, start=min_index,
                                                     end=max_index, width=True)

        # Get the number of bins in the integration range
        num_bins = max_index - min_index + 1

        asum /= num_bins
        asum_err2 /= (num_bins * num_bins)

        # Divide by sum
        value1 = array_manip.div_ncerr(value0[0], value0[1], asum, asum_err2)

        hlr_utils.result_insert(result, res_descr, value1, map_so, "y")

    return result
def create_E_vs_Q_igs(som, *args, **kwargs):
    """
    This function starts with the initial IGS wavelength axis and turns this
    into a 2D spectra with E and Q axes.

    @param som: The input object with initial IGS wavelength axis
    @type som: C{SOM.SOM}

    @param args: A mandatory list of axes for rebinning. There is a particular
                 order to them. They should be present in the following order:

                 Without errors
                   1. Energy transfer
                   2. Momentum transfer
                 With errors
                   1. Energy transfer
                   2. Energy transfer error^2
                   3. Momentum transfer
                   4. Momentum transfer error ^2
    @type args: C{nessi_list.NessiList}s
       
    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword withXVar: Flag for whether the function should be expecting the
                       associated axes to have errors. The default value will
                       be I{False}.
    @type withXVar: C{boolean}

    @keyword data_type: Name of the data type which can be either I{histogram},
                        I{density} or I{coordinate}. The default value will be
                        I{histogram}
    @type data_type: C{string}
    
    @keyword Q_filter: Flag to turn on or off Q filtering. The default behavior
                       is I{True}.
    @type Q_filter: C{boolean}
    
    @keyword so_id: The identifier represents a number, string, tuple or other
                    object that describes the resulting C{SO}
    @type so_id: C{int}, C{string}, C{tuple}, C{pixel ID}
    
    @keyword y_label: The y axis label
    @type y_label: C{string}
    
    @keyword y_units: The y axis units
    @type y_units: C{string}
    
    @keyword x_labels: This is a list of names that sets the individual x axis
    labels
    @type x_labels: C{list} of C{string}s
    
    @keyword x_units: This is a list of names that sets the individual x axis
    units
    @type x_units: C{list} of C{string}s

    @keyword split: This flag causes the counts and the fractional area to
                    be written out into separate files.
    @type split: C{boolean}

    @keyword configure: This is the object containing the driver configuration.
    @type configure: C{Configure}


    @return: Object containing a 2D C{SO} with E and Q axes
    @rtype: C{SOM.SOM}


    @raise RuntimeError: Anything other than a C{SOM} is passed to the function
    
    @raise RuntimeError: An instrument is not contained in the C{SOM}
    """
    import nessi_list

    # Setup some variables 
    dim = 2
    N_y = []
    N_tot = 1
    N_args = len(args)

    # Get T0 slope in order to calculate dT = dT_i + dT_0
    try:
        t_0_slope = som.attr_list["Time_zero_slope"][0]
        t_0_slope_err2 = som.attr_list["Time_zero_slope"][1]
    except KeyError:
        t_0_slope = float(0.0)
        t_0_slope_err2 = float(0.0)

    # Check withXVar keyword argument and also check number of given args.
    # Set xvar to the appropriate value
    try:
        value = kwargs["withXVar"]
        if value.lower() == "true":
            if N_args != 4:
                raise RuntimeError("Since you have requested x errors, 4 x "\
                                   +"axes must be provided.")
            else:
                xvar = True
        elif value.lower() == "false":
            if N_args != 2:
                raise RuntimeError("Since you did not requested x errors, 2 "\
                                   +"x axes must be provided.")
            else:
                xvar = False
        else:
            raise RuntimeError("Do not understand given parameter %s" % \
                               value)
    except KeyError:
        if N_args != 2:
            raise RuntimeError("Since you did not requested x errors, 2 "\
                               +"x axes must be provided.")
        else:
            xvar = False

    # Check dataType keyword argument. An offset will be set to 1 for the
    # histogram type and 0 for either density or coordinate
    try:
        data_type = kwargs["data_type"]
        if data_type.lower() == "histogram":
            offset = 1
        elif data_type.lower() == "density" or \
                 data_type.lower() == "coordinate":
            offset = 0
        else:
            raise RuntimeError("Do not understand data type given: %s" % \
                               data_type)
    # Default is offset for histogram
    except KeyError:
        offset = 1

    try:
        Q_filter = kwargs["Q_filter"]
    except KeyError:
        Q_filter = True

    # Check for split keyword
    try:
        split = kwargs["split"]
    except KeyError:
        split = False

    # Check for configure keyword
    try:
        configure = kwargs["configure"]
    except KeyError:
        configure = None

    so_dim = SOM.SO(dim)

    for i in range(dim):
        # Set the x-axis arguments from the *args list into the new SO
        if not xvar:
            # Axis positions are 1 (Q) and 0 (E)
            position = dim - i - 1
            so_dim.axis[i].val = args[position]
        else:
            # Axis positions are 2 (Q), 3 (eQ), 0 (E), 1 (eE)
            position = dim - 2 * i
            so_dim.axis[i].val = args[position]
            so_dim.axis[i].var = args[position + 1]

        # Set individual value axis sizes (not x-axis size)
        N_y.append(len(args[position]) - offset)

        # Calculate total 2D array size
        N_tot = N_tot * N_y[-1]

    # Create y and var_y lists from total 2D size
    so_dim.y = nessi_list.NessiList(N_tot)
    so_dim.var_y = nessi_list.NessiList(N_tot)
    
    # Create area sum and errors for the area sum lists from total 2D size
    area_sum = nessi_list.NessiList(N_tot)
    area_sum_err2 = nessi_list.NessiList(N_tot)

    # Create area sum and errors for the area sum lists from total 2D size
    bin_count = nessi_list.NessiList(N_tot)
    bin_count_err2 = nessi_list.NessiList(N_tot)
    
    inst = som.attr_list.instrument
    lambda_final = som.attr_list["Wavelength_final"]
    inst_name = inst.get_name()

    import bisect
    import math

    import dr_lib
    import utils

    arr_len = 0
    #: Vector of zeros for function calculations
    zero_vec = None
    
    for j in xrange(hlr_utils.get_length(som)):
        # Get counts
        counts = hlr_utils.get_value(som, j, "SOM", "y")
        counts_err2 = hlr_utils.get_err2(som, j, "SOM", "y")

        arr_len = len(counts)
        zero_vec = nessi_list.NessiList(arr_len)

        # Get mapping SO
        map_so = hlr_utils.get_map_so(som, None, j)

        # Get lambda_i
        l_i = hlr_utils.get_value(som, j, "SOM", "x")
        l_i_err2 = hlr_utils.get_err2(som, j, "SOM", "x")
        
        # Get lambda_f from instrument information
        l_f_tuple = hlr_utils.get_special(lambda_final, map_so)
        l_f = l_f_tuple[0]
        l_f_err2 = l_f_tuple[1]
        
        # Get source to sample distance
        (L_s, L_s_err2) = hlr_utils.get_parameter("primary", map_so, inst)

        # Get sample to detector distance
        L_d_tuple = hlr_utils.get_parameter("secondary", map_so, inst)
        L_d = L_d_tuple[0]

        # Get polar angle from instrument information
        (angle, angle_err2) = hlr_utils.get_parameter("polar", map_so, inst)

        # Get the detector pixel height
        dh_tuple = hlr_utils.get_parameter("dh", map_so, inst)
        dh = dh_tuple[0]
        # Need dh in units of Angstrom
        dh *= 1e10

        # Calculate T_i
        (T_i, T_i_err2) = axis_manip.wavelength_to_tof(l_i, l_i_err2, 
                                                       L_s, L_s_err2)

        # Scale counts by lambda_f / lambda_i
        (l_i_bc, l_i_bc_err2) = utils.calc_bin_centers(l_i, l_i_err2)

        (ratio, ratio_err2) = array_manip.div_ncerr(l_f, l_f_err2,
                                                    l_i_bc, l_i_bc_err2)

        (counts, counts_err2) = array_manip.mult_ncerr(counts, counts_err2,
                                                       ratio, ratio_err2)

        # Calculate E_i
        (E_i, E_i_err2) = axis_manip.wavelength_to_energy(l_i, l_i_err2)

        # Calculate E_f
        (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f, l_f_err2)

        # Calculate E_t
        (E_t, E_t_err2) = array_manip.sub_ncerr(E_i, E_i_err2, E_f, E_f_err2)

        if inst_name == "BSS":
            # Convert E_t from meV to ueV
            (E_t, E_t_err2) = array_manip.mult_ncerr(E_t, E_t_err2,
                                                     1000.0, 0.0)
            (counts, counts_err2) = array_manip.mult_ncerr(counts, counts_err2,
                                                           1.0/1000.0, 0.0)

        # Convert lambda_i to k_i
        (k_i, k_i_err2) = axis_manip.wavelength_to_scalar_k(l_i, l_i_err2)

        # Convert lambda_f to k_f
        (k_f, k_f_err2) = axis_manip.wavelength_to_scalar_k(l_f, l_f_err2)

        # Convert k_i and k_f to Q
        (Q, Q_err2) = axis_manip.init_scatt_wavevector_to_scalar_Q(k_i,
                                                                   k_i_err2,
                                                                   k_f,
                                                                   k_f_err2,
                                                                   angle,
                                                                   angle_err2)

        # Calculate dT = dT_0 + dT_i
        dT_i = utils.calc_bin_widths(T_i, T_i_err2)

        (l_i_bw, l_i_bw_err2) = utils.calc_bin_widths(l_i, l_i_err2)
        dT_0 = array_manip.mult_ncerr(l_i_bw, l_i_bw_err2,
                                      t_0_slope, t_0_slope_err2)

        dT_tuple = array_manip.add_ncerr(dT_i[0], dT_i[1], dT_0[0], dT_0[1])
        dT = dT_tuple[0]

        # Calculate Jacobian
        if inst_name == "BSS":
            (x_1, x_2,
             x_3, x_4) = dr_lib.calc_BSS_coeffs(map_so, inst, (E_i, E_i_err2),
                                                (Q, Q_err2), (k_i, k_i_err2),
                                                (T_i, T_i_err2), dh, angle,
                                                E_f, k_f, l_f, L_s, L_d,
                                                t_0_slope, zero_vec)
        else:
            raise RuntimeError("Do not know how to calculate x_i "\
                               +"coefficients for instrument %s" % inst_name)

        (A, A_err2) = dr_lib.calc_EQ_Jacobian(x_1, x_2, x_3, x_4, dT, dh,
                                              zero_vec)
        
        # Apply Jacobian: C/dlam * dlam / A(EQ) = C/EQ
        (jac_ratio, jac_ratio_err2) = array_manip.div_ncerr(l_i_bw,
                                                            l_i_bw_err2,
                                                            A, A_err2)
        (counts, counts_err2) = array_manip.mult_ncerr(counts, counts_err2,
                                                       jac_ratio,
                                                       jac_ratio_err2)
        
        # Reverse counts, E_t, k_i and Q
        E_t = axis_manip.reverse_array_cp(E_t)
        E_t_err2 = axis_manip.reverse_array_cp(E_t_err2)
        Q = axis_manip.reverse_array_cp(Q)
        Q_err2 = axis_manip.reverse_array_cp(Q_err2)        
        counts = axis_manip.reverse_array_cp(counts)
        counts_err2 = axis_manip.reverse_array_cp(counts_err2)
        k_i = axis_manip.reverse_array_cp(k_i)
        x_1 = axis_manip.reverse_array_cp(x_1)
        x_2 = axis_manip.reverse_array_cp(x_2)
        x_3 = axis_manip.reverse_array_cp(x_3)
        x_4 = axis_manip.reverse_array_cp(x_4)
        dT = axis_manip.reverse_array_cp(dT)        

        # Filter for duplicate Q values
        if Q_filter:
            k_i_cutoff = k_f * math.cos(angle)
            k_i_cutbin = bisect.bisect(k_i, k_i_cutoff)
            
            counts.__delslice__(0, k_i_cutbin)
            counts_err2.__delslice__(0, k_i_cutbin)
            Q.__delslice__(0, k_i_cutbin)
            Q_err2.__delslice__(0, k_i_cutbin)
            E_t.__delslice__(0, k_i_cutbin)
            E_t_err2.__delslice__(0, k_i_cutbin)
            x_1.__delslice__(0, k_i_cutbin)
            x_2.__delslice__(0, k_i_cutbin)
            x_3.__delslice__(0, k_i_cutbin)
            x_4.__delslice__(0, k_i_cutbin)            
            dT.__delslice__(0, k_i_cutbin)
            zero_vec.__delslice__(0, k_i_cutbin)

        try:
            if inst_name == "BSS":
                ((Q_1, E_t_1),
                 (Q_2, E_t_2),
                 (Q_3, E_t_3),
                 (Q_4, E_t_4)) = dr_lib.calc_BSS_EQ_verticies((E_t, E_t_err2),
                                                              (Q, Q_err2), x_1,
                                                              x_2, x_3, x_4,
                                                              dT, dh, zero_vec)
            else:
                raise RuntimeError("Do not know how to calculate (Q_i, "\
                                   +"E_t_i) verticies for instrument %s" \
                                   % inst_name)

        except IndexError:
            # All the data got Q filtered, move on
            continue

        try:
            (y_2d, y_2d_err2,
             area_new,
             bin_count_new) = axis_manip.rebin_2D_quad_to_rectlin(Q_1, E_t_1,
                                                           Q_2, E_t_2,
                                                           Q_3, E_t_3,
                                                           Q_4, E_t_4,
                                                           counts,
                                                           counts_err2,
                                                           so_dim.axis[0].val,
                                                           so_dim.axis[1].val)
        except IndexError, e:
            # Get the offending index from the error message
            index = int(str(e).split()[1].split('index')[-1].strip('[]'))
            print "Id:", map_so.id
            print "Index:", index
            print "Verticies: %f, %f, %f, %f, %f, %f, %f, %f" % (Q_1[index],
                                                                 E_t_1[index],
                                                                 Q_2[index],
                                                                 E_t_2[index],
                                                                 Q_3[index],
                                                                 E_t_3[index],
                                                                 Q_4[index],
                                                                 E_t_4[index])
            raise IndexError(str(e))

        # Add in together with previous results
        (so_dim.y, so_dim.var_y) = array_manip.add_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         y_2d, y_2d_err2)
        
        (area_sum, area_sum_err2) = array_manip.add_ncerr(area_sum,
                                                          area_sum_err2,
                                                          area_new,
                                                          area_sum_err2)

        if configure.dump_pix_contrib or configure.scale_sqe:
            if inst_name == "BSS":
                dOmega = dr_lib.calc_BSS_solid_angle(map_so, inst)
                (bin_count_new,
                 bin_count_err2) = array_manip.mult_ncerr(bin_count_new,
                                                          bin_count_err2,
                                                          dOmega, 0.0)
                
                (bin_count,
                 bin_count_err2) = array_manip.add_ncerr(bin_count,
                                                         bin_count_err2,
                                                         bin_count_new,
                                                         bin_count_err2)
        else:
            del bin_count_new
def create_param_vs_Y(som, param, param_func, param_axis, **kwargs):
    """
    This function takes a group of single spectrum with any given axes
    (wavelength, energy etc.). The function can optionally rebin those axes to
    a given axis. It then creates a 2D spectrum by using a parameter,
    parameter functiona and a given axis for the lookup locations and places
    each original spectrum in the found location.
    
    @param som: The input object with arbitrary (but same) axis spectra
    @type som: C{SOM.SOM}

    @param param: The parameter that will be used for creating the lookups.
    @type param: C{string}

    @param param_func: The function that will convert the parameter into the
                       values for lookups.
    @type param_func: C{string}

    @param param_axis: The axis that will be searched for the lookup values.
    @type param_axis: C{nessi_list.NessiList}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword rebin_axis: An axis to rebin the given spectra to.
    @type rebin_axis: C{nessi_list.NessiList}

    @keyword data_type: The name of the data type which can be either
                        I{histogram}, I{density} or I{coordinate}. The default
                        value will be I{histogram}.
    @type data_type: C{string}

    @keyword pixnorm: A flag to track the number of pixels that contribute to
                      a bin and then normalize the bin by that number.
    @type pixnorm: C{boolean}

    @keyword prnorm: A parameter to track and determine a range (max - min)
                     for each bin the requested parameter axis. The range will
                     then be divided into the final summed spectrum for the
                     given bin.
    @type prnorm: C{string}

    @keyword binnorm: A flag that turns on the scaling of each stripe of the
                      y-axis by the individual bins widths from the y-axis.
    @type binnorm: C{boolean}

    @keyword so_id: The identifier represents a number, string, tuple or other
                    object that describes the resulting C{SO}.
    @type so_id: C{int}, C{string}, C{tuple}, C{pixel ID}
    
    @keyword y_label: The dependent axis label
    @type y_label: C{string}
    
    @keyword y_units: The dependent axis units
    @type y_units: C{string}
    
    @keyword x_labels: The two independent axis labels
    @type x_labels: C{list} of C{string}s
    
    @keyword x_units: The two independent axis units
    @type x_units: C{list} of C{string}s


    @return: A two dimensional spectrum with the parameter as the x-axis and
             the given spectra axes as the y-axis.
    @rtype: C{SOM.SOM}
    """
    import array_manip
    import dr_lib
    import hlr_utils
    import nessi_list
    import SOM
    import utils

    # Check for rebinning axis
    try:
        rebin_axis = kwargs["rebin_axis"]
    except KeyError:
        rebin_axis = None

    # Check for pixnorm flag
    try:
        pixnorm = kwargs["pixnorm"]
    except KeyError:
        pixnorm = False

    try:
        binnorm = kwargs["binnorm"]
    except KeyError:
        binnorm = False

    # Check for prnorm flag
    try:
        prpar = kwargs["prnorm"]
        prnorm = True
    except KeyError:
        prnorm = False

    # Check dataType keyword argument. An offset will be set to 1 for the
    # histogram type and 0 for either density or coordinate
    try:
        data_type = kwargs["data_type"]
        if data_type.lower() == "histogram":
            offset = 1
        elif data_type.lower() == "density" or \
                 data_type.lower() == "coordinate":
            offset = 0
        else:
            raise RuntimeError("Do not understand data type given: %s" % \
                               data_type)
    # Default is offset for histogram
    except KeyError:
        offset = 1

    # Setup some variables
    dim = 2
    N_tot = 1

    # Create 2D spectrum object
    so_dim = SOM.SO(dim)

    # Set the axis locations
    param_axis_loc = 0
    arb_axis_loc = 1

    # Rebin original data to rebin_axis if necessary
    if rebin_axis is not None:
        (som1, som2) = dr_lib.rebin_axis_1D_frac(som, rebin_axis)
        len_arb_axis = len(rebin_axis) - offset
        so_dim.axis[arb_axis_loc].val = rebin_axis
    else:
        som1 = som
        len_arb_axis = len(som[0].axis[0].val) - offset
        so_dim.axis[arb_axis_loc].val = som[0].axis[0].val

    del som

    # Get parameter axis information
    len_param_axis = len(param_axis) - offset
    so_dim.axis[param_axis_loc].val = param_axis

    if pixnorm:
        pixarr = nessi_list.NessiList(len_param_axis)

    if prnorm:
        prarr = []
        for i in xrange(len_param_axis):
            prarr.append(nessi_list.NessiList())
        # Get the parameters for all the spectra
        ppfunc = hlr_utils.__getattribute__("param_array")
        prarr_lookup = ppfunc(som1, prpar)

    # Get the parameter lookup array
    pfunc = hlr_utils.__getattribute__(param_func)
    lookup_array = pfunc(som1, param)

    # Create y and var_y lists from total 2D size
    N_tot = len_param_axis * len_arb_axis
    so_dim.y = nessi_list.NessiList(N_tot)
    so_dim.var_y = nessi_list.NessiList(N_tot)
    if rebin_axis is not None:
        frac_area = nessi_list.NessiList(N_tot)
        frac_area_err2 = nessi_list.NessiList(N_tot)

    # Loop through data and create 2D spectrum
    len_som = hlr_utils.get_length(som1)
    for i in xrange(len_som):
        val = hlr_utils.get_value(som1, i, "SOM", "y")
        err2 = hlr_utils.get_err2(som1, i, "SOM", "y")

        bin_index = utils.bisect_helper(param_axis, lookup_array[i])
        start = bin_index * len_arb_axis

        if pixnorm:
            pixarr[bin_index] += 1

        if prnorm:
            prarr[bin_index].append(prarr_lookup[i])

        (so_dim.y, so_dim.var_y) = array_manip.add_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         val,
                                                         err2,
                                                         a_start=start)
        if rebin_axis is not None:
            val1 = hlr_utils.get_value(som2, i, "SOM", "y")
            err1_2 = hlr_utils.get_err2(som2, i, "SOM", "y")
            (frac_area, frac_area_err2) = array_manip.add_ncerr(frac_area,
                                                                frac_area_err2,
                                                                val1,
                                                                err1_2,
                                                                a_start=start)

    if rebin_axis is not None:
        (so_dim.y,
         so_dim.var_y) = array_manip.div_ncerr(so_dim.y, so_dim.var_y,
                                               frac_area, frac_area_err2)

    # If parameter range normalization enabled, find the range for the
    # parameter
    if prnorm:
        import math
        prrange = nessi_list.NessiList(len_param_axis)
        for i in xrange(len(prrange)):
            try:
                max_val = max(prarr[i])
            except ValueError:
                max_val = 0.0
            try:
                min_val = min(prarr[i])
            except ValueError:
                min_val = 0.0
            prrange[i] = math.fabs(max_val - min_val)

    # If pixel normalization tracking enabled, divided slices by pixel counts
    if pixnorm or prnorm:
        tmp_y = nessi_list.NessiList(N_tot)
        tmp_var_y = nessi_list.NessiList(N_tot)

        for i in range(len_param_axis):
            start = i * len_arb_axis
            end = (i + 1) * len_arb_axis

            slice_y = so_dim.y[start:end]
            slice_var_y = so_dim.var_y[start:end]

            divconst = 1.0

            if pixnorm:
                divconst *= pixarr[i]
            # Scale division constant if parameter range normalization enabled
            if prnorm:
                divconst *= prrange[i]

            (dslice_y,
             dslice_var_y) = array_manip.div_ncerr(slice_y, slice_var_y,
                                                   divconst, 0.0)

            (tmp_y, tmp_var_y) = array_manip.add_ncerr(tmp_y,
                                                       tmp_var_y,
                                                       dslice_y,
                                                       dslice_var_y,
                                                       a_start=start)

        so_dim.y = tmp_y
        so_dim.var_y = tmp_var_y

    if binnorm:
        tmp_y = nessi_list.NessiList(N_tot)
        tmp_var_y = nessi_list.NessiList(N_tot)

        if rebin_axis is not None:
            bin_const = utils.calc_bin_widths(rebin_axis)
        else:
            bin_const = utils.calc_bin_widths(som1[0].axis[1].val)

        for i in range(len_param_axis):
            start = i * len_arb_axis
            end = (i + 1) * len_arb_axis

            slice_y = so_dim.y[start:end]
            slice_var_y = so_dim.var_y[start:end]

            (dslice_y,
             dslice_var_y) = array_manip.mult_ncerr(slice_y, slice_var_y,
                                                    bin_const[0], bin_const[1])

            (tmp_y, tmp_var_y) = array_manip.add_ncerr(tmp_y,
                                                       tmp_var_y,
                                                       dslice_y,
                                                       dslice_var_y,
                                                       a_start=start)

        so_dim.y = tmp_y
        so_dim.var_y = tmp_var_y

    # Create final 2D spectrum object container
    comb_som = SOM.SOM()
    comb_som.copyAttributes(som1)

    del som1

    # Check for so_id keyword argument
    try:
        so_dim.id = kwargs["so_id"]
    except KeyError:
        so_dim.id = 0

    # Check for y_label keyword argument
    try:
        comb_som.setYLabel(kwargs["y_label"])
    except KeyError:
        comb_som.setYLabel("Counts")

    # Check for y_units keyword argument
    try:
        comb_som.setYUnits(kwargs["y_units"])
    except KeyError:
        comb_som.setYUnits("Counts / Arb")

    # Check for x_label keyword argument
    try:
        comb_som.setAllAxisLabels(kwargs["x_labels"])
    except KeyError:
        comb_som.setAllAxisLabels(["Parameter", "Arbitrary"])

    # Check for x_units keyword argument
    try:
        comb_som.setAllAxisUnits(kwargs["x_units"])
    except KeyError:
        comb_som.setAllAxisUnits(["Arb", "Arb"])

    comb_som.append(so_dim)

    del so_dim

    return comb_som
def __calc_x1(*args):
    """
    This function calculates the x1 coeffiecient to the S(Q,E) Jacobian

    @param args: A list of parameters used to calculate the x1 coefficient

    The following is a list of the arguments needed in there expected order
      1. Initial Wavevector
      2. Momentum Transfer
      3. Length Ratio (L_f / L_i)
      4. Final Wavevector
      5. Wavevector Final x Cos(polar)
      6. Wavevector Final x Sin(polar)
      7. Lambda Constant (2*pi/l_f^2)(dlf/dh)
      8. Derivative dazi_dh
      9. Derivative dpol_dh
      10. Derivative dpol_dtd
      11. Derivative dazi_dtd
      12. Cos(polar)
      13. Time-zero Slope Correction
      14. Vector of Zeros
    @type args: C{list}

    
    @return: The calculated x1 coefficient
    @rtype: (C{nessi_list.NessiList}, C{nessi_list.NessiList})
    """
    # Settle out the arguments to sensible names
    k_i = args[0]
    Q = args[1]
    len_ratio = args[2]
    k_f = args[3]
    k_f_cos_pol = args[4]
    k_f_sin_pol = args[5]
    lam_const = args[6]
    dpol_dh = args[7]
    dpol_dtd = args[8]
    dtd_over_dh = args[9]
    cos_pol = args[10]
    t_0_s_corr = args[11]
    z_vec = args[12]

    # (L_f / L_i) * (1 / k_f)^2 * (1 / 1 + ((h / m) * (t0_s / L_i)))
    const1 = (len_ratio * t_0_s_corr) / (k_f * k_f)
    # (dpol/dh + (dpol/dtd * dtd/dh)) * k_f * sin(pol)
    const2 = (dpol_dh + (dpol_dtd * dtd_over_dh)) * k_f_sin_pol

    # k_i^2
    temp1 = array_manip.mult_ncerr(k_i, z_vec, k_i, z_vec)
    # (L_f / L_i) * (k_i / k_f)^2 * (1 / 1 + ((h / m) * (t0_s / L_i)))
    temp2 = array_manip.mult_ncerr(temp1[0], temp1[1], const1, 0.0)
    # k_i - k_f * cos(pol)
    temp3 = array_manip.sub_ncerr(k_i, z_vec, k_f_cos_pol, 0.0)
    # (k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2
    temp4 = array_manip.mult_ncerr(temp2[0], temp2[1], temp3[0], temp3[1])

    # k_i * cos(pol)
    temp5 = array_manip.mult_ncerr(k_i, z_vec, cos_pol, 0.0)
    # k_f - k_i * cos(pol)
    temp6 = array_manip.sub_ncerr(k_f, 0.0, temp5[0], temp5[1])

    # (k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))
    temp7 = array_manip.sub_ncerr(temp4[0], temp4[1], temp6[0], temp6[1])
    # ((k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))) * (2 * pi / l_f^2) * dlf/dh
    temp8 = array_manip.mult_ncerr(temp7[0], temp7[1], lam_const, 0.0)

    # k_i * k_f * sin(pol) * (dpol/dh + (dpol/dtd * dtd/dh))
    temp9 = array_manip.mult_ncerr(k_i, z_vec, const2, 0.0)

    # ((k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))) * (2 * pi / l_f^2) * dlf/dh +
    # k_i * k_f * sin(pol) * (dpol/dh + (dpol/dtd * dtd/dh))
    temp10 = array_manip.add_ncerr(temp8[0], temp8[1], temp9[0], temp9[1])

    # (((k_i - k_f * cos(pol)) * (L_f / L_i) * (k_i / k_f)^2 -
    # (k_f - k_i * cos(pol))) * (2 * pi / l_f^2) * dlf/dh +
    # k_i * k_f * sin(pol) * (dpol/dh - (dpol/dtd dazi/dh / dazi/dtd))
    return array_manip.div_ncerr(temp10[0], temp10[1], Q, z_vec)
        comb_som[0].var_y = area_sum_err2

        # Write out summed counts into file
        hlr_utils.write_file(configure.output,
                             "text/Dave2d",
                             comb_som,
                             output_ext="fra",
                             verbose=configure.verbose,
                             data_ext=configure.ext_replacement,
                             path_replacement=configure.path_replacement,
                             message="fractional area")

    else:
        # Divide summed fractional counts by the sum of the fractional areas
        (so_dim.y,
         so_dim.var_y) = array_manip.div_ncerr(so_dim.y, so_dim.var_y,
                                               area_sum, area_sum_err2)

        if configure.scale_sqe:
            (so_dim.y,
             so_dim.var_y) = array_manip.div_ncerr(so_dim.y, so_dim.var_y,
                                                   bin_count, bin_count_err2)

        comb_som.append(so_dim)

    del so_dim

    return comb_som


def __set_som_attributes(tsom, inst_name, **kwargs):
    """
def create_E_vs_Q_igs(som, *args, **kwargs):
    """
    This function starts with the initial IGS wavelength axis and turns this
    into a 2D spectra with E and Q axes.

    @param som: The input object with initial IGS wavelength axis
    @type som: C{SOM.SOM}

    @param args: A mandatory list of axes for rebinning. There is a particular
                 order to them. They should be present in the following order:

                 Without errors
                   1. Energy transfer
                   2. Momentum transfer
                 With errors
                   1. Energy transfer
                   2. Energy transfer error^2
                   3. Momentum transfer
                   4. Momentum transfer error ^2
    @type args: C{nessi_list.NessiList}s
       
    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword withXVar: Flag for whether the function should be expecting the
                       associated axes to have errors. The default value will
                       be I{False}.
    @type withXVar: C{boolean}

    @keyword data_type: Name of the data type which can be either I{histogram},
                        I{density} or I{coordinate}. The default value will be
                        I{histogram}
    @type data_type: C{string}
    
    @keyword Q_filter: Flag to turn on or off Q filtering. The default behavior
                       is I{True}.
    @type Q_filter: C{boolean}
    
    @keyword so_id: The identifier represents a number, string, tuple or other
                    object that describes the resulting C{SO}
    @type so_id: C{int}, C{string}, C{tuple}, C{pixel ID}
    
    @keyword y_label: The y axis label
    @type y_label: C{string}
    
    @keyword y_units: The y axis units
    @type y_units: C{string}
    
    @keyword x_labels: This is a list of names that sets the individual x axis
    labels
    @type x_labels: C{list} of C{string}s
    
    @keyword x_units: This is a list of names that sets the individual x axis
    units
    @type x_units: C{list} of C{string}s

    @keyword split: This flag causes the counts and the fractional area to
                    be written out into separate files.
    @type split: C{boolean}

    @keyword configure: This is the object containing the driver configuration.
    @type configure: C{Configure}


    @return: Object containing a 2D C{SO} with E and Q axes
    @rtype: C{SOM.SOM}


    @raise RuntimeError: Anything other than a C{SOM} is passed to the function
    
    @raise RuntimeError: An instrument is not contained in the C{SOM}
    """
    import nessi_list

    # Setup some variables
    dim = 2
    N_y = []
    N_tot = 1
    N_args = len(args)

    # Get T0 slope in order to calculate dT = dT_i + dT_0
    try:
        t_0_slope = som.attr_list["Time_zero_slope"][0]
        t_0_slope_err2 = som.attr_list["Time_zero_slope"][1]
    except KeyError:
        t_0_slope = float(0.0)
        t_0_slope_err2 = float(0.0)

    # Check withXVar keyword argument and also check number of given args.
    # Set xvar to the appropriate value
    try:
        value = kwargs["withXVar"]
        if value.lower() == "true":
            if N_args != 4:
                raise RuntimeError("Since you have requested x errors, 4 x "\
                                   +"axes must be provided.")
            else:
                xvar = True
        elif value.lower() == "false":
            if N_args != 2:
                raise RuntimeError("Since you did not requested x errors, 2 "\
                                   +"x axes must be provided.")
            else:
                xvar = False
        else:
            raise RuntimeError("Do not understand given parameter %s" % \
                               value)
    except KeyError:
        if N_args != 2:
            raise RuntimeError("Since you did not requested x errors, 2 "\
                               +"x axes must be provided.")
        else:
            xvar = False

    # Check dataType keyword argument. An offset will be set to 1 for the
    # histogram type and 0 for either density or coordinate
    try:
        data_type = kwargs["data_type"]
        if data_type.lower() == "histogram":
            offset = 1
        elif data_type.lower() == "density" or \
                 data_type.lower() == "coordinate":
            offset = 0
        else:
            raise RuntimeError("Do not understand data type given: %s" % \
                               data_type)
    # Default is offset for histogram
    except KeyError:
        offset = 1

    try:
        Q_filter = kwargs["Q_filter"]
    except KeyError:
        Q_filter = True

    # Check for split keyword
    try:
        split = kwargs["split"]
    except KeyError:
        split = False

    # Check for configure keyword
    try:
        configure = kwargs["configure"]
    except KeyError:
        configure = None

    so_dim = SOM.SO(dim)

    for i in range(dim):
        # Set the x-axis arguments from the *args list into the new SO
        if not xvar:
            # Axis positions are 1 (Q) and 0 (E)
            position = dim - i - 1
            so_dim.axis[i].val = args[position]
        else:
            # Axis positions are 2 (Q), 3 (eQ), 0 (E), 1 (eE)
            position = dim - 2 * i
            so_dim.axis[i].val = args[position]
            so_dim.axis[i].var = args[position + 1]

        # Set individual value axis sizes (not x-axis size)
        N_y.append(len(args[position]) - offset)

        # Calculate total 2D array size
        N_tot = N_tot * N_y[-1]

    # Create y and var_y lists from total 2D size
    so_dim.y = nessi_list.NessiList(N_tot)
    so_dim.var_y = nessi_list.NessiList(N_tot)

    # Create area sum and errors for the area sum lists from total 2D size
    area_sum = nessi_list.NessiList(N_tot)
    area_sum_err2 = nessi_list.NessiList(N_tot)

    # Create area sum and errors for the area sum lists from total 2D size
    bin_count = nessi_list.NessiList(N_tot)
    bin_count_err2 = nessi_list.NessiList(N_tot)

    inst = som.attr_list.instrument
    lambda_final = som.attr_list["Wavelength_final"]
    inst_name = inst.get_name()

    import bisect
    import math

    import dr_lib
    import utils

    arr_len = 0
    #: Vector of zeros for function calculations
    zero_vec = None

    for j in xrange(hlr_utils.get_length(som)):
        # Get counts
        counts = hlr_utils.get_value(som, j, "SOM", "y")
        counts_err2 = hlr_utils.get_err2(som, j, "SOM", "y")

        arr_len = len(counts)
        zero_vec = nessi_list.NessiList(arr_len)

        # Get mapping SO
        map_so = hlr_utils.get_map_so(som, None, j)

        # Get lambda_i
        l_i = hlr_utils.get_value(som, j, "SOM", "x")
        l_i_err2 = hlr_utils.get_err2(som, j, "SOM", "x")

        # Get lambda_f from instrument information
        l_f_tuple = hlr_utils.get_special(lambda_final, map_so)
        l_f = l_f_tuple[0]
        l_f_err2 = l_f_tuple[1]

        # Get source to sample distance
        (L_s, L_s_err2) = hlr_utils.get_parameter("primary", map_so, inst)

        # Get sample to detector distance
        L_d_tuple = hlr_utils.get_parameter("secondary", map_so, inst)
        L_d = L_d_tuple[0]

        # Get polar angle from instrument information
        (angle, angle_err2) = hlr_utils.get_parameter("polar", map_so, inst)

        # Get the detector pixel height
        dh_tuple = hlr_utils.get_parameter("dh", map_so, inst)
        dh = dh_tuple[0]
        # Need dh in units of Angstrom
        dh *= 1e10

        # Calculate T_i
        (T_i, T_i_err2) = axis_manip.wavelength_to_tof(l_i, l_i_err2, L_s,
                                                       L_s_err2)

        # Scale counts by lambda_f / lambda_i
        (l_i_bc, l_i_bc_err2) = utils.calc_bin_centers(l_i, l_i_err2)

        (ratio, ratio_err2) = array_manip.div_ncerr(l_f, l_f_err2, l_i_bc,
                                                    l_i_bc_err2)

        (counts, counts_err2) = array_manip.mult_ncerr(counts, counts_err2,
                                                       ratio, ratio_err2)

        # Calculate E_i
        (E_i, E_i_err2) = axis_manip.wavelength_to_energy(l_i, l_i_err2)

        # Calculate E_f
        (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f, l_f_err2)

        # Calculate E_t
        (E_t, E_t_err2) = array_manip.sub_ncerr(E_i, E_i_err2, E_f, E_f_err2)

        if inst_name == "BSS":
            # Convert E_t from meV to ueV
            (E_t, E_t_err2) = array_manip.mult_ncerr(E_t, E_t_err2, 1000.0,
                                                     0.0)
            (counts,
             counts_err2) = array_manip.mult_ncerr(counts, counts_err2,
                                                   1.0 / 1000.0, 0.0)

        # Convert lambda_i to k_i
        (k_i, k_i_err2) = axis_manip.wavelength_to_scalar_k(l_i, l_i_err2)

        # Convert lambda_f to k_f
        (k_f, k_f_err2) = axis_manip.wavelength_to_scalar_k(l_f, l_f_err2)

        # Convert k_i and k_f to Q
        (Q, Q_err2) = axis_manip.init_scatt_wavevector_to_scalar_Q(
            k_i, k_i_err2, k_f, k_f_err2, angle, angle_err2)

        # Calculate dT = dT_0 + dT_i
        dT_i = utils.calc_bin_widths(T_i, T_i_err2)

        (l_i_bw, l_i_bw_err2) = utils.calc_bin_widths(l_i, l_i_err2)
        dT_0 = array_manip.mult_ncerr(l_i_bw, l_i_bw_err2, t_0_slope,
                                      t_0_slope_err2)

        dT_tuple = array_manip.add_ncerr(dT_i[0], dT_i[1], dT_0[0], dT_0[1])
        dT = dT_tuple[0]

        # Calculate Jacobian
        if inst_name == "BSS":
            (x_1, x_2, x_3, x_4) = dr_lib.calc_BSS_coeffs(
                map_so, inst, (E_i, E_i_err2), (Q, Q_err2), (k_i, k_i_err2),
                (T_i, T_i_err2), dh, angle, E_f, k_f, l_f, L_s, L_d, t_0_slope,
                zero_vec)
        else:
            raise RuntimeError("Do not know how to calculate x_i "\
                               +"coefficients for instrument %s" % inst_name)

        (A, A_err2) = dr_lib.calc_EQ_Jacobian(x_1, x_2, x_3, x_4, dT, dh,
                                              zero_vec)

        # Apply Jacobian: C/dlam * dlam / A(EQ) = C/EQ
        (jac_ratio,
         jac_ratio_err2) = array_manip.div_ncerr(l_i_bw, l_i_bw_err2, A,
                                                 A_err2)
        (counts, counts_err2) = array_manip.mult_ncerr(counts, counts_err2,
                                                       jac_ratio,
                                                       jac_ratio_err2)

        # Reverse counts, E_t, k_i and Q
        E_t = axis_manip.reverse_array_cp(E_t)
        E_t_err2 = axis_manip.reverse_array_cp(E_t_err2)
        Q = axis_manip.reverse_array_cp(Q)
        Q_err2 = axis_manip.reverse_array_cp(Q_err2)
        counts = axis_manip.reverse_array_cp(counts)
        counts_err2 = axis_manip.reverse_array_cp(counts_err2)
        k_i = axis_manip.reverse_array_cp(k_i)
        x_1 = axis_manip.reverse_array_cp(x_1)
        x_2 = axis_manip.reverse_array_cp(x_2)
        x_3 = axis_manip.reverse_array_cp(x_3)
        x_4 = axis_manip.reverse_array_cp(x_4)
        dT = axis_manip.reverse_array_cp(dT)

        # Filter for duplicate Q values
        if Q_filter:
            k_i_cutoff = k_f * math.cos(angle)
            k_i_cutbin = bisect.bisect(k_i, k_i_cutoff)

            counts.__delslice__(0, k_i_cutbin)
            counts_err2.__delslice__(0, k_i_cutbin)
            Q.__delslice__(0, k_i_cutbin)
            Q_err2.__delslice__(0, k_i_cutbin)
            E_t.__delslice__(0, k_i_cutbin)
            E_t_err2.__delslice__(0, k_i_cutbin)
            x_1.__delslice__(0, k_i_cutbin)
            x_2.__delslice__(0, k_i_cutbin)
            x_3.__delslice__(0, k_i_cutbin)
            x_4.__delslice__(0, k_i_cutbin)
            dT.__delslice__(0, k_i_cutbin)
            zero_vec.__delslice__(0, k_i_cutbin)

        try:
            if inst_name == "BSS":
                ((Q_1, E_t_1), (Q_2, E_t_2), (Q_3, E_t_3),
                 (Q_4, E_t_4)) = dr_lib.calc_BSS_EQ_verticies(
                     (E_t, E_t_err2), (Q, Q_err2), x_1, x_2, x_3, x_4, dT, dh,
                     zero_vec)
            else:
                raise RuntimeError("Do not know how to calculate (Q_i, "\
                                   +"E_t_i) verticies for instrument %s" \
                                   % inst_name)

        except IndexError:
            # All the data got Q filtered, move on
            continue

        try:
            (y_2d, y_2d_err2, area_new,
             bin_count_new) = axis_manip.rebin_2D_quad_to_rectlin(
                 Q_1, E_t_1, Q_2, E_t_2, Q_3, E_t_3, Q_4, E_t_4, counts,
                 counts_err2, so_dim.axis[0].val, so_dim.axis[1].val)
        except IndexError, e:
            # Get the offending index from the error message
            index = int(str(e).split()[1].split('index')[-1].strip('[]'))
            print "Id:", map_so.id
            print "Index:", index
            print "Verticies: %f, %f, %f, %f, %f, %f, %f, %f" % (
                Q_1[index], E_t_1[index], Q_2[index], E_t_2[index], Q_3[index],
                E_t_3[index], Q_4[index], E_t_4[index])
            raise IndexError(str(e))

        # Add in together with previous results
        (so_dim.y,
         so_dim.var_y) = array_manip.add_ncerr(so_dim.y, so_dim.var_y, y_2d,
                                               y_2d_err2)

        (area_sum,
         area_sum_err2) = array_manip.add_ncerr(area_sum, area_sum_err2,
                                                area_new, area_sum_err2)

        if configure.dump_pix_contrib or configure.scale_sqe:
            if inst_name == "BSS":
                dOmega = dr_lib.calc_BSS_solid_angle(map_so, inst)
                (bin_count_new, bin_count_err2) = array_manip.mult_ncerr(
                    bin_count_new, bin_count_err2, dOmega, 0.0)

                (bin_count, bin_count_err2) = array_manip.add_ncerr(
                    bin_count, bin_count_err2, bin_count_new, bin_count_err2)
        else:
            del bin_count_new
def igs_energy_transfer(obj, **kwargs):
    """
    @depricated: This function will eventually disappear when the full S(Q,E)
                 transformation for IGS detectors is completed and verified.
                 
    This function takes a SOM or a SO and calculates the energy transfer for
    the IGS class of instruments. It is different from
    common_lib.energy_transfer in that the final wavelength is provided in a
    SOM.Information, SOM.CompositeInformation or a tuple, then converted to
    energy in place before being given to the common_lib.energy_transfer
    function.

    Parameters:
    ----------
    -> obj
    -> kwargs is a list of key word arguments that the function accepts:
          units= a string containing the expected units for this function.
                 The default for this function is meV
          lambda_f= a SOM.Information, SOM.CompositeInformation or a tuple
                    containing the final wavelength information
          offset= a SOM.Information or SOM.CompositeInformation containing
                  the final energy offsets
          scale=<boolean> is a flag that determines if the energy transfer
                          results are scaled by the ratio of lambda_f/lambda_i.
                          The default is False

    Returns:
    -------
    <- A SOM or SO with the energy transfer calculated in units of THz

    Exceptions:
    ----------
    <- RuntimeError is raised if the x-axis units are not meV
    <- RuntimeError is raised if a SOM or SO is not given to the function
    <- RuntimeError is raised if the final wavelength is not provided to the
       function
    """

    # import the helper functions
    import hlr_utils

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    if o_descr == "number" or o_descr == "list":
        raise RuntimeError, "Must provide a SOM of a SO to the function."
    # Go on
    else:
        pass

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "meV"

    try:
        lambda_f = kwargs["lambda_f"]
    except KeyError:
        lambda_f = None

    try:
        offset = kwargs["offset"]
    except KeyError:
        offset = None

    try:
        scale = kwargs["scale"]
    except KeyError:
        scale = False

        
    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis = hlr_utils.one_d_units(obj, units)
    else:
        axis = 0

    if lambda_f is None:
        if o_descr == "SOM":
            try:
                lambda_f = obj.attr_list["Wavelength_final"]
            except KeyError:
                raise RuntimeError("Must provide a final wavelength via the "\
                                   +"incoming SOM or the lambda_f keyword")
        else:
            raise RuntimeError("Must provide a final wavelength via the "\
                                   +"lambda_f keyword")
    else:
        pass
    

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)
    if res_descr == "SOM":
        result = hlr_utils.force_units(result, "ueV", axis)
        result.setAxisLabel(axis, "energy_transfer")
        result.setYUnits("Counts/ueV")
        result.setYLabel("Intensity")
    else:
        pass

    # iterate through the values
    import array_manip
    import axis_manip
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "x", axis)
        err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)

        y_val = hlr_utils.get_value(obj, i, o_descr, "y", axis)
        y_err2 = hlr_utils.get_err2(obj, i, o_descr, "y", axis)
        
        map_so = hlr_utils.get_map_so(obj, None, i)
        
        l_f = hlr_utils.get_special(lambda_f, map_so)

        (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f[0], l_f[1])

        if offset is not None:
            info = hlr_utils.get_special(offset, map_so)
            try:
                E_f_new = array_manip.add_ncerr(E_f, E_f_err2,
                                                info[0], info[1])
            except TypeError:
                # Have to do this since add_ncerr does not support
                # scalar-scalar operations
                value1 = E_f + info[0]
                value2 = E_f_err2 + info[1]
                E_f_new = (value1, value2)
        else:
            E_f_new = (E_f, E_f_err2)

        # Scale counts by lambda_f / lambda_i
        if scale:
            l_i = axis_manip.energy_to_wavelength(val, err2)

            l_i_bc = utils.calc_bin_centers(l_i[0], l_i[1])

            ratio = array_manip.div_ncerr(l_f[0], l_f[1],
                                          l_i_bc[0], l_i_bc[1])

            scale_y = array_manip.mult_ncerr(y_val, y_err2, ratio[0], ratio[1])
        else:
            scale_y = (y_val, y_err2)

        value = array_manip.sub_ncerr(val, err2, E_f_new[0], E_f_new[1])
            
        # Convert from meV to ueV
        value2 = array_manip.mult_ncerr(value[0], value[1], 1000.0, 0.0)
        value3 = array_manip.mult_ncerr(scale_y[0], scale_y[1],
                                        1.0/1000.0, 0.0)

        hlr_utils.result_insert(result, res_descr, value3, map_so, "all",
                                0, [value2[0]])

    return result
Beispiel #33
0
def sum_by_rebin_frac(obj, axis_out, **kwargs):
    """
    This function uses the C{axis_manip.rebin_axis_1D_frac} function from the
    SCL to perform the rebinning. The function tracks the counts and fractional
    area from all spectra separately. The counts and fractional area are
    divided after all spectra have been parsed. 
    
    @param obj: Object to be rebinned and summed
    @type obj: C{SOM.SOM} or C{SOM.SO}
    
    @param axis_out: The axis to rebin the C{SOM} or C{SO} to
    @type axis_out: C{NessiList}

    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword configure: This is the object containing the driver configuration.
                        This will signal the function to write out the counts
                        and fractional area to files.
    @type configure: C{Configure}


    @return: Object that has been rebinned and summed according to the
             provided axis
    @rtype: C{SOM.SOM} or C{SOM.SO}


    @raise TypeError: The rebinning axis given is not a C{NessiList}
    @raise TypeError: The object being rebinned is not a C{SOM} or a C{SO}
    @raise TypeError: The dimension of the input object is not 1D
    """
    # import the helper functions
    import hlr_utils

    # set up for working through data
    try:
        axis_out.__type__
    except AttributeError:
        raise TypeError("Rebinning axis must be a NessiList!")

    o_descr = hlr_utils.get_descr(obj)

    if o_descr == "number" or o_descr == "list":
        raise TypeError("Do not know how to handle given type: %s" % \
                        o_descr)
    else:
        pass

    try:
        if obj.getDimension() != 1:
            raise TypeError("The input object must be 1D!. This one is "\
                            +"%dD." % obj.getDimension())
    except AttributeError:
        # obj is a SO
        if obj.dim() != 1:
            raise TypeError("The input object must be 1D!. This one is "\
                            +"%dD." % obj.dim())

    # Check for keywords
    try:
        config = kwargs["configure"]
    except KeyError:
        config = None

    (result, res_descr) = hlr_utils.empty_result(obj)

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    import array_manip
    import axis_manip

    len_data = len(axis_out) - 1

    counts = nessi_list.NessiList(len_data)
    counts_err2 = nessi_list.NessiList(len_data)
    frac_area = nessi_list.NessiList(len_data)
    frac_area_err2 = nessi_list.NessiList(len_data)

    for i in xrange(hlr_utils.get_length(obj)):
        axis_in = hlr_utils.get_value(obj, i, o_descr, "x", 0)
        val = hlr_utils.get_value(obj, i, o_descr)
        err2 = hlr_utils.get_err2(obj, i, o_descr)

        value = axis_manip.rebin_axis_1D_frac(axis_in, val, err2, axis_out)

        (counts, counts_err2) = array_manip.add_ncerr(counts, counts_err2,
                                                      value[0], value[1])

        (frac_area,
         frac_area_err2) = array_manip.add_ncerr(frac_area, frac_area_err2,
                                                 value[2], frac_area_err2)

    # Divide the total counts by the total fractional area
    value1 = array_manip.div_ncerr(counts, counts_err2, frac_area,
                                   frac_area_err2)
    xvals = []
    xvals.append(axis_out)

    map_so = hlr_utils.get_map_so(obj, None, 0)

    hlr_utils.result_insert(result, res_descr, value1, map_so, "all", 0, xvals)

    if config is not None:
        if o_descr == "SOM":
            import SOM
            o_som = SOM.SOM()
            o_som.copyAttributes(obj)

            so = hlr_utils.get_map_so(obj, None, 0)
            so.axis[0].val = axis_out
            so.y = counts
            so.var_y = counts_err2
            o_som.append(so)

            # Write out summed counts into file
            hlr_utils.write_file(config.output,
                                 "text/Spec",
                                 o_som,
                                 output_ext="cnt",
                                 verbose=config.verbose,
                                 data_ext=config.ext_replacement,
                                 path_replacement=config.path_replacement,
                                 message="summed counts")

            # Replace counts data with fractional area. The axes remain the
            # same
            o_som[0].y = frac_area
            o_som[0].var_y = frac_area_err2

            # Write out summed fractional area into file
            hlr_utils.write_file(config.output,
                                 "text/Spec",
                                 o_som,
                                 output_ext="fra",
                                 verbose=config.verbose,
                                 data_ext=config.ext_replacement,
                                 path_replacement=config.path_replacement,
                                 message="fractional area")

    return result
Beispiel #34
0
def feff_correct_mon(obj, **kwargs):
    """
    This function takes in a monitor spectra, calculates efficiencies based on
    the montior's wavelength axis and divides the monitor counts by the
    calculated efficiencies. The function is a M{constant * wavelength}.

    @param obj: Object containing monitor spectra
    @type obj: C{SOM.SOM} or C{SOM.SO}
    
    @param kwargs: A list of keyword arguments that the function accepts:
    
    @keyword units: The expected units for this function. The default for this
                    function is I{Angstroms}.
    @type units: C{string}

    @keyword eff_const: Use this provided effieciency constant. The default is
                        (0.00000085 / 1.8) Angstroms^-1.
    @type eff_const: L{hlr_utils.DrParameter}

    @keyword inst_name: The short name of an instrument.
    @type inst_name: C{string}
    
    
    @return: Efficiency corrected monitor spectra
    @rtype: C{SOM.SOM} or C{SOM.SO}
    """

    # import the helper functions
    import hlr_utils

    if obj is None:
        return obj

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "Angstroms"

    try:
        eff_const = kwargs["eff_const"]
    except KeyError:
        # This is for SNS (specifically BASIS) monitors
        eff_const = hlr_utils.DrParameter((0.00000085 / 1.8), 0.0,
                                          "Angstroms^-1")  # A^-1

    inst_name = kwargs.get("inst_name")

    # Primary axis for transformation. If a SO is passed, the function, will
    # assume the axis for transformation is at the 0 position
    if o_descr == "SOM":
        axis = hlr_utils.one_d_units(obj, units)
    else:
        axis = 0

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)

    # iterate through the values
    import array_manip
    import nessi_list
    import dr_lib

    for i in xrange(hlr_utils.get_length(obj)):
        val = hlr_utils.get_value(obj, i, o_descr, "x", axis)
        map_so = hlr_utils.get_map_so(obj, None, i)

        if inst_name is None:
            eff = nessi_list.NessiList()

            for j in xrange(len(val) - 1):
                bin_center = (val[j + 1] + val[j]) / 2.0
                eff.append(eff_const.getValue() * bin_center)

            eff_err2 = nessi_list.NessiList(len(eff))
        else:
            if inst_name == "SANS":
                (eff, eff_err2) = dr_lib.subexp_eff(eff_const, val)
            else:
                raise RuntimeError("Do not know how to handle %s instrument" \
                                   % inst_name)

        y_val = hlr_utils.get_value(obj, i, o_descr, "y")
        y_err2 = hlr_utils.get_err2(obj, i, o_descr, "y")

        value = array_manip.div_ncerr(y_val, y_err2, eff, eff_err2)

        hlr_utils.result_insert(result, res_descr, value, map_so, "y")

    return result
def create_param_vs_Y(som, param, param_func, param_axis, **kwargs):
    """
    This function takes a group of single spectrum with any given axes
    (wavelength, energy etc.). The function can optionally rebin those axes to
    a given axis. It then creates a 2D spectrum by using a parameter,
    parameter functiona and a given axis for the lookup locations and places
    each original spectrum in the found location.
    
    @param som: The input object with arbitrary (but same) axis spectra
    @type som: C{SOM.SOM}

    @param param: The parameter that will be used for creating the lookups.
    @type param: C{string}

    @param param_func: The function that will convert the parameter into the
                       values for lookups.
    @type param_func: C{string}

    @param param_axis: The axis that will be searched for the lookup values.
    @type param_axis: C{nessi_list.NessiList}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword rebin_axis: An axis to rebin the given spectra to.
    @type rebin_axis: C{nessi_list.NessiList}

    @keyword data_type: The name of the data type which can be either
                        I{histogram}, I{density} or I{coordinate}. The default
                        value will be I{histogram}.
    @type data_type: C{string}

    @keyword pixnorm: A flag to track the number of pixels that contribute to
                      a bin and then normalize the bin by that number.
    @type pixnorm: C{boolean}

    @keyword prnorm: A parameter to track and determine a range (max - min)
                     for each bin the requested parameter axis. The range will
                     then be divided into the final summed spectrum for the
                     given bin.
    @type prnorm: C{string}

    @keyword binnorm: A flag that turns on the scaling of each stripe of the
                      y-axis by the individual bins widths from the y-axis.
    @type binnorm: C{boolean}

    @keyword so_id: The identifier represents a number, string, tuple or other
                    object that describes the resulting C{SO}.
    @type so_id: C{int}, C{string}, C{tuple}, C{pixel ID}
    
    @keyword y_label: The dependent axis label
    @type y_label: C{string}
    
    @keyword y_units: The dependent axis units
    @type y_units: C{string}
    
    @keyword x_labels: The two independent axis labels
    @type x_labels: C{list} of C{string}s
    
    @keyword x_units: The two independent axis units
    @type x_units: C{list} of C{string}s


    @return: A two dimensional spectrum with the parameter as the x-axis and
             the given spectra axes as the y-axis.
    @rtype: C{SOM.SOM}
    """
    import array_manip
    import dr_lib
    import hlr_utils
    import nessi_list
    import SOM
    import utils

    # Check for rebinning axis
    try:
        rebin_axis = kwargs["rebin_axis"]
    except KeyError:
        rebin_axis = None

    # Check for pixnorm flag
    try:
        pixnorm = kwargs["pixnorm"]
    except KeyError:
        pixnorm = False

    try:
        binnorm = kwargs["binnorm"]
    except KeyError:
        binnorm = False        

    # Check for prnorm flag
    try:
        prpar = kwargs["prnorm"]
        prnorm = True
    except KeyError:
        prnorm = False

    # Check dataType keyword argument. An offset will be set to 1 for the
    # histogram type and 0 for either density or coordinate
    try:
        data_type = kwargs["data_type"]
        if data_type.lower() == "histogram":
            offset = 1
        elif data_type.lower() == "density" or \
                 data_type.lower() == "coordinate":
            offset = 0
        else:
            raise RuntimeError("Do not understand data type given: %s" % \
                               data_type)
    # Default is offset for histogram
    except KeyError:
        offset = 1

    # Setup some variables 
    dim = 2
    N_tot = 1

    # Create 2D spectrum object
    so_dim = SOM.SO(dim)

    # Set the axis locations
    param_axis_loc = 0    
    arb_axis_loc = 1

    # Rebin original data to rebin_axis if necessary
    if rebin_axis is not None:
        (som1, som2) = dr_lib.rebin_axis_1D_frac(som, rebin_axis)
        len_arb_axis = len(rebin_axis) - offset
        so_dim.axis[arb_axis_loc].val = rebin_axis
    else:
        som1 = som
        len_arb_axis = len(som[0].axis[0].val) - offset
        so_dim.axis[arb_axis_loc].val = som[0].axis[0].val

    del som

    # Get parameter axis information
    len_param_axis = len(param_axis) - offset
    so_dim.axis[param_axis_loc].val = param_axis

    if pixnorm:
        pixarr = nessi_list.NessiList(len_param_axis)

    if prnorm:
        prarr = []
        for i in xrange(len_param_axis):
            prarr.append(nessi_list.NessiList())
        # Get the parameters for all the spectra
        ppfunc = hlr_utils.__getattribute__("param_array")
        prarr_lookup = ppfunc(som1, prpar)

    # Get the parameter lookup array
    pfunc = hlr_utils.__getattribute__(param_func)
    lookup_array = pfunc(som1, param)

    # Create y and var_y lists from total 2D size
    N_tot = len_param_axis * len_arb_axis
    so_dim.y = nessi_list.NessiList(N_tot)
    so_dim.var_y = nessi_list.NessiList(N_tot)
    if rebin_axis is not None:
        frac_area = nessi_list.NessiList(N_tot)
        frac_area_err2 = nessi_list.NessiList(N_tot)

    # Loop through data and create 2D spectrum
    len_som = hlr_utils.get_length(som1)
    for i in xrange(len_som):
        val = hlr_utils.get_value(som1, i, "SOM", "y")
        err2 = hlr_utils.get_err2(som1, i, "SOM", "y")

        bin_index = utils.bisect_helper(param_axis, lookup_array[i])
        start = bin_index * len_arb_axis

        if pixnorm:
            pixarr[bin_index] += 1

        if prnorm:
            prarr[bin_index].append(prarr_lookup[i])

        (so_dim.y, so_dim.var_y) = array_manip.add_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         val,
                                                         err2,
                                                         a_start=start)
        if rebin_axis is not None:
            val1 = hlr_utils.get_value(som2, i, "SOM", "y")
            err1_2 = hlr_utils.get_err2(som2, i, "SOM", "y")
            (frac_area, frac_area_err2) = array_manip.add_ncerr(frac_area,
                                                                frac_area_err2,
                                                                val1,
                                                                err1_2,
                                                                a_start=start)

    if rebin_axis is not None:
        (so_dim.y, so_dim.var_y) = array_manip.div_ncerr(so_dim.y,
                                                         so_dim.var_y,
                                                         frac_area,
                                                         frac_area_err2)

    # If parameter range normalization enabled, find the range for the
    # parameter
    if prnorm:
        import math
        prrange = nessi_list.NessiList(len_param_axis)
        for i in xrange(len(prrange)):
            try:
                max_val = max(prarr[i])
            except ValueError:
                max_val = 0.0
            try:
                min_val = min(prarr[i])
            except ValueError:
                min_val = 0.0
            prrange[i] = math.fabs(max_val - min_val)

    # If pixel normalization tracking enabled, divided slices by pixel counts
    if pixnorm or prnorm:
        tmp_y = nessi_list.NessiList(N_tot)
        tmp_var_y = nessi_list.NessiList(N_tot)

        for i in range(len_param_axis):
            start = i * len_arb_axis
            end = (i + 1) * len_arb_axis

            slice_y = so_dim.y[start:end]
            slice_var_y = so_dim.var_y[start:end]

            divconst = 1.0
            
            if pixnorm:
                divconst *= pixarr[i]
            # Scale division constant if parameter range normalization enabled
            if prnorm:
                divconst *= prrange[i]

            (dslice_y, dslice_var_y) = array_manip.div_ncerr(slice_y,
                                                             slice_var_y,
                                                             divconst,
                                                             0.0)

            (tmp_y, tmp_var_y) = array_manip.add_ncerr(tmp_y,
                                                       tmp_var_y,
                                                       dslice_y,
                                                       dslice_var_y,
                                                       a_start=start)

        so_dim.y = tmp_y
        so_dim.var_y = tmp_var_y

    if binnorm:
        tmp_y = nessi_list.NessiList(N_tot)
        tmp_var_y = nessi_list.NessiList(N_tot)
        
        if rebin_axis is not None:
            bin_const = utils.calc_bin_widths(rebin_axis)
        else:
            bin_const = utils.calc_bin_widths(som1[0].axis[1].val)

        for i in range(len_param_axis):
            start = i * len_arb_axis
            end = (i + 1) * len_arb_axis
            
            slice_y = so_dim.y[start:end]
            slice_var_y = so_dim.var_y[start:end]
            
            (dslice_y, dslice_var_y) = array_manip.mult_ncerr(slice_y,
                                                              slice_var_y,
                                                              bin_const[0],
                                                              bin_const[1])
            
            (tmp_y, tmp_var_y) = array_manip.add_ncerr(tmp_y,
                                                       tmp_var_y,
                                                       dslice_y,
                                                       dslice_var_y,
                                                       a_start=start)

        so_dim.y = tmp_y
        so_dim.var_y = tmp_var_y

    # Create final 2D spectrum object container
    comb_som = SOM.SOM()
    comb_som.copyAttributes(som1)

    del som1

    # Check for so_id keyword argument
    try:
        so_dim.id = kwargs["so_id"]
    except KeyError:
        so_dim.id = 0

    # Check for y_label keyword argument
    try:
        comb_som.setYLabel(kwargs["y_label"])
    except KeyError:        
        comb_som.setYLabel("Counts")

    # Check for y_units keyword argument
    try:
        comb_som.setYUnits(kwargs["y_units"])
    except KeyError:
        comb_som.setYUnits("Counts / Arb")

    # Check for x_label keyword argument
    try:
        comb_som.setAllAxisLabels(kwargs["x_labels"])
    except KeyError:
        comb_som.setAllAxisLabels(["Parameter", "Arbitrary"])

    # Check for x_units keyword argument
    try:
        comb_som.setAllAxisUnits(kwargs["x_units"])
    except KeyError:
        comb_som.setAllAxisUnits(["Arb", "Arb"])

    comb_som.append(so_dim)

    del so_dim

    return comb_som
def energy_transfer(obj, itype, axis_const, **kwargs):
    """
    This function takes a SOM with a wavelength axis (initial for IGS and
    final for DGS) and calculates the energy transfer.  

    @param obj: The object containing the wavelength axis
    @type obj: C{SOM.SOM}

    @param itype: The instrument class type. The choices are either I{IGS} or
                  I{DGS}.
    @type itype: C{string}

    @param axis_const: The attribute name for the axis constant which is the 
                         final wavelength for I{IGS} and the initial energy for
                         I{DGS}.
    @type axis_const: C{string}

    @param kwargs: A list of keyword arguments that the function accepts:

    @keyword units: The units for the incoming axis. The default is
                    I{Angstroms}.
    @type units: C{string}

    @keyword change_units: A flag that signals the function to convert from
                           I{meV} to I{ueV}. The default is I{False}.
    @type change_units: C{boolean}

    @keyword scale: A flag to scale the y-axis by lambda_f/lambda_i for I{IGS}
                    and lambda_i/lambda_f for I{DGS}. The default is I{False}.
    @type scale: C{boolean}

    @keyword lojac: A flag that turns on the calculation and application of
                    the linear-order Jacobian. The default is I{False}.
    @type lojac: C{boolean}

    @keyword sa_norm: A flag to turn on solid angle normlaization.
    @type sa_norm: C{boolean}

    @return: Object with the energy transfer calculated in units of I{meV} or
             I{ueV}. The default is I{meV}.
    @rtype: C{SOM.SOM}


    @raise RuntimeError: The instrument class type is not recognized
    @raise RuntimeError: The x-axis units are not Angstroms
    @raise RuntimeError: A SOM is not given to the function
    """
    # Check the instrument class type to make sure its allowed
    allowed_types = ["DGS", "IGS"]

    if itype not in allowed_types:
        raise RuntimeError("The instrument class type %s is not known. "\
                           +"Please use DGS or IGS" % itype)

    # import the helper functions
    import hlr_utils

    # set up for working through data
    (result, res_descr) = hlr_utils.empty_result(obj)
    o_descr = hlr_utils.get_descr(obj)

    if o_descr != "SOM":
        raise RuntimeError("Must provide a SOM to the function.")
    # Go on
    else:
        pass

    # Setup keyword arguments
    try:
        units = kwargs["units"]
    except KeyError:
        units = "Angstroms"

    try:
        change_units = kwargs["change_units"]
    except KeyError:
        change_units = False

    try:
        scale = kwargs["scale"]
    except KeyError:
        scale = False

    try:
        sa_norm = kwargs["sa_norm"]
    except KeyError:
        sa_norm = False

    if sa_norm:
        inst = obj.attr_list.instrument

    try:
        lojac = kwargs["lojac"]
    except KeyError:
        lojac = False

    # Primary axis for transformation.
    axis = hlr_utils.one_d_units(obj, units)

    # Get the subtraction constant
    try:
        axis_c = obj.attr_list[axis_const]
    except KeyError:
        raise RuntimeError("Must provide a final wavelength (IGS) or initial "\
                           +"energy (DGS) via the incoming SOM")

    result = hlr_utils.copy_som_attr(result, res_descr, obj, o_descr)
    if change_units:
        unit_str = "ueV"
    else:
        unit_str = "meV"
    result = hlr_utils.force_units(result, unit_str, axis)
    result.setAxisLabel(axis, "energy_transfer")
    result.setYUnits("Counts/" + unit_str)
    result.setYLabel("Intensity")

    # iterate through the values
    import array_manip
    import axis_manip
    import dr_lib
    import utils

    for i in xrange(hlr_utils.get_length(obj)):
        if itype == "IGS":
            l_i = hlr_utils.get_value(obj, i, o_descr, "x", axis)
            l_i_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)
        else:
            l_f = hlr_utils.get_value(obj, i, o_descr, "x", axis)
            l_f_err2 = hlr_utils.get_err2(obj, i, o_descr, "x", axis)

        y_val = hlr_utils.get_value(obj, i, o_descr, "y", axis)
        y_err2 = hlr_utils.get_err2(obj, i, o_descr, "y", axis)

        map_so = hlr_utils.get_map_so(obj, None, i)

        if itype == "IGS":
            (E_i, E_i_err2) = axis_manip.wavelength_to_energy(l_i, l_i_err2)
            l_f = hlr_utils.get_special(axis_c, map_so)[:2]
            (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f[0], l_f[1])
            if lojac:
                (y_val,
                 y_err2) = utils.linear_order_jacobian(l_i, E_i, y_val, y_err2)
        else:
            (E_i, E_i_err2) = axis_c.toValErrTuple()
            (E_f, E_f_err2) = axis_manip.wavelength_to_energy(l_f, l_f_err2)
            if lojac:
                (y_val,
                 y_err2) = utils.linear_order_jacobian(l_f, E_f, y_val, y_err2)

        if scale:
            # Scale counts by lambda_f / lambda_i
            if itype == "IGS":
                (l_n, l_n_err2) = l_f
                (l_d, l_d_err2) = utils.calc_bin_centers(l_i, l_i_err2)
            else:
                (l_n, l_n_err2) = utils.calc_bin_centers(l_f, l_f_err2)
                (l_d,
                 l_d_err2) = axis_manip.energy_to_wavelength(E_i, E_i_err2)

            ratio = array_manip.div_ncerr(l_n, l_n_err2, l_d, l_d_err2)
            scale_y = array_manip.mult_ncerr(y_val, y_err2, ratio[0], ratio[1])
        else:
            scale_y = (y_val, y_err2)

        value = array_manip.sub_ncerr(E_i, E_i_err2, E_f, E_f_err2)

        if change_units:
            # Convert from meV to ueV
            value2 = array_manip.mult_ncerr(value[0], value[1], 1000.0, 0.0)
            scale_y = array_manip.mult_ncerr(scale_y[0], scale_y[1],
                                             1.0 / 1000.0, 0.0)
        else:
            value2 = value

        if sa_norm:
            if inst.get_name() == "BSS":
                dOmega = dr_lib.calc_BSS_solid_angle(map_so, inst)
                scale_y = array_manip.div_ncerr(scale_y[0], scale_y[1], dOmega,
                                                0.0)
            else:
                raise RuntimeError("Do not know how to get solid angle from "\
                                   +"%s" % inst.get_name())

        if itype == "IGS":
            # Reverse the values due to the conversion
            value_y = axis_manip.reverse_array_cp(scale_y[0])
            value_var_y = axis_manip.reverse_array_cp(scale_y[1])
            value_x = axis_manip.reverse_array_cp(value2[0])
        else:
            value_y = scale_y[0]
            value_var_y = scale_y[1]
            value_x = value2[0]

        hlr_utils.result_insert(result, res_descr, (value_y, value_var_y),
                                map_so, "all", 0, [value_x])

    return result