Skip to content

Processing

Module with functions for processing materials test data. This includes functionality for finding properties like yield strength Young's modulus from stress-strain curves, as well as post-processing functions for cleaning and correcting experimental measurements.

calculate_strain_rate(ds, strain_key='Strain', time_key='Time_s', strain_rate_key='Strain_Rate')

Calculate the strain rate of a stress-strain curve.

Parameters:

Name Type Description Default
di

DataItem with stress-strain curve

required
strain_key str

Key for strain data

'Strain'
time_key str

Key for time data

'Time_s'
strain_rate_key str

Key for strain rate data

'Strain_Rate'
Source code in paramaterial\processing.py
def calculate_strain_rate(ds: DataSet, strain_key: str = 'Strain', time_key: str = 'Time_s',
                          strain_rate_key: str = 'Strain_Rate') -> DataSet:
    """Calculate the strain rate of a stress-strain curve.

    Args:
        di: DataItem with stress-strain curve
        strain_key: Key for strain data
        time_key: Key for time data
        strain_rate_key: Key for strain rate data

    Returns: DataItem with strain rate added to data.
    """
    def _calculate_di_strain_rate(di):
        gradient = np.gradient(di.data[strain_key], di.data[time_key])
        di.data[strain_rate_key] = gradient
        di.data[f'Smoothed_{strain_rate_key}'] = np.convolve(gradient, np.ones(5)/60, mode='same')
        return di

    return ds.apply(_calculate_di_strain_rate)

correct_friction_UC(di, mu_key='mu', h0_key='h_0', D0_key='D_0', disp_key='Disp(mm)', force_key='Force(kN)')

Calculate the pressure and corrected stress for a uniaxial compression test with friction.

Parameters:

Name Type Description Default
di DataItem

DataItem with uniaxial compression test data

required
mu_key str

Key for friction coefficient in info

'mu'
h0_key str

Key for initial height in info

'h_0'
D0_key str

Key for initial diameter in info

'D_0'
disp_key str

Key for displacement data

'Disp(mm)'
force_key str

Key for force data

'Force(kN)'
Source code in paramaterial\processing.py
def correct_friction_UC(di: DataItem, mu_key: str = 'mu', h0_key: str = 'h_0', D0_key: str = 'D_0',
                        disp_key: str = 'Disp(mm)', force_key: str = 'Force(kN)') -> DataItem:
    """
    Calculate the pressure and corrected stress for a uniaxial compression test with friction.

    Args:
        di: DataItem with uniaxial compression test data
        mu_key: Key for friction coefficient in info
        h0_key: Key for initial height in info
        D0_key: Key for initial diameter in info
        disp_key: Key for displacement data
        force_key: Key for force data

    Returns: DataItem with corrected stress and pressure added to data.
    """
    mu = di.info[mu_key]  # friction coefficient
    h_0 = di.info[h0_key]  # initial height in axial direction
    D_0 = di.info[D0_key]  # initial diameter
    h = h_0 - di.data[disp_key]  # instantaneous height
    d = D_0*np.sqrt(h_0/h)  # instantaneous diameter
    P = di.data[force_key]*1000*4/(np.pi*d**2)  # pressure (MPa)
    di.data['Pressure(MPa)'] = P
    di.data['Corrected_Stress(MPa)'] = P/(1 + (mu*d)/(3*h))  # correct stress
    return di

find_E(ds, LPL_stress, UPL_stress, strain_key='Strain', stress_key='Stress_MPa', E_key='E')

Find the elastic modulus of a stress-strain curve by fitting a line to the points between the specified stresses.

Parameters:

Name Type Description Default
di

DataItem with stress-strain curve

required
LPL_stress float

Lower stress bound

required
UPL_stress float

Upper stress bound

required
strain_key str

Key for strain data

'Strain'
stress_key str

Key for stress data

'Stress_MPa'
E_key str

Key to store elastic modulus in info

'E'
Source code in paramaterial\processing.py
def find_E(ds: DataSet, LPL_stress: float, UPL_stress: float, strain_key: str = 'Strain',
                      stress_key: str = 'Stress_MPa', E_key: str = 'E'):
    """Find the elastic modulus of a stress-strain curve by fitting a line to the points between the specified stresses.

Args:
        di: DataItem with stress-strain curve
        LPL_stress: Lower stress bound
        UPL_stress: Upper stress bound
        strain_key: Key for strain data
        stress_key: Key for stress data
        E_key: Key to store elastic modulus in info
    """
    ds = ds.copy()

    def find_di_E(di):
        x_data = di.data[strain_key].values
        y_data = di.data[stress_key].values
        x = x_data[(y_data >= LPL_stress) & (y_data <= UPL_stress)]
        y = y_data[(y_data >= LPL_stress) & (y_data <= UPL_stress)]
        E = np.polyfit(x, y, 1)[0]
        di.info[E_key] = E
        return di

    return ds.apply(find_di_E)

