6061T6 aluminum data analysis

In this example, we use MatCal and python tools to plot our data and verify our assumption that the material exhibits orthotropic plasticity behavior. The tests that were performed for this material that are relevant to this example include ASTME8 uniaxial tension testing in three directions relative to the material rolling direction and Sandia’s shear top hat testing [3, 4] in six orientations relative to the material rolling direction.

Note

Useful Documentation links:

  1. BatchDataImporter

  2. plot()

  3. determine_pt2_offset_yield()

To use this data for calibrating the Hill48 yield surface [8], we must first define our material coordinate system. The local material directions will be denoted by numbers to adopt the convention of the Hill yield ratios in [27]. Since the material is extruded through rollers, the material coordinate system is a cartesian system aligned with the rolled plate. We decided that the material 22 direction aligns with the test rolling direction (RD), the material 11 direction will align with the long transverse direction (LT), and the material 33 direction aligns with the short transverse test direction (ST).

With the material coordinate system defined, we should determine which pairs of the six shear tests represent the same stress state for characterization of the Hill yield surface shear ratios. For each shear test, we create a free-body diagram of a material point with the test loading directions shown along with our chosen material directions. With this diagram we can see which tests are probing which shear stresses for calibrating the Hill yield shear ratios. This diagram is shown in Fig. 20.

../../_images/top_hat_shear_diagram.png

Fig. 20 The shear stress states for each test are shown here on free body diagrams of a material point on the top hat specimen where the shear bands form.

Fig. 20 shows that the first two letters in the test name determine the stress state that is being probed by the test. Since \sigma_{ij} and \sigma_{ji} are equal for a quasistatic material point, the stress state is independent of the order of the first two letters. For example, the RST and SRT tests both impose a primarily \sigma_{23}/\sigma_{32} stress in the shear band and can be used to calibrate the Hill ratio R_{23}. As a result, we will be assigning the data one of the three shear Hill ratios depending upon which Hill ratio they can be used to calibrate. The RTS/TRS tests will be assigned R12, the SRT/RST tests will be assigned R23 and the STR/TSR tests will be assigned R31. Similarly, the tension tests will be assigned R11, R22 and R33 for the LT, RD and ST tests, respectively. These assignments will be done under a direction state variable as described later in this example.

With the material directions and test names correlated to the Hill ratios, we can analyze the data and determine if we can calibrate a Hill yield surface for the material.

We begin by importing MatCal, NumPy and matplotlib, and setting global plotting options to our preferences.

import numpy as np
from matcal import *
import matplotlib.pyplot as plt
# sphinx_gallery_thumbnail_number = 3
plt.rc('text', usetex=True)
plt.rc('font', family='serif')
plt.rc('font', size=12)
figsize = (4,3)

Next, we use MatCal’s BatchDataImporter to import our preprocessed data files. These have been formatted such that the importer will assign unique states to each test. These states are predetermined and assigned with a data preprocessing tool (not shown here). The assignment is made by writing the state information as the first line in each data file according to CSV file data importing details. This allows us to easily import the data using the BatchDataImporter with the correct states already assigned.

The tension data is imported first and scaled so that the units are in psi.

tension_data_collection = BatchDataImporter("aluminum_6061_data/"
                                              "uniaxial_tension/processed_data/"
                                              "cleaned_[CANM]*.csv",).batch
tension_data_collection = scale_data_collection(tension_data_collection,
                                                  "engineering_stress", 1000)

Tension testing was performed at multiple temperatures in addition to the multiple directions. As a result, there are both temperature and direction state variables for these test. To see the states of the data sets uploaded, print the state_names() so that you can use these state names for manipulating the data.

print(tension_data_collection.state_names)
['temperature_5.330700e+02_direction_R11', 'temperature_5.330700e+02_direction_R22', 'temperature_5.330700e+02_direction_R33']

We then import the top hat shear data using the BatchDataImporter. This testing was only completed at room temperature and only has the direction state variable.

top_hat_data_collection = BatchDataImporter("aluminum_6061_data/"
                                              "top_hat_shear/processed_data/cleaned_*.csv").batch
print(top_hat_data_collection.state_names)
['direction_R23', 'direction_R12', 'direction_R31']

Next, we use the plot() and matplotlib tools to plot the data on two figures according to the test geometry with their color determined by state.

tension_fig = plt.figure(figsize=figsize, constrained_layout=True)
tension_data_collection.plot("engineering_strain", "engineering_stress",
                             state="temperature_5.330700e+02_direction_R22",
                             show=False, labels="$R_{22}$", figure=tension_fig,
                             color='tab:red')
tension_data_collection.plot("engineering_strain", "engineering_stress",
                             state="temperature_5.330700e+02_direction_R11",
                             show=False, labels="$R_{11}$", figure=tension_fig,
                             color='tab:blue')
tension_data_collection.plot("engineering_strain", "engineering_stress",
                             state="temperature_5.330700e+02_direction_R33",
                             labels="$R_{33}$", figure=tension_fig,
                             color='tab:green')
tension_data_collection.remove_field("time")

top_hat_fig = plt.figure(figsize=figsize, constrained_layout=True)
top_hat_data_collection.plot("displacement", "load", show=False,
                             state="direction_R12", labels="$R_{12}$",
                             figure=top_hat_fig, color='tab:cyan')
top_hat_data_collection.plot("displacement", "load", show=False,
                             state="direction_R23", labels="$R_{23}$",
                             figure=top_hat_fig, color='tab:orange')
top_hat_data_collection.plot("displacement", "load",
                             state="direction_R31", labels="$R_{31}$",
                             figure=top_hat_fig, color='tab:purple')
  • plot 6061T6 a anisotropy data analysis
  • plot 6061T6 a anisotropy data analysis

