7.3. Inset of Elastic Region

This is the second section on how to plot stress strain curves with Python and Matplotlib. In the previous section we built a stress strain curve from the data in two .xls data files. The plot built in the previous section included two lines, axes labels, a title and legend. The data in the plot came from two .xls excel files and was from a tensile test of two metal samples.

The code below builds the plot in the previous section.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

steel_df = pd.read_excel("Al60601_raw_data.xls")
al_df = pd.read_excel("Steel1018_raw_data.xls")

strain_al = al_df['CH5']*0.01
d_al = 0.506
stress_al = (al_df['FORCE']*0.001)/(np.pi*((d_al/2)**2))
strain_steel = steel_df['CH5']*0.01
d_steel = 0.506
stress_steel = (steel_df['FORCE']*0.001)/(np.pi*((d_steel/2)**2))

plt.plot(strain_al,stress_al,strain_steel,stress_steel)
plt.xlabel('strain (in/in)')
plt.ylabel('stress (ksi)')
plt.title('Stress Strain Curve of Aluminum 6061 and Steel 1045 in tension')
plt.legend(['Aluminum 6061','Steel 1045'])
plt.show()
WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero
WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero
../_images/stress_strain_curve_with_inset_elastic_region_2_1.png

In this section, we are going to add an inset curve to the plot that shows the linear elastic region. An inset curve is a small plot that is placed within a plot that shows a zoomed-in view of the larger plot. On a stress-strain cuve, an inset curve is used to zoom-in on the linear elastic region on the plot.

To build our inset curve, we will use Python and Matplotlib’s object-oriented interface. The plot above was constructed using the Matplotlib’s pyplot interface. For a simple plot, the pyplot interface above is sufficient.

But our plot with an inset curve is more complicated than the plot above. Matplotlib’s object-oriented interface allows us to access individual components of the plot, like axes, and set specific attributes on these components. This added versitility means we can include an inset curve within another plot.

To build the plot with an inset curve, first we need to import a couple modules: NumPy, Pandas, and Matplotlib. These three modules have common import aliases np, pd, and plt. The line %matplotlib inline is a Jupyter notebook magic command that will show plots directly in a Jupyter notebook. If you are coding your inset curve in a .py file, leave %matplotlib inline out, as it is not valid Python code.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

Next we need to bring in the data from two external files into variables in our script. The two external files that contain the data we are going to plot are linked below:

Al60601_raw_data.xls

Steel1018_raw_data.xls

If you are following along and building an inset curve yourself, make sure to download the .xls data files and place these data files in the same directory as you Jupyter notebook or Python script.

A conveniant Jupyter notebook magic command, %ls, lists the contents of the current directory (the directory where the notebook is saved). Make sure you can see the two .xls data files in this directory.

%ls
ABS_HIPS_plot_with_error_bars.png
Al60601_raw_data.xls
Steel1018_raw_data.xls
bar_plot_with_error_bars.ipynb
calculate-yield-strength-programmatically.ipynb
images/
mechanical-properties-from-stress-strain-curves.ipynb
mechanical-properties-intro.md
plotting-stress-strain-curves.ipynb
stress-strain_curve.png
stress_strain_curve_with_inset.png
stress_strain_curve_with_inset_elastic_region.ipynb

We can see the two data files Al60601_raw_data.xls and Steel1018_raw_data.xls in the same folder as our Jupyter notebook stress_strain_curve_with_inset_elastic_region.ipynb

Next, we will read in the two .xls data files using Pandas pd.read_excel() function. The result is two Pandas dataframes steel_df and al_df.

steel_df = pd.read_excel("Al60601_raw_data.xls")
al_df = pd.read_excel("Steel1018_raw_data.xls")
WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero
WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero

We can use Pandas df.head() method to view the first five rows of each dataframe.

steel_df.head()
TESTNUM POINTNUM TIME POSIT FORCE EXT CH5 CH6 CH7 CH8
0 542 1 8.470 0.02256 201.146011 -0.001444 0.007552 NaN NaN NaN
1 542 2 8.632 0.02330 206.599442 0.000302 0.007552 NaN NaN NaN
2 542 3 10.027 0.02846 287.512573 0.003044 0.018898 NaN NaN NaN
3 542 4 11.031 0.03232 365.380981 0.009881 0.022061 NaN NaN NaN
4 542 5 11.928 0.03616 447.813965 0.014085 0.033652 NaN NaN NaN
al_df.head()
TESTNUM POINTNUM TIME POSIT FORCE EXT CH5 CH6 CH7 CH8
0 523 1 6.189 0.07302 202.924728 0.000402 -0.028272 NaN NaN NaN
1 523 2 6.549 0.07396 205.714890 -0.000238 -0.034549 NaN NaN NaN
2 523 3 7.148 0.07624 217.763336 -0.000713 -0.030140 NaN NaN NaN
3 523 4 9.146 0.08438 316.306122 0.002377 -0.025968 NaN NaN NaN
4 523 5 10.041 0.08822 417.003357 0.003089 -0.024100 NaN NaN NaN