find_UTS(ds, strain_key='Strain', stress_key='Stress_MPa', max_strain=None)

Find the ultimate tensile strength (UTS) of an engineering stress-strain curves in the DataSet. The UTS is defined as the maximum stress in the curve. The UTS is added to the DataSet.info dictionary as 'UTS_1' and the strain at the UTS is added as 'UTS_0'.

Parameters:

Name Type Description Default
ds DataSet

DataSet containing the stress-strain curves.

required
strain_key str

Key for the strain data in the DataItem.

'Strain'
stress_key str

Key for the stress data in the DataItem.

'Stress_MPa'
max_strain Optional[float]

Maximum strain to consider when finding the UTS. If None, the maximum strain in the curve is used.

None
Source code in paramaterial\processing.py
def find_UTS(ds: DataSet, strain_key: str = 'Strain', stress_key: str = 'Stress_MPa',
             max_strain: Optional[float] = None) -> DataSet:
    """Find the ultimate tensile strength (UTS) of an engineering stress-strain curves in the DataSet. The UTS is
    defined
    as the maximum stress in the curve. The UTS is added to the DataSet.info dictionary as 'UTS_1' and the strain at the
    UTS is added as 'UTS_0'.

    Args:
        ds: DataSet containing the stress-strain curves.
        strain_key: Key for the strain data in the DataItem.
        stress_key: Key for the stress data in the DataItem.
        max_strain: Maximum strain to consider when finding the UTS. If None, the maximum strain in the curve is used.

    Returns: DataSet with UTS added to info_table.
    """
    ds = ds.copy()

    def find_di_UTS(di):
        data = di.data[di.data[strain_key] <= max_strain] if max_strain is not None else di.data
        x = data[strain_key].values
        y = data[stress_key].values
        di.info['UTS_1'] = np.max(y)
        di.info['UTS_0'] = x[np.argmax(y)]
        return di

    return ds.apply(find_di_UTS)

find_flow_stress_values(ds, strain_key='Strain', stress_key='Stress_MPa', temperature_key=None, rate_key=None, flow_strain=None)

Parameters:

Name Type Description Default
di

DataItem with stress-strain curve

required
strain_key str

Data key for reading strain.

'Strain'
stress_key str

Data key for reading stress.

'Stress_MPa'
flow_strain_key

Info key for writing flow strain.

required
flow_stress_key

Info key for storing flow stress.

required
flow_strain Union[int, float, Tuple[float, float]]

Strain at which to find the flow stress. If None, the maximum stress is used.

None
Source code in paramaterial\processing.py
def find_flow_stress_values(ds: DataSet, strain_key: str = 'Strain', stress_key: str = 'Stress_MPa',
                            temperature_key: str = None, rate_key: str = None,
                            flow_strain: Union[int, float, Tuple[float, float]] = None) -> DataSet:
    """

    Args:
        di: DataItem with stress-strain curve
        strain_key: Data key for reading strain.
        stress_key: Data key for reading stress.
        flow_strain_key: Info key for writing flow strain.
        flow_stress_key: Info key for storing flow stress.
        flow_strain: Strain at which to find the flow stress. If None, the maximum stress is used.

    Returns: DataItem with flow stress added to info.
    """
    def find_di_flow_stress_values(di, flow_strain):
        if flow_strain is None:
            flow_strain = di.data[strain_key].max()
        if (type(flow_strain) is float) or (type(flow_strain) is int):
            di.info[f'flow_{strain_key}'] = flow_strain
            di.info[f'flow_{stress_key}'] = di.data[stress_key][di.data[strain_key] <= flow_strain].max()
            if temperature_key is not None:
                di.info[f'flow_{temperature_key}'] = di.data[temperature_key][di.data[strain_key] <= flow_strain].max()
            if rate_key is not None:
                di.info[f'flow_{rate_key}'] = di.data[rate_key][di.data[strain_key] <= flow_strain].max()
        elif type(flow_strain) is tuple:
            # average the flow stress over a range of strains
            di.info[f'flow_{strain_key}'] = np.mean(flow_strain)
            di.info[f'flow_{stress_key}'] = di.data[stress_key][
                (di.data[strain_key] >= flow_strain[0])&(di.data[strain_key] <= flow_strain[1])].mean()
            if temperature_key is not None:
                di.info[f'flow_{temperature_key}'] = di.data[temperature_key][
                    (di.data[strain_key] >= flow_strain[0])&(di.data[strain_key] <= flow_strain[1])].mean()
            if rate_key is not None:
                di.info[f'flow_{rate_key}'] = di.data[rate_key][
                    (di.data[strain_key] >= flow_strain[0])&(di.data[strain_key] <= flow_strain[1])].mean()
        return di

    return ds.apply(find_di_flow_stress_values, flow_strain=flow_strain)

