304L bar calibration initial point estimation

In this example, we estimate an initial point for our full finite element model calibration to data from [12]. We will use MatFit on the ASTME8 tension data to provide the initial point for the next example, 304L stainless steel viscoplastic calibration.

Note

Useful Documentation links:

  1. Running MatFit

First, import all needed tools. We will be using tools from NumPy, MatPlotLib, MatFit and MatCal for this example.

import numpy as np
from matcal import *
from matfit.models import Voce
from matfit.fitting import MatFit
import matplotlib.pyplot as plt

plt.rc('text', usetex=True)
plt.rc('font', family='serif')
plt.rc('font', size=12)
figsize = (4,3)

We import the data using the BatchDataImporter

tension_data = BatchDataImporter("ductile_failure_ASTME8_304L_data/*.dat",
                                    file_type="csv").batch

To use MatFit, we need to extract certain quantities of interest (QoIs) from each engineering stress strain curve. We need the yield stress, ultimate stress, strain at ultimate stress and the elongation strain for each test. We extract those from the DataCollection below and store each QoI set in a list to be used with MatFit. We use NumPy and MatCal’s determine_pt2_offset_yield to determine these QoIs from the data.

qoi_sets = []
steel_elastic_mod = 29e3
for state, data_sets in tension_data.items():
    for data in data_sets:
        yield_pt = determine_pt2_offset_yield(data, steel_elastic_mod)
        yield_stress = yield_pt[1]
        ultimate_stress = np.max(data["engineering_stress"])
        argmax = np.argmax(data["engineering_stress"])
        strain_at_ultimate = data["engineering_strain"][argmax]
        elongation_strain = np.max(data["engineering_strain"])
        qoi_sets.append([yield_stress, ultimate_stress, strain_at_ultimate, elongation_strain])

Next, we write a function that will take those QoIs and provide an estimate for a Voce material model [32] using MatFit. The function returns the Voce material parameters of saturation stress (A) and Voce exponent (b) in a solution dictionary. Since we already have an estimate for the yield, we will only be calibrating A and b with MatFit. MatFit requires specific formatting of input parameters. See the MatFit documentation for more information [6]. The bounds for our two calibrated parameters are estimated from the stress-strain curves and previous experience with the model for austenitic stainless steels.

def get_voce_params(yield_stress, ultimate_stress, strain_at_ultimate, elongation_strain):
    material_specification = dict(
        ultimate_strength = ultimate_stress,
        strain_at_ultimate = strain_at_ultimate,
        elongation = elongation_strain,
        yield_stress = yield_stress,
        youngs_modulus=steel_elastic_mod,
        poissons_ratio=0.27,
        density=7.41e-4)

    voce_parameters = dict(
        hardening_modulus=dict(value=200, lower=0, upper=1e3, calibrate=True),
        exponential_coefficient=dict(value=2.0, lower=0.0, upper=5, calibrate=True),
        )
    voce_model = Voce(material_specification, voce_parameters, name='Voce')
    MF = MatFit(voce_model)
    MF.fit(solver_settings=dict(method='trf'))
    solution = MF.get_solution()
    return solution

Next, we write another function to take the QoIs and calculate our Voce material parameters. We will store those in a dictionary for further analysis.

voce_params = {"Ys":[], "As":[], "bs":[]}
for qoi_set in qoi_sets:
    voce_params["Ys"].append(qoi_set[0])
    solution = get_voce_params(*qoi_set)

    voce_params["As"].append(solution['hardening_modulus'])
    voce_params["bs"].append(solution['exponential_coefficient'])
{'ultimate_strength': Data(84.07199899), 'strain_at_ultimate': 0.619835, 'elongation': Data(0.789549), 'yield_stress': 32.25690309030112, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(83.4860623), 'strain_at_ultimate': 0.666751, 'elongation': Data(0.801673), 'yield_stress': 31.778833058762395, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(83.38864318), 'strain_at_ultimate': 0.62209, 'elongation': Data(0.855288), 'yield_stress': 31.41117205738219, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(83.19837909), 'strain_at_ultimate': 0.645916, 'elongation': Data(0.791821), 'yield_stress': 31.236284728200893, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(83.96771346), 'strain_at_ultimate': 0.568989, 'elongation': Data(0.830619), 'yield_stress': 33.30904540520916, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(84.32342826), 'strain_at_ultimate': 0.555085, 'elongation': Data(0.814481), 'yield_stress': 33.383401774459486, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(84.34824576), 'strain_at_ultimate': 0.578079, 'elongation': Data(0.81299), 'yield_stress': 33.517787207559174, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002
{'ultimate_strength': Data(84.56332736), 'strain_at_ultimate': 0.565082, 'elongation': Data(0.808276), 'yield_stress': 34.029358835664695, 'youngs_modulus': 29000.0, 'poissons_ratio': 0.27, 'density': 0.000741, 'hardening_modulus': {'value': 200, 'lower': 0, 'upper': 1000.0, 'calibrate': True}, 'exponential_coefficient': {'value': 2.0, 'lower': 0.0, 'upper': 5, 'calibrate': True}}
Missing parameter: hardening_model
Using parameters default value: hardening_model | voce
Missing parameter: yield_strength_offset
Using parameters default value: yield_strength_offset | 0.002

First, we make histograms of each parameter. We want to ensure the parameters are as expected and try to understand the cause of any multi-modal behavior.

figsize=[4,3]
plt.figure("Ys", figsize, constrained_layout=True)
plt.hist(voce_params["Ys"], density=True, alpha=0.8)
plt.xlabel("Y (ksi)")
plt.ylabel("PDF")

plt.figure("As", figsize, constrained_layout=True)
plt.hist(voce_params["As"], density=True, alpha=0.8)
plt.xlabel("A (Ksi)")
plt.ylabel("PDF")

plt.figure("bs", figsize, constrained_layout=True)
plt.hist(voce_params["bs"], density=True, alpha=0.8)
plt.xlabel("b")
plt.ylabel("PDF")
  • plot 304L b initial point estimation
  • plot 304L b initial point estimation
  • plot 304L b initial point estimation
Text(18.926410511363635, 0.5, 'PDF')

From these plots there is some slight grouping. However, the parameter values are not spread out over a large range indicating MatFit has provided a good initial guess for the parameters. We can plot the data collection and verify that two groupings of the data are present. We do this with MatCal’s plot() method for DataCollection objects.

tension_fig = plt.figure("data", (5,4), constrained_layout=True)
tension_data.plot("engineering_strain", "engineering_stress",
                  figure=tension_fig, labels='ASTME8 data',
                  color="#bdbdbd")
plt.xlabel("engineering strain")
plt.ylabel("engineering stress (ksi)")
plot 304L b initial point estimation
Text(20.771400166044003, 0.5, 'engineering stress (ksi)')

In this plot, two groupings of the data can be seen since there are two groups with different elongation strains. This verifies the results seen in the histograms. Since these tension specimens were extracted from a large diameter bar, the different groupings likely correspond to extraction location and the resulting groupings in stress-strain behavior are expected.

Since we are ignoring any material inhomogeneity for this calibration, we will take the average of all calculated values and save that as the initial point for our full finite element model calibration.

voce_initial_point = {}
voce_initial_point["Y_0"] = np.average(voce_params["Ys"])
voce_initial_point["A"] = np.average(voce_params["As"])
voce_initial_point["b"] = np.average(voce_params["bs"])

print(voce_initial_point)
matcal_save("voce_initial_point.serialized", voce_initial_point)
{'Y_0': 32.61534826969239, 'A': 173.15419453381776, 'b': 1.9386756344331433}

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

Gallery generated by Sphinx-Gallery