<jupyter_start><jupyter_text>Tension Lab Analysis Code ヽ(•‿•)ノ  Get All The File Names<jupyter_code>from os import listdir
listdir() #this is everything in the current folder
Files = [x for x in listdir() if '.csv' in x] #this is a list comprehension that finds all the current folder files that contain '.csv'
print(Files)
Files = [x for x in listdir() if '.csv' in x and 'Raw' in x] #replace 'Raw' with something for a given type of sample, for example 'Al'
print(Files)<jupyter_output><empty_output><jupyter_text>Define The Sample Parameters You will need to change these for every sample type<jupyter_code>thickness = 6.2 # Input thickness in mm
width = 12.3 # Input Width in mm
Area = thickness*width #in mm^2
print("Area =", Area, "mm^2" )<jupyter_output><empty_output><jupyter_text>Read One File We will be using Pandas as our data analysis library<jupyter_code>import pandas as pd #this imports the pandas data analysis library
Data0 = pd.read_csv(Files[0]) #this function reads the .csv file<jupyter_output><empty_output><jupyter_text>It pulls in all the CSV data from the files that we provided and stores it in a convenient format<jupyter_code>Data0.head() #run this to output the first 5 lines of data stored for Files[0]<jupyter_output><empty_output><jupyter_text>Read All The Files<jupyter_code>import pandas as pd #pandas is the data analysis library
Data = {x:{} for x in Files} #This is a list comprehension that creates an empty dictionary for all the file data.
                             #It allows all the data to be stored in one place.
for File in Files:
    Data[File] = pd.read_csv(File) #this stores the data for each file in Data[File], where 'File' is the name of the file<jupyter_output><empty_output><jupyter_text>Check what's in the files<jupyter_code>Data[Files[0]].head() #run this to output the first 5 lines of data stored for the first file (Files[0])
Data[Files[0]].tail() #run this to output the last 5 lines of data stored for the first file (Files[0])<jupyter_output><empty_output><jupyter_text>"Wait", you might say, "I don't know how any of these functions work!" That's okay, there's a help function :)*Note: you can right click and clear the output if it takes up too much space*<jupyter_code>#help(pd.read_csv)<jupyter_output><empty_output><jupyter_text>Add Extra Columns to the Data As was shown in intro to data analysis video, we can directly add things to a pandas data type All you need to do it take your dictionary and set dictName['new item']= *something*<jupyter_code>for File in Files:
    emptyList = [[]]*len(Data[File].index) #This is an empty list with the same length as the data file
    #Note, when you multiply Python lists it just copies the list, e.g. [[1]]*3=[[1],[1],[1]]
    
    #Here's some example new dictionary calculations
    Data[File]['Strain (mm/mm)'] = Data[File]['Axial Strain (mm/mm)'] #this adds another strain column to the data
    Data[File]['Stress (MPa)'] = Data[File]['Load (N)']/Area #this adds stress to the data
    
    #You'll need to do calculations here
    Data[File]['Instantaneous Area (mm^2)'] = emptyList #Calculate the instantaneous area using the original dimensions and the transverse strain
    Data[File]['True Stress (MPa)'] = emptyList #Add in the true stress here
    Data[File]['True Strain (mm/mm)'] = emptyList #Add in the true strain here,
Data[Files[0]].head()<jupyter_output><empty_output><jupyter_text>Plot All The Data<jupyter_code>import matplotlib.pyplot as plt #matlab-esque plotting library
fig = plt.figure()
ax = fig.gca()
for File in Files:
    ax.plot(Data[File]['Strain (mm/mm)'],Data[File]['Stress (MPa)'],label=File) #the label corresponds to what the legend will output
ax.set_xlim(left = 0)
ax.set_ylim(bottom = 0)
plt.title("Engineering Stress vs Strain")
plt.ylabel('Stress (MPa)')
plt.xlabel('Strain (mm/mm)')
plt.legend() #this turns the legend on, you can manually change entries using legend(['Sample 1', 'Sample 2',...])
plt.show()
###Copy and modify the code above to create a plot for true stress and true strain###<jupyter_output><empty_output><jupyter_text>Young's Modulus Calculation  We want to find the Young's modulus, i.e. the slope of stress vs strain in elastic region. There are a few different ways to do this calculation, but the trick is defining the "elastic region". You can define that region manually, or write a function that finds it for you. This calculation is very nuanced and can change for different tests on different materials.<jupyter_code>#Here we will make a subplot to show a zoomed section
eZoom = 0.01; sZoom = 350
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5))
for File in Files:
    ax1.plot(Data[File]['Strain (mm/mm)'],Data[File]['Stress (MPa)'],label=File)
    ax2.plot(Data[File]['Strain (mm/mm)'],Data[File]['Stress (MPa)'],label=File)