find_fracture_point(ds, strain_key='Strain', stress_key='Stress_MPa')

Find the fracture point for the stress-strain curves in the DataSet. The fracture point is defined as the maximum strain in the curve. The fracture point is added to the DataSet.info dictionary as 'FP_1' and the stress at the fracture point is added as 'FP_0'.

Parameters:

Name Type Description Default
ds DataSet

DataSet with stress-strain curves

required
strain_key str

Key for strain data

'Strain'
stress_key str

Key for stress data

'Stress_MPa'
Source code in paramaterial\processing.py
def find_fracture_point(ds: DataSet, strain_key: str = 'Strain', stress_key: str = 'Stress_MPa') -> DataSet:
    """Find the fracture point for the stress-strain curves in the DataSet. The fracture point is defined as the
    maximum strain in the curve. The fracture point is added to the DataSet.info dictionary as 'FP_1' and the stress at
    the fracture point is added as 'FP_0'.

    Args:
        ds: DataSet with stress-strain curves
        strain_key: Key for strain data
        stress_key: Key for stress data

    Returns: DataSet with fracture point added to info_table.
    """
    ds = ds.copy()

    def find_di_fracture_point(di):
        idx_max = di.data[strain_key].idxmax()
        di.info['FP_0'] = di.data[strain_key][idx_max]
        di.info['FP_1'] = di.data[stress_key][idx_max]
        return di

    return ds.apply(find_di_fracture_point)

find_proof_stress(ds, proof_strain=0.002, strain_key='Strain', stress_key='Stress_MPa', E_key='E')

Find the proof stress of a stress-strain curve.

Parameters:

Name Type Description Default
di

DataItem with stress-strain curve

required
proof_strain float

Strain at which to find the proof stress

0.002
strain_key str

Key for strain data

'Strain'
stress_key str

Key for stress data

'Stress_MPa'
Source code in paramaterial\processing.py
def find_proof_stress(ds: DataSet, proof_strain: float = 0.002, strain_key: str = 'Strain',
                      stress_key: str = 'Stress_MPa', E_key: str = 'E') -> DataSet:
    """Find the proof stress of a stress-strain curve.

    Args:
        di: DataItem with stress-strain curve
        proof_strain: Strain at which to find the proof stress
        strain_key: Key for strain data
        stress_key: Key for stress data

    Returns: DataItem with proof stress added to info.
    """

    def find_di_proof_stress(di):
        E = di.info[E_key]
        x_data = di.data[strain_key].values
        y_data = di.data[stress_key].values
        x_shift = proof_strain
        y_line = E*(x_data - x_shift)
        try:
            cut = np.where(np.diff(np.sign(y_line - y_data)) != 0)[0][-1]
            m = (y_data[cut + 1] - y_data[cut])/(x_data[cut + 1] - x_data[cut])
            xl = x_data[cut]
            yl = y_line[cut]
            xd = x_data[cut]
            yd = y_data[cut]
            K = np.array([[1, -E], [1, -m]])
            f = np.array([[yl - E*xl], [yd - m*xd]])
            d = np.linalg.solve(K, f).flatten()
            di.info[f'PS_{proof_strain}_0'] = d[1]
            di.info[f'PS_{proof_strain}_1'] = d[0]
        except IndexError:
            di.info[f'PS_{proof_strain}_0'] = np.nan
            di.info[f'PS_{proof_strain}_1'] = np.nan
        return di

    return ds.apply(find_di_proof_stress)

find_upl_and_lpl(ds, strain_key='Strain', stress_key='Stress_MPa', preload=0, preload_key='Stress_MPa', max_strain=None, suppress_numpy_warnings=True)

