xref: /honee/src/honee-meshtransform.c (revision 00359db47665a79ecb0241f6ccbf886b649022df)
1*7e3656bdSJames Wright // SPDX-FileCopyrightText: Copyright (c) 2017-2025, HONEE contributors.
2*7e3656bdSJames Wright // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause
3*7e3656bdSJames Wright 
4*7e3656bdSJames Wright #include <honee.h>
5*7e3656bdSJames Wright #include <petscdmplex.h>
6*7e3656bdSJames Wright 
7*7e3656bdSJames Wright #ifndef M_PI
8*7e3656bdSJames Wright #define M_PI 3.14159265358979323846
9*7e3656bdSJames Wright #endif
10*7e3656bdSJames Wright 
GetYNodeLocs(const MPI_Comm comm,const char path[PETSC_MAX_PATH_LEN],PetscReal ** pynodes,PetscInt * nynodes)11*7e3656bdSJames Wright static PetscErrorCode GetYNodeLocs(const MPI_Comm comm, const char path[PETSC_MAX_PATH_LEN], PetscReal **pynodes, PetscInt *nynodes) {
12*7e3656bdSJames Wright   int            ndims, dims[2] = {0.};
13*7e3656bdSJames Wright   FILE          *fp;
14*7e3656bdSJames Wright   const PetscInt char_array_len = 512;
15*7e3656bdSJames Wright   char           line[char_array_len];
16*7e3656bdSJames Wright   PetscReal     *node_locs;
17*7e3656bdSJames Wright 
18*7e3656bdSJames Wright   PetscFunctionBeginUser;
19*7e3656bdSJames Wright   PetscCall(PetscFOpen(comm, path, "r", &fp));
20*7e3656bdSJames Wright   PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line));
21*7e3656bdSJames Wright   {
22*7e3656bdSJames Wright     char **array;
23*7e3656bdSJames Wright 
24*7e3656bdSJames Wright     PetscCall(PetscStrToArray(line, ' ', &ndims, &array));
25*7e3656bdSJames Wright     for (PetscInt i = 0; i < ndims; i++) dims[i] = atoi(array[i]);
26*7e3656bdSJames Wright     PetscCall(PetscStrToArrayDestroy(ndims, array));
27*7e3656bdSJames Wright   }
28*7e3656bdSJames Wright   if (ndims < 2) dims[1] = 1;  // Assume 1 column of data is not otherwise specified
29*7e3656bdSJames Wright   *nynodes = dims[0];
30*7e3656bdSJames Wright   PetscCall(PetscMalloc1(*nynodes, &node_locs));
31*7e3656bdSJames Wright 
32*7e3656bdSJames Wright   for (PetscInt i = 0; i < dims[0]; i++) {
33*7e3656bdSJames Wright     char **array;
34*7e3656bdSJames Wright 
35*7e3656bdSJames Wright     PetscCall(PetscSynchronizedFGets(comm, fp, char_array_len, line));
36*7e3656bdSJames Wright     PetscCall(PetscStrToArray(line, ' ', &ndims, &array));
37*7e3656bdSJames Wright     PetscCheck(ndims == dims[1], comm, PETSC_ERR_FILE_UNEXPECTED,
38*7e3656bdSJames Wright                "Line %" PetscInt_FMT " of %s does not contain correct number of columns (%d instead of %d)", i, path, ndims, dims[1]);
39*7e3656bdSJames Wright 
40*7e3656bdSJames Wright     node_locs[i] = (PetscReal)atof(array[0]);
41*7e3656bdSJames Wright     PetscCall(PetscStrToArrayDestroy(ndims, array));
42*7e3656bdSJames Wright   }
43*7e3656bdSJames Wright   PetscCall(PetscFClose(comm, fp));
44*7e3656bdSJames Wright   *pynodes = node_locs;
45*7e3656bdSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
46*7e3656bdSJames Wright }
47*7e3656bdSJames Wright 
48*7e3656bdSJames Wright /* @brief Modify the domain and mesh for blasius
49*7e3656bdSJames Wright  *
50*7e3656bdSJames Wright  * Modifies mesh such that `N` elements are within `refine_height` with a geometric growth ratio of `growth`. Excess elements are then distributed
51*7e3656bdSJames Wright  * linearly in logspace to the top surface.
52*7e3656bdSJames Wright  *
53*7e3656bdSJames Wright  * The top surface is also angled downwards, so that it may be used as an outflow.
54*7e3656bdSJames Wright  * It's angle is controlled by `top_angle` (in units of degrees).
55*7e3656bdSJames Wright  *
56*7e3656bdSJames Wright  * If `node_locs` is not NULL, then the nodes will be placed at `node_locs` locations.
57*7e3656bdSJames Wright  * If it is NULL, then the modified coordinate values will be set in the array, along with `num_node_locs`.
58*7e3656bdSJames Wright  */
HoneeMeshTransform_PlateMesh(MPI_Comm comm,DM dm,PetscReal growth,PetscInt N,PetscReal refine_height,PetscReal top_angle,PetscReal * node_locs[],PetscInt * num_node_locs)59*7e3656bdSJames Wright static PetscErrorCode HoneeMeshTransform_PlateMesh(MPI_Comm comm, DM dm, PetscReal growth, PetscInt N, PetscReal refine_height, PetscReal top_angle,
60*7e3656bdSJames Wright                                                    PetscReal *node_locs[], PetscInt *num_node_locs) {
61*7e3656bdSJames Wright   PetscInt     narr, ncoords, dim;
62*7e3656bdSJames Wright   PetscReal    domain_min[3], domain_max[3], domain_size[3];
63*7e3656bdSJames Wright   PetscScalar *arr_coords;
64*7e3656bdSJames Wright   Vec          vec_coords;
65*7e3656bdSJames Wright 
66*7e3656bdSJames Wright   PetscFunctionBeginUser;
67*7e3656bdSJames Wright   PetscCall(DMGetDimension(dm, &dim));
68*7e3656bdSJames Wright   PetscReal angle_coeff = tan(top_angle * (M_PI / 180));
69*7e3656bdSJames Wright   // Get domain boundary information
70*7e3656bdSJames Wright   PetscCall(DMGetBoundingBox(dm, domain_min, domain_max));
71*7e3656bdSJames Wright   for (PetscInt i = 0; i < 3; i++) domain_size[i] = domain_max[i] - domain_min[i];
72*7e3656bdSJames Wright 
73*7e3656bdSJames Wright   // Get coords array from DM
74*7e3656bdSJames Wright   PetscCall(DMGetCoordinatesLocal(dm, &vec_coords));
75*7e3656bdSJames Wright   PetscCall(VecGetLocalSize(vec_coords, &narr));
76*7e3656bdSJames Wright   PetscCall(VecGetArray(vec_coords, &arr_coords));
77*7e3656bdSJames Wright 
78*7e3656bdSJames Wright   PetscScalar(*coords)[dim] = (PetscScalar(*)[dim])arr_coords;
79*7e3656bdSJames Wright   ncoords                   = narr / dim;
80*7e3656bdSJames Wright 
81*7e3656bdSJames Wright   // Get mesh information
82*7e3656bdSJames Wright   PetscInt nmax = 3, faces[3];
83*7e3656bdSJames Wright   PetscCall(PetscOptionsGetIntArray(NULL, NULL, "-dm_plex_box_faces", faces, &nmax, NULL));
84*7e3656bdSJames Wright   // Get element size of the box mesh, for indexing each node
85*7e3656bdSJames Wright   const PetscReal dybox = domain_size[1] / faces[1];
86*7e3656bdSJames Wright 
87*7e3656bdSJames Wright   if (!*node_locs) {
88*7e3656bdSJames Wright     // Calculate the first element height
89*7e3656bdSJames Wright     PetscReal dy1 = refine_height * (growth - 1) / (pow(growth, N) - 1);
90*7e3656bdSJames Wright 
91*7e3656bdSJames Wright     // Calculate log of sizing outside BL
92*7e3656bdSJames Wright     PetscReal logdy = (log(domain_max[1]) - log(refine_height)) / (faces[1] - N);
93*7e3656bdSJames Wright 
94*7e3656bdSJames Wright     *num_node_locs = faces[1] + 1;
95*7e3656bdSJames Wright     PetscReal *temp_node_locs;
96*7e3656bdSJames Wright     PetscCall(PetscMalloc1(*num_node_locs, &temp_node_locs));
97*7e3656bdSJames Wright 
98*7e3656bdSJames Wright     for (PetscInt i = 0; i < ncoords; i++) {
99*7e3656bdSJames Wright       PetscInt y_box_index = round(coords[i][1] / dybox);
100*7e3656bdSJames Wright       if (y_box_index <= N) {
101*7e3656bdSJames Wright         coords[i][1] =
102*7e3656bdSJames Wright             (1 - (coords[i][0] - domain_min[0]) * angle_coeff / domain_max[1]) * dy1 * (pow(growth, coords[i][1] / dybox) - 1) / (growth - 1);
103*7e3656bdSJames Wright       } else {
104*7e3656bdSJames Wright         PetscInt j   = y_box_index - N;
105*7e3656bdSJames Wright         coords[i][1] = (1 - (coords[i][0] - domain_min[0]) * angle_coeff / domain_max[1]) * exp(log(refine_height) + logdy * j);
106*7e3656bdSJames Wright       }
107*7e3656bdSJames Wright       if (coords[i][0] == domain_min[0] && coords[i][2] == domain_min[2]) temp_node_locs[y_box_index] = coords[i][1];
108*7e3656bdSJames Wright     }
109*7e3656bdSJames Wright 
110*7e3656bdSJames Wright     *node_locs = temp_node_locs;
111*7e3656bdSJames Wright   } else {
112*7e3656bdSJames Wright     PetscCheck(*num_node_locs >= faces[1] + 1, comm, PETSC_ERR_FILE_UNEXPECTED,
113*7e3656bdSJames Wright                "The y_node_locs_path has too few locations; There are %" PetscInt_FMT " + 1 nodes, but only %" PetscInt_FMT " locations given",
114*7e3656bdSJames Wright                faces[1] + 1, *num_node_locs);
115*7e3656bdSJames Wright     if (*num_node_locs > faces[1] + 1) {
116*7e3656bdSJames Wright       PetscCall(PetscPrintf(comm,
117*7e3656bdSJames Wright                             "WARNING: y_node_locs_path has more locations (%" PetscInt_FMT ") "
118*7e3656bdSJames Wright                             "than the mesh has nodes (%" PetscInt_FMT "). This maybe unintended.\n",
119*7e3656bdSJames Wright                             *num_node_locs, faces[1] + 1));
120*7e3656bdSJames Wright     }
121*7e3656bdSJames Wright     PetscScalar max_y = (*node_locs)[faces[1]];
122*7e3656bdSJames Wright 
123*7e3656bdSJames Wright     for (PetscInt i = 0; i < ncoords; i++) {
124*7e3656bdSJames Wright       // Determine which y-node we're at
125*7e3656bdSJames Wright       PetscInt y_box_index = round(coords[i][1] / dybox);
126*7e3656bdSJames Wright       coords[i][1]         = (1 - (coords[i][0] - domain_min[0]) * angle_coeff / max_y) * (*node_locs)[y_box_index];
127*7e3656bdSJames Wright     }
128*7e3656bdSJames Wright   }
129*7e3656bdSJames Wright 
130*7e3656bdSJames Wright   PetscCall(VecRestoreArray(vec_coords, &arr_coords));
131*7e3656bdSJames Wright   PetscCall(DMSetCoordinatesLocal(dm, vec_coords));
132*7e3656bdSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
133*7e3656bdSJames Wright }
134*7e3656bdSJames Wright 
135*7e3656bdSJames Wright /**
136*7e3656bdSJames Wright   @brief Transform the mesh based on pass options
137*7e3656bdSJames Wright 
138*7e3656bdSJames Wright   Use the option `-mesh_transform` to determine the type of mesh transformation to apply
139*7e3656bdSJames Wright 
140*7e3656bdSJames Wright   @param dm `DM` of mesh to be transformed
141*7e3656bdSJames Wright **/
HoneeMeshTransformFromOptions(DM dm)142*7e3656bdSJames Wright PetscErrorCode HoneeMeshTransformFromOptions(DM dm) {
143*7e3656bdSJames Wright   MPI_Comm comm = PetscObjectComm((PetscObject)dm);
144*7e3656bdSJames Wright 
145*7e3656bdSJames Wright   PetscFunctionBeginUser;
146*7e3656bdSJames Wright   PetscOptionsBegin(comm, NULL, "Options for HONEE Mesh Transformation", NULL);
147*7e3656bdSJames Wright   MeshTransform mesh_transform_type = MESH_TRANSFORM_NONE;
148*7e3656bdSJames Wright   PetscCall(PetscOptionsDeprecated("-mesh_transform", "-meshtransform_type", "HONEE 0.0", NULL));
149*7e3656bdSJames Wright   PetscCall(PetscOptionsEnum("-meshtransform_type", "Mesh transform to perform", NULL, MeshTransforms, (PetscEnum)mesh_transform_type,
150*7e3656bdSJames Wright                              (PetscEnum *)&mesh_transform_type, NULL));
151*7e3656bdSJames Wright 
152*7e3656bdSJames Wright   switch (mesh_transform_type) {
153*7e3656bdSJames Wright     case MESH_TRANSFORM_PLATEMESH: {
154*7e3656bdSJames Wright       PetscReal mesh_refine_height                   = 5.9e-4;  // m
155*7e3656bdSJames Wright       PetscReal mesh_growth                          = 1.08;    // [-]
156*7e3656bdSJames Wright       PetscInt  mesh_Ndelta                          = 45;      // [-]
157*7e3656bdSJames Wright       PetscReal mesh_top_angle                       = 5;       // degrees
158*7e3656bdSJames Wright       char      mesh_ynodes_path[PETSC_MAX_PATH_LEN] = "";
159*7e3656bdSJames Wright 
160*7e3656bdSJames Wright       PetscCall(PetscOptionsDeprecated("-platemesh_Ndelta", "-meshtransform_platemesh_Ndelta", "HONEE 0.0", NULL));
161*7e3656bdSJames Wright       PetscCall(PetscOptionsDeprecated("-platemesh_refine_height", "-meshtransform_platemesh_refine_height", "HONEE 0.0", NULL));
162*7e3656bdSJames Wright       PetscCall(PetscOptionsDeprecated("-platemesh_growth", "-meshtransform_platemesh_growth", "HONEE 0.0", NULL));
163*7e3656bdSJames Wright       PetscCall(PetscOptionsDeprecated("-platemesh_top_angle", "-meshtransform_platemesh_top_angle", "HONEE 0.0", NULL));
164*7e3656bdSJames Wright       PetscCall(PetscOptionsDeprecated("-platemesh_y_node_locs_path", "-meshtransform_platemesh_y_node_locs_path", "HONEE 0.0", NULL));
165*7e3656bdSJames Wright 
166*7e3656bdSJames Wright       PetscCall(PetscOptionsBoundedInt("-meshtransform_platemesh_Ndelta", "Velocity at boundary layer edge", NULL, mesh_Ndelta, &mesh_Ndelta, NULL,
167*7e3656bdSJames Wright                                        1));
168*7e3656bdSJames Wright       PetscCall(PetscOptionsScalar("-meshtransform_platemesh_refine_height", "Height of boundary layer mesh refinement", NULL, mesh_refine_height,
169*7e3656bdSJames Wright                                    &mesh_refine_height, NULL));
170*7e3656bdSJames Wright       PetscCall(PetscOptionsScalar("-meshtransform_platemesh_growth", "Geometric growth rate of boundary layer mesh", NULL, mesh_growth, &mesh_growth,
171*7e3656bdSJames Wright                                    NULL));
172*7e3656bdSJames Wright       PetscCall(PetscOptionsScalar("-meshtransform_platemesh_top_angle", "Geometric top_angle rate of boundary layer mesh", NULL, mesh_top_angle,
173*7e3656bdSJames Wright                                    &mesh_top_angle, NULL));
174*7e3656bdSJames Wright       PetscCall(PetscOptionsString("-meshtransform_platemesh_y_node_locs_path",
175*7e3656bdSJames Wright                                    "Path to file with y node locations. "
176*7e3656bdSJames Wright                                    "If empty, will use the algorithmic mesh warping.",
177*7e3656bdSJames Wright                                    NULL, mesh_ynodes_path, mesh_ynodes_path, sizeof(mesh_ynodes_path), NULL));
178*7e3656bdSJames Wright       PetscReal *mesh_ynodes  = NULL;
179*7e3656bdSJames Wright       PetscInt   mesh_nynodes = 0;
180*7e3656bdSJames Wright       if (strcmp(mesh_ynodes_path, "")) PetscCall(GetYNodeLocs(comm, mesh_ynodes_path, &mesh_ynodes, &mesh_nynodes));
181*7e3656bdSJames Wright       PetscCall(HoneeMeshTransform_PlateMesh(comm, dm, mesh_growth, mesh_Ndelta, mesh_refine_height, mesh_top_angle, &mesh_ynodes, &mesh_nynodes));
182*7e3656bdSJames Wright       PetscCall(PetscFree(mesh_ynodes));
183*7e3656bdSJames Wright     } break;
184*7e3656bdSJames Wright     case MESH_TRANSFORM_NONE:
185*7e3656bdSJames Wright       break;
186*7e3656bdSJames Wright   }
187*7e3656bdSJames Wright   PetscOptionsEnd();
188*7e3656bdSJames Wright 
189*7e3656bdSJames Wright   {  // Scale coordinates based on DM length scale
190*7e3656bdSJames Wright     // We want to make sure to do the scaling *after* the mesh transformation, so units in the transform are physical
191*7e3656bdSJames Wright     Vec       coords;
192*7e3656bdSJames Wright     PetscReal meter;
193*7e3656bdSJames Wright     PetscCall(DMPlexGetScale(dm, PETSC_UNIT_LENGTH, &meter));
194*7e3656bdSJames Wright     PetscCall(DMGetCoordinatesLocal(dm, &coords));
195*7e3656bdSJames Wright     PetscCall(VecScale(coords, meter));
196*7e3656bdSJames Wright     PetscCall(DMSetCoordinatesLocal(dm, coords));
197*7e3656bdSJames Wright   }
198*7e3656bdSJames Wright 
199*7e3656bdSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
200*7e3656bdSJames Wright }
201