Skip to content

Commit 1d12aac

Browse files
committed
decompose workflow of kratos into 4 substeps
1 parent 285faf7 commit 1d12aac

File tree

5 files changed

+402
-295
lines changed

5 files changed

+402
-295
lines changed

benchmarks/linear-elastic-plate-with-hole/FEniCS/kratos/Snakefile

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,83 @@ configuration_to_parameter_file = config["configuration_to_parameter_file"]
55
configurations = config["configurations"]
66
tool = "kratos"
77

8-
kratos_input_template = "{tool}/input_template.json"
9-
kratos_material_template = "{tool}/StructuralMaterials_template.json"
8+
kratos_input_template = f"{tool}/input_template.json"
9+
kratos_material_template = f"{tool}/StructuralMaterials_template.json"
1010

11-
rule run_simulation:
12-
input:
13-
script = "{tool}/run_simulation.py",
11+
rule mesh_to_mdpa:
12+
input:
1413
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
1514
mesh = f"{result_dir}/mesh/mesh_{{configuration}}.msh",
15+
script = f"{tool}/msh_to_mdpa.py",
16+
output:
17+
mdpa = f"{result_dir}/{tool}/mesh_{{configuration}}.mdpa",
18+
conda:
19+
"environment_simulation.yml",
20+
shell:
21+
"""
22+
python3 {input.script} \
23+
--input_parameter_file {input.parameters} \
24+
--input_mesh_file {input.mesh} \
25+
--output_mdpa_file {output.mdpa}
26+
"""
27+
28+
rule create_kratos_input:
29+
input:
30+
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
31+
mdpa = f"{result_dir}/{tool}/mesh_{{configuration}}.mdpa",
1632
kratos_input_template = kratos_input_template,
1733
kratos_material_template = kratos_material_template,
34+
script = f"{tool}/create_kratos_input.py",
1835
output:
19-
mdpa = f"{result_dir}/{{tool}}/mesh_{{configuration}}.mdpa",
20-
kratos_inputfile = f"{result_dir}/{{tool}}/ProjectParameters_{{configuration}}.json",
21-
kratos_materialfile = f"{result_dir}/{{tool}}/MaterialParameters_{{configuration}}.json",
22-
zip = f"{result_dir}/{{tool}}/solution_field_data_{{configuration}}.zip",
23-
metrics = f"{result_dir}/{{tool}}/solution_metrics_{{configuration}}.json",
36+
kratos_inputfile = f"{result_dir}/{tool}/ProjectParameters_{{configuration}}.json",
37+
kratos_materialfile = f"{result_dir}/{tool}/MaterialParameters_{{configuration}}.json",
2438
conda:
2539
"environment_simulation.yml",
2640
shell:
2741
"""
2842
python3 {input.script} \
2943
--input_parameter_file {input.parameters} \
30-
--input_mesh_file {input.mesh} \
44+
--input_mdpa_file {input.mdpa} \
3145
--input_kratos_input_template {input.kratos_input_template} \
3246
--input_material_template {input.kratos_material_template} \
33-
--output_mdpa_file {output.mdpa} \
3447
--output_kratos_inputfile {output.kratos_inputfile} \
35-
--output_kratos_materialfile {output.kratos_materialfile} \
48+
--output_kratos_materialfile {output.kratos_materialfile}
49+
"""
50+
51+
rule run_simulation:
52+
input:
53+
script = f"{tool}/run_simulation.py",
54+
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
55+
kratos_inputfile = f"{result_dir}/{tool}/ProjectParameters_{{configuration}}.json",
56+
kratos_materialfile = f"{result_dir}/{tool}/MaterialParameters_{{configuration}}.json",
57+
output:
58+
result_vtk = f"{result_dir}/{tool}/{{configuration}}/Structure_0_1.vtk",
59+
conda:
60+
"environment_simulation.yml",
61+
shell:
62+
"""
63+
python3 {input.script} \
64+
--input_parameter_file {input.parameters} \
65+
--input_kratos_inputfile {input.kratos_inputfile} \
66+
--input_kratos_materialfile {input.kratos_materialfile}
67+
"""
68+
69+
rule postprocess_results:
70+
input:
71+
parameters = lambda wildcards: configuration_to_parameter_file[wildcards.configuration],
72+
result_vtk = f"{result_dir}/{tool}/{{configuration}}/Structure_0_1.vtk",
73+
script = f"{tool}/postprocess_results.py",
74+
output:
75+
zip = f"{result_dir}/{tool}/solution_field_data_{{configuration}}.zip",
76+
metrics = f"{result_dir}/{tool}/solution_metrics_{{configuration}}.json",
77+
conda:
78+
"environment_simulation.yml",
79+
shell:
80+
"""
81+
python3 {input.script} \
82+
--input_parameter_file {input.parameters} \
83+
--input_result_vtk {input.result_vtk} \
3684
--output_solution_file_zip {output.zip} \
3785
--output_metrics_file {output.metrics}
38-
"""
86+
"""
87+
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import json
2+
import os
3+
from pint import UnitRegistry
4+
from argparse import ArgumentParser
5+
from pathlib import Path
6+
import sys
7+
# Ensure the parent directory is in the path to import PlateWithHoleSolution
8+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
9+
from plateWithHoleSolution import PlateWithHoleSolution
10+
11+
def create_kratos_input(
12+
parameter_file: str,
13+
mdpa_file: str,
14+
kratos_input_template_file: str,
15+
kratos_material_template_file: str,
16+
kratos_input_file: str,
17+
kratos_material_file: str,
18+
):
19+
ureg = UnitRegistry()
20+
with open(parameter_file) as f:
21+
parameters = json.load(f)
22+
23+
E = (
24+
ureg.Quantity(
25+
parameters["young-modulus"]["value"], parameters["young-modulus"]["unit"]
26+
)
27+
.to_base_units()
28+
.magnitude
29+
)
30+
nu = (
31+
ureg.Quantity(
32+
parameters["poisson-ratio"]["value"], parameters["poisson-ratio"]["unit"]
33+
)
34+
.to_base_units()
35+
.magnitude
36+
)
37+
radius = (
38+
ureg.Quantity(parameters["radius"]["value"], parameters["radius"]["unit"])
39+
.to_base_units()
40+
.magnitude
41+
)
42+
L = (
43+
ureg.Quantity(parameters["length"]["value"], parameters["length"]["unit"])
44+
.to_base_units()
45+
.magnitude
46+
)
47+
load = (
48+
ureg.Quantity(parameters["load"]["value"], parameters["load"]["unit"])
49+
.to_base_units()
50+
.magnitude
51+
)
52+
53+
analytical_solution = PlateWithHoleSolution(
54+
E=E,
55+
nu=nu,
56+
radius=radius,
57+
L=L,
58+
load=load,
59+
)
60+
61+
bc = analytical_solution.displacement_symbolic_str("X", "Y")
62+
63+
with open(kratos_material_template_file) as f:
64+
material_string = f.read()
65+
66+
material_string = material_string.replace(r'"{{YOUNG_MODULUS}}"', str(E))
67+
material_string = material_string.replace(r'"{{POISSON_RATIO}}"', str(nu))
68+
69+
with open(kratos_material_file, "w") as f:
70+
f.write(material_string)
71+
72+
with open(kratos_input_template_file) as f:
73+
project_parameters_string = f.read()
74+
project_parameters_string = project_parameters_string.replace(
75+
r"{{MESH_FILE}}", os.path.splitext(mdpa_file)[0]
76+
)
77+
project_parameters_string = project_parameters_string.replace(
78+
r"{{MATERIAL_FILE}}", kratos_material_file
79+
)
80+
project_parameters_string = project_parameters_string.replace(
81+
r"{{BOUNDARY_RIGHT_DISPLACEMENT_X}}", str(bc[0])
82+
)
83+
project_parameters_string = project_parameters_string.replace(
84+
r"{{BOUNDARY_RIGHT_DISPLACEMENT_Y}}", str(bc[1])
85+
)
86+
project_parameters_string = project_parameters_string.replace(
87+
r"{{BOUNDARY_TOP_DISPLACEMENT_X}}", str(bc[0])
88+
)
89+
project_parameters_string = project_parameters_string.replace(
90+
r"{{BOUNDARY_TOP_DISPLACEMENT_Y}}", str(bc[1])
91+
)
92+
config = parameters["configuration"]
93+
output_dir = os.path.join(os.path.dirname(os.path.abspath(kratos_input_file)), str(config))
94+
os.makedirs(output_dir, exist_ok=True)
95+
project_parameters_string = project_parameters_string.replace(r"{{OUTPUT_PATH}}", output_dir)
96+
97+
with open(kratos_input_file, "w") as f:
98+
f.write(project_parameters_string)
99+
100+
if __name__ == "__main__":
101+
parser = ArgumentParser(
102+
description="Create Kratos input and material files from templates and parameters."
103+
)
104+
parser.add_argument(
105+
"--input_parameter_file",
106+
required=True,
107+
help="JSON file containing simulation parameters (input)",
108+
)
109+
parser.add_argument(
110+
"--input_mdpa_file", required=True, help="Path to the MDPA mesh file (input)"
111+
)
112+
parser.add_argument(
113+
"--input_kratos_input_template",
114+
required=True,
115+
help="Path to the kratos input template file (input)",
116+
)
117+
parser.add_argument(
118+
"--input_material_template",
119+
required=True,
120+
help="Path to the kratos material template file (input)",
121+
)
122+
parser.add_argument(
123+
"--output_kratos_inputfile",
124+
required=True,
125+
help="Path to the kratos input file (output)",
126+
)
127+
parser.add_argument(
128+
"--output_kratos_materialfile",
129+
required=True,
130+
help="Path to the kratos material file (output)",
131+
)
132+
args, _ = parser.parse_known_args()
133+
134+
create_kratos_input(
135+
parameter_file=args.input_parameter_file,
136+
mdpa_file=args.input_mdpa_file,
137+
kratos_input_template_file=args.input_kratos_input_template,
138+
kratos_material_template_file=args.input_material_template,
139+
kratos_input_file=args.output_kratos_inputfile,
140+
kratos_material_file=args.output_kratos_materialfile,
141+
)
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import json
2+
import meshio
3+
import re
4+
import numpy as np
5+
from pint import UnitRegistry
6+
from argparse import ArgumentParser
7+
8+
def msh_to_mdpa(parameter_file: str, mesh_file: str, mdpa_file: str):
9+
"""
10+
This function converts the GMSH mesh to a Kratos MDPA file format.
11+
Due to limitations in the meshio conversion, several modifications are made to
12+
the mdpa file:
13+
- The element types are replaced with SmallDisplacementElement2D3N and SmallDisplacementElement2D6N
14+
since meshio only converts to Triangle2D3 and Triangle2D6 which only describe the geometry but
15+
not the finite elements.
16+
- The Line2D elements are removed since they are not used in Kratos.
17+
- The gmsh:dim_tags are removed since they are not used in Kratos.
18+
- SubModelParts for the boundary conditions are created.
19+
20+
At this point, we don't see a better way to do this conversion, so we use a lot of string manipulation.
21+
"""
22+
23+
ureg = UnitRegistry()
24+
with open(parameter_file) as f:
25+
parameters = json.load(f)
26+
radius = (
27+
ureg.Quantity(parameters["radius"]["value"], parameters["radius"]["unit"])
28+
.to_base_units()
29+
.magnitude
30+
)
31+
L = (
32+
ureg.Quantity(parameters["length"]["value"], parameters["length"]["unit"])
33+
.to_base_units()
34+
.magnitude
35+
)
36+
37+
x0 = 0.0
38+
x1 = x0 + radius
39+
x2 = x0 + L
40+
y0 = 0.0
41+
y1 = y0 + radius
42+
y2 = y0 + L
43+
mesh = meshio.read(mesh_file)
44+
45+
meshio.write(mdpa_file, mesh)
46+
47+
with open(mdpa_file, "r") as f:
48+
# replace all occurences of Triangle with SmallStrainElement
49+
text = f.read()
50+
51+
text = text.replace("Triangle2D3", "SmallDisplacementElement2D3N")
52+
text = text.replace("Triangle2D6", "SmallDisplacementElement2D6N")
53+
54+
text = re.sub(r"Begin\s+Elements\s+Line2D[\n\s\d]*End\s+Elements", "", text)
55+
56+
mesh_tags = np.array(
57+
re.findall(
58+
r"Begin\s+NodalData\s+gmsh:dim_tags[\s\n]*(.*)End\s+NodalData\s+gmsh:dim_tags",
59+
text,
60+
flags=re.DOTALL,
61+
)[0]
62+
.replace("np.int64", "")
63+
.replace("(", "")
64+
.replace(")", "")
65+
.split(),
66+
dtype=np.int32,
67+
).reshape(-1, 3)
68+
69+
text = re.sub(
70+
r"Begin\s+NodalData\s+gmsh:dim_tags[\s\n]*(.*)End\s+NodalData\s+gmsh:dim_tags",
71+
"",
72+
text,
73+
flags=re.DOTALL,
74+
)
75+
76+
append = "\nBegin SubModelPart boundary_left\n"
77+
append += " Begin SubModelPartNodes\n "
78+
nodes = np.argwhere(np.isclose(mesh.points[:, 0], x0)).flatten() + 1
79+
append += "\n ".join(map(str, nodes)) + "\n"
80+
append += " End SubModelPartNodes\n"
81+
append += "End SubModelPart\n"
82+
83+
text += append
84+
85+
append = "\nBegin SubModelPart boundary_bottom\n"
86+
append += " Begin SubModelPartNodes\n "
87+
nodes = np.argwhere(np.isclose(mesh.points[:, 1], y0)).flatten() + 1
88+
append += "\n ".join(map(str, nodes)) + "\n"
89+
append += " End SubModelPartNodes\n"
90+
append += "End SubModelPart\n"
91+
92+
text += append
93+
94+
append = "\nBegin SubModelPart boundary_right\n"
95+
append += " Begin SubModelPartNodes\n "
96+
nodes = np.argwhere(np.isclose(mesh.points[:, 0], x2)).flatten() + 1
97+
append += "\n ".join(map(str, nodes)) + "\n"
98+
append += " End SubModelPartNodes\n"
99+
append += "End SubModelPart\n"
100+
101+
text += append
102+
103+
append = "\nBegin SubModelPart boundary_top\n"
104+
append += " Begin SubModelPartNodes\n "
105+
nodes = np.argwhere(np.isclose(mesh.points[:, 1], y2)).flatten() + 1
106+
append += "\n ".join(map(str, nodes)) + "\n"
107+
append += " End SubModelPartNodes\n"
108+
append += "End SubModelPart\n"
109+
110+
text += append
111+
with open(mdpa_file, "w") as f:
112+
f.write(text)
113+
114+
if __name__ == "__main__":
115+
parser = ArgumentParser(
116+
description="Convert GMSH mesh to Kratos MDPA format."
117+
)
118+
parser.add_argument(
119+
"--input_parameter_file",
120+
required=True,
121+
help="JSON file containing simulation parameters (input)",
122+
)
123+
parser.add_argument(
124+
"--input_mesh_file", required=True, help="Path to the mesh file (input)"
125+
)
126+
parser.add_argument(
127+
"--output_mdpa_file",
128+
required=True,
129+
help="Path to the MDPA file (output)",
130+
)
131+
args, _ = parser.parse_known_args()
132+
msh_to_mdpa(args.input_parameter_file, args.input_mesh_file, args.output_mdpa_file)

0 commit comments

Comments
 (0)