Defining Flow Stress from Interpolated Data - Using Lambda Functions

In this notebook it is shown, how to define a flow stress function from interpolated data using the interpolation functions from scipy. The concept can be transferred to any other material data as well.

This notebook is essentially the same as Interpolating_Flow_Stress.ipynb, with one main difference: we will use lambda functions as hook values instead of real hook implementations. This is appropriate for usage in input files and for quick testing purposes, plugins shall and can not use this approach.

First, we need to import some packages.

import pyroll.basic as pr # PyRolL itself

import pandas as pd # pandas for data import
import numpy as np # numpy for numeric calculations
import scipy.interpolate as interpolate # scipy.interpolate for interpolations

We import the data to interpolate from a CSV file using pandas. The result is a dataframe containing all data from the file. Note, that the column names are derived from the first row of the file.

flow_stress_data = pd.read_csv("Interpolating_Flow_Stress_Data.csv")
flow_stress_data
phi kf
0 0.000000 100.000000
1 0.020202 106.203666
2 0.040404 107.637609
3 0.060606 108.625511
4 0.080808 109.403000
... ... ...
95 1.919192 124.320109
96 1.939394 124.396628
97 1.959596 124.472591
98 1.979798 124.548008
99 2.000000 124.622888

100 rows × 2 columns

We can interpolate the data using scipy to get an interpolation function, which we can evaluate as any other function.

flow_stress_function = interpolate.interp1d(flow_stress_data.phi, flow_stress_data.kf)

Here comes the difference, we do not define hook implementations, but give the interpolation function directly to the in profile. The flow_stress hook is the main hook for calculation flow stress, it returns the flow stress depending on the current state of the profile. Another hook regarding flow stress is flow_stress_function. It returns a function of the classic flow stress parameters strain, strain rate and temperature to enable plugins to calculate their own variations of flow stress. Here it needs a nested lambda function to be defined.

in_profile = pr.Profile.square(
    side=45e-3,
    corner_radius=3e-3,
    temperature=1100 + 273.15,
    strain=0,
    density=7.5e3,
    specific_heat_capacity=690,
    flow_stress=lambda self: flow_stress_function(self.strain),
    flow_stress_function=lambda self: lambda strain, strain_rate, temperature: flow_stress_function(strain)
)
in_profile
2024-04-19T06:31:04.669501 image/svg+xml Matplotlib v3.8.2, https://matplotlib.org/
SquareProfile
classifiers{'diamond', 'square'}
corner_radius0.003
cross_section
Polygon
area0.0020172289364149133
height0.061154328932550704
perimeter0.17484198694172845
width0.061154328932550704
density7500.0
diagonal0.06363961030678927
flow_stress<function <lambda> at 0x7f6b8e30b520>
flow_stress_function<function <lambda> at 0x7f6b8e30b640>
side0.045
specific_heat_capacity690
strain0
t0
temperature1373.15

And the groove, the working roll and the roll pass…

groove1 = pr.DiamondGroove(
    usable_width=76.55e-3,
    tip_depth=22.1e-3,
    r1=12e-3,
    r2=8e-3,
)
groove1
2024-04-19T06:31:04.783304 image/svg+xml Matplotlib v3.8.2, https://matplotlib.org/
DiamondGroove
alpha10.5236363668157296
alpha20.5236363668157296
classifiers{'diamond', 'generic_elongation'}
contour_line
LineString
height0.020862195195594648
length0.12428942750252188
width0.11360126410252692
depth0.020862195195594648
flank_angle0.5236363668157296
ground_width0.004287509401684611
r10.012
r20.008
tip_angleNone
tip_depth0.0221
usable_width0.07655
roll1 = pr.Roll(
    groove=groove1,
    nominal_radius=328e-3,
)
roll1
Roll
groove
2024-04-19T06:31:04.892745 image/svg+xml Matplotlib v3.8.2, https://matplotlib.org/
DiamondGroove
alpha10.5236363668157296
alpha20.5236363668157296
classifiers{'diamond', 'generic_elongation'}
contour_line
LineString
height0.020862195195594648
length0.12428942750252188
width0.11360126410252692
depth0.020862195195594648
flank_angle0.5236363668157296
ground_width0.004287509401684611
r10.012
r20.008
tip_angleNone
tip_depth0.0221
usable_width0.07655
nominal_radius0.328
pass1 = pr.RollPass(
    roll=roll1,
    gap=3e-3,
    velocity=1,
)
pass1
2024-04-19T06:31:05.236606 image/svg+xml Matplotlib v3.8.2, https://matplotlib.org/
RollPass
disk_elements[]
gap0.003
in_profileNone
label''
out_profileNone
roll
RollPass.Roll
groove
2024-04-19T06:31:05.006268 image/svg+xml Matplotlib v3.8.2, https://matplotlib.org/
DiamondGroove
alpha10.5236363668157296
alpha20.5236363668157296
classifiers{'diamond', 'generic_elongation'}
contour_line
LineString
height0.020862195195594648
length0.12428942750252188
width0.11360126410252692
depth0.020862195195594648
flank_angle0.5236363668157296
ground_width0.004287509401684611
r10.012
r20.008
tip_angleNone
tip_depth0.0221
usable_width0.07655
nominal_radius0.328
velocity1

Lets run the solution procedure of the roll pass. We see no errors claiming that a flow stress value is missing.

pass1.solve(in_profile)
2024-04-19T06:31:05.383160 image/svg+xml Matplotlib v3.8.2, https://matplotlib.org/
Profile
classifiers{'diamond', 'generic_elongation'}
cross_section
Polygon
area0.0018470652317200606
height0.0447243903911893
perimeter0.1684260697843319
width0.06560167360406856
cross_section_error-0.0360789946906479
cross_section_filling_ratio0.9639210053093521
density7500.0
filling_error-0.143021899359
filling_ratio0.856978100641
flow_stress<function <lambda> at 0x7f6b8e30b520>
flow_stress_function<function <lambda> at 0x7f6b8e30b640>
length0.0
specific_heat_capacity690
strain0.19349552465258638
t0.07056039637464354
temperature1369.19933374975

Lets check, if our new hooks are really used. We check that the flow stress value stored in the out profile of the pass is numerically equal to a manually calculated value using the respective strain. If everything went fine, we shall se no output of this statement, otherwise an error message.

assert np.isclose(pass1.out_profile.flow_stress, flow_stress_function(pass1.out_profile.strain))