We see a couple of columns in each dataframe. The columns we are interest in are below:

  • FORCE Force measurements from the load cell in pounds (lb)

  • EXT Extension measurements from the mechanical extensometer in percent (%), strain in percent

  • CH5 Extension readings from the laser extensometer in percent (%), strain in percent

We will use these columns from the dataframes in the following ways

  • FORCE will be converted to stress using the cross-sectional area of our test samples

\[ \sigma = \frac{F}{A_0} \]
  • EXT (mechanical exstensometer readings) will be converted into strain on our inset cuve

\[ \epsilon_{unitless} = \epsilon_{\%} \times 0.01 \]
  • CH5 (laser extensometer readings) will be converted into strain on the overall, large stress strain curve

\[ \epsilon_{unitless} = \epsilon_{\%} \times 0.01 \]
strain_al_plastic = al_df['CH5']*0.01
strain_al_elastic = al_df['EXT']*0.01
d_al = 0.506
stress_al = (al_df['FORCE']*0.001)/(np.pi*((d_al/2)**2))

strain_steel_plastic = steel_df['CH5']*0.01
strain_steel_elastic = steel_df['EXT']*0.01
d_steel = 0.506
stress_steel = (steel_df['FORCE']*0.001)/(np.pi*((d_steel/2)**2))

Now that we have the data in 6 pandas series, we can build a simple plot. We’ll use Matplotlib’s object-oriented interface to create to objects, a figure object called fig and an axes object ax1. Then we can run the .plot() method on our ax1 object and pass in two sets of series. The command plt.show() shows the plot.

fig, ax1 = plt.subplots()
ax1.plot(strain_al_plastic,stress_al,strain_steel_plastic,stress_steel)
plt.show()
../_images/stress_strain_curve_with_inset_elastic_region_16_0.png

We see a plot that contains two lines.

Next let’s use Matplotlib’s object-oriented interface to add axis labels and titles. Below is a chart that relates how to use Matplotlib’s plt approach, compared to the object-oriented approach that we’ll use.

plot component

plt approach

object-oriented approach

x-axis label

plt.xlabel('strain')

ax.set_xlabel('strain')

y-axis label

plt.ylabel('stress

ax.set_xyabel('stress')

title

plt.title('stress strain curve')

ax.set_title('stress strain curve')

legend

plt.legend(['Al6061','Steel1045])

ax.legend(['Al6061','Steel1045])

fig, ax1 = plt.subplots()
fig.set_size_inches([9,9])
ax1.plot(strain_al_plastic,stress_al,strain_steel_plastic,stress_steel)
ax1.legend(['Al6061','Steel1045'])
ax1.set_xlabel('Strain (in/in)')
ax1.set_ylabel('Stress (ksi)')
ax1.set_title('Stress Strain Curve of Al6061 and Steel1045 in Tension')

plt.show()
../_images/stress_strain_curve_with_inset_elastic_region_18_0.png

Next, let’s add the inset curve. Our inset curve is a zoomed-in view of the linear elastic region. We’ll use Matplotlib’s ax.add_axes([ ]) method. This method outputs a new axis object ax2. ax2 will be the axes that we plot the inset curve on. We can set attributes like .set_xlabel() on our ax2 object to add labels to the axis, add a title, and limit the x and y axis.

fig, ax1 = plt.subplots()
fig.set_size_inches([9,9])

ax1.plot(strain_al_plastic,stress_al,strain_steel_plastic,stress_steel)
ax1.legend(['Al6061','Steel1045'])
ax1.set_xlabel('Strain (in/in)')
ax1.set_ylabel('Stress (ksi)')
ax1.set_title('Stress Strain Curve of Al6061 and Steel1045 in Tension \n with inset curve of elastic region')

left, bottom, width, height = [0.35, 0.2, 0.25, 0.25]
ax2 = fig.add_axes([left, bottom, width, height])
ax2.plot(strain_al_elastic,stress_al,strain_steel_elastic, stress_steel)
ax2.set_xlabel('Strain (in/in)')
ax2.set_ylabel('Stress (ksi)')
ax2.set_title('Inset of Elastic Region')
ax2.set_xlim([0,0.006])
ax2.set_ylim([0,75])

fig.savefig('stress_strain_curve_with_inset.png')

plt.show()
../_images/stress_strain_curve_with_inset_elastic_region_20_0.png

We see a plot that contains an inset curve. The inset curve has two lines, just like the large plot. The inset curve has axis labels and a title. The same colors in the main plot is used in the inset plot.

Summary

In this section, we plotted a stress strain curve with Python and Matplotlib. We used Matplotlib’s object oriented interface to build the inset curve. Our inset curve was added on top of our main plot.