|
"""PLT Temperature Test Data Analysis |
|
|
|
The main function takes raw temperature measurement TDMS files and writes |
|
the calculated RT, 60C, 100C and 120C PLQY values, along with other relevant |
|
parameters to LIMS |
|
""" |
|
#----------------------------------------------------------------------------- |
|
# Created on June 17, 2013 |
|
# |
|
# @author: lcanavan |
|
#----------------------------------------------------------------------------- |
|
|
|
#----------------------------------------------------------------------------- |
|
# Imports |
|
#----------------------------------------------------------------------------- |
|
|
|
# Stdlib imports |
|
import os |
|
import glob |
|
# Third-party imports |
|
import pandas as pd |
|
import numpy as np |
|
from scipy.optimize import curve_fit |
|
from scipy.interpolate import interp1d |
|
from django.core.management.base import BaseCommand |
|
from django.forms.models import model_to_dict |
|
# Module imports |
|
import tdms_scrape |
|
import lowess |
|
from sample_inventory.models import ReactionManager |
|
from sample_characterization.models import UpdatedTemperatureTestResult |
|
|
|
#----------------------------------------------------------------------------- |
|
# Globals and constants |
|
#----------------------------------------------------------------------------- |
|
|
|
TDMS_GROUP = 'Tempdep_Data' |
|
REL_TEMPS = [303.15, 333.15, 373.15, 393.15] |
|
HEATING_MEASUREMENTS = 3 |
|
DELETE_FILES_AFTER_ANALYSIS = False |
|
|
|
#----------------------------------------------------------------------------- |
|
# Classes and functions |
|
#----------------------------------------------------------------------------- |
|
|
|
def linear_band_gap_energy(T,m,b): |
|
''' |
|
Linear function model for simplified Varshni's empirical expression where |
|
B -> 0. Takes floats of temperature, slope and intercept as inputs and |
|
returns a float of the estimated band gap energy wavelength. |
|
''' |
|
return m*T+b |
|
|
|
def emission_corrected_temp(wl,m,b): |
|
''' |
|
Inverse of linear_band_gap_energy function. Takes floats of band gap |
|
energy wavelength, slope and intercept and returns a float of the |
|
estimated emission corrected temperature. |
|
''' |
|
return (wl-b)/m |
|
|
|
def get_emission_corrected_temp(FilmTempK,BandGapEnergy): |
|
''' |
|
Takes 1D arrays of film temperature and calculated band gap energy |
|
and returns a 1D array of the corrected film temperature along with |
|
first parameters popt and pcov (r-square and var) |
|
''' |
|
popt, pcov = curve_fit(linear_band_gap_energy,FilmTempK,BandGapEnergy) |
|
|
|
CorrectedFilmTempK = emission_corrected_temp(BandGapEnergy, |
|
popt[0], |
|
popt[1] |
|
) |
|
|
|
return CorrectedFilmTempK, popt, pcov |
|
|
|
def getmovingaverage(FilmTempK,PLQY,*args,**kwargs): |
|
''' |
|
Takes 1D arrays of corrected film temp (x) and PLQY (y) and |
|
utilizes Cleveland's location-weighted moving average transpose |
|
to return a smoothed PLQY curve |
|
''' |
|
return lowess.lowess(FilmTempK,PLQY,*args,**kwargs) |
|
|
|
def get_interpolated_fit(FilmTempK,PLQY,PLQYAVG): |
|
''' |
|
Takes 1D arrays of film temperatures and unsmooth and smoothed |
|
PLQY measures and returns a function object of the interpolated |
|
PLQY curve, along with the calculated residual (quality of fit) |
|
''' |
|
interpfxn = interp1d(FilmTempK,PLQYAVG,kind='linear') |
|
raw_mean = PLQY.sum()/len(PLQY) |
|
SStot = ((PLQY-raw_mean)**2).sum() |
|
SSres = ((PLQY-interpfxn(FilmTempK))**2).sum() |
|
return interpfxn, 1-(SSres/SStot) |
|
|
|
def fit_interpolated_temps(interpfxn, REL_TEMPS): |
|
''' |
|
Takes a list of float temperatures of interest (global REL_TEMPS) and the |
|
derived interpolatation function (interpfxn) from getinterpvals |
|
and returns a list of floats of the interpolated PLQY values at the |
|
REL_TEMPS values |
|
''' |
|
interpolated_PLQYlist = [] |
|
for temp in REL_TEMPS: |
|
try: |
|
interpolated_PLQYlist.append(interpfxn(temp)) |
|
except ValueError: |
|
interpolated_PLQYlist.append(None) |
|
return interpolated_PLQYlist |
|
|
|
class Command(BaseCommand): |
|
args = 'File path to TDMS files' |
|
help = 'Performs film temperature test analysis' |
|
|
|
def handle(self,*args,**options): |
|
''' |
|
Performs the daily temperature test calculations |
|
|
|
Takes a filepath of the daily TDMS files as the single argument |
|
''' |
|
|
|
# Directory Level Stuff |
|
PATH = args[0] |
|
|
|
clean_raw_filelist = glob.glob(PATH+'*.tdms') |
|
|
|
if len(clean_raw_filelist) == 0: |
|
print 'No files to analyze' |
|
return |
|
|
|
print 'TDMS files sourced from: %s' %PATH |
|
print 'Total Number of TDMS Files: %d' %len(clean_raw_filelist) |
|
|
|
# Creates sorted-by-batchID object for calculations |
|
sorted_raw_filelist = tdms_scrape.sort_rawfiles_by_batchid(clean_raw_filelist, TDMS_GROUP) |
|
|
|
for batchID_set in sorted_raw_filelist.values(): |
|
|
|
try: |
|
# Initial get/cleaning of dataframe |
|
batchID_frame = tdms_scrape.get_summary_frame(batchID_set,TDMS_GROUP) |
|
batchID_frame['Film_Temp_K'] = batchID_frame['Film_Temp_C'] + 273.15 |
|
|
|
batchID_frame = tdms_scrape.get_clean_frame(batchID_frame) |
|
|
|
# Uniques = number of films per sample |
|
uniques = batchID_frame['SampleID'].unique() |
|
|
|
# Instantiates TemperatureTestResult model |
|
filmresult = UpdatedTemperatureTestResult(SampleLabel = batchID_frame['BatchID'][0], |
|
MeasureDate = batchID_frame['DateTime'][0], |
|
) |
|
filmresult.InitialRTPLQY = batchID_frame['PLQY'][:len(uniques)-1].mean() |
|
filmresult.PLPeakWL = batchID_frame['PLPeakWL'][:len(uniques)-1].mean() |
|
filmresult.PLCentroidWL = batchID_frame['WeightedMeanWL'][:len(uniques)-1].mean() |
|
filmresult.PLPeakFWHM = batchID_frame['PLPeakFWHM'][:len(uniques)-1].mean() |
|
filmresult.PLPeakDifference = filmresult.PLCentroidWL - filmresult.PLPeakWL |
|
filmresult.SampleTimeStamp = batchID_frame['Sample_Time_s'][0] |
|
|
|
# Removes film measurements taken during heating cycle |
|
batchID_frame = batchID_frame[len(uniques)*HEATING_MEASUREMENTS:] |
|
print 'Analyzing Batch ID: %s' %filmresult.SampleLabel |
|
|
|
# Does film temp correction using centroid WL |
|
correctedfilmtempk, popt, pcov = get_emission_corrected_temp(batchID_frame['Film_Temp_K'], |
|
batchID_frame['WeightedMeanWL'] |
|
) |
|
|
|
batchID_frame['Corrected_Film_Temp_K'] = correctedfilmtempk |
|
filmresult.PLPeakShift = popt[0] |
|
|
|
# Sort dataframe by emission corrected temp for interpolation |
|
sorted_batchID_frame = batchID_frame.sort(columns = ['Corrected_Film_Temp_K']) |
|
|
|
# Lowess smoothing (applied locally-weighted moving average to film temps) |
|
sorted_batchID_frame['PLQY_AVG'] = getmovingaverage(sorted_batchID_frame['Corrected_Film_Temp_K'], |
|
sorted_batchID_frame['PLQY'], |
|
f=3./5., |
|
iter=3 |
|
) |
|
|
|
# Do interpolation fitting |
|
interpfxn, interperror = get_interpolated_fit(sorted_batchID_frame['Corrected_Film_Temp_K'], |
|
sorted_batchID_frame['PLQY'], |
|
sorted_batchID_frame['PLQY_AVG'] |
|
) |
|
|
|
filmresult.FitQuality = interperror |
|
|
|
# Add interpolated PLQYs at REL_TEMPS to TemperatureTestResult Model |
|
interptemps = fit_interpolated_temps(interpfxn, REL_TEMPS) |
|
filmresult.PLQY25C = interptemps[0] |
|
filmresult.PLQY60C = interptemps[1] |
|
filmresult.PLQY100C = interptemps[2] |
|
filmresult.PLQY120C = interptemps[3] |
|
try: |
|
filmresult.PLQYHysteresis = filmresult.PLQY25C - filmresult.InitialRTPLQY |
|
except: |
|
filmresult.PLQYHysteresis = None |
|
filmresult.PLPeakBroadening = 0 |
|
|
|
# Attempt to match BatchID in TDMS file (SampleLabel) to ProductID in ReactionManager Model |
|
try: |
|
filmresult.ProductID = ReactionManager.objects.get(ReactionID = filmresult.SampleLabel.replace('-I','').upper()) |
|
except: |
|
filmresult.ProductID = ReactionManager.objects.get(pk=240) |
|
|
|
# Gets QuerySet of previous measurements of TMDS file films |
|
|
|
previous_meas = UpdatedTemperatureTestResult.objects.filter(SampleLabel = filmresult.SampleLabel.replace('-I','')) |
|
|
|
# If no previous measurements, assumes TDMS file is first measurement |
|
if len(previous_meas) > 0: |
|
first_meas = previous_meas[0] |
|
filmresult.ExposureTime = (filmresult.SampleTimeStamp - first_meas.SampleTimeStamp)/3600.0 |
|
filmresult.FirstMeasurement = False |
|
else: |
|
filmresult.ExposureTime = 0 |
|
filmresult.FirstMeasurement = True |
|
|
|
# TemperatureTestResult validation and commit |
|
filmresult.full_clean() |
|
filmresult.save() |
|
|
|
# Clear Daily folder of analyzed files |
|
if DELETE_FILES_AFTER_ANALYSIS: |
|
for file in batchID_set: |
|
os.remove(os.path.join(PATH, file)) |
|
|
|
except: |
|
pass |
|
if DELETE_FILES_AFTER_ANALYSIS: |
|
print 'Directory has been cleared: %s' %PATH |
|
print 'Analysis Complete' |
|
return |
|
|