Looking at the tension stress/strain and top hat load/displacement data, it seems that the material is anisotropic. However, the material exhibits significant variability even within direction. As a result, we want a more quantitative measure from which to judge anisotropy. One way to do this is to statistically quantify differences in the stress or load at different strain or displacement values between each direction. This can be done easily using NumPy data manipulation and plotting these data with box-and-whisker plots. Since we are most interested in anisotropic yield for this material, we will look at the 0.2% offset yield stress for the tension data.

In order to look at the 0.2% offset yield values, we need to extract those values from the data. We do that by generating elastic stress data with a 0.2% strain offset and determining where these generated data cross the experimental data. The MatCal function determine_pt2_offset_yield() performs this calculation. We can apply it to all of our data sets and create a box-and-whisker plot comparing the yield stresses for the different loading directions. We do that by looping over each state in the data collection and applying that function to each data set in each state. We store those values in a dictionary according to state and create the box-and-whisker plot.

yield_stresses = {"temperature_5.330700e+02_direction_R11":[],
                  "temperature_5.330700e+02_direction_R22":[],
                  "temperature_5.330700e+02_direction_R33":[]}
for state, data_sets in tension_data_collection.items():
    for data in data_sets:
        yield_pt = determine_pt2_offset_yield(data, 10e6)
        yield_stresses[state.name].append(yield_pt[1])

plt.figure(figsize=figsize, constrained_layout=True)
plt.boxplot(yield_stresses.values(), labels=["$R_{11}$", "$R_{22}$", "$R_{33}$"])
plt.xlabel("direction")
plt.ylabel("yield stress (psi)")
plt.show()
plot 6061T6 a anisotropy data analysis

This plot shows that the median yield stress values for the different directions are measurably different. In fact, the medians fall outside the maximums and minimums for the other direction data sets except for the single outlier in the R_{11} data. Also, there is little overlap for the different direction maximums and minimums This plot supports the assumption that an anisotropic yield function should be used to model the data. The overall spread in the medians for the yield stress in different directions is approximately ~10%.

r11_median = np.average(yield_stresses["temperature_5.330700e+02_direction_R11"])
r22_median = np.average(yield_stresses["temperature_5.330700e+02_direction_R22"])
r33_median = np.average(yield_stresses["temperature_5.330700e+02_direction_R33"])
medians = [r11_median, r22_median, r33_median]
normalized_median_range = (np.max(medians)-np.min(medians))/np.average(medians)
print(normalized_median_range)
0.09555345170158815

Note that there appears to be significant anisotropic hardening early in the stress strain curve. This is shown by comparing stresses at slightly higher strains. Now we create box-and-whisker plots and look at the normalized range of the medians for the engineering stress at 3% strain.

stresses = {"temperature_5.330700e+02_direction_R11":[],
                  "temperature_5.330700e+02_direction_R22":[],
                  "temperature_5.330700e+02_direction_R33":[]}
for state, data_sets in tension_data_collection.items():
    for data in data_sets:
        stress = np.interp(0.03, data["engineering_strain"], data["engineering_stress"])
        stresses[state.name].append(stress)

plt.figure(figsize=figsize, constrained_layout=True)
plt.boxplot(stresses.values(), labels=["$R_{11}$", "$R_{22}$", "$R_{33}$"])
plt.xlabel("direction")
plt.ylabel("stress at 3\% strain (psi)")
plt.show()

r11_median = np.average(stresses["temperature_5.330700e+02_direction_R11"])
r22_median = np.average(stresses["temperature_5.330700e+02_direction_R22"])
r33_median = np.average(stresses["temperature_5.330700e+02_direction_R33"])
medians = [r11_median, r22_median, r33_median]
normalized_median_range = (np.max(medians)-np.min(medians))/np.average(medians)
print(normalized_median_range)
plot 6061T6 a anisotropy data analysis
/gpfs/knkarls/projects/matcal-stable/external_matcal/documentation/advanced_examples/6061T6_anisotropic_calibration/plot_6061T6_a_anisotropy_data_analysis.py:252: SyntaxWarning: invalid escape sequence '\%'
  plt.ylabel("stress at 3\% strain (psi)")
0.026156214170045298

We can see that the spread in the medians has reduced significantly to 2.5%. However, a measurable difference still exists. Although a more complex material model with anisotropic hardening could capture this behavior, we will continue with our chosen model form for the purpose of this example.

We now complete a similar plot for the top hat data. We will compare the load at a 0.005” displacement which is where the data appears to become nonlinear.

top_hat_yield_load = {"direction_R12":[], "direction_R23":[], "direction_R31":[]}
for state, data_sets in top_hat_data_collection.items():
    for data in data_sets:
        estimated_yield_load = np.interp(0.005, data["displacement"], data["load"])
        top_hat_yield_load[state.name].append(estimated_yield_load)

plt.figure(figsize=figsize, constrained_layout=True)
plt.boxplot(top_hat_yield_load.values(), labels=["$R_{12}$", "$R_{23}$", "$R_{31}$"])
plt.xlabel("direction")
plt.ylabel("load at 0.005\" displacement (lbs)")
plt.show()
plot 6061T6 a anisotropy data analysis

Similarly to the tension data, the top hat data also shows mild anisotropy according to this measure. With this evidence to support our material model choice, we now move on to the next example where we use this data to estimate the initial point that will be used in our full finite element calibration for the material model.

Total running time of the script: (0 minutes 4.212 seconds)

Gallery generated by Sphinx-Gallery