From 13f2bc79a5d0a122942c2efbc9c42d5d9ad3f78b Mon Sep 17 00:00:00 2001 From: dtyagi Date: Mon, 18 May 2026 15:35:07 +0200 Subject: [PATCH 1/7] modified parameter.json: units in keys and isogeometric elements --- .../fenics/Snakefile | 33 +++++++++++++ .../fenics/run_benchmark.py | 5 +- .../fenics/run_simulation.py | 46 +++++++++--------- .../{Snakefile_kratos.smk => Snakefile.smk} | 12 +++++ .../kratos/create_kratos_input.py | 10 ++-- .../kratos/msh_to_mdpa.py | 4 +- .../kratos/postprocess_results.py | 32 ++++++------ .../kratos/run_benchmark.py | 13 ++--- .../linear-elastic-plate-with-hole.zip | Bin 7514 -> 6226 bytes 9 files changed, 98 insertions(+), 57 deletions(-) create mode 100644 examples/linear-elastic-plate-with-hole/fenics/Snakefile rename examples/linear-elastic-plate-with-hole/kratos/{Snakefile_kratos.smk => Snakefile.smk} (88%) diff --git a/examples/linear-elastic-plate-with-hole/fenics/Snakefile b/examples/linear-elastic-plate-with-hole/fenics/Snakefile new file mode 100644 index 0000000..b463a9f --- /dev/null +++ b/examples/linear-elastic-plate-with-hole/fenics/Snakefile @@ -0,0 +1,33 @@ +import json + +rule all: + input: + "solution_metrics.json", + "solution_field_data.zip" + +rule create_mesh: + input: + script = "create_mesh.py", + parameters = "parameters.json", + output: + mesh = "mesh.msh", + conda: "environment_mesh.yml" + shell: + """ + python3 {input.script} --input_parameter_file {input.parameters} --output_mesh_file {output.mesh} + """ + +rule run_simulation: + input: + script = "run_simulation.py", + parameters = "parameters.json", + mesh = "mesh.msh", + output: + zip = "solution_field_data.zip", + metrics = "solution_metrics.json", + conda: + "environment_simulation.yml" + shell: + """ + python3 {input.script} --input_parameter_file {input.parameters} --input_mesh_file {input.mesh} --output_solution_file_zip {output.zip} --output_metrics_file {output.metrics} + """ diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py b/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py index 22d1f5f..c8ed772 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py @@ -43,8 +43,9 @@ for file in root_unzipped_benchmark_dir.glob("parameters_*.json"): with open(file, "r") as f: data = json.load(f) - if data.get("element-size").get("value") >= 0.025: - + #if data.get("element_size[m]") >= 0.025: + #if data.get("configuration") == "1": + if 1==1: # Create output directory for the configuration output_dir = root_unzipped_benchmark_dir / "results" / data.get("configuration") output_dir.mkdir(parents=True, exist_ok=True) diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py index 1aec6e5..1aefda3 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py @@ -10,6 +10,7 @@ from dolfinx.fem.petsc import LinearProblem from mpi4py import MPI from pint import UnitRegistry +from ufl.algorithms import estimate_total_polynomial_degree # Add parent directory to sys.path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) @@ -29,7 +30,7 @@ def run_fenics_simulation( gdim=2, ) - V = df.fem.functionspace(mesh, ("CG", parameters["element-degree"], (2,))) + V = df.fem.functionspace(mesh, ("CG", parameters["isoparametric_element_degree"], (2,))) tags_left = facet_tags.find(1) tags_bottom = facet_tags.find(2) @@ -45,46 +46,46 @@ def run_fenics_simulation( E = ( ureg.Quantity( - parameters["young-modulus"]["value"], parameters["young-modulus"]["unit"] + parameters["young_modulus[Pa]"], "Pa" ) .to_base_units() .magnitude ) nu = ( ureg.Quantity( - parameters["poisson-ratio"]["value"], parameters["poisson-ratio"]["unit"] + parameters["poisson_ratio"], "" ) .to_base_units() .magnitude ) radius = ( - ureg.Quantity(parameters["radius"]["value"], parameters["radius"]["unit"]) + ureg.Quantity(parameters["radius[m]"], "m") .to_base_units() .magnitude ) L = ( - ureg.Quantity(parameters["length"]["value"], parameters["length"]["unit"]) + ureg.Quantity(parameters["length[m]"], "m") .to_base_units() .magnitude ) load = ( - ureg.Quantity(parameters["load"]["value"], parameters["load"]["unit"]) + ureg.Quantity(parameters["load[MPa]"], "MPa") .to_base_units() .magnitude ) - displacement_evaluation_point = parameters["displacement-evaluation-point"] + displacement_evaluation_point = parameters["displacement_evaluation_point[m]"] displacement_evaluation_x = ( ureg.Quantity( - displacement_evaluation_point["x"]["value"], - displacement_evaluation_point["x"]["unit"], + displacement_evaluation_point[0], + "m", ) .to_base_units() .magnitude ) displacement_evaluation_y = ( ureg.Quantity( - displacement_evaluation_point["y"]["value"], - displacement_evaluation_point["y"]["unit"], + displacement_evaluation_point[1], + "m", ) .to_base_units() .magnitude @@ -115,10 +116,6 @@ def as_tensor(v): dx = ufl.Measure( "dx", - metadata={ - "quadrature_degree": parameters["quadrature-degree"], - "quadrature_scheme": parameters["quadrature-rule"], - }, ) ds = ufl.Measure( "ds", @@ -238,10 +235,10 @@ def project( return uh plot_space_stress = df.fem.functionspace( - mesh, ("DG", parameters["element-degree"] - 1, (2, 2)) + mesh, ("DG", parameters["isoparametric_element_degree"] - 1, (2, 2)) ) plot_space_mises = df.fem.functionspace( - mesh, ("DG", parameters["element-degree"] - 1, (1,)) + mesh, ("DG", parameters["isoparametric_element_degree"] - 1, (1,)) ) stress_nodes_red = project(sigma(u), plot_space_stress, dx) stress_nodes_red.name = "stress" @@ -291,7 +288,8 @@ def mises_stress(u): quad_element = basix.ufl.quadrature_element( mesh.topology.cell_name(), value_shape=(1,), - degree=parameters["quadrature-degree"], + scheme="default", + degree=estimate_total_polynomial_degree(u_), ) Q_mises = df.fem.functionspace(mesh, quad_element) @@ -321,12 +319,12 @@ def mises_stress(u): # Save metrics metrics = { - "max_von_mises_stress_nodes": max_mises_stress_nodes, - "max_von_mises_stress_gauss_points": max_mises_stress_gauss_points, - "l2_error_displacement": l2_error_displacement, - "reaction_force_left_boundary_x": reaction_left_x, - "reaction_force_left_boundary_y": reaction_left_y, - f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})": displacement_x_at_evaluation_point, + "max_von_mises_stress_nodes[Pa]": max_mises_stress_nodes, + "max_von_mises_stress_gauss_points[Pa]": max_mises_stress_gauss_points, + "l2_error_displacement[m]": l2_error_displacement, + "reaction_force_left_boundary_x[N]": reaction_left_x, + "reaction_force_left_boundary_y[N]": reaction_left_y, + f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point, } if MPI.COMM_WORLD.rank == 0: diff --git a/examples/linear-elastic-plate-with-hole/kratos/Snakefile_kratos.smk b/examples/linear-elastic-plate-with-hole/kratos/Snakefile.smk similarity index 88% rename from examples/linear-elastic-plate-with-hole/kratos/Snakefile_kratos.smk rename to examples/linear-elastic-plate-with-hole/kratos/Snakefile.smk index 79f1170..0940cc6 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/Snakefile_kratos.smk +++ b/examples/linear-elastic-plate-with-hole/kratos/Snakefile.smk @@ -9,6 +9,18 @@ rule all: "solution_metrics.json", "solution_field_data.zip" +rule create_mesh: + input: + script = "create_mesh.py", + parameters = "parameters.json", + output: + mesh = "mesh.msh", + conda: "environment_mesh.yml" + shell: + """ + python3 {input.script} --input_parameter_file {input.parameters} --output_mesh_file {output.mesh} + """ + rule mesh_to_mdpa: input: parameters = "parameters.json", diff --git a/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py b/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py index f13c177..22a0eae 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py +++ b/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py @@ -37,30 +37,30 @@ def create_kratos_input( E = ( ureg.Quantity( - parameters["young-modulus"]["value"], parameters["young-modulus"]["unit"] + parameters["young_modulus[Pa]"], "Pa" ) .to_base_units() .magnitude ) nu = ( ureg.Quantity( - parameters["poisson-ratio"]["value"], parameters["poisson-ratio"]["unit"] + parameters["poisson_ratio"], "" ) .to_base_units() .magnitude ) radius = ( - ureg.Quantity(parameters["radius"]["value"], parameters["radius"]["unit"]) + ureg.Quantity(parameters["radius[m]"], "m") .to_base_units() .magnitude ) L = ( - ureg.Quantity(parameters["length"]["value"], parameters["length"]["unit"]) + ureg.Quantity(parameters["length[m]"], "m") .to_base_units() .magnitude ) load = ( - ureg.Quantity(parameters["load"]["value"], parameters["load"]["unit"]) + ureg.Quantity(parameters["load[MPa]"], "MPa") .to_base_units() .magnitude ) diff --git a/examples/linear-elastic-plate-with-hole/kratos/msh_to_mdpa.py b/examples/linear-elastic-plate-with-hole/kratos/msh_to_mdpa.py index b07b965..f82c1a3 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/msh_to_mdpa.py +++ b/examples/linear-elastic-plate-with-hole/kratos/msh_to_mdpa.py @@ -24,12 +24,12 @@ def msh_to_mdpa(parameter_file: str, mesh_file: str, mdpa_file: str): with open(parameter_file) as f: parameters = json.load(f) radius = ( - ureg.Quantity(parameters["radius"]["value"], parameters["radius"]["unit"]) + ureg.Quantity(parameters["radius[m]"], "m") .to_base_units() .magnitude ) L = ( - ureg.Quantity(parameters["length"]["value"], parameters["length"]["unit"]) + ureg.Quantity(parameters["length[m]"], "m") .to_base_units() .magnitude ) diff --git a/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py b/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py index 552e310..5babc74 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py +++ b/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py @@ -24,47 +24,47 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f E = ( ureg.Quantity( - parameters["young-modulus"]["value"], parameters["young-modulus"]["unit"] + parameters["young_modulus[Pa]"], "Pa" ) .to_base_units() .magnitude ) nu = ( ureg.Quantity( - parameters["poisson-ratio"]["value"], parameters["poisson-ratio"]["unit"] + parameters["poisson_ratio"], "" ) .to_base_units() .magnitude ) radius = ( - ureg.Quantity(parameters["radius"]["value"], parameters["radius"]["unit"]) + ureg.Quantity(parameters["radius[m]"], "m") .to_base_units() .magnitude ) L = ( - ureg.Quantity(parameters["length"]["value"], parameters["length"]["unit"]) + ureg.Quantity(parameters["length[m]"], "m") .to_base_units() .magnitude ) load = ( - ureg.Quantity(parameters["load"]["value"], parameters["load"]["unit"]) + ureg.Quantity(parameters["load[MPa]"], "MPa") .to_base_units() .magnitude ) - displacement_evaluation_point = parameters["displacement-evaluation-point"] + displacement_evaluation_point = parameters["displacement_evaluation_point[m]"] displacement_evaluation_x = ( ureg.Quantity( - displacement_evaluation_point["x"]["value"], - displacement_evaluation_point["x"]["unit"], + displacement_evaluation_point[0], + "m", ) .to_base_units() .magnitude ) displacement_evaluation_y = ( ureg.Quantity( - displacement_evaluation_point["y"]["value"], - displacement_evaluation_point["y"]["unit"], + displacement_evaluation_point[1], + "m", ) .to_base_units() .magnitude @@ -124,12 +124,12 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f displacement_x_at_evaluation_point = float(displacement_sampled[0, 0]) metrics = { - "max_von_mises_stress_nodes": max_von_mises_stress_nodes, - "max_von_mises_stress_gauss_points": max_von_mises_stress_gauss_points, - "l2_error_displacement": l2_error_displacement, - "reaction_force_left_boundary_x": reaction_force_left_boundary_x, - "reaction_force_left_boundary_y": reaction_force_left_boundary_y, - f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})": displacement_x_at_evaluation_point, + "max_von_mises_stress_nodes[Pa]": max_von_mises_stress_nodes, + "max_von_mises_stress_gauss_points[Pa]": max_von_mises_stress_gauss_points, + "l2_error_displacement[m]": l2_error_displacement, + "reaction_force_left_boundary_x[N]": reaction_force_left_boundary_x, + "reaction_force_left_boundary_y[N]": reaction_force_left_boundary_y, + f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point, } with open(output_metrics_file, "w") as f: json.dump(metrics, f, indent=4) diff --git a/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py b/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py index 968d443..89c2c18 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py +++ b/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py @@ -43,8 +43,8 @@ for file in root_unzipped_benchmark_dir.glob("parameters_*.json"): with open(file, "r") as f: data = json.load(f) - if data.get("element-size").get("value") >= 0.025: - + #if data.get("element_size[m]") >= 0.025: + if 1==1: # Create output directory for the configuration output_dir = root_unzipped_benchmark_dir / "results" / data.get("configuration") output_dir.mkdir(parents=True, exist_ok=True) @@ -60,11 +60,8 @@ continue else: shutil.copy(item, output_dir / item.name) - - # Run the Snakemake workflow from the benchmark to create the mesh for the configuration - subprocess.run(["snakemake", "--snakefile", "Snakefile", "--use-conda", "--force", "--cores", "all", "--conda-prefix", str(shared_env_dir), "--until", "create_mesh"], check=True, cwd=output_dir) - - # Run the Snakemake workflow to run the simulation and postprocess results for the configuration - subprocess.run(["snakemake", "--snakefile", "Snakefile_kratos.smk", "--use-conda", "--force", "--cores", "all", "--conda-prefix", str(shared_env_dir)], check=True, cwd=output_dir) + + # Run the Snakemake workflow for the configuration + subprocess.run(["snakemake", "--snakefile", "Snakefile.smk", "--use-conda", "--force", "--cores", "all", "--conda-prefix", str(shared_env_dir)], check=True, cwd=output_dir) print("Workflow executed successfully.") \ No newline at end of file diff --git a/examples/linear-elastic-plate-with-hole/linear-elastic-plate-with-hole.zip b/examples/linear-elastic-plate-with-hole/linear-elastic-plate-with-hole.zip index 072163324f669b94a6ab2d3059c26d45cbc793f5..4a53f2ad58f557e1a254dc24c3f94b242d3d1f7c 100644 GIT binary patch delta 3919 zcmb`KcT^Mk7RN&q2pBqs(3?t8I*1gd_ofJnBA^5a9i+)3vxz8zASEbOk={fJO_7E7 zDAJLxlvP5LA_xK!n(mv~HA{HzzO#S4$+_pAoH@Tc^ZT58zu(`Cp6r*6*I-~H!rhywa@`Bf#sKA->(EF7z_auf?f4TI|ZN}ywKO&j{9CeXMGSx@5IRDPL9A3CKwg% zVv~&(ZNp(3t5O@YxG`ncx{tQ39eUbzxMO3<)SAdKv!Acv1D7_Yk<_-?V%(fXiGwax zZE?%-NnRs1Z}iVTY))125r_mFbV}?DW53zpeyZW*YAi1#Skqd~Ug*d!Vt6ZcHR-Y| zSI5|I-1jBz-OOs z2~kn!m<^TPA)S>Qr{kzy=U`{wY{xFJN2RS)Y_dAt@;`*BzlwB|Q@DpL1L&^1u~pG- z862S7zSN44?C1$Osa9t)LQ^*(@0@Q#mr>o{p~u9m+CKOm>=(9`0U zI4l#Cr`){Jnj3qRAvdK@rJwQn8)N4Py%U`{{Ln%*y;F0>JzcHA*rAV+yI}|8wDr6K z^rGiv%)Xd96M@_1`Xupbb73VuwKUDqudAv4KtQ&}u8;cW7hqzG0XQIxb!U%#rl9^J z%tBuDaK6LEVqKLaMYD}Y`7?&vyY|mre9A1R+!hfR;WTXv>d&SgeGH_gDckMmN4Un2 z^n+KQ5SAjV4nNUg@|db4FkQ2yxg*N1UKeY_>ZCkooH8~SsE#XS3+2W3eo9OGB{K8U z

