-
Notifications
You must be signed in to change notification settings - Fork 68
Feature: Feature mesh deformation #2196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
52dbb3d
afee13d
04959e2
820f1e7
bd3f549
2d3af8f
fa8e214
59a421e
f0b2182
f13f6fb
6a8cafa
1749a48
7214e89
8105450
8b535bb
c11a6cc
3f4fe39
b20b36a
53a09de
c99f5b5
b82fa7e
504e60d
78fd6b2
14a07c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| /* | ||
| This file is part of t8code. | ||
| t8code is a C library to manage a collection (a forest) of multiple | ||
| connected adaptive space-trees of general element classes in parallel. | ||
|
|
||
| Copyright (C) 2026 the developers | ||
|
|
||
| t8code is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU General Public License as published by | ||
| the Free Software Foundation; either version 2 of the License, or | ||
| (at your option) any later version. | ||
|
|
||
| t8code is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU General Public License for more details. | ||
|
|
||
| You should have received a copy of the GNU General Public License | ||
| along with t8code; if not, write to the Free Software Foundation, Inc., | ||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| */ | ||
|
|
||
| /** \file t8_cmesh_mesh_deformation.cxx | ||
| * This file implements an example for CAD-based mesh deformation. | ||
| */ | ||
|
|
||
| #include <t8_cmesh/t8_cmesh.hxx> | ||
| #include <t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_connectivity.hxx> | ||
| #include <t8_cmesh/t8_cmesh_io/t8_cmesh_readmshfile.h> | ||
| #include <t8_schemes/t8_default/t8_default.hxx> | ||
| #if T8CODE_ENABLE_OCC | ||
| #include <t8_cad/t8_cad_handle.hxx> | ||
| #include <t8_cmesh/t8_cmesh_mesh_deformation/t8_cmesh_mesh_deformation.hxx> | ||
| #endif /* T8CODE_ENABLE_OCC */ | ||
| #include <t8_vtk/t8_vtk_writer.h> | ||
| #include <sc_options.h> | ||
|
|
||
| #include <iostream> | ||
| #include <string> | ||
| #include <vector> | ||
| #include <array> | ||
|
|
||
| int | ||
| main ([[maybe_unused]] int argc, [[maybe_unused]] char **argv) | ||
| { | ||
| #if T8CODE_ENABLE_OCC | ||
|
|
||
| char usage[BUFSIZ]; | ||
| /* Brief help message. */ | ||
| int sreturnA = snprintf (usage, BUFSIZ, "Usage:\t%s <OPTIONS>\n\t%s -h\t for a brief overview of all options.", | ||
| basename (argv[0]), basename (argv[0])); | ||
|
|
||
| char help[BUFSIZ]; | ||
| /* Long help message. */ | ||
| int sreturnB = snprintf ( | ||
| help, BUFSIZ, | ||
| "Deform a mesh based on a msh file with the new CAD geometry.\n" | ||
| "Required arguments are the input mesh file, the deformation geometry file, and the mesh dimension.\n\n%s\n", | ||
| usage); | ||
|
|
||
| if (sreturnA > BUFSIZ || sreturnB > BUFSIZ) { | ||
| /* The usage string or help message was truncated */ | ||
| /* Note: gcc >= 7.1 prints a warning if we | ||
| * do not check the return value of snprintf. */ | ||
| t8_debugf ("WARNING: Truncated usage string and help message to '%s' and '%s'\n", usage, help); | ||
| } | ||
|
|
||
| /* | ||
| * Initialization. | ||
| */ | ||
|
|
||
| /* Initialize MPI. This has to happen before we initialize sc or t8code. */ | ||
| int mpiret = sc_MPI_Init (&argc, &argv); | ||
|
|
||
| /* Error check the MPI return value. */ | ||
| SC_CHECK_MPI (mpiret); | ||
|
|
||
| /* Initialize the sc library, has to happen before we initialize t8code. */ | ||
| sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); | ||
|
|
||
| /* Initialize t8code with log level SC_LP_ESSENTIAL. See sc.h for more info on the log levels. */ | ||
| t8_init (SC_LP_ESSENTIAL); | ||
|
|
||
| int helpme = 0; | ||
| const char *msh_file = NULL; | ||
| const char *brep_file = NULL; | ||
sandro-elsweijer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| int dim, level; | ||
|
|
||
| /* Initialize command line argument parser. */ | ||
| sc_options_t *opt = sc_options_new (argv[0]); | ||
| sc_options_add_switch (opt, 'h', "help", &helpme, "Display a short help message."); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should mention, that this feature only works if t8code is linked against occ
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
| sc_options_add_string (opt, 'm', "mshfile", &msh_file, NULL, "File prefix of the input mesh file (without .msh)"); | ||
| sc_options_add_string (opt, 'b', "brepfile", &brep_file, NULL, | ||
| "File prefix of the deformation geometry file (without .brep)"); | ||
| sc_options_add_int (opt, 'd', "dimension", &dim, 0, "Dimension of the mesh (1, 2 or 3)"); | ||
| sc_options_add_int (opt, 'l', "level", &level, 2, "Uniform refinement level for the input mesh. Default: 2"); | ||
|
|
||
| int parsed = sc_options_parse (t8_get_package_id (), SC_LP_ERROR, opt, argc, argv); | ||
|
|
||
| if (helpme) { | ||
| t8_global_productionf ("%s\n", help); | ||
| sc_options_print_usage (t8_get_package_id (), SC_LP_ERROR, opt, NULL); | ||
| } | ||
| else if (msh_file == NULL || brep_file == NULL || dim == 0) { | ||
| t8_global_errorf ("ERROR: Missing required arguments: -m, -b, and -d are mandatory.\n\n"); | ||
| sc_options_print_usage (t8_get_package_id (), SC_LP_ERROR, opt, NULL); | ||
| } | ||
| else if (dim < 1 || dim > 3) { | ||
| t8_global_errorf ("ERROR: Invalid mesh dimension: dim=%d. Dimension must be 1, 2 or 3.\n\n", dim); | ||
| sc_options_print_usage (t8_get_package_id (), SC_LP_ERROR, opt, NULL); | ||
| } | ||
| else if (parsed >= 0) { | ||
|
|
||
| /* We will use MPI_COMM_WORLD as a communicator. */ | ||
| sc_MPI_Comm comm = sc_MPI_COMM_WORLD; | ||
|
|
||
| /* Create cmesh from msh. */ | ||
| t8_cmesh_t cmesh = t8_cmesh_from_msh_file (msh_file, 0, comm, dim, 0, 1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check beforehand if occ is linked, throw an error otherwise
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
| t8_forest_t forest = t8_forest_new_uniform (cmesh, t8_scheme_new_default (), level, 0, comm); | ||
|
|
||
| /* Load CAD geometry from .brep file. */ | ||
| auto cad = std::make_shared<t8_cad_handle> (brep_file); | ||
|
|
||
| /* Initialize the deformation object for the given mesh. */ | ||
| t8_cmesh_mesh_deformation deformation (cmesh); | ||
|
|
||
| /* Calculate displacements. */ | ||
| auto displacements = deformation.calculate_displacement_surface_vertices (cad.get ()); | ||
|
|
||
| /* Write output. */ | ||
| t8_forest_vtk_write_file (forest, "input_forest", 1, 1, 1, 1, 0, 0, NULL); | ||
|
|
||
| /* Apply displacements. */ | ||
| deformation.apply_vertex_displacements (displacements, cad); | ||
|
|
||
| /* Write output. */ | ||
| t8_forest_vtk_write_file (forest, "deformed_forest", 1, 1, 1, 1, 0, 0, NULL); | ||
|
|
||
| /* Cleanup. */ | ||
| t8_forest_unref (&forest); | ||
|
|
||
| t8_global_productionf ("Mesh deformation completed."); | ||
| } | ||
|
|
||
| sc_options_destroy (opt); | ||
|
|
||
| sc_finalize (); | ||
| mpiret = sc_MPI_Finalize (); | ||
| SC_CHECK_MPI (mpiret); | ||
|
|
||
| #else /* T8CODE_ENABLE_OCC */ | ||
| t8_global_errorf ("ERROR: This example requires OpenCASCADE support to be enabled in t8code.\n"); | ||
| #endif /* T8CODE_ENABLE_OCC */ | ||
|
|
||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| /* | ||
| This file is part of t8code. | ||
| t8code is a C library to manage a collection (a forest) of multiple | ||
| connected adaptive space-trees of general element classes in parallel. | ||
|
|
||
| Copyright (C) 2026 the developers | ||
|
|
||
| t8code is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU General Public License as published by | ||
| the Free Software Foundation; either version 2 of the License, or | ||
| (at your option) any later version. | ||
|
|
||
| t8code is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU General Public License for more details. | ||
|
|
||
| You should have received a copy of the GNU General Public License | ||
| along with t8code; if not, write to the Free Software Foundation, Inc., | ||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| */ | ||
|
|
||
| /** \file t8_cmesh_mesh_deformation.cxx | ||
| * This file implements the routines for CAD-based mesh deformation. | ||
| */ | ||
|
|
||
| #include <unordered_map> | ||
| #include <t8_cmesh/t8_cmesh_vertex_connectivity/t8_cmesh_vertex_connectivity.hxx> | ||
| #include <t8_cad/t8_cad_handle.hxx> | ||
| #include <t8_cmesh/t8_cmesh.h> | ||
| #include <t8_cmesh/t8_cmesh.hxx> | ||
| #include <t8_cmesh/t8_cmesh_mesh_deformation/t8_cmesh_mesh_deformation.hxx> | ||
| #include <t8_geometry/t8_geometry_handler.hxx> | ||
| #include <t8_geometry/t8_geometry_implementations/t8_geometry_cad.hxx> | ||
|
|
||
| std::unordered_map<t8_gloidx_t, t8_3D_vec> | ||
| t8_cmesh_mesh_deformation::calculate_displacement_surface_vertices (const t8_cad_handle *cad) | ||
| { | ||
| T8_ASSERT (t8_cmesh_is_committed (associated_cmesh)); | ||
|
|
||
| const int mesh_dimension = t8_cmesh_get_dimension (associated_cmesh); | ||
|
|
||
| /* Map from global vertex id -> displacement vector. */ | ||
| std::unordered_map<t8_gloidx_t, t8_3D_vec> displacements; | ||
|
|
||
| for (const auto &global_vertex : *(associated_cmesh->vertex_connectivity)) { | ||
|
|
||
| /* Get the list of all trees associated with the vertex. */ | ||
| const auto &tree_list = global_vertex.second; | ||
| const t8_gloidx_t global_vertex_id = global_vertex.first; | ||
|
|
||
| /* Get the first tree and the local corner index of the vertex in the tree. */ | ||
| const auto &first_tree = tree_list.front (); | ||
| const t8_locidx_t first_tree_id = first_tree.first; | ||
| const int local_corner_index = first_tree.second; | ||
|
|
||
| /* Get the first tree as a reference. */ | ||
| const int *first_tree_geom_attribute = static_cast<const int *> (t8_cmesh_get_attribute ( | ||
| associated_cmesh, t8_get_package_id (), T8_CMESH_NODE_GEOMETRY_ATTRIBUTE_KEY, first_tree_id)); | ||
|
|
||
| /* Check if the geometry attribute is available for this tree. */ | ||
| if (first_tree_geom_attribute == nullptr) { | ||
| t8_errorf ("Error: Geometry attribute missing for tree %d\n.", first_tree_id); | ||
| SC_ABORTF ("Geometry attribute is missing."); | ||
| } | ||
|
|
||
| const int first_tree_entity_dim = first_tree_geom_attribute[2 * tree_list[0].second]; | ||
| const int first_tree_entity_tag = first_tree_geom_attribute[2 * tree_list[0].second + 1]; | ||
|
|
||
| /* Check if all trees sharing this vertex have consistent geometry attributes. */ | ||
| #if T8_ENABLE_DEBUG | ||
| /* Iterate over all trees and compare to the reference tree. */ | ||
| for (const auto &[tree_id, local_corner_index] : tree_list) { | ||
|
|
||
| const int *geom_attribute = static_cast<const int *> ( | ||
| t8_cmesh_get_attribute (associated_cmesh, t8_get_package_id (), T8_CMESH_NODE_GEOMETRY_ATTRIBUTE_KEY, tree_id)); | ||
|
|
||
| const int entity_dim = geom_attribute[2 * local_corner_index]; | ||
| const int entity_tag = geom_attribute[2 * local_corner_index + 1]; | ||
|
|
||
| /* Check if the attribute of the vertex is the same in all trees. */ | ||
| if (!(entity_dim == first_tree_entity_dim && entity_tag == first_tree_entity_tag)) { | ||
| t8_errorf ( | ||
| "Error: Inconsistent entity info for global vertex %li: tree %d: dim=%d tag=%d, expected dim=%d tag=%d\n", | ||
| global_vertex_id, tree_id, entity_dim, entity_tag, first_tree_entity_dim, first_tree_entity_tag); | ||
| SC_ABORTF ("Inconsistency in vertex info.\n"); | ||
| } | ||
| } | ||
| #endif /* T8_ENABLE_DEBUG */ | ||
|
|
||
| /* Check if this vertex is a boundary node. */ | ||
| if (first_tree_entity_dim < mesh_dimension && first_tree_entity_dim >= 0) { | ||
|
|
||
| /* Get the pointer to the array of (u,v)-parameters for the CAD geometry. */ | ||
| const double *uv_attribute = (const double *) t8_cmesh_get_attribute ( | ||
| associated_cmesh, t8_get_package_id (), T8_CMESH_NODE_PARAMETERS_ATTRIBUTE_KEY, first_tree_id); | ||
|
|
||
| /* Check if the (u,v)-parameters are available. */ | ||
| if (uv_attribute == nullptr) { | ||
| t8_errorf ("Error: (u,v)-parameters are missing for tree %d\n.", first_tree_id); | ||
| SC_ABORT ("(u,v)-parameters are missing."); | ||
| } | ||
| /* Get the (u,v)-parameter of the vertex. */ | ||
| const double *uv_parameter = &uv_attribute[2 * local_corner_index]; | ||
|
|
||
| /* Get the pointer to the coordinate array as it was before the deformation. */ | ||
| const double *old_coords = (const double *) t8_cmesh_get_attribute ( | ||
| associated_cmesh, t8_get_package_id (), T8_CMESH_VERTICES_ATTRIBUTE_KEY, first_tree_id); | ||
|
|
||
| /* Check if the coordinates are available. */ | ||
| if (old_coords == nullptr) { | ||
| t8_errorf ("Error: Coordinates attribute missing for tree %d\n.", first_tree_id); | ||
| SC_ABORTF ("Vertex coordinates are missing."); | ||
| } | ||
|
|
||
| gp_Pnt new_coords; | ||
|
|
||
| /* Find the new coordinates of the vertex in the cad file, based on the geometry its lying on. */ | ||
| switch (first_tree_entity_dim) { | ||
| case 0: { | ||
| new_coords = cad->get_cad_point (first_tree_entity_tag); | ||
| break; | ||
| } | ||
| case 1: { | ||
| Handle_Geom_Curve curve = cad->get_cad_curve (first_tree_entity_tag); | ||
| curve->D0 (uv_parameter[0], new_coords); | ||
| break; | ||
| } | ||
| case 2: { | ||
| Handle_Geom_Surface surface = cad->get_cad_surface (first_tree_entity_tag); | ||
| surface->D0 (uv_parameter[0], uv_parameter[1], new_coords); | ||
| break; | ||
| } | ||
| default: | ||
| SC_ABORT_NOT_REACHED (); | ||
| } | ||
|
|
||
| /* Get the old coordinates before the deformation. */ | ||
| const double old_x = old_coords[3 * local_corner_index + 0]; | ||
| const double old_y = old_coords[3 * local_corner_index + 1]; | ||
| const double old_z = old_coords[3 * local_corner_index + 2]; | ||
|
|
||
| /* Calculate the displacement of the vertex which should be then done in the deformation. */ | ||
| displacements[global_vertex_id] = { new_coords.X () - old_x, new_coords.Y () - old_y, new_coords.Z () - old_z }; | ||
| } | ||
| } | ||
| return displacements; | ||
| } | ||
|
|
||
| void | ||
| t8_cmesh_mesh_deformation::apply_vertex_displacements (const std::unordered_map<t8_gloidx_t, t8_3D_vec> &displacements, | ||
| std::shared_ptr<t8_cad_handle> cad) | ||
| { | ||
| T8_ASSERT (t8_cmesh_is_committed (associated_cmesh)); | ||
|
|
||
| /* Iterate over all vertices in the displacement map. */ | ||
| for (const auto &[global_vertex, displacement] : displacements) { | ||
|
|
||
| /* Get the list of trees where this vertex exists. */ | ||
| const auto &tree_list = associated_cmesh->vertex_connectivity->get_tree_list_of_vertex (global_vertex); | ||
|
|
||
| /* Update the vertex coordinates in each tree. */ | ||
| for (const auto &[tree_id, local_vertex_index] : tree_list) { | ||
|
|
||
| /* Get the vertex coordinates of the current tree. */ | ||
| double *tree_vertex_coords = (double *) t8_cmesh_get_attribute (associated_cmesh, t8_get_package_id (), | ||
| T8_CMESH_VERTICES_ATTRIBUTE_KEY, tree_id); | ||
|
|
||
| /* Check if the coordinates are available. */ | ||
| if (tree_vertex_coords != nullptr) { | ||
| /* Update the coordinates of the vertex. */ | ||
| for (int coord_index = 0; coord_index < 3; ++coord_index) { | ||
| tree_vertex_coords[3 * local_vertex_index + coord_index] += displacement[coord_index]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /* Update the cad geometry. */ | ||
| t8_geometry_handler *geometry_handler = associated_cmesh->geometry_handler; | ||
| T8_ASSERT (geometry_handler != nullptr); | ||
|
|
||
| for (auto geom = geometry_handler->begin (); geom != geometry_handler->end (); ++geom) { | ||
| if (geom->second->t8_geom_get_type () == T8_GEOMETRY_TYPE_CAD) { | ||
| t8_geometry_cad *cad_geom = static_cast<t8_geometry_cad *> (geom->second.get ()); | ||
| cad_geom->update_cad_handle (cad); | ||
| break; | ||
| } | ||
| } | ||
sandro-elsweijer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.