#Plot the zoomed box
ax1.plot([0,0,eZoom,eZoom,0],[0,sZoom,sZoom,0,0],'r--')

#Add Labels
ax1.set_xlim(left=0)
ax1.set_ylim(bottom=0)
ax2.set_xlim(left = 0, right=eZoom)
ax2.set_ylim(bottom = 0, top=sZoom)
ax1.set_title("Engineering Stress vs Strain")
ax2.set_title("Engineering Stress vs Strain Magnified")
ax1.set_ylabel('Stress (MPa)')
ax2.set_ylabel('Stress (MPa)')
ax1.set_xlabel('Strain (mm/mm)')
ax2.set_xlabel('Strain (mm/mm)')
ax1.legend()
ax2.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>Modulus Fit Here, we will only use the manual method of defining two indices for which to fit the data<jupyter_code>from scipy.stats import linregress #This is a linear regression function built into the Scipy library. 
#You can call help(linregress) if you'd like to learn more. Or check out https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.linregress.html

def modulusFit(Strain,Stress,a,b):
    '''This is a linear fit to data between the data indices for a and b. Note, this will
    return an error if a or b are outside the length of Strain and Stress.'''
    
    #Fit the modulus
    E,C,R,P,Err = linregress(Strain[a:b],Stress[a:b]) #The data outputs the slope (E), intercept (C), regression (R) value, P-value and standard error
    #Note: Python lets you save multivariable outputs with a comma, i.e. a,b=[1,2] will give a=1 and b=2
    
    #Make a line for the fit data
    Y = [0.0, max(Stress)] #this is a list of length 2 for plotting the fit data later
    X = [(y-C)/E for y in Y] #these are points that you can plot to visualize the data being fit, inverted from y=E*x+C, x=(y-C)/E
    return E,C,R,X,Y<jupyter_output><empty_output><jupyter_text>Check how the fit works<jupyter_code>fig = plt.figure(3)
ax = fig.gca()

#We'll test out two fit points and see the difference
a1 = 5; b1 = 100
a2 = 20; b2 = 150
for File in Files:
    #Save dummy variables to make the code cleaner below
    strain = Data[File]['Strain (mm/mm)'].values
    stress = Data[File]['Stress (MPa)'].values
    
    #Use the two fits
    E1,C1,R1,X1,Y1 = modulusFit(strain,stress,a1,b1)
    E2,C2,R2,X2,Y2 = modulusFit(strain,stress,a2,b2)
    
    #Plot the data
    ax.plot(strain,stress)
    ax.plot(strain[a1],stress[a1],'rd') #This is the first point we're fitting from 
    ax.plot(strain[b1],stress[b1],'rs') #this is the last point we're fitting to
#     ax.plot(strain[a1:b1],stress[a1:b1],'r.') #this will show all the data we're fitting
    ax.plot(strain[a2],stress[a2],'bd')
    ax.plot(strain[b2],stress[b2],'bs')
#     ax.plot(strain[a2:b2],stress[a2:b2],'b.') #this will show all the data we're fitting
    
    #Plot the fits
    ax.plot(X1,Y1,label='Fit1, E='+str(round(E1*1e-3,1))+' GPa')
    ax.plot(X2,Y2,label='Fit2, E='+str(round(E2*1e-3,1))+' GPa')

ax.set_xlim(left = 0, right=0.004)
ax.set_ylim(bottom = 0, top=400)
plt.title("Modulus Fit")
plt.ylabel('Stress (MPa)')
plt.xlabel('Strain (mm/mm)')
plt.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>You can see that slightly different 'linear regions' for measuring the slope can produce noticeably different results  Once you have found good fitting points, you will need to save these results and average them for all the data<jupyter_code>#a = ?
#b = ?
youngsModuli = []
ER2Values = []
for File in Files:
    #Save dummy variables to make the code cleaner below
    strain = Data[File]['Strain (mm/mm)'].values
    stress = Data[File]['Stress (MPa)'].values
    
    #Use the two fits
    #E,C,R,X,Y = modulusFit(strain,stress,a,b)
    
    #youngsModuli += [E]
    #ER2Values += [R**2]<jupyter_output><empty_output><jupyter_text>Once you get all the values, you can find an average of the data<jupyter_code>from numpy import mean,std 