Determine the upper proportional limit (UPL) and lower proportional limit (LPL) of a stress-strain curve. The UPL is the point that minimizes the residuals of the slope fit between that point and the specified preload. The LPL is the point that minimizes the residuals of the slope fit between that point and the UPL. The elastic modulus is the slope between the UPL and LPL.

Parameters:

Name Type Description Default
di

DataItem with stress-strain curve

required
strain_key str

key for strain data

'Strain'
stress_key str

key for stress data

'Stress_MPa'
preload float

preload value

0
preload_key str

key for preload data

'Stress_MPa'
max_strain Optional[float]

maximum strain to consider

None
suppress_numpy_warnings bool

suppress numpy warnings

True

Returns:

Type Description
DataSet

DataItem with UPL, LPL, and E added to info.

Source code in paramaterial\processing.py
def find_upl_and_lpl(ds: DataSet, strain_key: str = 'Strain', stress_key: str = 'Stress_MPa', preload: float = 0,
                     preload_key: str = 'Stress_MPa', max_strain: Optional[float] = None,
                     suppress_numpy_warnings: bool = True) -> DataSet:
    """Determine the upper proportional limit (UPL) and lower proportional limit (LPL) of a stress-strain curve.
    The UPL is the point that minimizes the residuals of the slope fit between that point and the specified preload.
    The LPL is the point that minimizes the residuals of the slope fit between that point and the UPL.
    The elastic modulus is the slope between the UPL and LPL.

    Args:
        di: DataItem with stress-strain curve
        strain_key: key for strain data
        stress_key: key for stress data
        preload: preload value
        preload_key: key for preload data
        max_strain: maximum strain to consider
        suppress_numpy_warnings: suppress numpy warnings

    Returns:
        DataItem with UPL, LPL, and E added to info.
    """
    if suppress_numpy_warnings:
        np.seterr(all="ignore")

    ds = ds.copy()

    def _find_upl_and_lpl(di: DataItem) -> DataItem:
        data = di.data[di.data[strain_key] <= max_strain] if max_strain is not None else di.data

        UPL = (0, 0)
        LPL = (0, 0)

        def fit_line(_x, _y):
            n = len(_x)  # number of points
            m = (n*np.sum(_x*_y) - np.sum(_x)*np.sum(_y))/(n*np.sum(np.square(_x)) - np.square(np.sum(_x)))  # slope
            c = (np.sum(_y) - m*np.sum(_x))/n  # intercept
            S_xy = (n*np.sum(_x*_y) - np.sum(_x)*np.sum(_y))/(n - 1)  # empirical covariance
            S_x = np.sqrt((n*np.sum(np.square(_x)) - np.square(np.sum(_x)))/(n - 1))  # x standard deviation
            S_y = np.sqrt((n*np.sum(np.square(_y)) - np.square(np.sum(_y)))/(n - 1))  # y standard deviation
            r = S_xy/(S_x*S_y)  # correlation coefficient
            S_m = np.sqrt((1 - r**2)/(n - 2))*S_y/S_x  # slope standard deviation
            S_rel = S_m/m  # relative deviation of slope
            return S_rel

        x = data[strain_key].values
        y = data[stress_key].values

        x_upl = x[data[preload_key] >= preload]
        y_upl = y[data[preload_key] >= preload]

        S_min = np.inf
        for i in range(3, len(x_upl)):
            S_rel = fit_line(x_upl[:i], y_upl[:i])  # fit a line to the first i points after the preload
            if S_rel < S_min:
                S_min = S_rel
                UPL = (x_upl[i], y_upl[i])

        x_lpl = x[x <= UPL[0]]
        y_lpl = y[x <= UPL[0]]

        S_min = np.inf
        for j in range(len(x), 3, -1):
            S_rel = fit_line(x_lpl[j:], y_lpl[j:])  # fit a line to the last i points before the UPL
            if S_rel < S_min:
                S_min = S_rel
                LPL = [x_lpl[j], y_lpl[j]]

        # if LPL is less than preload, then find the first data point with stress greater than preload
        if LPL[1] < preload:
            LPL = (x[data[preload_key] >= preload][0], y[data[preload_key] >= preload][0])


        di.info['UPL_0'] = UPL[0]
        di.info['UPL_1'] = UPL[1]
        di.info['LPL_0'] = LPL[0]
        di.info['LPL_1'] = LPL[1]
        di.info['E'] = (UPL[1] - LPL[1])/(UPL[0] - LPL[0])
        return di

    return ds.apply(_find_upl_and_lpl)