Skip to content
Snippets Groups Projects
Select Git revision
  • d6cd25d5a604c83457e6f39e41f0da1c83fa34da
  • master default protected
  • debug_summertime_expulsion
3 results

makefile

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    bg_subtraction.py 37.58 KiB
    import numpy as np    
    import pathlib
    from functions.utils import round_to_multiple, find_nearest, shorten_matrix_elements, getIfile
    from functions.export import outputDatFile
    
    rootdir = str(pathlib.Path(__file__).parent.resolve())
    
    class Processing_Data:
        """ Class to filter, average, rebin, BG-subtract and generate PDFs from XRD/TS data.
    
        Raises:
            Exception: _description_
        """
        #----------------------------------------------------------------------------------------#
        #                                 Get Start and End of ramp                              #
        #----------------------------------------------------------------------------------------#
        def getTempPoints(self,rampTemp,holdTemp):
            """ 
            
            Arguments
            ----------
            rampTemp:   temperature the ramp is defined to start 
            holdTemp:   temperature the plato is defined to start
            """
            
            # Logging the event
            self.logger['Scan idizes of the ramp satrt and end collected'] = {  'Temperature the ramp is set to start': rampTemp,
                                                                                'Temperatrue the plato is set to start': holdTemp}
            
            # Set initial status varibles
            ramp_started = False
            plato_started = False
            plato_ended = False
    
            # Normalize the file index for start and end scan given 
            if self.startScan > 0:
                if self.endScan < self.number_of_files*self.number_of_steps:
                    file_index = np.linspace(self.startScan,self.endScan,self.number_of_files)
                else:
                    file_index = np.linspace(self.startScan,self.number_of_files*self.number_of_steps,self.number_of_files)
            else: 
                if self.endScan < self.number_of_files*self.number_of_steps:
                    file_index = np.linspace(1,self.endScan,self.number_of_files)
                else:
                    file_index = np.linspace(1,self.number_of_files*self.number_of_steps,self.number_of_files)
    
            # Give feedback to the user about the status of the integration. 
            # --> If more .metadata files are available the .chi/.dat.
            if len(file_index) < len(self.temp_array):   
                print('\n\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
                print('          Live integration lags behind by ' + str(len(self.temp_array) - len(file_index)) + ' scans')
                print(' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n')
    
            # Here we loop over the temperature array
            for i,j in enumerate(self.temp_array):
    
                # And check if this is still below the temperature threshold of the ramp.
                if j > rampTemp and ramp_started is False:
                    # If the threshold is reached, this experimantal temperature is logged and
                    # the status varible is updated
                    print('Ramp start: ', file_index[i],'Temp: ', j)
                    self.rampstart_index = int(file_index[i])
                    ramp_started = True 
                    self.logger['Ramp Start'] = {   'Index': file_index[i],
                                                    'Temperature': j}
                    
                # Here we check if the ramp has finished and the final temperature is reached
                if j > holdTemp and plato_started is False:
                    # If the threshold is reached, this experimantal temperature is logged and
                    # the status varible is updated
                    print('Plato start: ',file_index[i],'Temp: ', j)
                    self.platostart_index = int(file_index[i])
                    plato_started = True 
                    self.logger['Plato Start'] = {  'Index': file_index[i],
                                                    'Temperature': j}
                
                # Finally we check when the cooldown is reached
                if j < holdTemp-5 and plato_ended is False and plato_started is True:
                    
                    # If the threshold is reached, this experimantal temperature is logged and
                    # the status varible is updated
                    print('Plato endet: ', file_index[i],'Temp: ', j)
                    plato_ended = True
                    self.platoend_index = int(file_index[i]) 
                    self.logger['Plato End'] = {   'Index': file_index[i],
                                                    'Temperature': j}
            
            # If a threshold is never reached an error is risen.
            if 'platostart_index' not in dir(self):
                raise Exception(f"Temperature always below the defined set tempertaure of {holdTemp}. Temperature is at max {max(self.temp_array)}") 
            
            # If no cooldown is measured we stop
            if 'platoend_index' not in dir(self):
                self.platoend_index = len(self.temp_array)
            return self.logger, self.rampstart_index, self.platoend_index, self.platoend_index
    
        
        #----------------------------------------------------------------------------------------#
        #                               Get parts of ramps and platos                            #
        #----------------------------------------------------------------------------------------#
        def getRampPlatoLists(self,heat_ranges):
            """ Check which parts of the data belong to a plato (befor starting the temperature ramp and when holding the final temperature)
                and which parts belong to a temperature ramp.
            
            Arguments
            ----------
            heat ranges:    heat ranges dictionary including the start and end of the temp ramp
            """
            
            # Logging the event
            self.logger['I(q) for Ramp and Plato where collected in lists'] = {   'Heatrages input': heat_ranges,}
            
            # Create the empty lists
            plato_file_list = []
            self.plato_list = [[],[]]
            ramp_file_list = []
            self.ramp_list = [[],[]]
    
            # Step one is the plato at room temperature befor ramping
            step = 1
            
            # Give user feedback about the determined temperatures 
            print(heat_ranges)
            
            # Loop over every I(q) from each scan
            for i, j in enumerate(self.I):
                
                # If step is odd it´s a plato
                if step % 2 != 0:
                    plato_file_list.append(j)
                
                # If step is even it´s ramp
                if step % 2 == 0:
                    ramp_file_list.append(j)
    
                # Here we check when the ramp starts and account for averaging
                if  i == round_to_multiple(heat_ranges["ramp1_start"],self.average) /self.average:
                    
                    # We put the array of all I(q) from the first plato (at room temperature) as a first element of a list of all platos
                    self.plato_list[0].append(plato_file_list)
                    plato_file_list = []
                    
                    # We move on to the first ramp 
                    step = 2
    
                # Here we check when the first set temperature starts and account for averaging
                if i == round_to_multiple(heat_ranges["ramp1_end"],self.average) /self.average:
                    
                    # We put the array of all I(q) from the first ramp as a first element of a list of all ramps
                    self.ramp_list[0].append(ramp_file_list)
                    ramp_file_list = []
                    step = 3
    
                # Here we check when second ramp starts (if set) and account for averaging
                if i == round_to_multiple(heat_ranges["ramp2_start"],self.average) /self.average:
                    
                    # We put the array of all I(q) from the second plato (at the first set temperature) as a first element of a list of all platos
                    self.plato_list[0].append(plato_file_list)
                    plato_file_list = []
                    
                    # It´s rare but we also can move on to a second ramp 
                    step = 4
    
                # Here we check when the second set temperature (if set) starts and account for averaging
                if i == round_to_multiple(heat_ranges["ramp2_end"],self.average) /self.average:
                    
                    # We put the array of all I(q) from the second ramp as a first element of a list of all ramps
                    self.ramp_list[0].append(ramp_file_list)
                    ramp_file_list = []
                    
                    # It´s rare but we also can move on to a second hold tmperature
                    step = 5
    
            # It´s rare but we also can put the array of all I(q) from the third plato (at the second set temperature) as a first element of a list of all platos
            self.plato_list[0].append(plato_file_list)
            plato_file_list = []
            
            return self.logger, self.plato_list, self.ramp_list
    
    
        #----------------------------------------------------------------------------------------#
        #                                  Background Subtraction                                #
        #----------------------------------------------------------------------------------------#
        def bgSubtraction(self, heat_ranges, outputPath, bg_sampleName: str = "background_undifiend_by_user", plato_bg_list = None, ramp_bg_list = None,  mode: str='last', 
                          Qmaxinst: float=30, FactorMax: float=0.99,single_BG_file: str=None, empty_glas_int: float=0, correction: list = 0,
                          empty_glas: str=None, Qmin: float=None, Qmax: float=None, qparts: int=30, convergence_critierium: float= 0.9, convergence_step: float=0.001,
                          use_PDF_maximization: bool=False, R_limits: list = [1.8, 2.5], grid_size: int=8, reducing_steps: int=5, starting_step_width: float=0.1):
            """Automated BG subtraction 
    
            Args:
                heat_ranges (dict): heat ranges dictionary including the start and end of the temp ramp.
                outputPath (str): Path to where .dat generated after BG subtration are saved. 
                bg_sampleName (str): Name of the BG sample for .dat output. Defaults to "background_undifiend_by_user".
                plato_bg_list (list): list of array from the plato related background scans. Not needed for single background file. Defaults to None.
                ramp_bg_list (list): list of array from the ramp related background scans. Not needed for single background file. Defaults to None.
                mode (str, optional): Choose "last" to repeat the last background scan or "average" to use an everage of the plato. Defaults to 'last'.
                FactorMax (float, optional): Maximum factor allowed for the background intesity subtracted from the data. Defaults to 0.99.
                single_BG_file (str, optional): Path to a single background if only one background file should be subtracted from all data. Defaults to None.
                empty_glas_int (float, optional): Intensity with which a constant contribution of glas is removed from each scan. Defaults to 0.
                empty_glas (str, optional): Path to the empty glas file. Defaults to None.
                Qmin (float, optional): Minimum Q that is included in the BG subtrastion (this can purposly differ from Qmin for PDF generation). Defaults to None.
                Qmax (float, optional): Maximum Q that is included in the BG subtrastion (this can purposly differ from Qmax for PDF generation). Defaults to None.
                use_PDF_maximization (bool, optional): Make a rought PDF around the expected Peak and maximize the PDF iterativ. Defaults to False. 
                R_limits (list): List containing the Q-range for the PDF maximization [lowest Q, higherst Q].Defaults to [2,4].
                reducing_steps (int, optional): Number of improved steps after which the step width is reduced. Defaults to 5.
                starting_step_width (float, optional): Starting scattering range allowed for the random values generated around the current best value. Defaults to 0.1.
    
            Returns:
                self: updated self varibles
            """
            
            self.flipps = 0
            self.alltime_low = 1e10
            self.alltime_low_area = 1e10
            self.alltime_low_area2 = 1e10
            self.time_average = None
            
            # Logging the event
            self.logger['Background was subtracted'] = {'heat ranges used': heat_ranges,
                                                        'Name of the background': bg_sampleName,
                                                        'Mode used for shorter background array': mode,
                                                        'Maximum factor allowed': FactorMax,
                                                        'Path to single background file (if used)': single_BG_file,
                                                        'Q cutted at Qmaxinst': Qmaxinst,}
            
            # FInding the corresponding Qmax and Qmin in the q array, or which number is closest to it and getting the index
            if Qmax is not None:
                Qmax = find_nearest(self.q,Qmax)[0]
            else:
                Qmax = None
            if Qmin is not None:
                Qmin = find_nearest(self.q,Qmin)[0]
            else:
                Qmin = 0
            self.closest_Qmax = Qmax
            # self.q = self.q[Qmin:Qmax]
            # shorten_matrix_elements(self.I,[Qmin,Qmax])
            
            if  isinstance(correction, (int, float)):
                correction = [float(correction)] * len(self.I)
    
            # Loading empty glas contrebution if given
            if empty_glas is not None: 
                self.q_empty_glas, self.I_empty_glas = np.loadtxt(empty_glas,skiprows=1).T
            else:
                self.q_empty_glas = 0
                self.I_empty_glas = 0
            
            # Setting new lists for factors 
            self.factor_list = []
            self.raw_factor_list = []
            self.subtracted_I = []
            
            # If the BG is temperature or time dependent one has two options if the data set is shorter then the real experiment 
            if single_BG_file is None:
                
                # Setting new lists for ramps and platos 
                self.plato_bg_list = plato_bg_list
                self.ramp_bg_list = ramp_bg_list
                
                # The first option is to use the last scan over and over
                if mode == 'last':
                    
                    # Then we append the last scan as many times as scans are missing to mich the real experiment
                    for i,j in enumerate(self.plato_list[0]):
                        if len(j) > len(self.plato_bg_list[0][i]):
                            for k in range(int(len(j)-len(self.plato_bg_list[0][i]))):
                                self.plato_bg_list[0][i].append(self.plato_bg_list[0][i][-1])
                                
                # Or one can average over the final temperature plato
                # However the scale is often changing and I recoment using the first option
                if mode == 'average':
                    average = 0
                    average_list = []
                    
                    # First we load all data files and add them up
                    for i,j in enumerate(self.plato_bg_list[0]): 
                        for a,b in enumerate(j): 
                            average = average + getIfile(b)
                            
                        # Then we devide by the number of all scans forming the overall average
                        average = average/len(j)
                        
                        # Then we append the averaged scan as many times as plato scans of the final temperature exsist
                        average_list.append(average)
                        average = 0
    
                self.bg_matrix = self.plato_bg_list[0][0] + self.ramp_bg_list[0][0] + self.plato_bg_list[0][1]
                
                if self.glas_Gr is True:
                    self.bg_matrix_Gr = self.plato_bg_list[1][0] + self.ramp_bg_list[1][0] + self.plato_bg_list[1][1]
                
                # Cropping the BG data as well in q before subtraction (mask affected data), so it does not form artefacts in the data
                # shorten_matrix_elements(self.plato_bg_list[0][0],[0,Qmax])
                # shorten_matrix_elements(self.plato_bg_list[0][1],[0,Qmax])
                # shorten_matrix_elements(self.ramp_bg_list[0][0],[0,Qmax])
                # shorten_matrix_elements(self.bg_matrix,[0,Qmax])
            
            # If one single BG is sufficient one can use this as well (a long exposed or averaged file works nicely)
            if single_BG_file is not None:
                
                # We get the single background file
                self.I_bg = getIfile(single_BG_file)
                
                # Then we loop over all I(q)
                for i,j in enumerate(self.I):  
                    
                    # We can determine the BG scaling factor eighter by the intensity of the glas peak in the PDF or
                    # by letting the minimum of the scan to be at the lowest point to be exactly 0 (within the given Q range)
                    if self.glas_Gr is True:
                        self.factor = max(self.Gr[i]) / max(self.Gr_bg)
                        print('max Gr:  ' + str(max(self.Gr[i])))
                        print('max Gr bg:  ' + str(max(self.Gr_bg)))                    
                    
                    # If the factor is not determined from the glas peak intensity (for some organic it is highly overshadowed)
                    else:
                        self.factor = np.trapz(y=(j[Qmin:Qmax]), x=self.q[Qmin:Qmax], dx=1e-10) / np.trapz(y=self.I_bg[Qmin:Qmax], x=self.q[Qmin:Qmax], dx=1e-10)
                        
                        # positive_parts = 0
                        # negative_parts = 0
                        # I_condition = True
                        # while I_condition:
                            
                        #     for qpart in range(qparts):
                        #         I_processed = j - self.factor* self.I_bg 
                        #         absolute_integral_I =  np.trapz(y=(abs(I_processed)), x=self.q[Qmin:Qmax], dx=1e-10)
                        #         positive_integral_of_I = (absolute_integral_I + np.trapz(y=I_processed, x=self.q[Qmin:Qmax], dx=1e-10)) / 2
                        #         negative_integral_of_I = absolute_integral_I - positive_integral_of_I
                        #         part_value = positive_integral_of_I - negative_integral_of_I
                    
                        #         if part_value > 0:
                        #             positive_parts += 1
                        #         else:
                        #             negative_parts += 1   
                        # if positive_parts /  negative_parts > convergence_critierium:
                        #     I_condition = False
                        # else:
                        #     self.factor = self.factor - convergence_step
                            
                        print(f"Initial BG-factor guess is: {self.factor}")
                        # I_bg_frac = j[Qmin:Qmax] / self.I_bg[Qmin:Qmax] 
                        #self.factor = min(I_bg_frac)
                       
                    
                    # Check if the calculated factor is over the allowed limit, otherwise it is reseted
                    if FactorMax is not None:
                        if self.factor > FactorMax:
                            self.factor = FactorMax
                    
                        
                        
                    if i ==0:
                        outputDatFile(rootdir + '/../processed/' +self.sampleName +'/Dat_files/bg_linaer_max_Dat/',self.q,[self.I_bg],(i+1)*self.average,'background_'+self.sampleName, clear_folder=True)
                    else:
                        outputDatFile(rootdir + '/../processed/' +self.sampleName +'/Dat_files/bg_linaer_max_Dat/',self.q,[self.I_bg],(i+1)*self.average,'background_'+self.sampleName)
                    
                    # After creating the .dat of the BG the PDFs can be calculated in monte carlo style 
                    # for maximization of the G(r)
                    if use_PDF_maximization is True:
                        # self.factor, maxvalue = self.monte_carlo_PDF_maximization(rootdir + '/../processed/' + self.sampleName + '/Dat_files/meas_Dat/' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt", 
                        #                                 rootdir + '/../processed/' + self.sampleName + '/Dat_files/bg_linaer_max_Dat/' + 'background_' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt",
                        #                                 outputPath, scan_number=i, R_limits = R_limits)
                                                        #r"C:\Users\admin\Nextcloud\Python_Code\ProjectPDF\PDF_generation\From_server\PDF_generation\processed\cell2_fe3s4_in_bnoh_160c_60min\empty_glasinlet_0p3m-00001.dat",outputPath)
                        
                        self.factor, maxvalue = self.linear_grid_PDF_maximization(rootdir + '/../processed/' + self.sampleName + '/Dat_files/meas_Dat/' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt", 
                                                        rootdir + '/../processed/' + self.sampleName + '/Dat_files/bg_linaer_max_Dat/' + 'background_' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt",
                                                        outputPath, scan_number=i, R_limits = R_limits,     
                                                        grid_size=grid_size, reducing_steps=reducing_steps, starting_step_width=starting_step_width)
                        
                    # Aplly the current factor to the data
                    subtracted_I = j - self.factor*self.I_bg
                    self.I[i] = j - self.factor*self.I_bg
                        
            else:
                
                #
                I = 0
                
                # Counter for the files in a ramp or plato processed already
                count = 0
                # Offset between the number of room temperature files between BG and experimantal run
                self.bg_offset = 0
                
                # If the BG scans have less data points then the experimantal run we start later in the experimantal run,
                # by setting the count to when the offset between the two data sets
                # --> Thereby the earliest data is ignored as we don´t have enought BG files 
                #     (it would be better to treat it the same way as the end of run data, but mostly it should´t differ so I did´t include it so fare)
                if len(self.plato_bg_list[0][0]) > (round_to_multiple(heat_ranges["ramp1_start"],self.average)/self.average):
                    self.bg_offset = int(len(self.plato_bg_list[0][0]) - (round_to_multiple(heat_ranges["ramp1_start"],self.average)/self.average) - 1)
                    print('BG is offset by:  ' + str(self.bg_offset) + '  number of averaged scans')
                    count = self.bg_offset
                
                # Steps are used in the same manner as in getRampPlatoLists (step 1 is the room temperature plato)
                step = 1
                
                # Now we can loop over every I(q) from each scan in the experimantel run
                for i,j in enumerate(self.I):  
                    self.current_I = j
                    # Here we check when the ramp starts and account for averaging
                    if i > round_to_multiple(heat_ranges["ramp1_start"],self.average)/self.average and step < 2:
                        step = 2
                        # We reset the counter after each step
                        count = 0
    
                    # Here we check when the first set temperature starts and account for averaging
                    if i-1 == round_to_multiple(heat_ranges["ramp1_end"],self.average)/self.average:
                        step = 3
                        # We reset the counter after each step
                        count = 0
    
                    # Here we check when second ramp starts (if set) and account for averaging
                    if i-1 == round_to_multiple(heat_ranges["ramp2_start"],self.average)/self.average:
                        step = 4
                        # We reset the counter after each step
                        count = 0
    
                    # Here we check when the second set temperature (if set) starts and account for averaging
                    if i-1 == round_to_multiple(heat_ranges["ramp2_end"],self.average)/self.average:
                        step = 5
                        # We reset the counter after each step
                        count = 0
    
                    # The ramps should have the same nbumber of files in  BG and experimantel run, so they are not so complicated
                    if step == 2:
                        
                        # If better implemented this doesn´t need to be a try function :(
                        try:
                            
                            # Reshape the order of the BG to match the experimantel temperature curve
                            self.I_bg =  self.ramp_bg_list[0][0][count]
                            
                            if self.glas_Gr is True:
                                self.Gr_bg = self.ramp_bg_list[1][0][count]
                            
                            # Give the user feedback what scan is in process and where in the run it is
                            print('Scan:  ' + str(count),'    in Ramping with lenght: ' + str(len(self.ramp_bg_list[0][0])-1), '   step in procedure:  ' + str(step))
                        
                        except:
                            # This should be taken over by the automation 
                            # (if averaging and temperature differneces between BG and experimantel run make up for a unfortuned 1 scan differece in the end)
                            print('optimize step 2')
    
                    if step == 4:
                        
                        # If better implemented this doesn´t need to be a try function :(
                        try:
                            
                            # Reshape the order of the BG to match the experimantel temperature curve
                            self.I_bg = self.ramp_bg_list[0][1][count]
                            if self.glas_Gr is True:
                                self.Gr_bg = self.ramp_bg_list[1][1][count]
                             
                            # Give the user feedback what scan is in process and where in the run it is
                            print('Scan:  ' + str(count),'    in Ramping with lenght: ' + str(len(self.ramp_bg_list[0][1])-1), '   step in procedure:  ' + str(step))
                        except:
                            # This should be taken over by the automation 
                            # (if averaging and temperature differneces between BG and experimantel run make up for a unfortuned 1 scan differece in the end)
                            print('optimize step 4')
    
                    # The platos will most of the time have diffent numbers of files in BG and experimantel run, so we use the extended versions of the BG run we created in the beginning
                    # with last copies the last scan until it matches the experimantal run leght or average which uses an average of the BG plato and extends it
                    if mode == 'last':
                        
                        # If better implemented this doesn´t need to be a try function :(
                        if step == 1:
                            
                            # Reshape the order of the BG to match the experimantel temperature curve
                            self.I_bg =  self.plato_bg_list[0][0][count]
                            if self.glas_Gr is True:
                                self.Gr_bg =  self.plato_bg_list[1][0][count]
                            
                            # Give the user feedback what scan is in process and where in the run it is
                            print('Scan:  ' + str(count),'    in Plato with lenght: ' + str(len(self.plato_bg_list[0][0])-1), '   step in procedure:  ' + str(step))
    
                        # If better implemented this doesn´t need to be a try function :(
                        if step == 3:
                            try:
                                
                                # Reshape the order of the BG to match the experimantel temperature curve
                                self.I_bg = self.plato_bg_list[0][1][count]
                                if self.glas_Gr is True:
                                    self.Gr_bg = self.plato_bg_list[1][1][count]
                                
                                 # Give the user feedback what scan is in process and where in the run it is
                                print('Scan:  ' + str(count),'    in Plato with lenght: ' + str(len(self.plato_bg_list[0][1])-1), '   step in procedure:  ' + str(step))
                            except:
                                # This should be taken over by the automation 
                                # (if averaging and temperature differneces between BG and experimantel run make up for a unfortuned 1 scan differece in the end)
                                print('optimize step 3')
    
                        # If better implemented this doesn´t need to be a try function :(
                        if step == 5:
                            try:
                                
                                # Reshape the order of the BG to match the experimantel temperature curve
                                self.I_bg = self.plato_bg_list[0][2][count]
                                if self.glas_Gr is True:
                                    self.Gr_bg = self.plato_bg_list[1][2][count]
                                
                                # Give the user feedback what scan is in process and where in the run it is
                                print('Scan:  ' + str(count),'    in Plato with lenght: ' + str(len(self.plato_bg_list[0][2])-1), '   step in procedure:  ' + str(step))
                            except:
                                # This should be taken over by the automation 
                                # (if averaging and temperature differneces between BG and experimantel run make up for a unfortuned 1 scan differece in the end)
                                print('optimize step 5')
    
                    # This is for the average of the BG plato that is extended, here the BG platos are simply the average of the current plato
                    if mode == 'average':
                        if step == 1:
                            self.I_bg = average_list[0]
                            
                        if step == 3:
                            self.I_bg = average_list[1]
                            
                        if step == 5:
                            self.I_bg = average_list[2]
                    
                    # Here we subtract the empty glas file with a constant scale before determaning the factor for the subtraction
                    if empty_glas_int != 0:  
                        # Here we also shorten the leght to be matching to Qmaxinst
                        self.I_empty_glas = self.I_empty_glas[:len(j)]  
                        j = j - self.I_empty_glas*max(j)*empty_glas_int
                
                    # We can determine the BG scaling factor eighter by the intensity of the glas peak in the PDF or
                    # by letting the minimum of the scan to be at the lowest point to be exactly 0 (within the given Q range)
                    if self.glas_Gr is True:
                        self.factor = max(self.Gr[i]) / max(self.Gr_bg)
                        print('max Gr:  ' + str(max(self.Gr[i])))
                        print('max Gr bg:  ' + str(max(self.Gr_bg)))                    
                    
                    # If the factor is not determined from the glas peak intensity (for some organic it is highly overshadowed)
                    else:
                        reduced_I = j[Qmin:Qmax]
                        reduced_bg_I = self.I_bg[Qmin:Qmax]
                        reduced_q = self.q[Qmin:Qmax]
                        self.factor = (np.trapz(y=reduced_I, x=reduced_q , dx=1e-10) )/ np.trapz(y=reduced_bg_I, x=reduced_q , dx=1e-10) + correction[i]
    
                        # I_condition = True
                        # counter = 0
                        # while I_condition:
                        #     print(self.factor)
                        #     positive_parts = 0
                        #     negative_parts = 0
                            
                        #     for qpart in range(qparts):
                        #         part_start = int((len(reduced_I) / qparts) * qpart)
                        #         part_end = int((len(reduced_I) / qparts) * (qpart+1) - 1)
                        #         if qpart == qparts -1:
                        #             if len(reduced_I) % qparts != 0:
                        #                 part_end = None
                                    
                                
                        #         I_processed = reduced_I[part_start:part_end] - self.factor* reduced_bg_I[part_start:part_end]
                        #         # print(part_start,part_end)
                        #         absolute_integral_I =  np.trapz(y=(abs(I_processed)), x=reduced_q[part_start:part_end] , dx=1e-10)
                        #         integral_I = np.trapz(y=I_processed, x=reduced_q[part_start:part_end], dx=1e-10)
                        #         positive_integral_of_I = (absolute_integral_I + integral_I) / 2
                        #         negative_integral_of_I =  positive_integral_of_I - absolute_integral_I
                        #         # part_value = positive_integral_of_I + negative_integral_of_I
                    
                        #         if integral_I > 0:
                        #             positive_parts += 1
                        #         else:
                        #             negative_parts += 1   
                        #     print((positive_parts /  negative_parts) )
                        #     if (positive_parts /  negative_parts) > convergence_critierium:
                        #         if (positive_parts /  negative_parts) < 0.95:
                        #             I_condition = False
                        #         else:
                        #             self.factor = self.factor + (convergence_step/13)
                        #     else:
                        #         self.factor = self.factor - convergence_step
                        #         counter += 1
                        #     if counter % 10 == 0:
                        #         convergence_step *= 0.1
                            
                        print(f"Initial BG-factor guess is: {self.factor}")
                        # I_bg_frac = j[Qmin:Qmax] / self.I_bg[Qmin:Qmax] 
                        # self.factor = min(I_bg_frac)
                    
                    # Check if the calculated factor is over the allowed limit, otherwise it is reseted
                    if FactorMax is not None:
                        if self.factor > FactorMax:
                            self.factor = FactorMax
                    
                    # For the creation of the PDFs, which are needed for the monte carlo BG subtraction,
                    # we need the BG in the correct order (ramp-BG with the same time as ramp-experimental)
                    # Therefore we now create the .dat after sorting it
                    
                    # If we reprocess with different averages we need to clear the folder, therefore we do this for the first scan.
                    if i ==0:
                        outputDatFile(rootdir + '/../processed/' +self.sampleName +'/Dat_files/bg_linaer_max_Dat/',self.q,[self.I_bg],(i+1)*self.average,'background_'+self.sampleName, clear_folder=True)
                    else:
                        outputDatFile(rootdir + '/../processed/' +self.sampleName +'/Dat_files/bg_linaer_max_Dat/',self.q,[self.I_bg],(i+1)*self.average,'background_'+self.sampleName)
                    
                    # After creating the .dat of the BG the PDFs can be calculated in monte carlo style 
                    # for maximization of the G(r)
                    if use_PDF_maximization is True:
                        # self.factor, maxvalue = self.monte_carlo_PDF_maximization(rootdir + '/../processed/' + self.sampleName + '/Dat_files/meas_Dat/' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt",
                        #                                 rootdir + '/../processed/' + self.sampleName + '/Dat_files/bg_linaer_max_Dat/' + 'background_' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt",
                        #                                 outputPath, scan_number=i, R_limits = R_limits)
                        
                        
                                                        #r"C:\Users\admin\Nextcloud\Python_Code\ProjectPDF\PDF_generation\From_server\PDF_generation\processed\cell2_fe3s4_in_bnoh_160c_60min\empty_glasinlet_0p3m-00001.dat",outputPath)
                        self.factor, maxvalue = self. linear_grid_PDF_maximization(rootdir + '/../processed/' + self.sampleName + '/Dat_files/meas_Dat/' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt",
                                                        rootdir + '/../processed/' + self.sampleName + '/Dat_files/bg_linaer_max_Dat/' + 'background_' + self.sampleName + "_" + str((i+1)*self.average).zfill(5)+".txt",
                                                        outputPath, scan_number=i, R_limits = R_limits)
                       
                      
                    
                    else:
                        # The user gets the feedback about the calculated BG scale
                        print('BG_scale:  ' + str(self.factor))
    
                    # Here we apply the BG factor and subtract the BG from the experimantal I(q)
                    subtracted_I = j - self.factor*self.I_bg
                    self.I[i] = j - self.factor*self.I_bg
                        
                    # We store the BG subtracted I(q) and the BG scaling factor in self varibles for later access.
                    self.subtracted_I.append(subtracted_I)
                    self.factor_list.append(self.factor)
                    
                    # AT last the counter for each scans in each step is increased by 1.
                    count = count + 1
            
            print("Total negative G(r) amplitude corrections preformed:" + str(self.flipps)) 
    
            return self.logger