#You can call the mean and std (standard deviation) for your Young's Moduli values<jupyter_output><empty_output><jupyter_text>Poisson's Ratio Calculation We need to measure the slope of the axial vs transverse strain to find the Poisson's ratio We know that this is only valid in the linear elastic region, and we can use the same strain range from the modulus fit<jupyter_code>#Here we will make a subplot to show a zoomed section
eZoom = 0.01; aZoom = 0.002
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14,7))
for File in Files:
    ax1.plot(Data[File]['Axial Strain (mm/mm)'],Data[File]['Transverse Strain (mm/mm)'],label=File)
    ax2.plot(Data[File]['Axial Strain (mm/mm)'],Data[File]['Transverse Strain (mm/mm)'],label=File)

#Plot the zoomed box
ax1.plot([0,0,eZoom,eZoom,0],[0,aZoom,aZoom,0,0],'r--')

#Add Labels
ax1.set_xlim(left=0)
ax1.set_ylim(bottom=0)
ax2.set_xlim(left = 0, right=eZoom)
ax2.set_ylim(bottom = 0, top=aZoom)
ax1.set_title("Axial Stress vs Transverse Strain")
ax2.set_title("Axial Stress vs Transverse Strain Magnified")
ax1.set_xlabel('Axial Strain (mm/mm)')
ax2.set_xlabel('Axial Strain (mm/mm)')
ax1.set_ylabel('Transverse Strain (mm/mm)')
ax2.set_ylabel('Transverse Strain (mm/mm)')
ax1.legend()
ax2.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>We can define the same sort of slope fit function for Poisson's ratio  We could also reuse the modulus fit funciton above, but we'll redefine it here with different variables just for clarity<jupyter_code>from scipy.stats import linregress #This is a linear regression function built into the Scipy library. You can call help(linregress) if you'd like to learn more.

def PoissonFit(axialStrain,transverseStrain,a,b):
    '''This is a linear fit to data between the data indices for a and b. Note, this will
    return an error if a or b are outside the length of Strain.'''
    
    #Fit the modulus
    nu,C,R,P,Err = linregress(axialStrain[a:b],transverseStrain[a:b]) #The data outputs the slope (nu), intercept (C), regression (R) value, P-value and standard error
    #Note: Python lets you save multivariable outputs with a comma, i.e. a,b=[1,2] will give a=1 and b=2
    
    #Make a line for the fit data
    Y = [0.0, transverseStrain[round(1.5*b)]]
    X = [(y-C)/nu for y in Y] #these are points that you can plot to visualize the data being fit, inverted from y=nu*x+C, x=(y-C)/nu
    return nu,R,X,Y<jupyter_output><empty_output><jupyter_text>Now let's see how the fit works<jupyter_code>fig = plt.figure(5)
ax = fig.gca()
a = 60; b = 150 #Note: you will have to play with these values for a given test type
for File in Files:
    #Create dummy variables to make plotting easier
    aStrain = Data[File]['Axial Strain (mm/mm)'].values
    tStrain = Data[File]['Transverse Strain (mm/mm)'].values
    
    #Do the fit
    nu,R,X,Y = PoissonFit(aStrain,tStrain,a,b)
    
    #Plot things
    ax.plot(aStrain,tStrain)
    ax.plot(aStrain[a],tStrain[a],'rd') #this is the first point we're fitting from
    ax.plot(aStrain[b],tStrain[b],'bs') #this is the last point we're fitting from
    ax.plot(aStrain[a:b],tStrain[a:b],'b.') #These are all the points we're fitting
    
    #Plot the fits
    ax.plot(X,Y,label=r"Poisson's Ratio Fit, $\nu$="+str(round(nu,3)))

