struct_post 0.1.4.2__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- struct_post/__init__.py +2 -1
- struct_post/beam.py +2 -2
- struct_post/coupon.py +67 -33
- {struct_post-0.1.4.2.dist-info → struct_post-0.1.5.dist-info}/METADATA +1 -1
- struct_post-0.1.5.dist-info/RECORD +9 -0
- struct_post-0.1.4.2.dist-info/RECORD +0 -9
- {struct_post-0.1.4.2.dist-info → struct_post-0.1.5.dist-info}/WHEEL +0 -0
- {struct_post-0.1.4.2.dist-info → struct_post-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {struct_post-0.1.4.2.dist-info → struct_post-0.1.5.dist-info}/top_level.txt +0 -0
struct_post/__init__.py
CHANGED
struct_post/beam.py
CHANGED
struct_post/coupon.py
CHANGED
@@ -1,10 +1,44 @@
|
|
1
|
-
def
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
def coupon_csvdata_read(testdata_file_name: str,
|
2
|
+
force_index: int,
|
3
|
+
strain_index: int):
|
4
|
+
"""
|
5
|
+
Read tensile coupon test data from a CSV file and extract force and strain columns.
|
6
|
+
|
7
|
+
Args:
|
8
|
+
testdata_file_name (str): Path to the CSV file containing the test data.
|
9
|
+
force_index (int): 1-based column index for the force data.
|
10
|
+
strain_index (int): 1-based column index for the strain data.
|
11
|
+
|
12
|
+
Returns:
|
13
|
+
tuple:
|
14
|
+
df_new (pd.DataFrame): DataFrame containing only 'Force' and 'Strain' columns.
|
15
|
+
sample_name (str): Name of the sample extracted from the file name (without extension).
|
16
|
+
|
17
|
+
Notes:
|
18
|
+
- The CSV file is expected to have a header in the first row.
|
19
|
+
- The second row is skipped during reading (`skiprows=[1]`).
|
20
|
+
- The first column is treated as the index column (`index_col=0`).
|
21
|
+
- Force and strain columns are extracted based on the provided 1-based indices.
|
22
|
+
"""
|
23
|
+
|
24
|
+
import pandas as pd
|
25
|
+
df = pd.read_csv(testdata_file_name, skiprows=[1])
|
26
|
+
Force = df[df.columns[force_index - 1]]
|
27
|
+
Strain = df[df.columns[strain_index - 1]]
|
28
|
+
df_new = pd.concat([Force, Strain], axis=1)
|
29
|
+
df_new.columns = ['Force', 'Strain']
|
30
|
+
sample_name = testdata_file_name[:-4]
|
31
|
+
return sample_name, df_new
|
32
|
+
|
33
|
+
def coupon_test_analysis (sample_name: str,
|
34
|
+
Force: float,
|
35
|
+
Strain: float,
|
36
|
+
thickness: float = 2.5,
|
37
|
+
width: float = 10,
|
38
|
+
showfig: bool = True,
|
39
|
+
savefig: bool = False,
|
40
|
+
low_bound: float = 0.1,
|
41
|
+
up_bound: float = 0.3,
|
8
42
|
):
|
9
43
|
"""
|
10
44
|
Post-process a tensile coupon test and plot stress-strain curve.
|
@@ -25,36 +59,27 @@ def coupon_test_analysis (testdata_file_name: str,
|
|
25
59
|
import numpy as np
|
26
60
|
import matplotlib.pyplot as plt
|
27
61
|
#%matplotlib widget
|
62
|
+
|
28
63
|
# Constants
|
29
|
-
thickness # mm
|
30
|
-
width # mm
|
31
64
|
area = thickness * width # Calculate the area of the specimen
|
32
65
|
|
33
66
|
# Load tensile test data
|
34
|
-
|
35
|
-
|
36
|
-
#df.columns = [f"{col[0]} {col[1]}" for col in df.columns]
|
37
|
-
|
38
|
-
# Extract relevant columns
|
39
|
-
#time = df["Time (sec)"]
|
40
|
-
#displacement = df["Crosshead separation (mm)"]
|
41
|
-
force = df[df.columns[1]]
|
42
|
-
elongation = df[df.columns[2]]
|
43
|
-
strain = elongation # Strain in mm/mm
|
67
|
+
|
68
|
+
strain = Strain # Strain in mm/mm
|
44
69
|
|
45
70
|
# Calculate stress and strain
|
46
|
-
force =
|
71
|
+
force = Force * 1000 # Convert kN to N
|
47
72
|
stress = (force / area) # N/m^2 or Pa
|
48
73
|
uts = stress.max()
|
49
74
|
|
50
75
|
#find the data before uts
|
51
76
|
idx_peak = np.argmax(stress)
|
52
|
-
strain_up =
|
77
|
+
strain_up = Strain[:idx_peak+1]
|
53
78
|
stress_up = stress[:idx_peak+1]
|
54
79
|
|
55
80
|
#Boundary for 20% - 50% of UTS
|
56
|
-
lower_bound =
|
57
|
-
upper_bound =
|
81
|
+
lower_bound = low_bound * uts
|
82
|
+
upper_bound = up_bound * uts
|
58
83
|
|
59
84
|
elastic_reg = (lower_bound <= stress_up) & (stress_up <= upper_bound)
|
60
85
|
|
@@ -67,7 +92,7 @@ def coupon_test_analysis (testdata_file_name: str,
|
|
67
92
|
#print(f"Intercept: {intercept} MPa")
|
68
93
|
|
69
94
|
# Select over 30% of UTS, as yield stress will over 30% uts
|
70
|
-
strain_new =
|
95
|
+
strain_new = strain
|
71
96
|
stress_new = force / area
|
72
97
|
mask = (lower_bound <= stress)
|
73
98
|
strain_mask = strain_new[mask]
|
@@ -78,7 +103,7 @@ def coupon_test_analysis (testdata_file_name: str,
|
|
78
103
|
|
79
104
|
#Find the Yield strength
|
80
105
|
diff = stress_mask - offset_line
|
81
|
-
cross_index = np.where(diff <= 0)[0][0]
|
106
|
+
cross_index = np.where(diff <= 0)[0][0] +1
|
82
107
|
x1 = strain_mask[cross_index-1]
|
83
108
|
x2 = strain_mask[cross_index]
|
84
109
|
y1 = diff[cross_index-1]
|
@@ -98,7 +123,7 @@ def coupon_test_analysis (testdata_file_name: str,
|
|
98
123
|
|
99
124
|
ax.set_xlabel('Strain (mm/mm)')
|
100
125
|
ax.set_ylabel('Stress (MPa)')
|
101
|
-
ax.set_title(
|
126
|
+
ax.set_title(sample_name + ' Stress-Strain Curve with Mechanical Properties')
|
102
127
|
ax.legend()
|
103
128
|
ax.grid(True)
|
104
129
|
ax.set_ylim(0, 1.2 *uts)
|
@@ -111,13 +136,14 @@ def coupon_test_analysis (testdata_file_name: str,
|
|
111
136
|
|
112
137
|
# Save fig or not
|
113
138
|
if savefig == True:
|
114
|
-
fig.savefig(
|
139
|
+
fig.savefig(sample_name, dpi=300, bbox_inches='tight')
|
115
140
|
|
116
141
|
# Print results
|
117
|
-
print(f"Sample: {
|
142
|
+
print(f"Sample: {sample_name}")
|
118
143
|
print(f"Young's Modulus (E): {E:.2f} MPa")
|
119
144
|
print(f"Ultimate Tensile Strength (UTS): {uts:.2f} MPa")
|
120
145
|
print(f"Yield Strength: {yield_strength:.2f} MPa")
|
146
|
+
print(yield_strain)
|
121
147
|
print('-' * 40)
|
122
148
|
|
123
149
|
# Prepare results dictionary
|
@@ -126,7 +152,7 @@ def coupon_test_analysis (testdata_file_name: str,
|
|
126
152
|
"UTS_MPa": uts,
|
127
153
|
"Yield_Strength_MPa": yield_strength
|
128
154
|
}
|
129
|
-
return
|
155
|
+
return sample_name, results
|
130
156
|
|
131
157
|
def coupon_sample_geodata_read (Excelfile_name: str):
|
132
158
|
'''
|
@@ -155,15 +181,16 @@ def coupon_sample_geodata_read (Excelfile_name: str):
|
|
155
181
|
for row in ws.iter_rows(min_row=2, values_only=True):
|
156
182
|
if all(cell is None for cell in row): # skip empty
|
157
183
|
continue
|
158
|
-
# 取前三列数据
|
159
184
|
sample_file_name = str (row[0] + ".csv")
|
160
185
|
sample_name = coupon_SampleDetails(row[0], row[1], row[2], sample_file_name)
|
161
186
|
samples.append(sample_name)
|
162
187
|
return samples
|
163
188
|
|
164
189
|
def coupon_batch_analysis(Coupon_geodata: str,
|
165
|
-
|
166
|
-
|
190
|
+
force_index: float,
|
191
|
+
strain_index:float,
|
192
|
+
showfig: bool = True,
|
193
|
+
savefig: bool = False):
|
167
194
|
"""
|
168
195
|
Perform batch analysis on a list of samples and return the results.
|
169
196
|
|
@@ -187,7 +214,14 @@ def coupon_batch_analysis(Coupon_geodata: str,
|
|
187
214
|
"""
|
188
215
|
SARS = []
|
189
216
|
for Coupon_detail in Coupon_geodata:
|
190
|
-
|
217
|
+
csvdata = coupon_csvdata_read(Coupon_detail.sample_file_name,force_index,strain_index)
|
218
|
+
result = coupon_test_analysis(Coupon_detail.sample_file_name[:-4],
|
219
|
+
csvdata[1]['Force'],
|
220
|
+
csvdata[1]['Strain'],
|
221
|
+
Coupon_detail.thickness,
|
222
|
+
Coupon_detail.width,
|
223
|
+
showfig,
|
224
|
+
savefig)
|
191
225
|
SAR = coupon_SampleAnalysisResults(result[0], result[1]['E_MPa'], result[1]['UTS_MPa'], result[1]['Yield_Strength_MPa'])
|
192
226
|
SARS.append(SAR)
|
193
227
|
return SARS
|
@@ -0,0 +1,9 @@
|
|
1
|
+
struct_post/__init__.py,sha256=aNaAaoGGLodY5FTsVGCnUhzuXW9F50Mi_7xm-nQ-xFg,182
|
2
|
+
struct_post/beam.py,sha256=KbdvwCREbd92nEiB2-aG0TH5e54mZR2Qpm2DSgQM5og,1501
|
3
|
+
struct_post/coupon.py,sha256=_SnQw29URYg6BZpbmQafxua4sFwwBWh1at2YXw2cH9g,11435
|
4
|
+
struct_post/read_lvm_file.py,sha256=3x-vbkgEQ-G3f939euIMqwjzVEt9zHtvNneYd1-ViDw,2539
|
5
|
+
struct_post-0.1.5.dist-info/licenses/LICENSE,sha256=0KCwPGoSiY1zA6U97FZJob9MJbaJOo6sRZF-xv6A1YI,1089
|
6
|
+
struct_post-0.1.5.dist-info/METADATA,sha256=k823UW7XZvq6NnT1bDeE8Nh-dwi_z_F2V-qPCzeYuxc,656
|
7
|
+
struct_post-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
+
struct_post-0.1.5.dist-info/top_level.txt,sha256=jgiqPN0SMTMUomzOey9HfIrScTFmOzyuLpwIJ-Sz1-0,12
|
9
|
+
struct_post-0.1.5.dist-info/RECORD,,
|
@@ -1,9 +0,0 @@
|
|
1
|
-
struct_post/__init__.py,sha256=FLF9ADS5Ms8mVU5dap4FYhmZ78VUecTmC224_dfbDhs,153
|
2
|
-
struct_post/beam.py,sha256=y-dV-taU1JNcyZYVTeJDe0i2aOkDoCUp4Ar5I8QC9iw,1495
|
3
|
-
struct_post/coupon.py,sha256=pi-aGs-EnKosq7QwPR1unskTVoNZ8kZ3o3gQjlFSf34,9835
|
4
|
-
struct_post/read_lvm_file.py,sha256=3x-vbkgEQ-G3f939euIMqwjzVEt9zHtvNneYd1-ViDw,2539
|
5
|
-
struct_post-0.1.4.2.dist-info/licenses/LICENSE,sha256=0KCwPGoSiY1zA6U97FZJob9MJbaJOo6sRZF-xv6A1YI,1089
|
6
|
-
struct_post-0.1.4.2.dist-info/METADATA,sha256=qWJegBWeVg5ofmt3TgU82WY4IGN2YIAIX5_doqW5QL4,658
|
7
|
-
struct_post-0.1.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
-
struct_post-0.1.4.2.dist-info/top_level.txt,sha256=jgiqPN0SMTMUomzOey9HfIrScTFmOzyuLpwIJ-Sz1-0,12
|
9
|
-
struct_post-0.1.4.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|