292 lines
9.9 KiB
Python
292 lines
9.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Convert Stress-Strain Tables to true values.
|
|
|
|
This Skript takes a stress-strain table or a collection of such and converts
|
|
the technical strains into true strains.
|
|
It is also capable of outputting comparison graphs of the converted tables.
|
|
|
|
"""
|
|
|
|
import os, sys, re
|
|
import numpy as np
|
|
import pandas as pd
|
|
import matplotlib.pyplot as plt
|
|
|
|
def parse_args():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Convert Stress-Strain Tables to true plastic flow values.',
|
|
usage='python %(prog)s [options] INPUTFILE [-o OUTPUTFILE]',
|
|
epilog='''
|
|
''')
|
|
|
|
parser.add_argument('input',
|
|
metavar='INPUTFILE',
|
|
help='path to input file containing stress-strain data')
|
|
|
|
parser.add_argument('-v', '--version',
|
|
action='version',
|
|
version='%(prog)s 1.1')
|
|
|
|
parser.add_argument('-e',
|
|
action='store_true',
|
|
dest='autodetect_emod',
|
|
help='enables autodetection of Young\'s modulus. (currently nonfunctional)')
|
|
|
|
group0 = parser.add_mutually_exclusive_group()
|
|
group0.add_argument('-c',
|
|
action='store_true',
|
|
dest='compressionflag',
|
|
help='set computation for compressive test data.')
|
|
|
|
group0.add_argument('-n',
|
|
action='store_false',
|
|
dest='cut_at_uts',
|
|
help='extend computation of tensile data beyond ultimate tensile strength.')
|
|
|
|
parser.add_argument('-N',
|
|
action='store_false',
|
|
dest='cut_neg_slope',
|
|
help='allow exported data to have a negative slope (may cause errors in FEM).')
|
|
|
|
parser.add_argument('-x',
|
|
metavar = 'SCALE',
|
|
default = 1.0,
|
|
type = float,
|
|
dest='scale_x',
|
|
help='set scaling factor for X data. Defaults to 1.')
|
|
|
|
parser.add_argument('-y',
|
|
metavar = 'SCALE',
|
|
default = 1.0,
|
|
type = float,
|
|
dest='scale_y',
|
|
help='set scaling factor for Y data. Defaults to 1.')
|
|
|
|
parser.add_argument('-p',
|
|
action='store_true',
|
|
dest='plotflag',
|
|
help='plot transformation steps from technical to true flow curves.')
|
|
|
|
parser.add_argument('-P',
|
|
action='store_true',
|
|
dest='comparisonflag',
|
|
help='plot comparison of true flow curves.')
|
|
|
|
group = parser.add_mutually_exclusive_group()
|
|
group.add_argument('-r',
|
|
metavar=('X', 'Y'),
|
|
type=float,
|
|
nargs=2,
|
|
dest='range_man',
|
|
help='''set global plot range in X-axis (strain, dimensionless)
|
|
and Y-axis (stress, MPa).''')
|
|
|
|
group.add_argument('-a',
|
|
action='store_true',
|
|
dest='range_auto',
|
|
help='''determine global plot range automatically.''')
|
|
|
|
parser.add_argument('-o',
|
|
metavar='OUTPUTFILE',
|
|
type=str,
|
|
dest='output',
|
|
help='output file path')
|
|
|
|
parser.add_argument('-s',
|
|
action='store_true',
|
|
dest='serial',
|
|
help='concatinates all curves for import into Abaqus.')
|
|
|
|
parser.add_argument('-t',
|
|
metavar = 'LENGTH',
|
|
default = 0,
|
|
type = int,
|
|
dest='output_length',
|
|
help='constrain output to a maximum length.')
|
|
|
|
return parser.parse_args()
|
|
|
|
def sanitize_title(s):
|
|
"""turns a unicode or string into a purely alphanumeric string."""
|
|
return str(s.encode('ascii', 'ignore').decode())
|
|
|
|
def reduce_lenth(data):
|
|
num_split = args.output_length
|
|
assert num_split>2
|
|
first = data[0]
|
|
last = data[-1]
|
|
rest = data[1:-1]
|
|
subs = np.array_split(rest, num_split-2)
|
|
short = np.array([a.mean() for a in subs if a.size!=0])
|
|
return np.insert(short, [0,len(short)], [first, last])
|
|
|
|
|
|
if __name__=="__main__":
|
|
args = parse_args()
|
|
|
|
inpath = os.path.abspath(args.input)
|
|
|
|
datafile = pd.ExcelFile(inpath)
|
|
sheets = datafile.sheet_names
|
|
frames = {sheet:datafile.parse(sheet) for sheet in sheets}
|
|
|
|
if args.output:
|
|
outpath = os.path.abspath(args.output)
|
|
outfilename, ext = os.path.splitext(outpath)
|
|
else:
|
|
outpath = outfilename = ext = False
|
|
|
|
if args.range_man:
|
|
xmax, ymax = args.range_man
|
|
|
|
if args.range_auto:
|
|
xmax = 0
|
|
ymax = 0
|
|
for frame in frames.values():
|
|
e_data, sig_data = np.transpose(frame.values)[:2] * np.array([args.scale_x, args.scale_y])
|
|
xmax = max(xmax, np.max(np.abs(e_data)))
|
|
ymax = max(ymax, np.max(sig_data*(e_data + 1)))
|
|
xmax = 1.05 * xmax
|
|
ymax = 1.05 * ymax
|
|
|
|
e_max_global = 0
|
|
s_max_global = 0
|
|
df_dict = {}
|
|
|
|
for title, frame in frames.items():
|
|
|
|
print(title)
|
|
temp = sanitize_title(title)
|
|
|
|
e_data, sig_data, Emod = np.transpose(frame.values)[:3]
|
|
|
|
Emod = Emod[0] * args.scale_y
|
|
e_tech = e_data * args.scale_x
|
|
sig_tech = sig_data * args.scale_y
|
|
assert np.sign(np.median(sig_tech)) == np.sign(np.median(e_tech)), "Stress and strain do not seem to share sign!"
|
|
|
|
if Emod not in (np.inf, -np.inf, 0):
|
|
e_tech_plastic = e_tech - e_tech[0] - sig_tech/Emod
|
|
else:
|
|
e_tech_plastic = e_tech
|
|
|
|
try:
|
|
idx_plastic = np.where(e_tech_plastic < 0)[0][-1]
|
|
except IndexError:
|
|
idx_plastic = 0
|
|
|
|
if args.compressionflag:
|
|
e_true = -np.log(1 - e_tech)
|
|
e_true_plastic = -np.log(1 - e_tech_plastic)
|
|
sig_true = sig_tech * (1 - e_tech)
|
|
idx_ultimate = -1
|
|
else:
|
|
e_true = np.log(1 + e_tech)
|
|
e_true_plastic = np.log(1 + e_tech_plastic)
|
|
sig_true = sig_tech * (1 + e_tech)
|
|
if args.cut_at_uts:
|
|
smax = np.max(sig_tech)
|
|
idx_ultimate = np.where(sig_tech == smax)[0][0]
|
|
else:
|
|
idx_ultimate = -1
|
|
|
|
sig_slice = sig_true[idx_plastic:idx_ultimate]
|
|
e_slice = e_true_plastic[idx_plastic:idx_ultimate]
|
|
|
|
if args.cut_neg_slope:
|
|
idx_negative_slope = [i for i in range(len(sig_slice)) if i != np.argmax(sig_slice[0:i+1])]
|
|
sig_slice = np.delete(sig_slice, idx_negative_slope)
|
|
e_slice = np.delete(e_slice, idx_negative_slope)
|
|
|
|
e_slice[0] = 0
|
|
|
|
if args.output_length > 0:
|
|
e_slice = reduce_lenth(e_slice)
|
|
sig_slice = reduce_lenth(sig_slice)
|
|
|
|
e_max_global = max(e_max_global, np.max(e_slice))
|
|
s_max_global = max(s_max_global, np.max(sig_slice))
|
|
|
|
df_dict[temp] = pd.DataFrame({'Sig':sig_slice, 'Eps':e_slice})
|
|
|
|
if args.plotflag:
|
|
plt.figure(figsize=[5, 2.8])
|
|
plt.plot(e_tech, sig_tech/args.scale_y, 'g')
|
|
plt.plot(e_true, sig_true/args.scale_y, 'r')
|
|
plt.plot(e_true[idx_plastic:idx_ultimate], sig_true[idx_plastic:idx_ultimate]/args.scale_y, 'k')
|
|
plt.plot(e_slice, sig_slice/args.scale_y, 'b')
|
|
plt.title(title)
|
|
plt.ylabel(r'$\sigma$ [MPa]')
|
|
plt.xlabel(r'$\epsilon$ [-]')
|
|
if args.range_auto or args.range_man:
|
|
plt.ylim(0, ymax)
|
|
plt.xlim(0, xmax)
|
|
plt.grid(True, which='major', linestyle='--')
|
|
plt.grid(True, which='minor', linestyle=':')
|
|
|
|
# plt.axes().set_aspect('equal')
|
|
if outpath:
|
|
plotpath = '{}_{}.png'.format(os.path.splitext(outpath)[0],temp)
|
|
plt.savefig(plotpath, bbox_inches="tight", dpi=144)
|
|
else:
|
|
plt.show()
|
|
plt.close()
|
|
|
|
if args.comparisonflag:
|
|
plt.figure(figsize=[5, 2.8])
|
|
for temp, frame in df_dict.items():
|
|
e_frame = frame['Eps']
|
|
sig_frame = frame['Sig']
|
|
plt.plot(e_frame, sig_frame/args.scale_y, label=temp)
|
|
plt.title('True Stress-Strain Curves')
|
|
plt.ylabel(r'$\sigma$ [MPa]')
|
|
plt.xlabel(r'$\epsilon$ [-]')
|
|
if args.range_auto or args.range_man:
|
|
plt.ylim(0, ymax)
|
|
plt.xlim(0, xmax)
|
|
else:
|
|
plt.ylim(0, s_max_global/args.scale_y*1.05)
|
|
plt.xlim(0, e_max_global*1.05)
|
|
plt.grid(True, which='major', linestyle='--')
|
|
plt.grid(True, which='minor', linestyle=':')
|
|
plt.legend(loc='lower right')
|
|
|
|
# plt.axes().set_aspect('equal')
|
|
if outpath:
|
|
plotpath = outfilename + '.png'
|
|
plt.savefig(plotpath, bbox_inches="tight", dpi=144)
|
|
else:
|
|
plt.show()
|
|
plt.close()
|
|
|
|
if args.output and args.serial:
|
|
frame_out = pd.DataFrame(columns=['Sig', 'Eps', 'T'])
|
|
for temp, frame in df_dict.items():
|
|
nums = re.findall(r"[-+]?\d*\.\d+|\d+", temp)
|
|
try:
|
|
frame['T'] = int(nums[0])
|
|
except ValueError:
|
|
frame['T'] = float(nums[0])
|
|
except IndexError as e:
|
|
raise e('Could not extract number from frame title to use as temperature!')
|
|
frame_out = frame_out.append(frame, ignore_index=True, sort=False)
|
|
frame_out.sort_values(by=['T', 'Eps'], inplace=True)
|
|
frame_out = frame_out.reindex(columns=['Sig', 'Eps', 'T'])
|
|
|
|
if ext and ext.lower() in ['.xls', '.xlsx']:
|
|
frame_out.to_excel(outpath,index=False)
|
|
else:
|
|
frame_out.to_csv(outpath,index=False)
|
|
elif args.output:
|
|
if ext and ext.lower() in ['.xls', '.xlsx']:
|
|
with pd.ExcelWriter(outpath) as writer:
|
|
for temp, frame in df_dict.items():
|
|
frame.to_excel(writer, sheet_name=temp,
|
|
index=False)
|
|
else:
|
|
for temp, frame in df_dict.items():
|
|
newpath = ("_{}".format(temp)).join(os.path.splitext(outpath))
|
|
frame.to_csv(newpath ,index=False)
|
|
else:
|
|
pass #frame_out.to_csv(sys.stdout,index=False)
|