The main graphene peaks are modeled as lmfit Pseudo Voigt peaks.
The script is aimed at a standard evaluation of these properties for multiple spectra, to obtain reasonable statistics.
In [1]:
#The default definition of peaks and ranges in the graphene spectrum
GRAPHENEPEAKS = [
{
#The name of the peak
'prefix': 'D',
#The range in the spectrum: baseline correction will irgnore this range
'range': {'min': 1290.0, 'max': 1420.0},
#constraints for the peak position
'center': {'min': 1340.0, 'init': 1350, 'max': 1360.0, 'vary': True},
#constraints for the peak width
'sigma': {'min': 5.0, 'init': 30.0, 'max': 40.0, 'vary': True},
#constraints for the gaussian to lorentzian ratio
'fraction': {'min': 0.01, 'init': 0.8, 'max': 1.0, 'vary': True},
#constraints for the peak height
'amplitude': {'min': 0.0, 'vary': True},
#If set to False, the peak will not be fitted, only initialized
'vary': True
},
{
#The name of the peak
'prefix': 'G',
#The range in the spectrum: baseline correction will irgnore this range
'range': {'min': 1480.0, 'max': 1700.0},
#constraints for the peak position
'center': {'min': 1560.0, 'init': 1585.0, 'max': 1610.0, 'vary': True},
#constraints for the peak width
'sigma': {'min': 5.0, 'init': 25.0, 'max': 50.0, 'vary': True},
#constraints for the gaussian to lorentzian ratio
'fraction': {'min': 0.01, 'init': 0.4, 'max': 1.0, 'vary': True},
#constraints for the peak height
'amplitude': {'min': 0.0, 'vary': True},
#If set to False, the peak will not be fitted, only initialized
'vary': True
},
{
#The name of the peak
'prefix': 'Dplus',
#The range in the spectrum: baseline correction will irgnore this range
'range': {'min': 2350.0, 'max': 2540.0},
#constraints for the peak position
'center': {'min': 2420.0, 'init': 2450.0, 'max': 2500.0, 'vary': True},
#constraints for the peak width
'sigma': {'min': 5.0, 'init': 25.0, 'max': 50.0, 'vary': True},
#constraints for the gaussian to lorentzian ratio
'fraction': {'min': 0.01, 'init': 0.4, 'max': 1.0, 'vary': True},
#constraints for the peak height
'amplitude': {'min': 0.0, 'vary': True},
#If set to False, the peak will not be fitted, only initialized
'vary': False
},
{
#The name of the peak
'prefix': 'twoD',
#The range in the spectrum: baseline correction will irgnore this range
'range': {'min': 2580.0, 'max': 2790.0},
#constraints for the peak position
'center': {'min': 2650.0, 'init': 2670, 'max': 2750.0, 'vary': True},
#constraints for the peak width
'sigma': {'min': 5.0, 'init': 25.0, 'max': 80.0, 'vary': True},
#constraints for the gaussian to lorentzian ratio
'fraction': {'min': 0.01, 'init': 0.4, 'max': 1.0, 'vary': True},
#constraints for the peak height
'amplitude': {'min': 0.0, 'vary': True},
#If set to False, the peak will not be fitted, only initialized
'vary': True
}
]
The required packages are mostly part of the anaconda distribution. lmfit is installed separately. Tested with Python 3.5.2 and lmfit 0.9.5
In [2]:
%load_ext autoreload
%autoreload 2
In [3]:
#from multipeak import Dataset, MultiPseudoVoigtModel, GrapheneModelResults, printmd
import multipeak
from copy import deepcopy
In [4]:
from sys import version as pythonVersion
from lmfit._version import get_versions
print('Python version:', pythonVersion)
print('lmfit version:', get_versions()['version'])
This is to prevent cell scrolling for longer outputs:
In [5]:
%%javascript
IPython.OutputArea.auto_scroll_threshold = 9999;
µ-Raman spectra (100X objective) of graphene have to be supplied in a text file, with the Ramanshift in cm-1 and the intensity in separate columns. The resolution should be better than 1.5 cm-1, the range from at least from 1265 cm-1 to 2810 cm-1 for fitting of the default peaks.
The file can contain multiple datasets with an equal number of datapoints, each dataset is then individually processed and average values and histograms are generated.
Optionally, the data will be cut to the range specified by TASK['xmin'] and TASK['xmax'].
The default fitting uses separate fitting tasks with specific baseline subtraction for the D + G band and the 2D band. In general this leads to more robust results, compared to model a sigle baseline with a higher order polynomial.
In [6]:
TASK = {
#Filename as full path. The file can contain multiple datasets with an equal number of datapoints
#'filename': 'examples/CVD_graphene_example.txt', #optional
#If filename is left unspecified a file dialog will open in:
'initialDir': './',
#Columns in the file to be used
'posx_column': 0, #optional
'posy_column': 1, #optional
'x_column': 2, #raman shift
'y_column': 3, #raman intensity
#Axis labels for plotting
'xlabel': 'Raman shift',
'xunit': 'cm^{-1}',
'ylabel': 'Intensity',
'yunit': 'counts',
#Set to the order of polynomial for baseline subtraction, 0 for no baseline
#Maxium allowed value is 7
#Note that this is ignored if a partially defined baseline is used
'baselineOrder': 6,
#Minimum signal-to-noise ratio, if 0 all speactra are used
'snr': 100,
#The minimum number of remaining spectra after signal-to-noise selection
'minimumStatisctics': 100, #evaluation according to the standard must include 100 spectra
#Minium x value
'xmin': 1200.0, #optional
#Maximum x value
'xmax': 2810.0, #optional
#Dictionary where the peaks are defined
'peaks': deepcopy(GRAPHENEPEAKS)
}
Customizations for pyplot figures:
In [7]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (12.0, 8.0)
plt.rcParams.update({'font.size': 14})
In [8]:
#we first read the data
DATASET = multipeak.Dataset(task=TASK, maxNumber=1000)
In [9]:
"""Partially defined polynomial baseline"""
BASELINE = [
{
#partial baseline is defined
'untilX': 1750.0, #in wavenumbers
#and has
'polynomialOrder': 2,
#and requires minimum signal-to-noise ratio
'snr': 100 #which can be 0 to include all spectra
},
{
'untilX': 2555.0,
'polynomialOrder': 4,
'snr': 0
},
{
'untilX': 2810.0,
'polynomialOrder': 1,
'snr': 100
}
]
In [10]:
DATASET.subtractMultiBaseline(baselineParts=BASELINE, exampleSpec=0)
Datasets are omitted if the signal to noise ratio is lower than the value defined in the baseline parts. This is recommended, because for low signal spectra the evaluation is not reliable. The signal to noise ratio is determined by the squared variance of the data divided by the squared variance of the data points excluding the peak ranges.
In [11]:
DATASET.filterDatasetBySNR()
In [12]:
#finally we build and fit the model
GRAPHENEMODEL = multipeak.MultiPseudoVoigtModel(DATASET, weights=DATASET.weights['peaks'])
In [13]:
GRAPHENEMODEL.runFit()
In [14]:
#build the graphene specific evaluations from the array of fit results
RESULTS = multipeak.GrapheneModelResults(GRAPHENEMODEL, DATASET)
In [15]:
multipeak.printmd('### Average fit results')
RESULTS.printPeakResultsTable()
multipeak.printmd('### Peak ratios')
RESULTS.printDerivedResultsTable(derivedParams=[
'DtoG_area',
'DtoG_height',
'twoDtoG_area',
'twoDtoG_height'
])
multipeak.printmd('### 2D symmetry')
RESULTS.printDerivedResultsTable(derivedParams=[
'twoDr2',
'twoDmaxRes'
])
multipeak.printmd('### Fitted spectra overview')
RESULTS.plot3D(save=True)
multipeak.printmd('### Fit of average spectrum')
RESULTS.plotAvgFit(saveName='avgFit')
print(GRAPHENEMODEL.avgFitResult.fit_report())
In [16]:
exampleFitNumber = 0
#multipeak.printmd('## Example fit')
#RESULTS.plotFit(exampleFitNumber)
#print(GRAPHENEMODEL.fitResults[exampleFitNumber].fit_report())
In [17]:
multipeak.printmd('### Normalized squared maximum residuals (NSR)')
RESULTS.plot2Dresiduals()
multipeak.printmd('### 2D R2 intervals')
RESULTS.print2Dr2Intervals(0.995)
RESULTS.print2Dr2Intervals(0.99)
RESULTS.print2Dr2Intervals(0.98)
RESULTS.print2Dr2(save=True)
multipeak.printmd('### 2D symmetry outliers')
RESULTS.plot2DOutliers(3, save=True)
In [18]:
multipeak.printmd('### D/G intervals')
RESULTS.printDtoGIntervals(0.2)
RESULTS.printDtoGIntervals(0.5)
RESULTS.printDtoGIntervals(0.8)
RESULTS.printDtoGIntervals(1.0)
multipeak.printmd('## Peak ratios')
RESULTS.printPeakRatios(save=True)
multipeak.printmd('## D/G outliers')
RESULTS.plotDtoGOutliers(3, save=True)
In [19]:
multipeak.printmd('### Peak widths')
RESULTS.printParamResults('fwhm')
RESULTS.plotPeakParamHist('G', 'fwhm', xunit='cm-1', bins=50, saveName='G-width-hist')
RESULTS.plotPeakParamHist('twoD', 'fwhm', xunit='cm-1', bins=50, saveName='2D-width-hist')
multipeak.printmd('### Peak positions')
RESULTS.printParamResults('center')
RESULTS.plotPeakParamHist('G', 'center', xunit='cm-1', bins=50, saveName='G-position-hist')
RESULTS.plotPeakParamHist('twoD', 'center', xunit='cm-1', bins=50, saveName='2D-position-hist')
In [20]:
RESULTS.writeOutput('fitReport')
RESULTS.writePeakResultsTable('peakResults')
In [21]:
RESULTS.printAvgRes([
'DtoG_area',
'twoDr2',
'twoDtoG_area',
'G_fwhm',
'twoD_fwhm',
'G_amplitude',
'twoDtoG_height'
], delimiter=' ')
In [22]:
%%javascript
IPython.notebook.save_notebook();
In [23]:
from nbconvert import HTMLExporter
import codecs
import nbformat
import time
time.sleep(1)
exporter = HTMLExporter()
output_notebook = nbformat.read('DtoG-2Dsymmetry.ipynb', as_version=4)
output, resources = exporter.from_notebook_node(output_notebook)
codecs.open(RESULTS.baseFilename + '.html', 'w', encoding='utf-8').write(output)
Good sciencing everyone!