From 1e147854a7173ca0d4191f99b64cc5d46726aa06 Mon Sep 17 00:00:00 2001 From: rasmusvt Date: Mon, 27 Jun 2022 16:43:42 +0200 Subject: [PATCH] Update documentation for determination of edge position --- nafuma/xanes/calib.py | 76 ++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/nafuma/xanes/calib.py b/nafuma/xanes/calib.py index 66c4229..d6fecc2 100644 --- a/nafuma/xanes/calib.py +++ b/nafuma/xanes/calib.py @@ -444,44 +444,56 @@ def estimate_edge_position(data: dict, options={}, index=0): return estimated_edge_shift def determine_edge_position(data: dict, options={}): - ''' Determines the edge position by 1) first differential maximum and/or 2) second differential zero-point. Calculates differential and/or double differential by periods''' + ''' Determines the edge position by 1) first differential maximum and/or 2) second differential zero-point. Calculates differential and/or double differential by diff.periods and double_diff.periods respectively. + The differentiated and/or doubly differentiated data is fitted to a polynomial of diff.polyorder and/or double_diff.polyorder around the estimated edge position. The estimated edge position is set to be the x-value of the data + point at maximum of the differentiated data. The region to be fitted to the polynomial is determined by fit_region, which defaults to 5 times the distance between two data points, giving five data points to fit to. + + Allows plotting and saving of three plots to assess the quality of the fit, and also allows logging. + + Requires that XANES-data is already loaded in data['xanes_data']. This allows the user to choose when to determine the edge position - whether before or after normalisation, flattening etc.''' required_options = ['save_values', 'log', 'logfile', 'save_plots', 'save_folder', 'diff', 'diff.polyorder', 'diff.periods', 'double_diff', 'double_diff.polyorder', 'double_diff.periods', 'fit_region'] default_options = { - 'save_values': True, - 'log': False, - 'logfile': f'{datetime.now().strftime("%Y-%m-%d-%H-%M-%S")}_determine_edge_position.log', - 'show_plots': False, - 'save_plots': False, - 'save_folder': './', - 'diff': True, - 'diff.polyorder': 2, - 'diff.periods': 2, #Periods needs to be an even number for the shifting of values to work properly, - 'double_diff': False, - 'double_diff.polyorder': 2, - 'double_diff.periods': 2, #Periods needs to be an even number for the shifting of values to work properly, + 'save_values': True, # Whether the edge positions should be stored in a dictionary within the main data dictionary. + 'log': False, # Toggles logging on/off + 'logfile': f'{datetime.now().strftime("%Y-%m-%d-%H-%M-%S")}_determine_edge_position.log', # Sets the path to the logfile. Ignored if log == False + 'show_plots': False, # Toggles on/off whether plots should be shown. For sequential data, saving the plots and inspecting them there is probably better. + 'save_plots': False, # Toggles on/off whether plots should be saved. + 'save_folder': './', # Sets the path to where the plots should be saved. Creates folder if doesn't exist. Ignored if save_plots == False + 'diff': True, # Toggles calculation of the edge position based on differential data + 'diff.polyorder': 2, # Sets the order of the polynomial to fit edge region of the differential to + 'diff.periods': 2, # Sets the number of data points between which the first order difference should be calculated. Needs to be even for subsequent shifting of data to function. + 'double_diff': False, # Toggles calculation of the edge position based on double differential data + 'double_diff.polyorder': 2, # Sets the order of the polynomial to fit edge region of the double differential to + 'double_diff.periods': 2, # Sets the number of data points between which the second order difference should be calculated. Needs to be even for subsequent shifting of data to function. 'fit_region': None # The length of the region to find points to fit to a function - } options = aux.update_options(options=options, required_options=required_options, default_options=default_options) + + # Check if periods are even if options['diff'] and options['diff.periods'] % 2 != 0: + if options['log']: + aux.write_log(message='Periods for differentiation is not even. Ending run.', options=options) raise Exception("NB! Periods needs to be an even number for the shifting of values to work properly") if options['double_diff'] and options['double_diff.periods'] % 2 != 0: + aux.write_log(message='Periods for double differentiation is not even. Ending run.', options=options) raise Exception("NB! Periods needs to be an even number for the shifting of values to work properly") - ##### - + + # Prepare dataframes for differential data if options['diff']: df_diff = pd.DataFrame(data['xanes_data']['ZapEnergy']) if options['double_diff']: df_double_diff = pd.DataFrame(data['xanes_data']['ZapEnergy']) if options['save_values']: - data['e0'] = {} + data['e0_diff'] = {} + data['e0_double_diff'] = {} + # Get rough estimate of edge position for i, filename in enumerate(data['path']): estimated_edge_pos = estimate_edge_position(data, options=options, index=i) @@ -489,12 +501,13 @@ def determine_edge_position(data: dict, options={}): options['fit_region'] = (5)*(data['xanes_data']['ZapEnergy'].iloc[1] - data['xanes_data']['ZapEnergy'].iloc[0]) - #========================== Fitting first differential ========== + #========================== Fitting the first order derivative ========== if options['diff']: - df_diff[filename] = data['xanes_data'][filename].diff(periods=options['periods']) - df_diff[filename]=df_diff[filename].shift(-int(options['periods']/2)) + df_diff[filename] = data['xanes_data'][filename].diff(periods=options['diff.periods']) + df_diff[filename]=df_diff[filename].shift(-int(options['diff.periods']/2)) # Shifts the data back so that the difference between the points is located in the middle of the two points the caluclated difference is between + # Picks out the points to be fitted df_diff_edge = df_diff.loc[(df_diff["ZapEnergy"] <= estimated_edge_pos+options['fit_region']) & ((df_diff["ZapEnergy"] >= estimated_edge_pos-options['fit_region']))] @@ -516,12 +529,12 @@ def determine_edge_position(data: dict, options={}): aux.write_log(message=f"Edge position estimated by the differential maximum is: {str(round(edge_pos_diff,5))} keV", options=options) if options['save_values']: - data['e0'][filename] = edge_pos_diff - + data['e0_diff'][filename] = edge_pos_diff + #========================== Fitting the second order derivative ========== if options['double_diff']: - df_double_diff[filename] = data['xanes_data'][filename].diff(periods=options['periods']).diff(periods=options['periods']) - df_double_diff[filename]=df_double_diff[filename].shift(-int(options['periods'])) + df_double_diff[filename] = data['xanes_data'][filename].diff(periods=options['double_diff.periods']).diff(periods=options['double_diff.periods']) + df_double_diff[filename]=df_double_diff[filename].shift(-int(options['double_diff.periods'])) # Pick out region of interest df_double_diff_edge = df_double_diff.loc[(df_double_diff["ZapEnergy"] < estimated_edge_pos+options['fit_region']) & ((df_double_diff["ZapEnergy"] > estimated_edge_pos-options['fit_region']))] @@ -547,8 +560,15 @@ def determine_edge_position(data: dict, options={}): if options['diff']: aux.write_log(message=f"Difference between edge position estimated from differential maximum and double differential zero-point is {(edge_pos_diff-edge_pos_double_diff)*1000} eV.") + if options['save_values']: + data['e0_double_diff'][filename] = edge_pos_double_diff + + + # Make and show / save plots if options['save_plots'] or options['show_plots']: + + # If both are enabled if options['diff'] and options['double_diff']: fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(ncols=3, nrows=2, figsize=(20,20)) @@ -586,7 +606,7 @@ def determine_edge_position(data: dict, options={}): ax6.axvline(x=estimated_edge_pos, ls='--', c='red') - + # If only first order differentials is enabled elif options['diff']: fig, (ax1, ax2, ax3) = plt.subplots(ncols=3,nrows=1, figsize=(20, 10)) @@ -605,7 +625,7 @@ def determine_edge_position(data: dict, options={}): ax3.axvline(x=edge_pos_diff, ls='--', c='green') ax3.axvline(x=estimated_edge_pos, ls='--', c='red') - + # If only second order differentials is enabled elif options['double_diff']: fig, (ax1, ax2, ax3) = plt.subplots(ncols=3,nrows=1, figsize=(20, 10)) @@ -624,6 +644,8 @@ def determine_edge_position(data: dict, options={}): ax3.axvline(x=edge_pos_double_diff, ls='--', c='green') ax3.axvline(x=estimated_edge_pos, ls='--', c='red') + + # Save plots if toggled if options['save_plots']: if not os.path.isdir(options['save_folder']): os.makedirs(options['save_folder']) @@ -632,6 +654,8 @@ def determine_edge_position(data: dict, options={}): plt.savefig(dst, transparent=False) + + # Close plots if show_plots not toggled if not options['show_plots']: plt.close()