#Format the plot
ax.set_xlim(left = 0, right=0.01)
ax.set_ylim(bottom = 0, top=0.002)
plt.title("Axial vs Transverse Strain Magnified")
plt.xlabel('Axial Strain (mm/mm)')
plt.ylabel('Transverse Strain (mm/mm)')
plt.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>You can see that the Poisson's ratio fit isn't  perfect and, like the modulus fit, will change depending on exactly where we fit the data. You will need to play with these values for each data set to make sure you are fitting the correct region  Once you have found good fitting points, you will need to save these results and average them for all the data<jupyter_code>#a = ?
#b = ?
PoissonsRatios = []
nuR2Values = []
for File in Files:
    #Save dummy variables to make the code cleaner below
    aStrain = Data[File]['Axial Strain (mm/mm)'].values
    tStrain = Data[File]['Transverse Strain (mm/mm)'].values
    
    #Use the two fits
    #nu,R,X,Y = PoissonFit(aStrain,tStrain,a,b)
    
    #PoissonsRatios += [nu]
    #R2Values += [R**2]<jupyter_output><empty_output><jupyter_text>Once you get all the values, you can find an average of the data<jupyter_code>from numpy import mean,std 
#You can call the mean and std (standard deviation) for your Poisson's ratio values<jupyter_output><empty_output><jupyter_text>Yield strength calculation  We will define a function for finding the yield stress based on the 0.2% offset method Note that this is only applicable for ductile materials<jupyter_code>def yieldStress(Strain,Stress,E,C,eOffset=0.002):
    '''This function will find the yield stress based on a 0.2% offset strain method.
    You must input the stress, strain, Youngs modulus E, fit line intercept point b, and
    can change the strain offset value.'''
    
    yP = next(i for i,x in enumerate(Strain) if Stress[i] < E*(x-eOffset) + C)
    #This code finds the first point where the Stress exceeds the strain offset line defined by y=E*(x-eOffset)+b
    #This is the simplest way to determine a slope intercept, but it only works if the stress and the offset line intersect
    
    return yP #this returns the index i of the yield stress, if you want it to return the stress, use: return Stress[yP]<jupyter_output><empty_output><jupyter_text>Brief aside - here's a quick look at the function we just used<jupyter_code>aList = [1,3,5,2,1,6,8,7] #this is a list of integers
aListSquared = [x**2 for x in aList] #this is a list comprehension that squares every entry in aList, it will go through the list elements sequentially
aListIndex = [i for i,x in enumerate(aList)] #this spits out all the indices of the elements in a list, here just 0,1,2,3...
aListPart = [x for x in aList if x>4] #this finds all the list elements that are greater than 4
aListNext = next(x for x in aList if x>4) #this finds the next list element that is greater than 4
aListNextIndex = next(i for i,x in enumerate(aList) if x>4) #this finds the index of the next list value greater than 4
print(aList)
print(aListSquared)
print(aListIndex)
print(aListPart)
print(aListNext)
print(aListNextIndex)
print(aList[aListNextIndex])<jupyter_output><empty_output><jupyter_text>Let's see how that yield function works by plotting the results!<jupyter_code>fig = plt.figure()
ax = fig.gca()
a = 10; b = 120 #Note: you will have to play with these values for a given test type
eOff = 0.002
for File in Files:
    strain = Data[File]['Strain (mm/mm)'].values
    stress = Data[File]['Stress (MPa)'].values
    
    #Find modulus and yield
    E,C,R,X,Y = modulusFit(strain,stress,a,b)
    iYield = yieldStress(strain,stress,E,C,eOffset=eOff)
    xOffset = [x+eOff for x in X]
    
    #Plot the data
    ax.plot(strain,stress)
    ax.plot(strain[a],stress[a],'bd') #This is the first point we're fitting from
    ax.plot(strain[b],stress[b],'bs') #this is the last point we're fitting from
    ax.plot(strain[a:b],stress[a:b],'b.') #These are all the points we're fitting
    ax.plot(strain[iYield],stress[iYield],'o', label=r'Yield, $\sigma_y$='+str(round(stress[iYield],1))+' MPa')
    
    #Plot the fits
    ax.plot(X,Y,label='Modulus, E='+str(round(E1*1e-3,1))+' GPa')
    ax.plot(xOffset,Y,'--')