S;vv_#uLBY&P*SElW=EUsV6+F%R(%zK_L5U+;2Zv7vHG76+Bj`~Jm`L77kDJV> zR6hDPaa?=BL;S$+9imOL&X18sqXyTkp7*mV&b!=KUcJRkkLt-`4J<>;Y8>e2X}c1i z*d1)o#`B@6PiksJQNC4zv0AzF)1^URdnHIKOK&Oiz3R>D)LEZ0{=KG+dyzbmp6jSut!-dozV)pJ z7bI<9bmfdVqn&hoCLqR@uWl6f7FPP;V|Np3qYI;R$XPcparAD`#CYv&%IeF$>bq79 zYh4;WHSu#(GzBEyT(=?Fw-pf@{%3E|2Crj*VAt!31NqFyJ&nF$3dKeyEk;h`ZP{&P zcE?pL96XH&WHFZJ*ZWKcYiGuLBv4+70v%s+!YsJyD@V<`*EPI$R)ZA~B1ip)^DaI; z<@GpJ#-ILW1Vx}d}7bPgZsBsooFc*7BDp}4nh)!-<>k^(GApu35 z!Q+Au1Mdt1p&Hap1u7WK(u9VV6UG4klqt3^2i{?a3E}gYk-!$nfE+(CFasHIMvzqX zeC=_Vw(L5ctR2?rtYWE8Df+s;_L9vYU z1c_5-$Re-KVus`(@iBdw1!CZHM)V!T0NUvu12qZ;5FHMf5CHRa@^|t=2cZ40ImpT@ z9e2OxS;=~n)Bn-bHb6A}e$_qT*DunsIC)S~)NF$Fe+d}*258Z3F)F~-Fwjm(%_Y=b8u zV&PiM@&b0d}b9DK)Gapp`=#HY|cMZ?? zx`XR`dz_{~qeO*2!XgMTf&j?z{{ryU_i(|LldO{4m{h!0t>2S!ebJ_hwi^Z|l1UCq zzL$Dw!SV2OEgzEPFa|<8=EKmMhTP2owN2*P>pj}`vX{Z}&^%O9{bz49LcNi@zchDIkZ6pX$v*xu<$`r1vRhkC;bHL~&oB%rjfH*_fgsDO94@P4fP04vCV96vDV zhBwNxMl5UUwO=qE&3?Qr6e1%o-HoEboLLpM{`{`nq1(@8qjNDFVHW1Bye+I(M$GG* z9%VJ|`5MJ{*8|&0Y(YmOydtwY;8iOC6m3+$xh)$6~eG^o(>z)DnMpJE^nZ% z63@KyIVjQ;f^bgOyx#9OO_h@R02ewYrcf!@>H<~x-4shD<(xBJJ|6vsF*t5GX-1i& z((&$u)+>IK$oia+qVUC~ON*I4XA!=e-@u8WXK3fkWO@lwfcCmKAqEr_RH1xRld4ef0J03RHo>`JY6ttP&iqet{kQMI$SMv#9|=dfZIdh zj4r*F9i$Thi;)VdGX$FBIhpGj`jxz`B2tPX>J`5RR~BB{KG%RlnK^D!bFtV~+W{Eu z+XWBb+kRMZ##%gA<1uE84vmGcQBbKn6<$S|k9RNQ1xLlr$hmE0HiF$rJ>g*1W7o$b*DO^8ltiDL^_ zAVeq5)FpOMxM|9K*&#p8`R6ziJ)cq1w+hP{BnYR~A^J!if#~>(INc@L4zi)$pi8%s z;8w2n5X*XetI^I=!HTIGC2(#bd6wiF{dTh8VVy;-plkcgcO6YtM$SRA0?6-!lSctZ z8N7r1k&O$+8@s*ePXsBxz~h8hzJuGN;X6s-###np;03A*b=k zaOF(#E4Ef@7OA@Mnq?!zx^G>>1F@M?OkPop>n%6JY&GECR4fYm7CECvu`Qx5WzUB1 zyeZzavoUk#93BP;$tIqIivgnLH4drc!R$RlJ!+Bh!Ql)}ZeZ&?xxo{yK0 znXK1P{(TiBYLr6`Y8v@jAOQj^9^mC?CW{-Cp;ZQu0Fc`s>i+$&*vqzWw|}n&P%U3* nUiKR0k9i@#LN0cJ{fAorczP^N=ng=Yn82R`EeyuU3H|#I@%&}$ delta 5194 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 TEX*|2AZpM$2?%tFg>dbEtOfEJ From 7c9e2cfcf3e895137571be42e557114ce595e9d8 Mon Sep 17 00:00:00 2001 From: dtyagi Date: Mon, 18 May 2026 15:39:15 +0200 Subject: [PATCH 2/7] run_benchmark.py correction --- .../linear-elastic-plate-with-hole/fenics/run_benchmark.py | 5 ++--- .../linear-elastic-plate-with-hole/kratos/run_benchmark.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py b/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py index c8ed772..3e90ba8 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py @@ -43,9 +43,8 @@ for file in root_unzipped_benchmark_dir.glob("parameters_*.json"): with open(file, "r") as f: data = json.load(f) - #if data.get("element_size[m]") >= 0.025: - #if data.get("configuration") == "1": - if 1==1: + if data.get("element_size[m]") >= 0.025: + # Create output directory for the configuration output_dir = root_unzipped_benchmark_dir / "results" / data.get("configuration") output_dir.mkdir(parents=True, exist_ok=True) diff --git a/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py b/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py index 89c2c18..56c7117 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py +++ b/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py @@ -43,8 +43,8 @@ for file in root_unzipped_benchmark_dir.glob("parameters_*.json"): with open(file, "r") as f: data = json.load(f) - #if data.get("element_size[m]") >= 0.025: - if 1==1: + if data.get("element_size[m]") >= 0.025: + # Create output directory for the configuration output_dir = root_unzipped_benchmark_dir / "results" / data.get("configuration") output_dir.mkdir(parents=True, exist_ok=True) From 2326360feb6f395c03fe0e1ef396209ca5825e8e Mon Sep 17 00:00:00 2001 From: dtyagi Date: Mon, 18 May 2026 18:23:29 +0200 Subject: [PATCH 3/7] new solution metrics added --- .../linear elasticity/plate-with-hole.md | 35 ++++++++++++------- .../fenics/run_simulation.py | 16 ++++++--- .../kratos/postprocess_results.py | 18 ++++++---- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/docs/benchmarks/linear elasticity/plate-with-hole.md b/docs/benchmarks/linear elasticity/plate-with-hole.md index fcc8861..9e2120f 100644 --- a/docs/benchmarks/linear elasticity/plate-with-hole.md +++ b/docs/benchmarks/linear elasticity/plate-with-hole.md @@ -96,31 +96,40 @@ $$ In order to solve the weak formulation, the finite-element method (FEM) can be used. This method discretizes the domain $\Omega$ into so called finite elements that can for example be triangles or quadrilaterals in 2D. On these elements, ansatz functions are defined such that they are continous on the boundaries between elements. These functions form a basis for the solution space for an approximate solution $\boldsymbol{u}_h$ of the problem. -## Comparison of approximate solution with analytical solution +## Solution metrics -The approxiamte solution and the analytical solution can be compared with the $L_2$ norm which is defined as +The approximate solution is compared with the analytical solution using the $L_2$ norm which is defined as $$ -\Vert \boldsymbol{u}\Vert_{L_2} = \sqrt{\int_\Omega \Vert\boldsymbol{u}(\boldsymbol{x})\Vert_2^2 \mathrm d \boldsymbol x} +\Vert \boldsymbol{f}\Vert_{L_2} = \sqrt{\int_\Omega \left|\boldsymbol{f}(\boldsymbol{x})\right|^2 \mathrm d \boldsymbol x} $$ -and the error in the $L_2$ norm +This norm is computed for the error between the finite element solution and the analytical solution of displacements i.e., $\Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{L_2}$. + +The maximum displacement error is computed at the nodes of the finite element mesh with respect to the analytical solution. $$ -e_{L_2} = \Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{L_2}. +e_{\max} += +\max_{i \in \mathcal{N}} +\left\| +\mathbf{u}(\mathbf{x}_i) +- +\mathbf{u}_h(\mathbf{x}_i) +\right\| $$ -Alternatively, the sup norm can be used which is defined as +where $\mathcal{N}$ denotes the set of nodes of the finite element mesh. -$$ -\Vert \boldsymbol{u}\Vert_{\inf} = \sup_{\boldsymbol x} \Vert \boldsymbol{u}(\boldsymbol{x}) \Vert -$$ +Max Von-Mises stress + +Displacement at the top-right corner of the plate. + +The reaction force at the left boundary. + +Number of Degrees of Freedom which supports in comparing the outputs between different meshes. -and the error in the sup norm -$$ -e_{\inf} = \Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{\inf}. -$$ With these metrices, we can perform a convergence analysis for different approximations $\boldsymbol{u}_h$ which differ in the element size $h$. Plotting the error over the used element-size in a log-log plot lets us determine the convergence order of the approximation. diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py index 1aefda3..3b0b368 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py @@ -170,6 +170,7 @@ def traction_top_expr(x: np.ndarray) -> np.ndarray: reaction_left_y_local = df.fem.assemble_scalar(df.fem.form(traction[1] * ds(1))) reaction_left_x = MPI.COMM_WORLD.allreduce(reaction_left_x_local, op=MPI.SUM) reaction_left_y = MPI.COMM_WORLD.allreduce(reaction_left_y_local, op=MPI.SUM) + num_dofs = V.dofmap.index_map.size_global * V.dofmap.index_map_bs # Compute L2 error norm between FE displacement and analytical displacement. @@ -187,6 +188,14 @@ def analytical_displacement_expr(x: np.ndarray) -> np.ndarray: l2_error_sq_global = MPI.COMM_WORLD.allreduce(l2_error_sq_local, op=MPI.SUM) l2_error_displacement = np.sqrt(l2_error_sq_global) + # Compute max nodal displacement error magnitude (global across MPI) + block_size = V.dofmap.index_map_bs + nodal_error = (u.x.array - u_analytical.x.array).reshape(-1, block_size) + max_displacement_error_nodes_local = np.max(np.linalg.norm(nodal_error, axis=1)) + max_displacement_error_nodes = MPI.COMM_WORLD.allreduce( + max_displacement_error_nodes_local, op=MPI.MAX + ) + # Evaluate displacement at the specified evaluation point displacement_eval_point = np.array( [[displacement_evaluation_x, displacement_evaluation_y, 0.0]], @@ -281,8 +290,6 @@ def mises_stress(u): ) as vtk: vtk.write_function(mises_stress_nodes, 0.0) - # extract maximum von Mises stress - max_mises_stress_nodes = np.max(mises_stress_nodes.x.array) # Compute von Mises stress at quadrature (Gauss) points and extract maximum (global across MPI) quad_element = basix.ufl.quadrature_element( @@ -319,9 +326,10 @@ def mises_stress(u): # Save metrics metrics = { - "max_von_mises_stress_nodes[Pa]": max_mises_stress_nodes, - "max_von_mises_stress_gauss_points[Pa]": max_mises_stress_gauss_points, + "number_of_dofs[-]": num_dofs, + "max_von_mises_stress[Pa]": max_mises_stress_gauss_points, "l2_error_displacement[m]": l2_error_displacement, + "max_displacement_error[m]": max_displacement_error_nodes, "reaction_force_left_boundary_x[N]": reaction_left_x, "reaction_force_left_boundary_y[N]": reaction_left_y, f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point, diff --git a/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py b/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py index 5babc74..6fb8344 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py +++ b/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py @@ -77,10 +77,8 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f L=L, load=load, ) - # Compute maximum von Mises stress at nodes and Gauss points. - max_von_mises_stress_nodes = float(np.max(mesh.point_data["VON_MISES_STRESS"])) - - max_von_mises_stress_gauss_points = max_von_mises_stress_nodes + # Compute maximum von Mises stress at Gauss points. + max_von_mises_stress_gauss_points = 0 for key, values in mesh.cell_data.items(): if "VON_MISES_STRESS" in key: max_von_mises_stress_gauss_points = float(np.max(values)) @@ -123,10 +121,18 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f else: displacement_x_at_evaluation_point = float(displacement_sampled[0, 0]) + # Compute nodal displacement error (Euclidean norm of error vector at each node) + nodal_displacement_error = np.linalg.norm(displacement - u_ref, axis=1) + max_displacement_error_nodes = float(np.max(nodal_displacement_error)) + + # Compute the number of dofs + num_dofs = int(mesh.n_points * 2) + metrics = { - "max_von_mises_stress_nodes[Pa]": max_von_mises_stress_nodes, - "max_von_mises_stress_gauss_points[Pa]": max_von_mises_stress_gauss_points, + "number_of_dofs[-]": num_dofs, + "max_von_mises_stress[Pa]": max_von_mises_stress_gauss_points, "l2_error_displacement[m]": l2_error_displacement, + "max_displacement_error[m]": max_displacement_error_nodes, "reaction_force_left_boundary_x[N]": reaction_force_left_boundary_x, "reaction_force_left_boundary_y[N]": reaction_force_left_boundary_y, f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point, From 2eba581da87b656ab6f57c914c6af666670d29e4 Mon Sep 17 00:00:00 2001 From: dtyagi Date: Tue, 19 May 2026 13:14:08 +0200 Subject: [PATCH 4/7] updating ouput metrics in md file --- .vscode/settings.json | 4 ++ .../linear elasticity/plate-with-hole.md | 51 +++++++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4b5a294 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:conda", + "python-envs.defaultPackageManager": "ms-python.python:conda" +} \ No newline at end of file diff --git a/docs/benchmarks/linear elasticity/plate-with-hole.md b/docs/benchmarks/linear elasticity/plate-with-hole.md index 9e2120f..cb37073 100644 --- a/docs/benchmarks/linear elasticity/plate-with-hole.md +++ b/docs/benchmarks/linear elasticity/plate-with-hole.md @@ -94,45 +94,40 @@ B(\boldsymbol u,\boldsymbol v) &= \int_{\Omega} \boldsymbol\varepsilon(\boldsymb $$ -In order to solve the weak formulation, the finite-element method (FEM) can be used. This method discretizes the domain $\Omega$ into so called finite elements that can for example be triangles or quadrilaterals in 2D. On these elements, ansatz functions are defined such that they are continous on the boundaries between elements. These functions form a basis for the solution space for an approximate solution $\boldsymbol{u}_h$ of the problem. +In order to solve the weak formulation, the finite-element method (FEM) can be used. This method discretizes the domain $\Omega$ into so called finite elements that can for example be triangles or quadrilaterals in 2D. On these elements, ansatz functions are defined such that they are continuous on the boundaries between elements. These functions form a basis for the solution space for an approximate solution $\boldsymbol{u}_h$ of the problem. -## Solution metrics +## Output metrics -The approximate solution is compared with the analytical solution using the $L_2$ norm which is defined as +- The approximate solution is compared with the analytical solution using the $L_2$ norm which is defined as -$$ -\Vert \boldsymbol{f}\Vert_{L_2} = \sqrt{\int_\Omega \left|\boldsymbol{f}(\boldsymbol{x})\right|^2 \mathrm d \boldsymbol x} -$$ - -This norm is computed for the error between the finite element solution and the analytical solution of displacements i.e., $\Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{L_2}$. - -The maximum displacement error is computed at the nodes of the finite element mesh with respect to the analytical solution. - -$$ -e_{\max} -= -\max_{i \in \mathcal{N}} -\left\| -\mathbf{u}(\mathbf{x}_i) -- -\mathbf{u}_h(\mathbf{x}_i) -\right\| -$$ - -where $\mathcal{N}$ denotes the set of nodes of the finite element mesh. + $$ + \Vert \boldsymbol{f}\Vert_{L_2} = \sqrt{\int_\Omega \left|\boldsymbol{f}(\boldsymbol{x})\right|^2 \mathrm d \boldsymbol x} + $$ -Max Von-Mises stress + This norm is computed for the error between the finite element solution and the analytical solution of displacements i.e., $\Vert \boldsymbol{u}-\boldsymbol{u}_h\Vert_{L_2}$. -Displacement at the top-right corner of the plate. +- The maximum displacement error is computed at the nodes of the finite element mesh with respect to the analytical solution. -The reaction force at the left boundary. + $$ + e_{\max} + = + \max_{i \in \mathcal{N}} + \left\| + \mathbf{u}(\mathbf{x}_i) + - + \mathbf{u}_h(\mathbf{x}_i) + \right\| + $$ -Number of Degrees of Freedom which supports in comparing the outputs between different meshes. + where $\mathcal{N}$ denotes the set of nodes of the finite element mesh. +- Max Von-Mises stress +- Displacement at the top-right corner of the plate. +- The reaction force at the left boundary. -With these metrices, we can perform a convergence analysis for different approximations $\boldsymbol{u}_h$ which differ in the element size $h$. Plotting the error over the used element-size in a log-log plot lets us determine the convergence order of the approximation. +- The number of Degrees of Freedom in the finite element mesh. ## Table of parameters From f7177bc7a7d194761cb2d14acfb991b795c0d2e9 Mon Sep 17 00:00:00 2001 From: dtyagi Date: Wed, 20 May 2026 16:56:39 +0200 Subject: [PATCH 5/7] parameter.json update --- .../fenics/run_simulation.py | 48 ++++++------------ .../kratos/create_kratos_input.py | 6 +-- .../kratos/postprocess_results.py | 35 ++++--------- .../linear-elastic-plate-with-hole.zip | Bin 6226 -> 6032 bytes 4 files changed, 28 insertions(+), 61 deletions(-) diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py index 3b0b368..371faf9 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py @@ -46,14 +46,14 @@ def run_fenics_simulation( E = ( ureg.Quantity( - parameters["young_modulus[Pa]"], "Pa" + parameters["youngs_modulus[Pa]"], "Pa" ) .to_base_units() .magnitude ) nu = ( ureg.Quantity( - parameters["poisson_ratio"], "" + parameters["poissons_ratio"], "" ) .to_base_units() .magnitude @@ -69,24 +69,7 @@ def run_fenics_simulation( .magnitude ) load = ( - ureg.Quantity(parameters["load[MPa]"], "MPa") - .to_base_units() - .magnitude - ) - displacement_evaluation_point = parameters["displacement_evaluation_point[m]"] - displacement_evaluation_x = ( - ureg.Quantity( - displacement_evaluation_point[0], - "m", - ) - .to_base_units() - .magnitude - ) - displacement_evaluation_y = ( - ureg.Quantity( - displacement_evaluation_point[1], - "m", - ) + ureg.Quantity(parameters["load[Pa]"], "Pa") .to_base_units() .magnitude ) @@ -198,7 +181,7 @@ def analytical_displacement_expr(x: np.ndarray) -> np.ndarray: # Evaluate displacement at the specified evaluation point displacement_eval_point = np.array( - [[displacement_evaluation_x, displacement_evaluation_y, 0.0]], + [[1.0, 1.0, 0.0]], dtype=np.float64, ) tree = df.geometry.bb_tree(mesh, mesh.topology.dim) @@ -208,12 +191,13 @@ def analytical_displacement_expr(x: np.ndarray) -> np.ndarray: colliding_cells = df.geometry.compute_colliding_cells( mesh, cell_candidates, displacement_eval_point ) - local_displacement_x = None + local_displacement = None if len(colliding_cells.links(0)) > 0: cell = colliding_cells.links(0)[0] - local_displacement_x = u.eval( + # u.eval returns a 2D array: shape (num_points, value_size) + local_displacement = u.eval( displacement_eval_point, np.array([cell], dtype=np.int32) - )[0] + )[0].tolist() # [ux, uy] def project( @@ -307,22 +291,22 @@ def mises_stress(u): np.max(mises_qp.x.array), op=MPI.MAX ) - displacement_x_at_evaluation_point = None + displacement_at_evaluation_point = None if MPI.COMM_WORLD.rank == 0: - displacement_x_candidates = ( - MPI.COMM_WORLD.gather(local_displacement_x, root=0) or [] + displacement_candidates = ( + MPI.COMM_WORLD.gather(local_displacement, root=0) or [] ) - for value in displacement_x_candidates: + for value in displacement_candidates: if value is not None: - displacement_x_at_evaluation_point = value + displacement_at_evaluation_point = value break - if displacement_x_at_evaluation_point is None: + if displacement_at_evaluation_point is None: raise ValueError( "Could not evaluate displacement at the configured evaluation point." ) else: - MPI.COMM_WORLD.gather(local_displacement_x, root=0) + MPI.COMM_WORLD.gather(local_displacement, root=0) # Save metrics metrics = { @@ -332,7 +316,7 @@ def mises_stress(u): "max_displacement_error[m]": max_displacement_error_nodes, "reaction_force_left_boundary_x[N]": reaction_left_x, "reaction_force_left_boundary_y[N]": reaction_left_y, - f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point, + "displacement_top_right_corner[m]": displacement_at_evaluation_point, # [ux, uy] } if MPI.COMM_WORLD.rank == 0: diff --git a/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py b/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py index 22a0eae..f9d727d 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py +++ b/examples/linear-elastic-plate-with-hole/kratos/create_kratos_input.py @@ -37,14 +37,14 @@ def create_kratos_input( E = ( ureg.Quantity( - parameters["young_modulus[Pa]"], "Pa" + parameters["youngs_modulus[Pa]"], "Pa" ) .to_base_units() .magnitude ) nu = ( ureg.Quantity( - parameters["poisson_ratio"], "" + parameters["poissons_ratio"], "" ) .to_base_units() .magnitude @@ -60,7 +60,7 @@ def create_kratos_input( .magnitude ) load = ( - ureg.Quantity(parameters["load[MPa]"], "MPa") + ureg.Quantity(parameters["load[Pa]"], "Pa") .to_base_units() .magnitude ) diff --git a/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py b/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py index 6fb8344..914aa00 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py +++ b/examples/linear-elastic-plate-with-hole/kratos/postprocess_results.py @@ -24,14 +24,14 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f E = ( ureg.Quantity( - parameters["young_modulus[Pa]"], "Pa" + parameters["youngs_modulus[Pa]"], "Pa" ) .to_base_units() .magnitude ) nu = ( ureg.Quantity( - parameters["poisson_ratio"], "" + parameters["poissons_ratio"], "" ) .to_base_units() .magnitude @@ -47,25 +47,7 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f .magnitude ) load = ( - ureg.Quantity(parameters["load[MPa]"], "MPa") - .to_base_units() - .magnitude - ) - - displacement_evaluation_point = parameters["displacement_evaluation_point[m]"] - displacement_evaluation_x = ( - ureg.Quantity( - displacement_evaluation_point[0], - "m", - ) - .to_base_units() - .magnitude - ) - displacement_evaluation_y = ( - ureg.Quantity( - displacement_evaluation_point[1], - "m", - ) + ureg.Quantity(parameters["load[Pa]"], "Pa") .to_base_units() .magnitude ) @@ -110,16 +92,17 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f reaction_force_left_boundary_x = float(np.sum(reaction[left_boundary_mask, 0])) reaction_force_left_boundary_y = float(np.sum(reaction[left_boundary_mask, 1])) + # Compute displacement at the top-right corner probe_points = pyvista.PolyData( - np.array([[displacement_evaluation_x, displacement_evaluation_y, 0.0]], dtype=float) + np.array([[1.0, 1.0, 0.0]], dtype=float) ) sampled = probe_points.sample(mesh) displacement_sampled = sampled.point_data.get("DISPLACEMENT") if displacement_sampled is None: - closest_id = mesh.find_closest_point([displacement_evaluation_x, displacement_evaluation_y, 0.0]) - displacement_x_at_evaluation_point = float(displacement[closest_id, 0]) + closest_id = mesh.find_closest_point([1.0, 1.0, 0.0]) + displacement_at_evaluation_point = [float(displacement[closest_id, 0]), float(displacement[closest_id, 1])] else: - displacement_x_at_evaluation_point = float(displacement_sampled[0, 0]) + displacement_at_evaluation_point = [float(displacement_sampled[0, 0]), float(displacement_sampled[0, 1])] # Compute nodal displacement error (Euclidean norm of error vector at each node) nodal_displacement_error = np.linalg.norm(displacement - u_ref, axis=1) @@ -135,7 +118,7 @@ def postprocess_results(input_parameter_file, input_result_vtk, output_metrics_f "max_displacement_error[m]": max_displacement_error_nodes, "reaction_force_left_boundary_x[N]": reaction_force_left_boundary_x, "reaction_force_left_boundary_y[N]": reaction_force_left_boundary_y, - f"displacement_x_at_evaluation_point (x={displacement_evaluation_x}, y={displacement_evaluation_y})[m]": displacement_x_at_evaluation_point, + "displacement_top_right_corner[m]": displacement_at_evaluation_point, # [ux, uy] } with open(output_metrics_file, "w") as f: json.dump(metrics, f, indent=4) diff --git a/examples/linear-elastic-plate-with-hole/linear-elastic-plate-with-hole.zip b/examples/linear-elastic-plate-with-hole/linear-elastic-plate-with-hole.zip index 4a53f2ad58f557e1a254dc24c3f94b242d3d1f7c..080c1da2555dd27328b4769ad7a740f1fee64278 100644 GIT binary patch delta 2411 zcmZ{l4?NTPAIHDi*fN{Nrd5mOOkKHhxx{`g^xGusC$v*iI{yl#o9K@p-A?+utH{^M zl-V+mP$Nu8oIgLu946gSaz`OQonO?ijC1n)e6Rb~cR#=1_IN+u+vBs(_p|5c{eHh* zo%ShfQn&=WN^<0?N^&vpl_C1MlxyUMCy<)|Sb$F(-y@$oquvdnFW8H;RGiU5jzr_g zaS~*JA8qjS!PnXP`K(L+!MK4%KtBN3L#*p3nwQI{odhjg$`MvBu!Rs7ZYx2F0Rd7!5tZG>EiTT zJN54Rg1zLUptL6V4g5k!@D>n=GWKA4HUeY-LEZu!o(2)Y`wRy2Em$;zSdb{_a8OiO zY}nzLz4q%?N5*`4=yM?%5)-b03qWJe1-QMt0B62v&&dc61(&bOMK0jS`IlCAc2;%s zvWBLUwb@^G4~LL`yK?{I=J1Zv0PSR}7u(oZ$}`oqW|%$@78@1~RP{&qU%W0%uG#?j z^BHzd<31@Hmao>x5AT2l%~RS;eZ8V4PRevATiUCG335V{xwSPkcXR-0r_loJ>ql+Z!UJ<5Zmj()|JJ z%pHjnP`r29CtqAR_xP?ux~ek*ry6qCjIiwTLk~ap4O;BH`(_Z^Byl&jsktO~dd2|Z zjG`I-DhG`Eb%rsV^We)Ambe^nqA`cF4yh(@477749`4vQO|QOUZC38*y}sh_9)j6w z+vk>jCZ!I2h1w>I*K2aNr`(tCFPFWH=9F$yQ}?mh)g zdn{98DugB$%*hqa33B$oLR<t2fP19xKYQTxc9L_d`^VC?} zJ5JYWTJ_m?oRcvgg1+kSv$DnuRhKU&uMP_t$&fPv7k|2_cgiorm0#P`n&RZ%Ru|m_0(%UJ7Y*Gl>Q3^ zcgX5}PabY_PyIF^U-4#x{jL$I+kzv{13b5WbUI+&Xx_cezxrG5>iQG(`4sz&0)XQ6 zK&2Q^Be50DYH-MJj49yEcM=8tH*%VL)fWDh*=6iiL~CEdUpdIk+F> z`0l&@{TcM{a=#8~f5_$ZxA9Rw`pe=OFJ@xFoKg$jm#@iL7FsS-&0l_WrQ%A9a$KZk zjt9wB-T9hRV~oIV!N6Xy&RDPBHXNKOb-nt!)9v-X1#Ln(*VW=i0n)zH6Z`FMnw6dI zioS7*L)^UbQEwS7!=2K-@?hvDmdVqHJJVzPtPt4n^HVd393w~{!rlM7r2$Pr&RBS|ewQyaQ{S6>a}=rfAaft}2!&;I;S+5W1xK{Qqs@$d8JBiygv zyVPt`sLC)WJD8o@zTlo?M*WGnLGPmjJBx88Fa1S*nxqm(-PKNiMLaj+518}-}Ng-ke(=8XON6U_xRV6gg!|Gu{@m`7hlLcJGc z@B<#=Tv$yOgW}53g$AJvNV)7yq>Ifi%P40-CVg(2^ zC3-fbO#FlfNsguvTx`08gbEnQ$R=d?vD0Fqw~0fA z;C3pLgo**kV3QRxNZ78^|IaRP_Ch8J9Vn2IEl4}Qub%+~G4#%Hg$)=i655;~!*&do K(PnexCjJ9xOTrNV delta 2619 zcmb`JdoMCX`k<}ZEBqMdCv34Gjl%YbLM={_xt;OUi1FEKi>?! zhr|pM_%5`7ir?D%HyyPAAN>1Qk62liU<|P zxmYAjOk%#-%T=Orr+WK|B~w})Mz>{&PyXyk~xA79@bQMk-M>|eiSV7Eo?<#f+BlTxCr00zSNjDZtifW9O!vX1njg|ruPEeqCU zrwVR+5wUOaMa40IyKy>5*1G2O-MSKkrDvbZiz;5<`X3(6SN1rh-on;3) zA6#hVbYjz_dNl>Ax5~%~L;sAjkXLFjZ&U5?DtFxdjDlw`{kVcVCOfczGERPIhCJ68 z`$y~K%3%4u&QS#wU8TH<`E~>owKp*|mGwwBJnYdCIxQ2M9+I18Q`tf<9cX&!H;X?} zmhb&d{s{k1x8L^vC4?JgV~uw{6N0%21VaD;4P%f|+bLTC17ZBX7_3L`WS=}JOIQub zW?0lm-K@0HYOZR(Xj@KT`Wi+a?~?${5EWcGM3A?q#CAT94>Uc3xgTBM>|D6qd)S9~ z0yKlEVtMUHB{V@wDB7rmQK1qz9!hcatU30`Q}*gwFLS*|0Ed*|iXDD=7eIhY{iFmd z5eWOiiUPw`MwKE97zpDt20e%-qUk3|R|I~=AeeZg*N=g{Di`N=RN1aM4=tR`K?A46<;uaw*!jm{j z8_|!aB9uG5v}!1N&-NA%v{y5vUM`+ba)cmw5}g~O{xscR-VhxmiA2Wf1lO{S5Ghy5 zh9s6-_vx^QM^b4RgkYK^U+tIj%%VlrA*q2Cdm~|f;rM+1X?IlQn|GjU_?8ZpeB?bq z0jl*A1zQmcMq)6AyDW~P2p9=l2+{LN`bN{7O?z1R%Xv1{+uJmC3^Wf_J&CCvFR*sIl?p0qN5c(hAzdtc?X%w%uw(H{=qGC)yt~c~=1c2h5CkW+*H!fZ z0w`4kLaGRaePWVOL*N+W7%b%~AW`-q{)PmY4wv{B=)5|K+&;@S)a9p^8QKvy{X8IN zK2@djzJ0Pq5m3bjr){x)__hOjgsb z$JNtiE!$4EiOcA46YBVOsO3m*6i${y_~3kWWu0&Kc#K@{P_J=PM$EXgd#8GX86*d)9A3N*a3C?P zsPoDZE914DGOjrmg4<0@D^e>90=_ z)lmuti}XL^e=0CK@XT3XqT!y Date: Wed, 20 May 2026 19:01:10 +0200 Subject: [PATCH 6/7] fenics output metrics corrected & inclusion of tool metadata --- .../fenics/run_benchmark.py | 10 ++++++++++ .../fenics/run_simulation.py | 2 +- .../kratos/run_benchmark.py | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py b/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py index 3e90ba8..852fec5 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_benchmark.py @@ -34,6 +34,16 @@ shared_env_dir = root_unzipped_benchmark_dir / "conda_envs" shared_env_dir.mkdir(parents=True, exist_ok=True) +#################################################################################################### +#################################################################################################### +# Simulation tool metadata (to be included in the RO-Crate) +#################################################################################################### +#################################################################################################### + +tool_name = "fenics" +tool_uri = "https://github.com/FEniCS/dolfinx" +tool_version = "0.9.0" + #################################################################################################### #################################################################################################### # Conditional execution of parameter configurations diff --git a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py index 371faf9..eee0b1d 100644 --- a/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py +++ b/examples/linear-elastic-plate-with-hole/fenics/run_simulation.py @@ -197,7 +197,7 @@ def analytical_displacement_expr(x: np.ndarray) -> np.ndarray: # u.eval returns a 2D array: shape (num_points, value_size) local_displacement = u.eval( displacement_eval_point, np.array([cell], dtype=np.int32) - )[0].tolist() # [ux, uy] + ).tolist() # [ux, uy] def project( diff --git a/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py b/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py index 56c7117..f417bb1 100644 --- a/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py +++ b/examples/linear-elastic-plate-with-hole/kratos/run_benchmark.py @@ -34,6 +34,16 @@ shared_env_dir = root_unzipped_benchmark_dir / "conda_envs" shared_env_dir.mkdir(parents=True, exist_ok=True) +#################################################################################################### +#################################################################################################### +# Simulation tool metadata (to be included in the RO-Crate) +#################################################################################################### +#################################################################################################### + +tool_name = "kratos" +tool_uri = "https://github.com/KratosMultiphysics/Kratos" +tool_version = "10.3.1" + #################################################################################################### #################################################################################################### # Conditional execution of parameter configurations From d7696040ec1cf6b9c1f2f0de779d11bdbb7eece9 Mon Sep 17 00:00:00 2001 From: dtyagi Date: Wed, 20 May 2026 19:05:36 +0200 Subject: [PATCH 7/7] removed nextflow workflow --- .github/workflows/run-benchmark.yml | 13 -- benchmarks/common/nextflow.config | 27 --- .../fenics/fenics.nf | 35 ---- .../kratos/kratos.nf | 123 ------------- .../linear-elastic-plate-with-hole/main.nf | 173 ------------------ 5 files changed, 371 deletions(-) delete mode 100644 benchmarks/common/nextflow.config delete mode 100644 benchmarks/linear-elastic-plate-with-hole/fenics/fenics.nf delete mode 100644 benchmarks/linear-elastic-plate-with-hole/kratos/kratos.nf delete mode 100644 benchmarks/linear-elastic-plate-with-hole/main.nf diff --git a/.github/workflows/run-benchmark.yml b/.github/workflows/run-benchmark.yml index 546edb1..b4cec3a 100644 --- a/.github/workflows/run-benchmark.yml +++ b/.github/workflows/run-benchmark.yml @@ -54,12 +54,6 @@ jobs: --report-metadata4ing-config metadata4ing.config \ --report-metadata4ing-filename $SNAKEMAKE_RESULT_FILE - - name: run_linear-elastic-plate-with-hole-benchmarks_nextflow - shell: bash -l {0} - run: | - cd $GITHUB_WORKSPACE/benchmarks/linear-elastic-plate-with-hole/ - nextflow run main.nf -params-file workflow_config.json -c ../common/nextflow.config -plugins nf-prov@1.4.0 - - name: Archive Linear Elastic plate with a hole benchmark data for snakemake uses: actions/upload-artifact@v4 with: @@ -67,13 +61,6 @@ jobs: path: | benchmarks/linear-elastic-plate-with-hole/${{ env.SNAKEMAKE_RESULT_FILE }}.zip - - name: Archive Linear Elastic plate with a hole benchmark data for nextflow - uses: actions/upload-artifact@v4 - with: - name: nextflow_results_linear-elastic-plate-with-hole - path: | - benchmarks/linear-elastic-plate-with-hole/nextflow_results/ - process-artifacts: runs-on: ubuntu-latest needs: run-simulation diff --git a/benchmarks/common/nextflow.config b/benchmarks/common/nextflow.config deleted file mode 100644 index 36401ae..0000000 --- a/benchmarks/common/nextflow.config +++ /dev/null @@ -1,27 +0,0 @@ -conda { - enabled = true -} - -params.result_dir = "nextflow_results/${params.benchmark}" - -prov { - formats { - dag { - file = "${params.result_dir}/nf_prov_dag.html" - overwrite = true - } - legacy { - file = "${params.result_dir}/nf_prov_legacy.json" - overwrite = true - } - wrroc { - agent { - name = 'Firstname Lastname' - orcid = 'https://orcid.org/0000-0000-0000-0000' - } - file = "${params.result_dir}/ro-crate-metadata.json" - license = 'https://spdx.org/licenses/MIT' - overwrite = true - } - } -} diff --git a/benchmarks/linear-elastic-plate-with-hole/fenics/fenics.nf b/benchmarks/linear-elastic-plate-with-hole/fenics/fenics.nf deleted file mode 100644 index a655fe8..0000000 --- a/benchmarks/linear-elastic-plate-with-hole/fenics/fenics.nf +++ /dev/null @@ -1,35 +0,0 @@ -params.tool = "fenics" - -process run_simulation { - publishDir "${params.result_dir}/${params.tool}/" - conda './fenics/environment_simulation.yml' - - input: - path python_script - tuple val(configuration), path(parameter_file), path(mesh_file) - - - output: - tuple val(configuration), path("solution_field_data_${configuration}.zip"), path("solution_metrics_${configuration}.json") - - script: - """ - python3 $python_script --input_parameter_file $parameter_file --input_mesh_file $mesh_file --output_solution_file_zip "solution_field_data_${configuration}.zip" --output_metrics_file "solution_metrics_${configuration}.json" - """ -} - -workflow fenics_workflow { - - take: - mesh_data // tuple(configuration, parameters, mesh) - result_dir - - main: - params.result_dir = result_dir - run_sim_script = Channel.value(file('fenics/run_fenics_simulation.py')) - output_process_run_simulation = run_simulation( run_sim_script, mesh_data ) - - emit: - output_process_run_simulation - -} \ No newline at end of file diff --git a/benchmarks/linear-elastic-plate-with-hole/kratos/kratos.nf b/benchmarks/linear-elastic-plate-with-hole/kratos/kratos.nf deleted file mode 100644 index c5d8b91..0000000 --- a/benchmarks/linear-elastic-plate-with-hole/kratos/kratos.nf +++ /dev/null @@ -1,123 +0,0 @@ -params.tool = "kratos" - -process mesh_to_mdpa { - publishDir "${params.result_dir}/${params.tool}/" - conda './kratos/environment_simulation.yml' - - input: - path python_script - tuple val(configuration), path(parameter_file), path(mesh_file) - - output: - tuple val(configuration), path("mesh_${configuration}.mdpa") - - script: - """ - python3 ${python_script} \ - --input_parameter_file ${parameter_file} \ - --input_mesh_file ${mesh_file} \ - --output_mdpa_file mesh_${configuration}.mdpa - """ -} - -process create_kratos_input_and_run_simulation { - - // The process combines the creation of the Kratos input file (json) and the execution of the simulation. Initially, these were two separate processes. - // The combination was necessary because the create_kratos_input.py specifies the location of the mesh file and the output location of the simulation results in - // the json file. In the case of Nextflow, these locations are related to the process's sub-directory (inside the work directory). Executing the simulation as a - // separate process results in a failure to find the mesh file and write the output files unless the paths (in the json file) are explicitly provided as an input - // to the process. - // This is not an issue in the case of Snakemake, as the working directory doesn't automatically change between different rules. - - publishDir "${params.result_dir}/${params.tool}/" - conda './kratos/environment_simulation.yml' - - input: - path script_create_kratos_input - path script_run_kratos - tuple val(configuration), path(parameters), path(mdpa) - path kratos_input_template - path kratos_material_template - - output: - tuple val(configuration), path("ProjectParameters_${configuration}.json"), path("MaterialParameters_${configuration}.json"), path("${configuration}/Structure_0_1.vtk") - - script: - """ - python3 ${script_create_kratos_input} \ - --input_parameter_file ${parameters} \ - --input_mdpa_file ${mdpa} \ - --input_kratos_input_template ${kratos_input_template} \ - --input_material_template ${kratos_material_template} \ - --output_kratos_inputfile ProjectParameters_${configuration}.json \ - --output_kratos_materialfile MaterialParameters_${configuration}.json - - python3 ${script_run_kratos} \ - --input_parameter_file ${parameters} \ - --input_kratos_inputfile "ProjectParameters_${configuration}.json" \ - --input_kratos_materialfile "MaterialParameters_${configuration}.json" - """ -} - -process postprocess_kratos_results { - publishDir "${params.result_dir}/${params.tool}/" - conda './kratos/environment_simulation.yml' - - input: - path python_script - tuple val(configuration), path(parameter_file), path(result_vtk) - - output: - tuple val(configuration), path("solution_field_data_${configuration}.zip"), path("solution_metrics_${configuration}.json") - - script: - """ - python3 ${python_script} \ - --input_parameter_file ${parameter_file} \ - --input_result_vtk ${result_vtk} \ - --output_solution_file_zip solution_field_data_${configuration}.zip \ - --output_metrics_file solution_metrics_${configuration}.json - """ -} - -workflow kratos_workflow { - take: - mesh_data // tuple(configuration, parameters, mesh) //change the name - result_dir - - main: - params.result_dir = result_dir - - // Define script paths - msh_to_mdpa_script = Channel.value(file('kratos/msh_to_mdpa.py')) - create_input_script = Channel.value(file('kratos/create_kratos_input.py')) - run_sim_script = Channel.value(file('kratos/run_kratos_simulation.py')) - postprocess_script = Channel.value(file('kratos/postprocess_results.py')) - - // Template files - kratos_input_template = Channel.value(file('kratos/input_template.json')) - kratos_material_template = Channel.value(file('kratos/StructuralMaterials_template.json')) - - // Process pipeline - output_process_mesh_to_mdpa = mesh_to_mdpa(msh_to_mdpa_script, mesh_data) - - input_process_create_kratos_input = mesh_data.join(output_process_mesh_to_mdpa).map { tuple(it[0], it[1], it[3]) } - - //input_process_create_kratos_input.view() - output_create_kratos_input_and_run_simulation = create_kratos_input_and_run_simulation( - create_input_script, - run_sim_script, - input_process_create_kratos_input, - kratos_input_template, - kratos_material_template - ) - - input_process_postprocess_kratos_results = mesh_data.join(output_create_kratos_input_and_run_simulation).map { tuple(it[0], it[1], it[5]) } - - - output_process_postprocess_kratos_results = postprocess_kratos_results(postprocess_script,input_process_postprocess_kratos_results) - - emit: - output_process_postprocess_kratos_results -} - diff --git a/benchmarks/linear-elastic-plate-with-hole/main.nf b/benchmarks/linear-elastic-plate-with-hole/main.nf deleted file mode 100644 index e6bf069..0000000 --- a/benchmarks/linear-elastic-plate-with-hole/main.nf +++ /dev/null @@ -1,173 +0,0 @@ - -include { fenics_workflow } from './fenics/fenics.nf' -include { kratos_workflow } from './kratos/kratos.nf' - -process create_mesh { - //publishDir "$result_dir/mesh/" - publishDir "${params.result_dir}/mesh/" - conda 'environment_mesh.yml' - - input: - path python_script - val configuration - path parameter_file - - output: - // val(configuration) works as matching key with the input channel in the workflow - tuple val(configuration), path("mesh_${configuration}.msh") - - script: - """ - python3 $python_script --input_parameter_file $parameter_file --output_mesh_file "mesh_${configuration}.msh" - """ -} - -process summary{ - publishDir "${params.result_dir}/${tool}/" - conda 'environment_postprocessing.yml' - - input: - path python_script - val configuration - val parameter_file - val mesh_file - val solution_metrics - val solution_field_data - val benchmark - val benchmark_uri - val tool - - output: - path("summary.json") - - script: - """ - python3 $python_script \ - --input_configuration ${configuration.join(' ')} \ - --input_parameter_file ${parameter_file.join(' ')} \ - --input_mesh_file ${mesh_file.join(' ')} \ - --input_solution_metrics ${solution_metrics.join(' ')} \ - --input_solution_field_data ${solution_field_data.join(' ')} \ - --input_benchmark ${benchmark} \ - --input_benchmark_uri ${benchmark_uri} \ - --output_summary_json "summary.json" - - """ -} - - -def prepare_inputs_for_process_summary(input_process_run_simulation, output_process_run_simulation) { - - // Input: channels of the input and the output of the simulation process - // Output: a tuple of channels to be used as input for the summary process - // Purpose: To prepare inputs for the summary process (invoked once per simulation tool) from the output of the simulation process (depending on the number of configurations, invoked multiple times per simulation tool). - - // Firstly, the join operation is performed between the input and output channels of the simulation process based on a matching key (configuration). - - // Secondly, the individual components (configuration, parameter_file, mesh_file, solution_field_data, solution_metrics) are extracted from the joined tuples and collected into separate lists. - // The collect() method outputs a channel with a single entry as the summary process runs once per simulation tool. This single entry is a list of all the configurations or parameter files or mesh files etc. - - def matched_channels = input_process_run_simulation.join(output_process_run_simulation) - - def branched_channels = matched_channels.multiMap{ v, w, x, y, z -> - configuration : v - parameter_file : w - mesh : x - solution_field : y - metrics : z } - - return [ - branched_channels.configuration.collect(), - branched_channels.parameter_file.collect(), - branched_channels.mesh.collect(), - branched_channels.solution_field.collect(), - branched_channels.metrics.collect() - ] -} - -workflow { - main: - - def parameter_files_path = [] - params.configurations.each { elem -> - parameter_files_path.add(file(params.configuration_to_parameter_file[elem])) - } - - def ch_parameter_files = Channel.fromList(parameter_files_path) - def ch_configurations = Channel.fromList(params.configurations) - def ch_mesh_python_script = Channel.value(file('create_mesh.py')) - - //Creating Mesh - - output_process_create_mesh = create_mesh(ch_mesh_python_script, ch_configurations, ch_parameter_files) - - input_process_run_simulation = ch_configurations.merge(ch_parameter_files).join(output_process_create_mesh) - - //Running Simulation - - ch_tools = Channel.fromList(params.tools) - - input_process_run_simulation_with_tool = ch_tools.combine(input_process_run_simulation) - input_fenics_workflow = input_process_run_simulation_with_tool.filter{ it[0] == 'fenics' }.map{_w,x,y,z -> tuple(x,y,z)} - input_kratos_workflow = input_process_run_simulation_with_tool.filter{ it[0] == 'kratos' }.map{_w,x,y,z -> tuple(x,y,z)} - - - fenics_workflow(input_fenics_workflow, params.result_dir) - output_fenics_workflow = fenics_workflow.out - def (fenics_configurations,\ - fenics_parameter_files,\ - fenics_meshes,\ - fenics_solution_fields,\ - fenics_summary_metrics) = prepare_inputs_for_process_summary(input_fenics_workflow, output_fenics_workflow) - - - kratos_workflow(input_kratos_workflow, params.result_dir) - output_kratos_workflow = kratos_workflow.out - def (kratos_configurations, \ - kratos_parameter_files, \ - kratos_meshes, \ - kratos_solution_fields, \ - kratos_summary_metrics) = prepare_inputs_for_process_summary(input_kratos_workflow, output_kratos_workflow) - - - // channels are concatenated in the same order as they are passed to the .concat. The order should be consistent with the order of tools in ch_tools. - input_summary_configuration = fenics_configurations.concat(kratos_configurations) - input_summary_parameter_file = fenics_parameter_files.concat(kratos_parameter_files) - input_summary_mesh = fenics_meshes.concat(kratos_meshes) - input_summary_solution_field = fenics_solution_fields.concat(kratos_solution_fields) - input_summary_metrics = fenics_summary_metrics.concat(kratos_summary_metrics) - - //Summarizing results - def ch_benchmark = Channel.value(params.benchmark) - def ch_benchmark_uri = Channel.value(params.benchmark_uri) - def ch_summarize_python_script = Channel.value(file('../common/summarize_results.py')) - summary(ch_summarize_python_script, \ - input_summary_configuration, \ - input_summary_parameter_file, \ - input_summary_mesh, \ - input_summary_metrics, \ - input_summary_solution_field, \ - ch_benchmark, \ - ch_benchmark_uri, \ - ch_tools) - -} -/* -Steps to add a new simulation tool to the workflow: - -1. Write the tool-specific workflow, scripts, environment file and store them in the benchmarks/linear-elastic-plate-with-hole/tool_name/. -2. Add the tool name to "tools" workflow_config.json (generated here using generate_config.py) -3. Include the tool-specific workflow script at the top of this file. -4. Create an input channel for the new tool (e.g. see the definition of input_fenics_workflow) -5. Invoke the new tool-specific workflow (similar to fenics_workflow) & using its output, prepare inputs for the summary process. -6. Concatenate the prepared inputs to form the final input channels for the summary process. - ---------------------------------------------------------------------------------------------------------------------------------- - -Remark: Care should be taken to track the entries in the I/O channels, as the process output for a given configuration -may not arrive in the same order as the inputs were sent. When reusing channel entries after process execution, outputs should -be matched with their corresponding inputs using a common key. - -Information on channel operations: https://www.nextflow.io/docs/latest/reference/operator.html -Information on channels: https://training.nextflow.io/2.2/basic_training/channels/ -*/ \ No newline at end of file