From 9bdcccb2d42bb034cc4b891c26ce8146716c8c69 Mon Sep 17 00:00:00 2001 From: Javi Garate Date: Thu, 21 May 2026 16:07:38 +0200 Subject: [PATCH 1/3] Add first script to python caller --- kratos.gid/scripts/Custom/python_writer.py | 40 ++++++++++++++++++++++ kratos.gid/scripts/Menus.tcl | 17 +++++++++ kratos.gid/scripts/Utils.tcl | 15 ++++++++ 3 files changed, 72 insertions(+) create mode 100644 kratos.gid/scripts/Custom/python_writer.py diff --git a/kratos.gid/scripts/Custom/python_writer.py b/kratos.gid/scripts/Custom/python_writer.py new file mode 100644 index 000000000..f4762b79d --- /dev/null +++ b/kratos.gid/scripts/Custom/python_writer.py @@ -0,0 +1,40 @@ +import numpy as np +import tohil +from objarray_numpy_tools import objarray_to_nparray,nparray_to_objarray +from pathlib import Path + +#to create functions and variables for all tcl available ones +tcl=tohil.import_tcl() + + +def my_meshio_write_mesh2(filename): + # Gets the nodes in the GID Mesh + info_nodes=tuple(tcl.GiD_Info('mesh','nodes','-array2')) + node_ids,node_xyzs=info_nodes + #tcl.W(node_xyzs) + + for element_type in ['line','triangle','quadrilateral','tetrahedra','pyramid','prism','hexahedra']: + info_elements=tuple(tcl.GiD_Info('mesh','elements',element_type,'-array2')) + if (len(info_elements)): + elements_data=info_elements[0] + element_type_ret,element_ids_original,connectivities_original,materials=elements_data + element_ids=objarray_to_nparray(element_ids_original) + connectivities=objarray_to_nparray(connectivities_original) + tcl.W(element_type_ret) + tcl.W(element_ids) + + group_names=tcl.GiD_Groups("list") + for group_name in group_names: + group_name=str(group_name) # convert from tohil.tclobj to Python str + # get nodes of the group (returns "" when empty, so convert first then check length) + group_node_ids=objarray_to_nparray(tcl.GiD_EntitiesGroups("get", group_name, "nodes")) + + + # get elements of the group (returns "" when empty, so convert first then check length) + group_element_ids=objarray_to_nparray(tcl.GiD_EntitiesGroups("get", group_name, "elements")) + + return 0 + +# main +def start(filename): + return my_meshio_write_mesh2(filename) \ No newline at end of file diff --git a/kratos.gid/scripts/Menus.tcl b/kratos.gid/scripts/Menus.tcl index a3ae1203f..f9c6c641c 100644 --- a/kratos.gid/scripts/Menus.tcl +++ b/kratos.gid/scripts/Menus.tcl @@ -148,6 +148,11 @@ proc Kratos::ChangeMenus { } { GiDMenu::InsertOption "Kratos" [list "---"] [incr pos] PRE "" "" "" replace = GiDMenu::InsertOption "Kratos" [list "Write calculation files - No run" ] [incr pos] PRE [list Kratos::WriteCalculationFilesEvent] "" "" replace = GiDMenu::InsertOption "Kratos" [list "Run - No write" ] [incr pos] PRE [list Kratos::ForceRun] "" "" replace = + GiDMenu::InsertOption "Kratos" [list "Custom scripts"] [incr pos] PRE "" "" "" replace _ + foreach custom_script [Kratos::GetCustomScripts] { + GiDMenu::InsertOption "Kratos" [list "Custom scripts" "$custom_script"] [incr pos] PRE [list Kratos::ExecuteCustomScript $custom_script] "" "" replace _ + } + # GiDMenu::InsertOption "Kratos" [list "---"] [incr pos] PRE "" "" "" replace = # GiDMenu::InsertOption "Kratos" [list "You are in $fromode" ] [incr pos] PRE [list ] "" "" replace = # GiDMenu::InsertOption "Kratos" [list "Switch to $tomode" ] [incr pos] PRE [list Kratos::SwitchMode] "" "" replace = @@ -187,6 +192,18 @@ proc Kratos::About {} { Splash } +proc Kratos::GetCustomScripts { } { + # Custom scripts are defined in the folder scipts/Custom/*.py, and they are added to the menu with the name of the file (without extension) + variable kratos_private + set custom_scripts [list ] + if {[file exists [file join $kratos_private(Path) scripts Custom]]} { + set files [glob -nocomplain -directory [file join $kratos_private(Path) scripts Custom] *.py] + foreach file $files { + lappend custom_scripts [file rootname [file tail $file]] + } + } + return $custom_scripts +} proc Kratos::Splash { } { variable kratos_private diff --git a/kratos.gid/scripts/Utils.tcl b/kratos.gid/scripts/Utils.tcl index 374c3a9b0..333492f5e 100644 --- a/kratos.gid/scripts/Utils.tcl +++ b/kratos.gid/scripts/Utils.tcl @@ -409,3 +409,18 @@ if { ![GidUtils::IsTkDisabled] } { } } + +proc Kratos::ExecuteCustomScript {script_name} { + variable kratos_private + set script_path [file join $kratos_private(Path) scripts Custom ${script_name}.py] + if {[file exists $script_path]} { + # first version will assume that its for writing meshes in mdpa format + # must be python + # W "Executing custom script: $script_path" + GiD_Python_Source $script_path + GiD_Python_Call ${script_name}.start "[GiD_Info project ModelName].mdpa" + # [GiD_Python_Call gid_meshio.my_meshio_write_mesh2 $filename] + } else { + W "Custom script $script_name not found in path $script_path" + } +} \ No newline at end of file From 0a57e0b36f5e7ef7c08778986a07d410b70d5d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20G=C3=A1rate?= Date: Fri, 29 May 2026 10:04:06 +0200 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- kratos.gid/scripts/Utils.tcl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kratos.gid/scripts/Utils.tcl b/kratos.gid/scripts/Utils.tcl index 333492f5e..bf550385c 100644 --- a/kratos.gid/scripts/Utils.tcl +++ b/kratos.gid/scripts/Utils.tcl @@ -417,8 +417,13 @@ proc Kratos::ExecuteCustomScript {script_name} { # first version will assume that its for writing meshes in mdpa format # must be python # W "Executing custom script: $script_path" - GiD_Python_Source $script_path - GiD_Python_Call ${script_name}.start "[GiD_Info project ModelName].mdpa" + if {[catch {GiD_Python_Source $script_path} err]} { + W "Failed to load custom script $script_name from $script_path: $err" + return + } + if {[catch {GiD_Python_Call ${script_name}.start "[GiD_Info project ModelName].mdpa"} err]} { + W "Failed to execute custom script $script_name: $err" + } # [GiD_Python_Call gid_meshio.my_meshio_write_mesh2 $filename] } else { W "Custom script $script_name not found in path $script_path" From 6e8ae53d54d8ffba768d653884fd7f475e263137 Mon Sep 17 00:00:00 2001 From: Javi Garate Date: Fri, 29 May 2026 12:33:51 +0200 Subject: [PATCH 3/3] improve example --- kratos.gid/scripts/Custom/python_writer.py | 55 ++++++++++++++++++++-- kratos.gid/scripts/Utils.tcl | 14 +++--- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/kratos.gid/scripts/Custom/python_writer.py b/kratos.gid/scripts/Custom/python_writer.py index f4762b79d..b8e37963e 100644 --- a/kratos.gid/scripts/Custom/python_writer.py +++ b/kratos.gid/scripts/Custom/python_writer.py @@ -7,11 +7,47 @@ tcl=tohil.import_tcl() +def _build_output_mdpa_path(gid_folder_path): + gid_folder=Path(gid_folder_path) + + # In GiD, ModelName can arrive without the .gid suffix. + if (not gid_folder.is_dir()) and (gid_folder.suffix.lower() != ".gid"): + gid_folder_with_suffix=Path(f"{gid_folder}.gid") + if gid_folder_with_suffix.is_dir(): + gid_folder=gid_folder_with_suffix + + case_name=gid_folder.stem + return gid_folder / f"{case_name}.mdpa" + + +def _normalize_node_xyzs(node_xyzs_original): + node_xyzs_array=objarray_to_nparray(node_xyzs_original) + flat=np.asarray(node_xyzs_array, dtype=object).reshape(-1) + + # Sometimes tohil returns a single string with all coordinates. + if flat.size == 1 and isinstance(flat[0], str): + tokens=flat[0].replace(",", " ").split() + flat_numeric=np.asarray(tokens, dtype=float) + else: + flat_numeric=np.asarray(flat, dtype=float) + + if flat_numeric.size % 3 != 0: + raise ValueError(f"Invalid flattened coordinates length: {flat_numeric.size}") + + return flat_numeric.reshape((-1, 3)) + + def my_meshio_write_mesh2(filename): + output_mdpa_path=_build_output_mdpa_path(filename) + output_mdpa_path.parent.mkdir(parents=True, exist_ok=True) + # Gets the nodes in the GID Mesh info_nodes=tuple(tcl.GiD_Info('mesh','nodes','-array2')) - node_ids,node_xyzs=info_nodes - #tcl.W(node_xyzs) + node_ids_original,node_xyzs=info_nodes + node_ids=objarray_to_nparray(node_ids_original) + node_xyzs_matrix=_normalize_node_xyzs(node_xyzs) + # tcl.W(node_ids) + # tcl.W(node_xyzs) for element_type in ['line','triangle','quadrilateral','tetrahedra','pyramid','prism','hexahedra']: info_elements=tuple(tcl.GiD_Info('mesh','elements',element_type,'-array2')) @@ -20,8 +56,8 @@ def my_meshio_write_mesh2(filename): element_type_ret,element_ids_original,connectivities_original,materials=elements_data element_ids=objarray_to_nparray(element_ids_original) connectivities=objarray_to_nparray(connectivities_original) - tcl.W(element_type_ret) - tcl.W(element_ids) + # tcl.W(element_type_ret) + # tcl.W(element_ids) group_names=tcl.GiD_Groups("list") for group_name in group_names: @@ -32,9 +68,20 @@ def my_meshio_write_mesh2(filename): # get elements of the group (returns "" when empty, so convert first then check length) group_element_ids=objarray_to_nparray(tcl.GiD_EntitiesGroups("get", group_name, "elements")) + + with open(output_mdpa_path, "w", encoding="utf-8") as mdpa_file: + mdpa_file.write(f"# Nodes count: {len(node_ids)}\n") + mdpa_file.write("Begin Nodes\n") + # GiD returns flattened coords: x1 y1 z1 x2 y2 z2 ... + for node_id, (x,y,z) in zip(node_ids, node_xyzs_matrix): + mdpa_file.write(f" {node_id} {x} {y} {z}\n") + mdpa_file.write("End Nodes\n") + + return 0 # main def start(filename): + # tcl.W("pollo") return my_meshio_write_mesh2(filename) \ No newline at end of file diff --git a/kratos.gid/scripts/Utils.tcl b/kratos.gid/scripts/Utils.tcl index bf550385c..3d09d0483 100644 --- a/kratos.gid/scripts/Utils.tcl +++ b/kratos.gid/scripts/Utils.tcl @@ -410,6 +410,7 @@ if { ![GidUtils::IsTkDisabled] } { } + proc Kratos::ExecuteCustomScript {script_name} { variable kratos_private set script_path [file join $kratos_private(Path) scripts Custom ${script_name}.py] @@ -417,14 +418,11 @@ proc Kratos::ExecuteCustomScript {script_name} { # first version will assume that its for writing meshes in mdpa format # must be python # W "Executing custom script: $script_path" - if {[catch {GiD_Python_Source $script_path} err]} { - W "Failed to load custom script $script_name from $script_path: $err" - return - } - if {[catch {GiD_Python_Call ${script_name}.start "[GiD_Info project ModelName].mdpa"} err]} { - W "Failed to execute custom script $script_name: $err" - } - # [GiD_Python_Call gid_meshio.my_meshio_write_mesh2 $filename] + # GiD_Python_Source $script_path + GiD_Python_Import_File $script_path + GiD_Python_Source $script_path + set res [GiD_Python_Call ${script_name}.start "[GiD_Info project ModelName]"] + W "Custom script $script_name finished with result: $res" } else { W "Custom script $script_name not found in path $script_path" }