ax.set_xlim(left = 0, right=0.01)
ax.set_ylim(bottom = 0, top=400)
plt.title("Yield Stress Calculation")
plt.ylabel('Stress (MPa)')
plt.xlabel('Strain (mm/mm)')
plt.legend()
plt.show()<jupyter_output><empty_output><jupyter_text>Once you have found good fitting points, you will need to save these results and average them for all the data<jupyter_code>#a = ?
#b = ?
yieldStrengths = []
yR2Values = []
for File in Files:
    #Save dummy variables to make the code cleaner below
    strain = Data[File]['Strain (mm/mm)'].values
    stress = Data[File]['Stress (MPa)'].values
    
    #Find modulus and yield
#     E,C,R,X,Y = modulusFit(strain,stress,a,b)
#     iYield = yieldStress(strain,stress,E,C,eOffset=eOff)
    
    #yieldStrengths += [stress[iYield]]
    #yR2Values += [R**2]<jupyter_output><empty_output><jupyter_text>Once you get all the values, you can find an average of the data<jupyter_code>from numpy import mean,std 
#You can call the mean and std (standard deviation) for your yield strength values<jupyter_output><empty_output><jupyter_text>Calculate Some Ultimate Values  Ultimate Stress and Strain Calculation<jupyter_code>for File in Files:
    ultimateStress = Data[File]['Stress (MPa)'].max()
    ultimateStrain = [] #calculate the ultimate strain here, i.e. the strain at the ultimate stress
    print("Ultimate Stress =", round(ultimateStress,1), "MPa for",File) 
#     print("Ultimate Strain =", ultimateStrain, "for",File)<jupyter_output><empty_output><jupyter_text>Fracture Stress and Strain Calculation<jupyter_code>for File in Files:
    fractureStress = [] #calculate the fracture stress here, i.e. the stress where the sample fractures
    fractureStrain = [] #calculate the fracture strain here, 
    #hint look at the max function for pandas, or use data.values and take the [-1] which is the last value
#     print("Fracture Stress =", round(fractureStress,1), "MPa for",File) 
#     print("Fracture Strain =", fractureStrain, "for",File)<jupyter_output><empty_output><jupyter_text>Percent Elongation Calculation<jupyter_code>#Input the sample parameters
Lo = 25.4 #initial sample length, change for each specimen

for File in Files:
    deltaL = Data[File]['Extension (mm)'].max()
    L = Lo+deltaL
    #percentElongation = #calculate the percent elongation here
#     print(percentElongation)<jupyter_output><empty_output><jupyter_text>Calculate the Disspiated Energy  Tensile toughness<jupyter_code>#There are many integration functions built into python libraries, here we will use numpy trapozoidal rule
from numpy import trapz #this is the trapezoidal rule integration function

#We want to integrate over axial stress and strain to find the area under the curve
for File in Files:
    #Choose your data
    yData = Data[File]['Stress (MPa)']
    xData = Data[File]['Axial Strain (mm/mm)']
    
    #Calculate the area
    tensileToughness = trapz(yData,x=xData) #if we don't include xData, it will take the spacing to be 1
    
    #Print the result
    print('Tensile toughness =',round(tensileToughness,2),'MPa') #We're rounding to the nearest 0.01<jupyter_output><empty_output><jupyter_text>Modulus of resilience<jupyter_code>#There are many integration functions built into python libraries, here we will use numpy trapozoidal rule
from numpy import trapz #this is the trapezoidal rule integration function

#We want to integrate over axial stress and strain to find the area under the curve
for File in Files:
    #### Here you will need to use the yield strength point that was calculated above (or recalculate it here)
    #### to find the modulus of resilience of the material
    #### You can copy the process in the 'tensile toughness' section above, or use a different method
#     print('Modulus of Resilience = ', round(modulusOfResilience,2)),'MPa') #We're rounding to the nearest 0.01<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11416788211034552, 'fraction_numerical': 0.013931499270733793, 'mean_word_length': 4.793706293706293, 'pattern_counts': {'":': 0, '<': 118, '<?xml version=': 0, '>': 120, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '29', 'dir': 'jupyter-structured-clean-dedup', 'id': '454475', 'license': 'Unlicense', 'n_tokens_mistral': 7029, 'n_tokens_neox': 6653, 'n_words': 2327, 'path': 'Labs/Lab 2 Tension/Tension_Data_Analysis_Template.ipynb', 'repo_name': 'lucasmeza/me354_autumn2020', 'size': '19883'}