def calibrate_dgs_data(datalist, conf, dkcur, **kwargs): """ This function combines Steps 3 through 6 in Section 2.1.1 of the data reduction process for Direct Geometry Spectrometers as specified by the document at U{http://neutrons.ornl.gov/asg/projects/SCL/reqspec/DR_Lib_RS.doc}. The function takes a list of file names, a L{hlr_utils.Configure} object and processes the data accordingly. @param datalist: A list containing the filenames of the data to be processed. @type datalist: C{list} of C{string}s @param conf: Object that contains the current setup of the driver. @type conf: L{hlr_utils.Configure} @param dkcur: The object containing the TOF dark current data. @type dkcur: C{SOM.SOM} @param kwargs: A list of keyword arguments that the function accepts: @keyword inst_geom_dst: File object that contains instrument geometry information. @type inst_geom_dst: C{DST.GeomDST} @keyword tib_const: A time-independent background constant to subtract from every pixel. @type tib_const: L{hlr_utils.DrParameter} @keyword dataset_type: The practical name of the dataset being processed. The default value is I{data}. @type dataset_type: C{string} @keyword cwp: A list of chopper phase corrections in units of microseconds. @type cwp: C{list} of C{float}s @keyword timer: Timing object so the function can perform timing estimates. @type timer: C{sns_timer.DiffTime} @return: Object that has undergone all requested processing steps @rtype: C{SOM.SOM} """ import common_lib import dr_lib import hlr_utils # Check keywords try: tib_const = kwargs["tib_const"] except KeyError: tib_const = None try: dataset_type = kwargs["dataset_type"] except KeyError: dataset_type = "data" try: t = kwargs["timer"] except KeyError: t = None try: i_geom_dst = kwargs["inst_geom_dst"] except KeyError: i_geom_dst = None dataset_cwp = kwargs.get("cwp") # Open the appropriate datafiles if conf.verbose: print "Reading %s file" % dataset_type data_paths = conf.data_paths.toPath() if conf.no_mon_norm: mon_paths = None else: mon_paths = conf.usmon_path.toPath() # Check for mask file since normalization drive doesn't understand option try: mask_file = conf.mask_file except AttributeError: mask_file = None if t is not None: oldtime = t.getOldTime() (dp_som0, dm_som0) = dr_lib.add_files_dm(datalist, Data_Paths=data_paths, Mon_Paths=mon_paths, SO_Axis=conf.so_axis, Signal_ROI=conf.roi_file, Signal_MASK=mask_file, dataset_type=dataset_type, dataset_cwp=dataset_cwp, Verbose=conf.verbose, Timer=t) if t is not None: t.setOldTime(oldtime) t.getTime(msg="After reading %s file" % dataset_type) # Cut the spectra if necessary dp_somA = dr_lib.cut_spectra(dp_som0, conf.tof_cut_min, conf.tof_cut_max) del dp_som0 dp_somB = dr_lib.fix_bin_contents(dp_somA) del dp_somA if dp_somB.attr_list.instrument.get_name() != "CNCS": if conf.verbose: print "Cutting spectrum at minimum TOF" if t is not None: t.getTime(False) # Calculate minimum TOF for physical neutrons if conf.initial_energy is not None: initial_wavelength = common_lib.energy_to_wavelength(\ conf.initial_energy.toValErrTuple()) initial_velocity = common_lib.wavelength_to_velocity(\ initial_wavelength) else: # This should actually calculate it, but don't have a way right now pass if conf.time_zero_offset is not None: time_zero_offset = conf.time_zero_offset.toValErrTuple() else: # This should actually calculate it, but don't have a way right now time_zero_offset = (0.0, 0.0) ss_length = dp_somB.attr_list.instrument.get_primary() tof_min = (ss_length[0] / initial_velocity[0]) + time_zero_offset[0] # Cut all spectra a the minimum TOF dp_som1 = dr_lib.cut_spectra(dp_somB, tof_min, None) if t is not None: t.getTime(msg="After cutting spectrum at minimum TOF ") else: dp_som1 = dp_somB del dp_somB if dm_som0 is not None: dm_som1 = dr_lib.fix_bin_contents(dm_som0) else: dm_som1 = dm_som0 del dm_som0 # Override geometry if necessary if conf.inst_geom is not None: i_geom_dst.setGeometry(data_paths, dp_som1) if conf.inst_geom is not None and dm_som1 is not None: i_geom_dst.setGeometry(mon_paths, dm_som1) # Step 3: Integrate the upstream monitor if dm_som1 is not None: if conf.verbose: print "Integrating upstream monitor spectrum" if t is not None: t.getTime(False) if conf.mon_int_range is None: start_val = float("inf") end_val = float("inf") else: start_val = conf.mon_int_range[0] end_val = conf.mon_int_range[1] dm_som2 = dr_lib.integrate_spectra(dm_som1, start=start_val, end=end_val, width=True) if t is not None: t.getTime(msg="After integrating upstream monitor spectrum ") else: dm_som2 = dm_som1 del dm_som1 tib_norm_const = None # Step 4: Divide data set by summed monitor spectrum if dm_som2 is not None: if conf.verbose: print "Normalizing %s by monitor sum" % dataset_type if t is not None: t.getTime(False) dp_som2 = common_lib.div_ncerr(dp_som1, dm_som2, length_one_som=True) tib_norm_const = dm_som2[0].y if t is not None: t.getTime(msg="After normalizing %s by monitor sum" % dataset_type) elif conf.pc_norm: if conf.verbose: print "Normalizing %s by proton charge" % dataset_type pc_tag = dataset_type+"-proton_charge" pc = dp_som1.attr_list[pc_tag] # Scale the proton charge and then set the scale PC back to attributes if conf.scale_pc is not None: if conf.verbose: print "Scaling %s proton charge" % dataset_type pc = hlr_utils.scale_proton_charge(pc, conf.scale_pc) dp_som1.attr_list[pc_tag] = pc tib_norm_const = pc.getValue() if t is not None: t.getTime(False) dp_som2 = common_lib.div_ncerr(dp_som1, (pc.getValue(), 0.0)) if t is not None: t.getTime(msg="After normalizing %s by proton charge" \ % dataset_type) else: dp_som2 = dp_som1 del dp_som1, dm_som2 # Step 5: Scale dark current by data set measurement time if dkcur is not None: if conf.verbose: print "Scaling dark current by %s acquisition time" % dataset_type if t is not None: t.getTime(False) dstime_tag = dataset_type+"-duration" dstime = dp_som2.attr_list[dstime_tag] dkcur1 = common_lib.div_ncerr(dkcur, (dstime.getValue(), 0.0)) if t is not None: t.getTime(msg="After scaling dark current by %s acquisition time" \ % dataset_type) else: dkcur1 = dkcur del dkcur # Step 6: Subtract scaled dark current from data set if dkcur1 is not None: if conf.verbose: print "Subtracting %s by scaled dark current" % dataset_type if t is not None: t.getTime(False) dp_som3 = common_lib.sub_ncerr(dp_som2, dkcur1) if t is not None: t.getTime(msg="After subtracting %s by scaled dark current" \ % dataset_type) elif tib_const is not None and dkcur1 is None: if conf.verbose: print "Subtracting TIB constant from %s" % dataset_type # Normalize the TIB constant by dividing by the current normalization # the duration (if necessary) and the conversion from seconds to # microseconds tib_c = tib_const.toValErrTuple() conv_sec_to_usec = 1.0e-6 if tib_norm_const is None: tib_norm_const = 1 duration = 1 else: duration_tag = dataset_type+"-duration" duration = dp_som2.attr_list[duration_tag].getValue() norm_const = (duration * conv_sec_to_usec) / tib_norm_const tib_val = tib_c[0] * norm_const tib_err2 = tib_c[1] * (norm_const * norm_const) if t is not None: t.getTime(False) dp_som3 = common_lib.sub_ncerr(dp_som2, (tib_val, tib_err2)) if t is not None: t.getTime(msg="After subtracting TIB constant from %s" \ % dataset_type) elif conf.tib_range is not None and dkcur1 is None: if conf.verbose: print "Determining TIB constant from %s" % dataset_type if t is not None: t.getTime(False) TIB = dr_lib.determine_time_indep_bkg(dp_som2, conf.tib_range, is_range=True) if t is not None: t.getTime(msg="After determining TIB constant from %s" \ % dataset_type) if conf.dump_tib: file_comment = "TIB TOF Range: [%d, %d]" % (conf.tib_range[0], conf.tib_range[1]) hlr_utils.write_file(conf.output, "text/num-info", TIB, output_ext="tib", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="time-independent background "\ +"information", tag="Average TIB", units="counts/usec", comments=[file_comment]) if conf.verbose: print "Subtracting TIB constant from %s" % dataset_type if t is not None: t.getTime(False) dp_som3 = common_lib.sub_ncerr(dp_som2, TIB) if t is not None: t.getTime(msg="After subtracting TIB constant from %s" \ % dataset_type) del TIB else: dp_som3 = dp_som2 del dp_som2, dkcur1 if conf.dump_ctof_comb: dp_som3_1 = dr_lib.sum_all_spectra(dp_som3) hlr_utils.write_file(conf.output, "text/Spec", dp_som3_1, output_ext="ctof", extra_tag=dataset_type, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, verbose=conf.verbose, message="combined calibrated TOF information") del dp_som3_1 return dp_som3
def process_igs_data(datalist, conf, **kwargs): """ This function combines Steps 1 through 8 of the data reduction process for Inverse Geometry Spectrometers as specified by the documents at U{http://neutrons.ornl.gov/asg/projects/SCL/reqspec/DR_Lib_RS.doc}. The function takes a list of file names, a L{hlr_utils.Configure} object and processes the data accordingly. This function should really only be used in the context of I{amorphous_reduction} and I{calc_norm_eff}. @param datalist: A list containing the filenames of the data to be processed. @type datalist: C{list} of C{string}s @param conf: Object that contains the current setup of the driver. @type conf: L{hlr_utils.Configure} @param kwargs: A list of keyword arguments that the function accepts: @keyword inst_geom_dst: File object that contains instrument geometry information. @type inst_geom_dst: C{DST.GeomDST} @keyword dataset_type: The practical name of the dataset being processed. The default value is I{data}. @type dataset_type: C{string} @keyword tib_const: Object providing the time-independent background constant to subtract. @type tib_const: L{hlr_utils.DrParameter} @keyword bkg_som: Object that will be used for early background subtraction @type bkg_som: C{SOM.SOM} @keyword timer: Timing object so the function can perform timing estimates. @type timer: C{sns_timer.DiffTime} @return: Object that has undergone all requested processing steps @rtype: C{SOM.SOM} """ import hlr_utils # Check keywords try: dataset_type = kwargs["dataset_type"] except KeyError: dataset_type = "data" try: t = kwargs["timer"] except KeyError: t = None try: if kwargs["tib_const"] is not None: tib_const = kwargs["tib_const"].toValErrTuple() else: tib_const = None except KeyError: tib_const = None try: i_geom_dst = kwargs["inst_geom_dst"] except KeyError: i_geom_dst = None try: bkg_som = kwargs["bkg_som"] except KeyError: bkg_som = None # Step 1: Open appropriate data files if not conf.mc: so_axis = "time_of_flight" else: so_axis = "Time_of_Flight" # Add so_axis to Configure object conf.so_axis = so_axis if conf.verbose: print "Reading %s file" % dataset_type # Special case handling for normalization data. Dynamically trying to # determine if incoming file is a previously calculated one. if dataset_type == "normalization": try: # Check the first incoming file dst_type = hlr_utils.file_peeker(datalist[0]) # If file_peeker succeeds, the DST is different than the function # returns dst_type = "text/num-info" # Let ROI file handle filtering data_paths = None except RuntimeError: # It's a NeXus file dst_type = "application/x-NeXus" data_paths = conf.data_paths.toPath() else: dst_type = "application/x-NeXus" data_paths = conf.data_paths.toPath() # The [0] is to get the data SOM and ignore the None background SOM dp_som0 = dr_lib.add_files(datalist, Data_Paths=data_paths, SO_Axis=so_axis, Signal_ROI=conf.roi_file, dataset_type=dataset_type, dst_type=dst_type, Verbose=conf.verbose, Timer=t) if t is not None: t.getTime(msg="After reading %s " % dataset_type) if dst_type == "text/num-info": # Since we have a pre-calculated normalization dataset, set the flag # and return the SOM now conf.pre_norm = True # Make the labels and units compatible with a NeXus file based SOM dp_som0.setAxisLabel(0, "wavelength") dp_som0.setAxisUnits(0, "Angstroms") dp_som0.setYUnits("Counts/A") return dp_som0 else: if dataset_type == "normalization": # Since we have a NeXus file, we need to continue conf.pre_norm = False # Cut the spectra if necessary dp_somA = dr_lib.cut_spectra(dp_som0, conf.tof_cut_min, conf.tof_cut_max) del dp_som0 dp_som1 = dr_lib.fix_bin_contents(dp_somA) del dp_somA if conf.inst_geom is not None: i_geom_dst.setGeometry(conf.data_paths.toPath(), dp_som1) if conf.no_mon_norm: dm_som1 = None else: if conf.verbose: print "Reading in monitor data from %s file" % dataset_type # The [0] is to get the data SOM and ignore the None background SOM dm_som0 = dr_lib.add_files(datalist, Data_Paths=conf.mon_path.toPath(), SO_Axis=so_axis, dataset_type=dataset_type, Verbose=conf.verbose, Timer=t) if t is not None: t.getTime(msg="After reading monitor data ") dm_som1 = dr_lib.fix_bin_contents(dm_som0) del dm_som0 if conf.inst_geom is not None: i_geom_dst.setGeometry(conf.mon_path.toPath(), dm_som1) if bkg_som is not None: bkg_pcharge = bkg_som.attr_list["background-proton_charge"].getValue() data_pcharge = dp_som1.attr_list[dataset_type + "-proton_charge"].getValue() ratio = data_pcharge / bkg_pcharge bkg_som1 = common_lib.mult_ncerr(bkg_som, (ratio, 0.0)) del bkg_som dp_som2 = dr_lib.subtract_bkg_from_data(dp_som1, bkg_som1, verbose=conf.verbose, timer=t, dataset1=dataset_type, dataset2="background") else: dp_som2 = dp_som1 del dp_som1 # Step 2: Dead Time Correction # No dead time correction is being applied to the data yet # Step 3: Time-independent background determination if conf.verbose and conf.tib_tofs is not None: print "Determining time-independent background from data" if t is not None and conf.tib_tofs is not None: t.getTime(False) B = dr_lib.determine_time_indep_bkg(dp_som2, conf.tib_tofs) if t is not None and B is not None: t.getTime(msg="After determining time-independent background ") if conf.dump_tib and B is not None: file_comment = "TOFs: %s" % conf.tib_tofs hlr_utils.write_file(conf.output, "text/num-info", B, output_ext="tib", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="time-independent background "\ +"information", tag="Average", units="counts", comments=[file_comment]) # Step 4: Subtract time-independent background if conf.verbose and B is not None: print "Subtracting time-independent background from data" if t is not None: t.getTime(False) if B is not None: dp_som3 = common_lib.sub_ncerr(dp_som2, B) else: dp_som3 = dp_som2 if B is not None and t is not None: t.getTime(msg="After subtracting time-independent background ") del dp_som2, B # Step 5: Subtract time-independent background constant if conf.verbose and tib_const is not None: print "Subtracting time-independent background constant from data" if t is not None and tib_const is not None: t.getTime(False) if tib_const is not None: dp_som4 = common_lib.sub_ncerr(dp_som3, tib_const) else: dp_som4 = dp_som3 if t is not None and tib_const is not None: t.getTime(msg="After subtracting time-independent background "\ +"constant ") del dp_som3 # Provide override capability for final wavelength, time-zero slope and # time-zero offset if conf.wavelength_final is not None: dp_som4.attr_list["Wavelength_final"] = \ conf.wavelength_final.toValErrTuple() # Note: time_zero_slope MUST be a tuple if conf.time_zero_slope is not None: dp_som4.attr_list["Time_zero_slope"] = \ conf.time_zero_slope.toValErrTuple() if dm_som1 is not None: dm_som1.attr_list["Time_zero_slope"] = \ conf.time_zero_slope.toValErrTuple() # Note: time_zero_offset MUST be a tuple if conf.time_zero_offset is not None: dp_som4.attr_list["Time_zero_offset"] = \ conf.time_zero_offset.toValErrTuple() if dm_som1 is not None: dm_som1.attr_list["Time_zero_offset"] = \ conf.time_zero_offset.toValErrTuple() # Step 6: Convert TOF to wavelength for data and monitor if conf.verbose: print "Converting TOF to wavelength" if t is not None: t.getTime(False) # Convert monitor if dm_som1 is not None: dm_som2 = common_lib.tof_to_wavelength_lin_time_zero( dm_som1, units="microsecond") else: dm_som2 = None # Convert detector pixels dp_som5 = common_lib.tof_to_initial_wavelength_igs_lin_time_zero( dp_som4, units="microsecond", run_filter=conf.filter) if t is not None: t.getTime(msg="After converting TOF to wavelength ") if conf.dump_wave: hlr_utils.write_file(conf.output, "text/Spec", dp_som5, output_ext="pxl", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="pixel wavelength information") if conf.dump_mon_wave and dm_som2 is not None: hlr_utils.write_file(conf.output, "text/Spec", dm_som2, output_ext="mxl", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="monitor wavelength information") del dp_som4, dm_som1 # Step 7: Efficiency correct monitor if conf.verbose and dm_som2 is not None and not conf.no_mon_effc: print "Efficiency correct monitor data" if t is not None: t.getTime(False) if not conf.no_mon_effc: dm_som3 = dr_lib.feff_correct_mon(dm_som2) else: dm_som3 = dm_som2 if t is not None and dm_som2 is not None and not conf.no_mon_effc: t.getTime(msg="After efficiency correcting monitor ") if conf.dump_mon_effc and not conf.no_mon_effc and dm_som3 is not None: hlr_utils.write_file(conf.output, "text/Spec", dm_som3, output_ext="mel", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="monitor wavelength information "\ +"(efficiency)") del dm_som2 # Step 8: Rebin monitor axis onto detector pixel axis if conf.verbose and dm_som3 is not None: print "Rebin monitor axis to detector pixel axis" if t is not None: t.getTime(False) dm_som4 = dr_lib.rebin_monitor(dm_som3, dp_som5) if t is not None and dm_som4 is not None: t.getTime(msg="After rebinning monitor ") del dm_som3 if conf.dump_mon_rebin and dm_som4 is not None: hlr_utils.write_file(conf.output, "text/Spec", dm_som4, output_ext="mrl", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="monitor wavelength information "\ +"(rebinned)") # The lambda-dependent background is only done on sample data (aka data) # for the BSS instrument at the SNS if conf.inst == "BSS" and conf.ldb_const is not None and \ dataset_type == "data": # Step 9: Convert chopper center wavelength to TOF center if conf.verbose: print "Converting chopper center wavelength to TOF" if t is not None: t.getTime(False) tof_center = dr_lib.convert_single_to_list(\ "initial_wavelength_igs_lin_time_zero_to_tof", conf.chopper_lambda_cent.toValErrTuple(), dp_som5) # Step 10: Calculate beginning and end of detector TOF spectrum if conf.verbose: print "Calculating beginning and ending TOF ranges" half_inv_chop_freq = 0.5 / conf.chopper_freq.toValErrTuple()[0] # Above is in seconds, need microseconds half_inv_chop_freq *= 1.0e6 tof_begin = common_lib.sub_ncerr(tof_center, (half_inv_chop_freq, 0.0)) tof_end = common_lib.add_ncerr(tof_center, (half_inv_chop_freq, 0.0)) # Step 11: Convert TOF_begin and TOF_end to wavelength if conf.verbose: print "Converting TOF_begin and TOF_end to wavelength" # Check for time-zero slope information try: tz_slope = conf.time_zero_slope.toValErrTuple() except AttributeError: tz_slope = (0.0, 0.0) # Check for time-zero offset information try: tz_offset = conf.time_zero_offset.toValErrTuple() except AttributeError: tz_offset = (0.0, 0.0) l_begin = common_lib.tof_to_initial_wavelength_igs_lin_time_zero(\ tof_begin, time_zero_slope=tz_slope, time_zero_offset=tz_offset, iobj=dp_som5, run_filter=False) l_end = common_lib.tof_to_initial_wavelength_igs_lin_time_zero(\ tof_end, time_zero_slope=tz_slope, time_zero_offset=tz_offset, iobj=dp_som5, run_filter=False) # Step 12: tof-least-bkg to lambda-least-bkg if conf.verbose: print "Converting TOF least background to wavelength" lambda_least_bkg = dr_lib.convert_single_to_list(\ "tof_to_initial_wavelength_igs_lin_time_zero", conf.tof_least_bkg.toValErrTuple(), dp_som5) if t is not None: t.getTime(msg="After converting boundary positions ") # Step 13: Create lambda-dependent background spectrum if conf.verbose: print "Creating lambda-dependent background spectra" if t is not None: t.getTime(False) ldb_som = dr_lib.shift_spectrum(dm_som4, lambda_least_bkg, l_begin, l_end, conf.ldb_const.getValue()) if t is not None: t.getTime(msg="After creating lambda-dependent background "\ +"spectra ") # Step 14: Subtract lambda-dependent background from sample data if conf.verbose: print "Subtracting lambda-dependent background from data" if t is not None: t.getTime(False) dp_som6 = common_lib.sub_ncerr(dp_som5, ldb_som) if t is not None: t.getTime(msg="After subtracting lambda-dependent background "\ +"from data ") else: dp_som6 = dp_som5 del dp_som5 # Step 15: Normalize data by monitor if conf.verbose and dm_som4 is not None: print "Normalizing data by monitor" if t is not None: t.getTime(False) if dm_som4 is not None: dp_som7 = common_lib.div_ncerr(dp_som6, dm_som4) if t is not None: t.getTime(msg="After normalizing data by monitor ") else: dp_som7 = dp_som6 if conf.dump_wave_mnorm: dp_som7_1 = dr_lib.sum_all_spectra(dp_som7,\ rebin_axis=conf.lambda_bins.toNessiList()) write_message = "combined pixel wavelength information" if dm_som4 is not None: write_message += " (monitor normalized)" hlr_utils.write_file(conf.output, "text/Spec", dp_som7_1, output_ext="pml", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message=write_message) del dp_som7_1 del dm_som4, dp_som6 return dp_som7
def calibrate_dgs_data(datalist, conf, dkcur, **kwargs): """ This function combines Steps 3 through 6 in Section 2.1.1 of the data reduction process for Direct Geometry Spectrometers as specified by the document at U{http://neutrons.ornl.gov/asg/projects/SCL/reqspec/DR_Lib_RS.doc}. The function takes a list of file names, a L{hlr_utils.Configure} object and processes the data accordingly. @param datalist: A list containing the filenames of the data to be processed. @type datalist: C{list} of C{string}s @param conf: Object that contains the current setup of the driver. @type conf: L{hlr_utils.Configure} @param dkcur: The object containing the TOF dark current data. @type dkcur: C{SOM.SOM} @param kwargs: A list of keyword arguments that the function accepts: @keyword inst_geom_dst: File object that contains instrument geometry information. @type inst_geom_dst: C{DST.GeomDST} @keyword tib_const: A time-independent background constant to subtract from every pixel. @type tib_const: L{hlr_utils.DrParameter} @keyword dataset_type: The practical name of the dataset being processed. The default value is I{data}. @type dataset_type: C{string} @keyword cwp: A list of chopper phase corrections in units of microseconds. @type cwp: C{list} of C{float}s @keyword timer: Timing object so the function can perform timing estimates. @type timer: C{sns_timer.DiffTime} @return: Object that has undergone all requested processing steps @rtype: C{SOM.SOM} """ import common_lib import dr_lib import hlr_utils # Check keywords try: tib_const = kwargs["tib_const"] except KeyError: tib_const = None try: dataset_type = kwargs["dataset_type"] except KeyError: dataset_type = "data" try: t = kwargs["timer"] except KeyError: t = None try: i_geom_dst = kwargs["inst_geom_dst"] except KeyError: i_geom_dst = None dataset_cwp = kwargs.get("cwp") # Open the appropriate datafiles if conf.verbose: print "Reading %s file" % dataset_type data_paths = conf.data_paths.toPath() if conf.no_mon_norm: mon_paths = None else: mon_paths = conf.usmon_path.toPath() # Check for mask file since normalization drive doesn't understand option try: mask_file = conf.mask_file except AttributeError: mask_file = None if t is not None: oldtime = t.getOldTime() (dp_som0, dm_som0) = dr_lib.add_files_dm(datalist, Data_Paths=data_paths, Mon_Paths=mon_paths, SO_Axis=conf.so_axis, Signal_ROI=conf.roi_file, Signal_MASK=mask_file, dataset_type=dataset_type, dataset_cwp=dataset_cwp, Verbose=conf.verbose, Timer=t) if t is not None: t.setOldTime(oldtime) t.getTime(msg="After reading %s file" % dataset_type) # Cut the spectra if necessary dp_somA = dr_lib.cut_spectra(dp_som0, conf.tof_cut_min, conf.tof_cut_max) del dp_som0 dp_somB = dr_lib.fix_bin_contents(dp_somA) del dp_somA if dp_somB.attr_list.instrument.get_name() != "CNCS": if conf.verbose: print "Cutting spectrum at minimum TOF" if t is not None: t.getTime(False) # Calculate minimum TOF for physical neutrons if conf.initial_energy is not None: initial_wavelength = common_lib.energy_to_wavelength(\ conf.initial_energy.toValErrTuple()) initial_velocity = common_lib.wavelength_to_velocity(\ initial_wavelength) else: # This should actually calculate it, but don't have a way right now pass if conf.time_zero_offset is not None: time_zero_offset = conf.time_zero_offset.toValErrTuple() else: # This should actually calculate it, but don't have a way right now time_zero_offset = (0.0, 0.0) ss_length = dp_somB.attr_list.instrument.get_primary() tof_min = (ss_length[0] / initial_velocity[0]) + time_zero_offset[0] # Cut all spectra a the minimum TOF dp_som1 = dr_lib.cut_spectra(dp_somB, tof_min, None) if t is not None: t.getTime(msg="After cutting spectrum at minimum TOF ") else: dp_som1 = dp_somB del dp_somB if dm_som0 is not None: dm_som1 = dr_lib.fix_bin_contents(dm_som0) else: dm_som1 = dm_som0 del dm_som0 # Override geometry if necessary if conf.inst_geom is not None: i_geom_dst.setGeometry(data_paths, dp_som1) if conf.inst_geom is not None and dm_som1 is not None: i_geom_dst.setGeometry(mon_paths, dm_som1) # Step 3: Integrate the upstream monitor if dm_som1 is not None: if conf.verbose: print "Integrating upstream monitor spectrum" if t is not None: t.getTime(False) if conf.mon_int_range is None: start_val = float("inf") end_val = float("inf") else: start_val = conf.mon_int_range[0] end_val = conf.mon_int_range[1] dm_som2 = dr_lib.integrate_spectra(dm_som1, start=start_val, end=end_val, width=True) if t is not None: t.getTime(msg="After integrating upstream monitor spectrum ") else: dm_som2 = dm_som1 del dm_som1 tib_norm_const = None # Step 4: Divide data set by summed monitor spectrum if dm_som2 is not None: if conf.verbose: print "Normalizing %s by monitor sum" % dataset_type if t is not None: t.getTime(False) dp_som2 = common_lib.div_ncerr(dp_som1, dm_som2, length_one_som=True) tib_norm_const = dm_som2[0].y if t is not None: t.getTime(msg="After normalizing %s by monitor sum" % dataset_type) elif conf.pc_norm: if conf.verbose: print "Normalizing %s by proton charge" % dataset_type pc_tag = dataset_type + "-proton_charge" pc = dp_som1.attr_list[pc_tag] # Scale the proton charge and then set the scale PC back to attributes if conf.scale_pc is not None: if conf.verbose: print "Scaling %s proton charge" % dataset_type pc = hlr_utils.scale_proton_charge(pc, conf.scale_pc) dp_som1.attr_list[pc_tag] = pc tib_norm_const = pc.getValue() if t is not None: t.getTime(False) dp_som2 = common_lib.div_ncerr(dp_som1, (pc.getValue(), 0.0)) if t is not None: t.getTime(msg="After normalizing %s by proton charge" \ % dataset_type) else: dp_som2 = dp_som1 del dp_som1, dm_som2 # Step 5: Scale dark current by data set measurement time if dkcur is not None: if conf.verbose: print "Scaling dark current by %s acquisition time" % dataset_type if t is not None: t.getTime(False) dstime_tag = dataset_type + "-duration" dstime = dp_som2.attr_list[dstime_tag] dkcur1 = common_lib.div_ncerr(dkcur, (dstime.getValue(), 0.0)) if t is not None: t.getTime(msg="After scaling dark current by %s acquisition time" \ % dataset_type) else: dkcur1 = dkcur del dkcur # Step 6: Subtract scaled dark current from data set if dkcur1 is not None: if conf.verbose: print "Subtracting %s by scaled dark current" % dataset_type if t is not None: t.getTime(False) dp_som3 = common_lib.sub_ncerr(dp_som2, dkcur1) if t is not None: t.getTime(msg="After subtracting %s by scaled dark current" \ % dataset_type) elif tib_const is not None and dkcur1 is None: if conf.verbose: print "Subtracting TIB constant from %s" % dataset_type # Normalize the TIB constant by dividing by the current normalization # the duration (if necessary) and the conversion from seconds to # microseconds tib_c = tib_const.toValErrTuple() conv_sec_to_usec = 1.0e-6 if tib_norm_const is None: tib_norm_const = 1 duration = 1 else: duration_tag = dataset_type + "-duration" duration = dp_som2.attr_list[duration_tag].getValue() norm_const = (duration * conv_sec_to_usec) / tib_norm_const tib_val = tib_c[0] * norm_const tib_err2 = tib_c[1] * (norm_const * norm_const) if t is not None: t.getTime(False) dp_som3 = common_lib.sub_ncerr(dp_som2, (tib_val, tib_err2)) if t is not None: t.getTime(msg="After subtracting TIB constant from %s" \ % dataset_type) elif conf.tib_range is not None and dkcur1 is None: if conf.verbose: print "Determining TIB constant from %s" % dataset_type if t is not None: t.getTime(False) TIB = dr_lib.determine_time_indep_bkg(dp_som2, conf.tib_range, is_range=True) if t is not None: t.getTime(msg="After determining TIB constant from %s" \ % dataset_type) if conf.dump_tib: file_comment = "TIB TOF Range: [%d, %d]" % (conf.tib_range[0], conf.tib_range[1]) hlr_utils.write_file(conf.output, "text/num-info", TIB, output_ext="tib", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="time-independent background "\ +"information", tag="Average TIB", units="counts/usec", comments=[file_comment]) if conf.verbose: print "Subtracting TIB constant from %s" % dataset_type if t is not None: t.getTime(False) dp_som3 = common_lib.sub_ncerr(dp_som2, TIB) if t is not None: t.getTime(msg="After subtracting TIB constant from %s" \ % dataset_type) del TIB else: dp_som3 = dp_som2 del dp_som2, dkcur1 if conf.dump_ctof_comb: dp_som3_1 = dr_lib.sum_all_spectra(dp_som3) hlr_utils.write_file(conf.output, "text/Spec", dp_som3_1, output_ext="ctof", extra_tag=dataset_type, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, verbose=conf.verbose, message="combined calibrated TOF information") del dp_som3_1 return dp_som3
def process_igs_data(datalist, conf, **kwargs): """ This function combines Steps 1 through 8 of the data reduction process for Inverse Geometry Spectrometers as specified by the documents at U{http://neutrons.ornl.gov/asg/projects/SCL/reqspec/DR_Lib_RS.doc}. The function takes a list of file names, a L{hlr_utils.Configure} object and processes the data accordingly. This function should really only be used in the context of I{amorphous_reduction} and I{calc_norm_eff}. @param datalist: A list containing the filenames of the data to be processed. @type datalist: C{list} of C{string}s @param conf: Object that contains the current setup of the driver. @type conf: L{hlr_utils.Configure} @param kwargs: A list of keyword arguments that the function accepts: @keyword inst_geom_dst: File object that contains instrument geometry information. @type inst_geom_dst: C{DST.GeomDST} @keyword dataset_type: The practical name of the dataset being processed. The default value is I{data}. @type dataset_type: C{string} @keyword tib_const: Object providing the time-independent background constant to subtract. @type tib_const: L{hlr_utils.DrParameter} @keyword bkg_som: Object that will be used for early background subtraction @type bkg_som: C{SOM.SOM} @keyword timer: Timing object so the function can perform timing estimates. @type timer: C{sns_timer.DiffTime} @return: Object that has undergone all requested processing steps @rtype: C{SOM.SOM} """ import hlr_utils # Check keywords try: dataset_type = kwargs["dataset_type"] except KeyError: dataset_type = "data" try: t = kwargs["timer"] except KeyError: t = None try: if kwargs["tib_const"] is not None: tib_const = kwargs["tib_const"].toValErrTuple() else: tib_const = None except KeyError: tib_const = None try: i_geom_dst = kwargs["inst_geom_dst"] except KeyError: i_geom_dst = None try: bkg_som = kwargs["bkg_som"] except KeyError: bkg_som = None # Step 1: Open appropriate data files if not conf.mc: so_axis = "time_of_flight" else: so_axis = "Time_of_Flight" # Add so_axis to Configure object conf.so_axis = so_axis if conf.verbose: print "Reading %s file" % dataset_type # Special case handling for normalization data. Dynamically trying to # determine if incoming file is a previously calculated one. if dataset_type == "normalization": try: # Check the first incoming file dst_type = hlr_utils.file_peeker(datalist[0]) # If file_peeker succeeds, the DST is different than the function # returns dst_type = "text/num-info" # Let ROI file handle filtering data_paths = None except RuntimeError: # It's a NeXus file dst_type = "application/x-NeXus" data_paths = conf.data_paths.toPath() else: dst_type = "application/x-NeXus" data_paths = conf.data_paths.toPath() # The [0] is to get the data SOM and ignore the None background SOM dp_som0 = dr_lib.add_files( datalist, Data_Paths=data_paths, SO_Axis=so_axis, Signal_ROI=conf.roi_file, dataset_type=dataset_type, dst_type=dst_type, Verbose=conf.verbose, Timer=t, ) if t is not None: t.getTime(msg="After reading %s " % dataset_type) if dst_type == "text/num-info": # Since we have a pre-calculated normalization dataset, set the flag # and return the SOM now conf.pre_norm = True # Make the labels and units compatible with a NeXus file based SOM dp_som0.setAxisLabel(0, "wavelength") dp_som0.setAxisUnits(0, "Angstroms") dp_som0.setYUnits("Counts/A") return dp_som0 else: if dataset_type == "normalization": # Since we have a NeXus file, we need to continue conf.pre_norm = False # Cut the spectra if necessary dp_somA = dr_lib.cut_spectra(dp_som0, conf.tof_cut_min, conf.tof_cut_max) del dp_som0 dp_som1 = dr_lib.fix_bin_contents(dp_somA) del dp_somA if conf.inst_geom is not None: i_geom_dst.setGeometry(conf.data_paths.toPath(), dp_som1) if conf.no_mon_norm: dm_som1 = None else: if conf.verbose: print "Reading in monitor data from %s file" % dataset_type # The [0] is to get the data SOM and ignore the None background SOM dm_som0 = dr_lib.add_files( datalist, Data_Paths=conf.mon_path.toPath(), SO_Axis=so_axis, dataset_type=dataset_type, Verbose=conf.verbose, Timer=t, ) if t is not None: t.getTime(msg="After reading monitor data ") dm_som1 = dr_lib.fix_bin_contents(dm_som0) del dm_som0 if conf.inst_geom is not None: i_geom_dst.setGeometry(conf.mon_path.toPath(), dm_som1) if bkg_som is not None: bkg_pcharge = bkg_som.attr_list["background-proton_charge"].getValue() data_pcharge = dp_som1.attr_list[dataset_type + "-proton_charge"].getValue() ratio = data_pcharge / bkg_pcharge bkg_som1 = common_lib.mult_ncerr(bkg_som, (ratio, 0.0)) del bkg_som dp_som2 = dr_lib.subtract_bkg_from_data( dp_som1, bkg_som1, verbose=conf.verbose, timer=t, dataset1=dataset_type, dataset2="background" ) else: dp_som2 = dp_som1 del dp_som1 # Step 2: Dead Time Correction # No dead time correction is being applied to the data yet # Step 3: Time-independent background determination if conf.verbose and conf.tib_tofs is not None: print "Determining time-independent background from data" if t is not None and conf.tib_tofs is not None: t.getTime(False) B = dr_lib.determine_time_indep_bkg(dp_som2, conf.tib_tofs) if t is not None and B is not None: t.getTime(msg="After determining time-independent background ") if conf.dump_tib and B is not None: file_comment = "TOFs: %s" % conf.tib_tofs hlr_utils.write_file( conf.output, "text/num-info", B, output_ext="tib", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="time-independent background " + "information", tag="Average", units="counts", comments=[file_comment], ) # Step 4: Subtract time-independent background if conf.verbose and B is not None: print "Subtracting time-independent background from data" if t is not None: t.getTime(False) if B is not None: dp_som3 = common_lib.sub_ncerr(dp_som2, B) else: dp_som3 = dp_som2 if B is not None and t is not None: t.getTime(msg="After subtracting time-independent background ") del dp_som2, B # Step 5: Subtract time-independent background constant if conf.verbose and tib_const is not None: print "Subtracting time-independent background constant from data" if t is not None and tib_const is not None: t.getTime(False) if tib_const is not None: dp_som4 = common_lib.sub_ncerr(dp_som3, tib_const) else: dp_som4 = dp_som3 if t is not None and tib_const is not None: t.getTime(msg="After subtracting time-independent background " + "constant ") del dp_som3 # Provide override capability for final wavelength, time-zero slope and # time-zero offset if conf.wavelength_final is not None: dp_som4.attr_list["Wavelength_final"] = conf.wavelength_final.toValErrTuple() # Note: time_zero_slope MUST be a tuple if conf.time_zero_slope is not None: dp_som4.attr_list["Time_zero_slope"] = conf.time_zero_slope.toValErrTuple() if dm_som1 is not None: dm_som1.attr_list["Time_zero_slope"] = conf.time_zero_slope.toValErrTuple() # Note: time_zero_offset MUST be a tuple if conf.time_zero_offset is not None: dp_som4.attr_list["Time_zero_offset"] = conf.time_zero_offset.toValErrTuple() if dm_som1 is not None: dm_som1.attr_list["Time_zero_offset"] = conf.time_zero_offset.toValErrTuple() # Step 6: Convert TOF to wavelength for data and monitor if conf.verbose: print "Converting TOF to wavelength" if t is not None: t.getTime(False) # Convert monitor if dm_som1 is not None: dm_som2 = common_lib.tof_to_wavelength_lin_time_zero(dm_som1, units="microsecond") else: dm_som2 = None # Convert detector pixels dp_som5 = common_lib.tof_to_initial_wavelength_igs_lin_time_zero( dp_som4, units="microsecond", run_filter=conf.filter ) if t is not None: t.getTime(msg="After converting TOF to wavelength ") if conf.dump_wave: hlr_utils.write_file( conf.output, "text/Spec", dp_som5, output_ext="pxl", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="pixel wavelength information", ) if conf.dump_mon_wave and dm_som2 is not None: hlr_utils.write_file( conf.output, "text/Spec", dm_som2, output_ext="mxl", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="monitor wavelength information", ) del dp_som4, dm_som1 # Step 7: Efficiency correct monitor if conf.verbose and dm_som2 is not None and not conf.no_mon_effc: print "Efficiency correct monitor data" if t is not None: t.getTime(False) if not conf.no_mon_effc: dm_som3 = dr_lib.feff_correct_mon(dm_som2) else: dm_som3 = dm_som2 if t is not None and dm_som2 is not None and not conf.no_mon_effc: t.getTime(msg="After efficiency correcting monitor ") if conf.dump_mon_effc and not conf.no_mon_effc and dm_som3 is not None: hlr_utils.write_file( conf.output, "text/Spec", dm_som3, output_ext="mel", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="monitor wavelength information " + "(efficiency)", ) del dm_som2 # Step 8: Rebin monitor axis onto detector pixel axis if conf.verbose and dm_som3 is not None: print "Rebin monitor axis to detector pixel axis" if t is not None: t.getTime(False) dm_som4 = dr_lib.rebin_monitor(dm_som3, dp_som5) if t is not None and dm_som4 is not None: t.getTime(msg="After rebinning monitor ") del dm_som3 if conf.dump_mon_rebin and dm_som4 is not None: hlr_utils.write_file( conf.output, "text/Spec", dm_som4, output_ext="mrl", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message="monitor wavelength information " + "(rebinned)", ) # The lambda-dependent background is only done on sample data (aka data) # for the BSS instrument at the SNS if conf.inst == "BSS" and conf.ldb_const is not None and dataset_type == "data": # Step 9: Convert chopper center wavelength to TOF center if conf.verbose: print "Converting chopper center wavelength to TOF" if t is not None: t.getTime(False) tof_center = dr_lib.convert_single_to_list( "initial_wavelength_igs_lin_time_zero_to_tof", conf.chopper_lambda_cent.toValErrTuple(), dp_som5 ) # Step 10: Calculate beginning and end of detector TOF spectrum if conf.verbose: print "Calculating beginning and ending TOF ranges" half_inv_chop_freq = 0.5 / conf.chopper_freq.toValErrTuple()[0] # Above is in seconds, need microseconds half_inv_chop_freq *= 1.0e6 tof_begin = common_lib.sub_ncerr(tof_center, (half_inv_chop_freq, 0.0)) tof_end = common_lib.add_ncerr(tof_center, (half_inv_chop_freq, 0.0)) # Step 11: Convert TOF_begin and TOF_end to wavelength if conf.verbose: print "Converting TOF_begin and TOF_end to wavelength" # Check for time-zero slope information try: tz_slope = conf.time_zero_slope.toValErrTuple() except AttributeError: tz_slope = (0.0, 0.0) # Check for time-zero offset information try: tz_offset = conf.time_zero_offset.toValErrTuple() except AttributeError: tz_offset = (0.0, 0.0) l_begin = common_lib.tof_to_initial_wavelength_igs_lin_time_zero( tof_begin, time_zero_slope=tz_slope, time_zero_offset=tz_offset, iobj=dp_som5, run_filter=False ) l_end = common_lib.tof_to_initial_wavelength_igs_lin_time_zero( tof_end, time_zero_slope=tz_slope, time_zero_offset=tz_offset, iobj=dp_som5, run_filter=False ) # Step 12: tof-least-bkg to lambda-least-bkg if conf.verbose: print "Converting TOF least background to wavelength" lambda_least_bkg = dr_lib.convert_single_to_list( "tof_to_initial_wavelength_igs_lin_time_zero", conf.tof_least_bkg.toValErrTuple(), dp_som5 ) if t is not None: t.getTime(msg="After converting boundary positions ") # Step 13: Create lambda-dependent background spectrum if conf.verbose: print "Creating lambda-dependent background spectra" if t is not None: t.getTime(False) ldb_som = dr_lib.shift_spectrum(dm_som4, lambda_least_bkg, l_begin, l_end, conf.ldb_const.getValue()) if t is not None: t.getTime(msg="After creating lambda-dependent background " + "spectra ") # Step 14: Subtract lambda-dependent background from sample data if conf.verbose: print "Subtracting lambda-dependent background from data" if t is not None: t.getTime(False) dp_som6 = common_lib.sub_ncerr(dp_som5, ldb_som) if t is not None: t.getTime(msg="After subtracting lambda-dependent background " + "from data ") else: dp_som6 = dp_som5 del dp_som5 # Step 15: Normalize data by monitor if conf.verbose and dm_som4 is not None: print "Normalizing data by monitor" if t is not None: t.getTime(False) if dm_som4 is not None: dp_som7 = common_lib.div_ncerr(dp_som6, dm_som4) if t is not None: t.getTime(msg="After normalizing data by monitor ") else: dp_som7 = dp_som6 if conf.dump_wave_mnorm: dp_som7_1 = dr_lib.sum_all_spectra(dp_som7, rebin_axis=conf.lambda_bins.toNessiList()) write_message = "combined pixel wavelength information" if dm_som4 is not None: write_message += " (monitor normalized)" hlr_utils.write_file( conf.output, "text/Spec", dp_som7_1, output_ext="pml", extra_tag=dataset_type, verbose=conf.verbose, data_ext=conf.ext_replacement, path_replacement=conf.path_replacement, message=write_message, ) del dp_som7_1 del dm_som4, dp_som6 return dp_som7