Skip to content

Commit bde18dc

Browse files
authored
Geo/soil test direct shear (#13566)
* Build Direct Simple Shear test * Enabling the GUI to run the Direct Simple Shear test * Plotting the results * Reformat the file structures to be more generalized * Remove the tmp folder once the plotting is finished * Added Kratos logo to the UI * Added tests icon pictures to the UI * Added a label.py file to contain all the constants * Added pre-release license agreement * Added functionality to store the one-time agreement of the user * Added File and Help menu at the top * Added About section to the Help menu with Kratos and Deltares logos * Added the title and the version number
1 parent f66b110 commit bde18dc

File tree

20 files changed

+1033
-298
lines changed

20 files changed

+1033
-298
lines changed
5.07 KB
Loading
4.64 KB
Loading
Binary file not shown.
14 KB
Loading
3.64 KB
Loading

applications/GeoMechanicsApplication/python_scripts/element_test/triaxial.py renamed to applications/GeoMechanicsApplication/python_scripts/element_test/generic_test_runner.py

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,7 @@
1414
raise ImportError(f"Tests folder not found at: {tests_folder_path}")
1515

1616

17-
class TriaxialTest:
18-
def __init__(self, json_file_path):
19-
self.json_file_path = json_file_path
20-
self.data = self._read_json()
21-
22-
def _read_json(self):
23-
with open(self.json_file_path, 'r') as file:
24-
return json.load(file)
25-
26-
def _write_json(self):
27-
with open(self.json_file_path, 'w') as file:
28-
json.dump(self.data, file, indent=4)
29-
30-
def modify_umat_parameters(self, new_value):
31-
self.data['properties'][0]['Material']['Variables']['UMAT_PARAMETERS'][0] = new_value
32-
self._write_json()
33-
34-
def read_umat_parameters(self):
35-
try:
36-
umat_parameters = self.data['properties'][0]['Material']['Variables']['UMAT_PARAMETERS']
37-
cohesion = umat_parameters[2]
38-
friction_angle = umat_parameters[3]
39-
return cohesion, friction_angle
40-
except KeyError:
41-
raise KeyError("Cohesion and Friction angle not found in the UMAT_PARAMETERS.")
42-
43-
class TriaxialTestRunner:
17+
class GenericTestRunner:
4418
def __init__(self, output_file_paths, work_dir):
4519
self.output_file_paths = output_file_paths
4620
self.work_dir = work_dir
@@ -51,11 +25,12 @@ def run(self):
5125

5226
stress, mean_stress, von_mises, _, strain = self._collect_results()
5327
tensors = self._extract_stress_tensors(stress)
54-
yy_strain, vol_strain = self._compute_strains(strain)
28+
shear_stress_xy = self._extract_shear_stress_xy(stress)
29+
yy_strain, vol_strain, shear_strain_xy = self._compute_strains(strain)
5530
von_mises_values = self._compute_scalar_stresses(von_mises)
5631
mean_stress_values = self._compute_scalar_stresses(mean_stress)
5732

58-
return tensors, yy_strain, vol_strain, von_mises_values, mean_stress_values
33+
return tensors, yy_strain, vol_strain, von_mises_values, mean_stress_values, shear_stress_xy, shear_strain_xy
5934

6035
def _load_stage_parameters(self):
6136
parameter_files = [os.path.join(self.work_dir, 'ProjectParameters.json')]
@@ -119,16 +94,28 @@ def _extract_stress_tensors(self, stress_results):
11994
reshaped[time_step] = [tensor]
12095
return reshaped
12196

97+
def _extract_shear_stress_xy(self, stress_results):
98+
shear_stress_xy = []
99+
for result in stress_results:
100+
values = result["values"]
101+
if not values:
102+
continue
103+
stress_components = values[0]["value"][0]
104+
shear_xy = stress_components[3]
105+
shear_stress_xy.append(shear_xy)
106+
return shear_stress_xy
107+
122108
def _compute_strains(self, strain_results):
123-
yy, vol = [], []
109+
yy, vol, shear_xy = [], [], []
124110
for result in strain_results:
125111
values = result["values"]
126112
if not values:
127113
continue
128114
eps_xx, eps_yy, eps_zz, eps_xy = values[0]["value"][0][:4]
129115
vol.append(eps_xx + eps_yy + eps_zz)
130116
yy.append(eps_yy)
131-
return yy, vol
117+
shear_xy.append(eps_xy)
118+
return yy, vol, shear_xy
132119

133120
def _compute_scalar_stresses(self, results):
134121
return [r["values"][0]["value"][1] for r in results if r["values"]]

applications/GeoMechanicsApplication/python_scripts/element_test/mdpa_editor.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def update_maximum_strain(self, maximum_strain):
2727
replacer = self._replacer_factory(prescribed_displacement)
2828
new_text, count = re.subn(pattern, replacer, self.raw_text)
2929
if count == 0:
30-
log_message("Could not update maximum strain.", "Warning")
30+
log_message("Could not update maximum strain.", "warn")
3131
else:
3232
self.raw_text = new_text
3333
MdpaEditor.save(self)
@@ -38,7 +38,7 @@ def update_initial_effective_cell_pressure(self, initial_effective_cell_pressure
3838
replacer = self._replacer_factory(initial_effective_cell_pressure)
3939
new_text, count = re.subn(pattern, replacer, self.raw_text)
4040
if count == 0:
41-
log_message("Could not update initial effective cell pressure.", "Warning")
41+
log_message("Could not update initial effective cell pressure.", "warn")
4242
else:
4343
self.raw_text = new_text
4444
MdpaEditor.save(self)
@@ -50,7 +50,7 @@ def update_first_timestep(self, num_steps, end_time):
5050
replacer = self._replacer_factory(first_timestep)
5151
new_text, count = re.subn(pattern, replacer, self.raw_text)
5252
if count == 0:
53-
log_message("Could not apply the first time step.", "Warning")
53+
log_message("Could not apply the first time step.", "warn")
5454
else:
5555
self.raw_text = new_text
5656
MdpaEditor.save(self)
@@ -62,7 +62,19 @@ def update_end_time(self, end_time):
6262
new_text, count = re.subn(pattern, replacement, self.raw_text)
6363

6464
if count == 0:
65-
log_message("Could not update the end time.", "Warning")
65+
log_message("Could not update the end time.", "warn")
66+
else:
67+
self.raw_text = new_text
68+
MdpaEditor.save(self)
69+
70+
def update_middle_maximum_strain(self, maximum_strain):
71+
pattern = r'\$middle_maximum_strain\b'
72+
prescribed_middle_displacement = (-maximum_strain / 2) / 100
73+
74+
replacer = self._replacer_factory(prescribed_middle_displacement)
75+
new_text, count = re.subn(pattern, replacer, self.raw_text)
76+
if count == 0:
77+
log_message("Could not update middle maximum strain.", "warn")
6678
else:
6779
self.raw_text = new_text
6880
MdpaEditor.save(self)
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import numpy as np
2+
from ui_logger import log_message
3+
from ui_labels import (
4+
SIGMA1_LABEL, SIGMA3_LABEL, SIGMA1_SIGMA3_DIFF_LABEL, VERTICAL_STRAIN_LABEL,
5+
VOLUMETRIC_STRAIN_LABEL, SHEAR_STRAIN_LABEL, SHEAR_STRESS_LABEL, EFFECTIVE_STRESS_LABEL,
6+
MOBILIZED_SHEAR_STRESS_LABEL, P_STRESS_LABEL, Q_STRESS_LABEL, TITLE_SIGMA1_VS_SIGMA3,
7+
TITLE_DIFF_PRINCIPAL_SIGMA_VS_STRAIN, TITLE_VOL_VS_VERT_STRAIN, TITLE_MOHR, TITLE_P_VS_Q, TITLE_SHEAR_VS_STRAIN,
8+
LEGEND_MC, LEGEND_MC_FAILURE
9+
)
10+
11+
12+
def plot_principal_stresses_triaxial(ax, sigma_1, sigma_3):
13+
ax.plot(sigma_3, sigma_1, '-', color='blue', label=TITLE_SIGMA1_VS_SIGMA3)
14+
ax.set_title(TITLE_SIGMA1_VS_SIGMA3)
15+
ax.set_xlabel(SIGMA3_LABEL)
16+
ax.set_ylabel(SIGMA1_LABEL)
17+
ax.grid(True)
18+
ax.locator_params(nbins=8)
19+
20+
min_val = 0
21+
max_val_x = max(sigma_3)
22+
max_val_y = min(sigma_1)
23+
padding_x = 0.1 * (max_val_x - min_val)
24+
padding_y = 0.1 * (max_val_y - min_val)
25+
ax.set_xlim(min_val, max_val_x + padding_x)
26+
ax.set_ylim(min_val, max_val_y + padding_y)
27+
ax.minorticks_on()
28+
29+
def plot_delta_sigma_triaxial(ax, vertical_strain, sigma_diff):
30+
ax.plot(vertical_strain, sigma_diff, '-', color='blue', label=SIGMA1_SIGMA3_DIFF_LABEL)
31+
ax.set_title(TITLE_DIFF_PRINCIPAL_SIGMA_VS_STRAIN)
32+
ax.set_xlabel(VERTICAL_STRAIN_LABEL)
33+
ax.set_ylabel(SIGMA1_SIGMA3_DIFF_LABEL)
34+
ax.grid(True)
35+
ax.invert_xaxis()
36+
ax.locator_params(nbins=8)
37+
ax.minorticks_on()
38+
39+
def plot_volumetric_vertical_strain_triaxial(ax, vertical_strain, volumetric_strain):
40+
ax.plot(vertical_strain, volumetric_strain, '-', color='blue', label=TITLE_VOL_VS_VERT_STRAIN)
41+
ax.set_title(TITLE_VOL_VS_VERT_STRAIN)
42+
ax.set_xlabel(VERTICAL_STRAIN_LABEL)
43+
ax.set_ylabel(VOLUMETRIC_STRAIN_LABEL)
44+
ax.grid(True)
45+
ax.invert_xaxis()
46+
ax.invert_yaxis()
47+
ax.locator_params(nbins=8)
48+
ax.minorticks_on()
49+
50+
def plot_mohr_coulomb_triaxial(ax, sigma_1, sigma_3, cohesion=None, friction_angle=None):
51+
if np.isclose(sigma_1, sigma_3):
52+
log_message("σ₁ is equal to σ₃. Mohr circle collapses to a point.", "warn")
53+
center = (sigma_1 + sigma_3) / 2
54+
radius = (sigma_1 - sigma_3) / 2
55+
theta = np.linspace(0, np.pi, 200)
56+
sigma = center + radius * np.cos(theta)
57+
tau = -radius * np.sin(theta)
58+
59+
ax.plot(sigma, tau, label=LEGEND_MC, color='blue')
60+
61+
if cohesion is not None and friction_angle is not None:
62+
phi_rad = np.radians(friction_angle)
63+
x_line = np.linspace(0, sigma_1, 200)
64+
y_line = x_line * np.tan(phi_rad) - cohesion
65+
ax.plot(x_line, -y_line, 'r--', label=LEGEND_MC_FAILURE)
66+
ax.legend(loc='upper left')
67+
68+
ax.set_title(LEGEND_MC)
69+
ax.set_xlabel(EFFECTIVE_STRESS_LABEL)
70+
ax.set_ylabel(MOBILIZED_SHEAR_STRESS_LABEL)
71+
ax.grid(True)
72+
ax.invert_xaxis()
73+
ax.set_xlim(left=0, right= 1.2*np.max(sigma_1))
74+
ax.set_ylim(bottom=0, top = -0.6*np.max(sigma_1))
75+
ax.minorticks_on()
76+
77+
def plot_p_q_triaxial(ax, p_list, q_list):
78+
ax.plot(p_list, q_list, '-', color='blue', label=TITLE_P_VS_Q)
79+
ax.set_title(TITLE_P_VS_Q)
80+
ax.set_xlabel(P_STRESS_LABEL)
81+
ax.set_ylabel(Q_STRESS_LABEL)
82+
ax.grid(True)
83+
ax.invert_xaxis()
84+
ax.locator_params(nbins=8)
85+
ax.minorticks_on()
86+
87+
def plot_principal_stresses_direct_shear(ax, sigma_1, sigma_3):
88+
ax.plot(sigma_3, sigma_1, '-', color='blue', label=TITLE_SIGMA1_VS_SIGMA3)
89+
ax.set_title(TITLE_SIGMA1_VS_SIGMA3)
90+
ax.set_xlabel(SIGMA3_LABEL)
91+
ax.set_ylabel(SIGMA1_LABEL)
92+
ax.grid(True)
93+
ax.locator_params(nbins=8)
94+
95+
min_x, max_x = min(sigma_3), max(sigma_3)
96+
min_y, max_y = min(sigma_1), max(sigma_1)
97+
x_padding = 0.1 * (max_x - min_x) if max_x > min_x else 1.0
98+
y_padding = 0.1 * (max_y - min_y) if max_y > min_y else 1.0
99+
ax.set_xlim(min_x - x_padding, max_x + x_padding)
100+
ax.set_ylim(min_y - y_padding, max_y + y_padding)
101+
102+
ax.invert_xaxis()
103+
ax.invert_yaxis()
104+
ax.minorticks_on()
105+
106+
def plot_strain_stress_direct_shear(ax, shear_strain_xy, shear_stress_xy):
107+
gamma_xy = 2 * np.array(shear_strain_xy)
108+
ax.plot(np.abs(gamma_xy), np.abs(shear_stress_xy), '-', color='blue', label=TITLE_SHEAR_VS_STRAIN)
109+
ax.set_title(TITLE_SHEAR_VS_STRAIN)
110+
ax.set_xlabel(SHEAR_STRAIN_LABEL)
111+
ax.set_ylabel(SHEAR_STRESS_LABEL)
112+
ax.grid(True)
113+
ax.locator_params(nbins=8)
114+
ax.minorticks_on()
115+
116+
def plot_mohr_coulomb_direct_shear(ax, sigma_1, sigma_3, cohesion=None, friction_angle=None):
117+
if np.isclose(sigma_1, sigma_3):
118+
log_message("σ₁ is equal to σ₃. Mohr circle collapses to a point.", "warn")
119+
center = (sigma_1 + sigma_3) / 2
120+
radius = (sigma_1 - sigma_3) / 2
121+
theta = np.linspace(0, np.pi, 400)
122+
sigma = center + radius * np.cos(theta)
123+
tau = -radius * np.sin(theta)
124+
125+
ax.plot(sigma, tau, label=LEGEND_MC, color='blue')
126+
127+
if cohesion is not None and friction_angle is not None:
128+
phi_rad = np.radians(friction_angle)
129+
max_sigma = center + radius
130+
x_line = np.linspace(0, max_sigma * 1.5, 400)
131+
y_line = x_line * np.tan(phi_rad) - cohesion
132+
ax.plot(x_line, -y_line, 'r--', label=LEGEND_MC_FAILURE)
133+
ax.legend(loc='upper left')
134+
135+
ax.set_title(LEGEND_MC)
136+
ax.set_xlabel(EFFECTIVE_STRESS_LABEL)
137+
ax.set_ylabel(MOBILIZED_SHEAR_STRESS_LABEL)
138+
ax.grid(True)
139+
ax.invert_xaxis()
140+
141+
epsilon = 0.1
142+
relative_diff = np.abs(sigma_1 - sigma_3) / max(np.abs(sigma_1), 1e-6)
143+
144+
if relative_diff < epsilon:
145+
ax.set_xlim(center - (1.2 * radius), center + (1.2 * radius))
146+
ax.set_ylim(bottom=0, top = -0.9*(np.max(sigma_1) - np.max(sigma_3)))
147+
148+
else:
149+
if sigma_1 > 0 or sigma_3 > 0:
150+
ax.set_xlim(left=1.2*np.max(sigma_3), right=1.2*np.max(sigma_1))
151+
ax.set_ylim(bottom=0, top = -0.9*(np.max(sigma_1) - np.max(sigma_3)))
152+
else:
153+
ax.set_xlim(left=0, right=1.2*np.max(sigma_1))
154+
ax.set_ylim(bottom=0, top = -0.9*np.max(sigma_1))
155+
156+
157+
ax.minorticks_on()
158+
159+
def plot_p_q_direct_shear(ax, p_list, q_list):
160+
ax.plot(p_list, q_list, '-', color='blue', label=TITLE_P_VS_Q)
161+
ax.set_title(TITLE_P_VS_Q)
162+
ax.set_xlabel(P_STRESS_LABEL)
163+
ax.set_ylabel(Q_STRESS_LABEL)
164+
ax.grid(True)
165+
ax.invert_xaxis()
166+
ax.set_xlim(left=0, right=1.2*np.max(p_list))
167+
ax.locator_params(nbins=8)
168+
ax.minorticks_on()

0 commit comments

Comments
 (0)