xref: /libCEED/examples/fluids/problems/blasius.c (revision 30e9fa817d8b49e10111560e5f5e44c44a518fda)
188626eedSJames Wright // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
288626eedSJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
388626eedSJames Wright //
488626eedSJames Wright // SPDX-License-Identifier: BSD-2-Clause
588626eedSJames Wright //
688626eedSJames Wright // This file is part of CEED:  http://github.com/ceed
788626eedSJames Wright 
888626eedSJames Wright /// @file
988626eedSJames Wright /// Utility functions for setting up Blasius Boundary Layer
1088626eedSJames Wright 
1188626eedSJames Wright #include "../navierstokes.h"
1288626eedSJames Wright #include "../qfunctions/blasius.h"
13ba6664aeSJames Wright #include "stg_shur14.h"
1488626eedSJames Wright 
15d2714514SJames Wright static PetscErrorCode GetYNodeLocs(const MPI_Comm comm,
16d2714514SJames Wright                                    const char path[PETSC_MAX_PATH_LEN], PetscReal **pynodes,
17d2714514SJames Wright                                    PetscInt *nynodes) {
18d2714514SJames Wright   PetscErrorCode ierr;
19d2714514SJames Wright   PetscInt ndims, dims[2];
20d2714514SJames Wright   FILE *fp;
21d2714514SJames Wright   const PetscInt char_array_len = 512;
22d2714514SJames Wright   char line[char_array_len];
23d2714514SJames Wright   char **array;
24d2714514SJames Wright   PetscReal *node_locs;
25d2714514SJames Wright   PetscFunctionBeginUser;
26d2714514SJames Wright 
27d2714514SJames Wright   ierr = PetscFOpen(comm, path, "r", &fp); CHKERRQ(ierr);
28d2714514SJames Wright   ierr = PetscSynchronizedFGets(comm, fp, char_array_len, line); CHKERRQ(ierr);
29d2714514SJames Wright   ierr = PetscStrToArray(line, ' ', &ndims, &array); CHKERRQ(ierr);
30d2714514SJames Wright 
31d2714514SJames Wright   for (PetscInt i=0; i<ndims; i++)  dims[i] = atoi(array[i]);
32d2714514SJames Wright   if (ndims<2) dims[1] = 1; // Assume 1 column of data is not otherwise specified
33d2714514SJames Wright   *nynodes = dims[0];
34d2714514SJames Wright   ierr = PetscMalloc1(*nynodes, &node_locs); CHKERRQ(ierr);
35d2714514SJames Wright 
36d2714514SJames Wright   for (PetscInt i=0; i<dims[0]; i++) {
37d2714514SJames Wright     ierr = PetscSynchronizedFGets(comm, fp, char_array_len, line); CHKERRQ(ierr);
38d2714514SJames Wright     ierr = PetscStrToArray(line, ' ', &ndims, &array); CHKERRQ(ierr);
39d2714514SJames Wright     if (ndims < dims[1]) SETERRQ(comm, -1,
40d2714514SJames Wright                                    "Line %d of %s does not contain enough columns (%d instead of %d)", i,
41d2714514SJames Wright                                    path, ndims, dims[1]);
42d2714514SJames Wright 
43d2714514SJames Wright     node_locs[i] = (PetscReal) atof(array[0]);
44d2714514SJames Wright   }
45d2714514SJames Wright   ierr = PetscFClose(comm, fp); CHKERRQ(ierr);
46d2714514SJames Wright   *pynodes = node_locs;
47d2714514SJames Wright   PetscFunctionReturn(0);
48d2714514SJames Wright }
49d2714514SJames Wright 
5088626eedSJames Wright /* \brief Modify the domain and mesh for blasius
5188626eedSJames Wright  *
52ba6664aeSJames Wright  * Modifies mesh such that `N` elements are within `refine_height` with a
53ba6664aeSJames Wright  * geometric growth ratio of `growth`. Excess elements are then distributed
54ba6664aeSJames Wright  * linearly in logspace to the top surface.
5588626eedSJames Wright  *
5688626eedSJames Wright  * The top surface is also angled downwards, so that it may be used as an
57ba6664aeSJames Wright  * outflow. It's angle is controlled by `top_angle` (in units of degrees).
58d2714514SJames Wright  *
59d2714514SJames Wright  * If `node_locs` is not NULL, then the nodes will be placed at `node_locs`
60d2714514SJames Wright  * locations.
6188626eedSJames Wright  */
62d2714514SJames Wright static PetscErrorCode ModifyMesh(MPI_Comm comm, DM dm, PetscInt dim,
63d2714514SJames Wright                                  PetscReal growth, PetscInt N,
64d2714514SJames Wright                                  PetscReal refine_height, PetscReal top_angle,
65d2714514SJames Wright                                  PetscReal node_locs[], PetscInt num_node_locs) {
6688626eedSJames Wright   PetscInt ierr, narr, ncoords;
6788626eedSJames Wright   PetscReal domain_min[3], domain_max[3], domain_size[3];
6888626eedSJames Wright   PetscScalar *arr_coords;
6988626eedSJames Wright   Vec vec_coords;
7088626eedSJames Wright   PetscFunctionBeginUser;
7188626eedSJames Wright 
7288626eedSJames Wright   PetscReal angle_coeff = tan(top_angle*(M_PI/180));
7388626eedSJames Wright 
7488626eedSJames Wright   // Get domain boundary information
7588626eedSJames Wright   ierr = DMGetBoundingBox(dm, domain_min, domain_max); CHKERRQ(ierr);
76ba6664aeSJames Wright   for (PetscInt i=0; i<3; i++) domain_size[i] = domain_max[i] - domain_min[i];
7788626eedSJames Wright 
7888626eedSJames Wright   // Get coords array from DM
7988626eedSJames Wright   ierr = DMGetCoordinatesLocal(dm, &vec_coords); CHKERRQ(ierr);
8088626eedSJames Wright   ierr = VecGetLocalSize(vec_coords, &narr); CHKERRQ(ierr);
8188626eedSJames Wright   ierr = VecGetArray(vec_coords, &arr_coords); CHKERRQ(ierr);
8288626eedSJames Wright 
8388626eedSJames Wright   PetscScalar (*coords)[dim] = (PetscScalar(*)[dim]) arr_coords;
8488626eedSJames Wright   ncoords = narr/dim;
8588626eedSJames Wright 
8688626eedSJames Wright   // Get mesh information
8788626eedSJames Wright   PetscInt nmax = 3, faces[3];
8888626eedSJames Wright   ierr = PetscOptionsGetIntArray(NULL, NULL, "-dm_plex_box_faces", faces, &nmax,
8988626eedSJames Wright                                  NULL); CHKERRQ(ierr);
90d2714514SJames Wright   // Get element size of the box mesh, for indexing each node
91d2714514SJames Wright   const PetscReal dybox = domain_size[1]/faces[1];
9288626eedSJames Wright 
93d2714514SJames Wright   if (!node_locs) {
9488626eedSJames Wright     // Calculate the first element height
9588626eedSJames Wright     PetscReal dy1   = refine_height*(growth-1)/(pow(growth, N)-1);
9688626eedSJames Wright 
9788626eedSJames Wright     // Calculate log of sizing outside BL
9888626eedSJames Wright     PetscReal logdy = (log(domain_max[1]) - log(refine_height)) / (faces[1] - N);
9988626eedSJames Wright 
100ba6664aeSJames Wright     for (PetscInt i=0; i<ncoords; i++) {
10188626eedSJames Wright       PetscInt y_box_index = round(coords[i][1]/dybox);
10288626eedSJames Wright       if (y_box_index <= N) {
103d2714514SJames Wright         coords[i][1] = (1 - ((coords[i][0] - domain_min[0])/domain_size[0])*angle_coeff)
104d2714514SJames Wright                        * dy1 * (pow(growth, coords[i][1]/dybox)-1)/(growth-1);
10588626eedSJames Wright       } else {
10688626eedSJames Wright         PetscInt j = y_box_index - N;
107d2714514SJames Wright         coords[i][1] = (1 - ((coords[i][0] - domain_min[0])/domain_size[0])*angle_coeff)
108d2714514SJames Wright                        * exp(log(refine_height) + logdy*j);
109d2714514SJames Wright       }
110d2714514SJames Wright     }
111d2714514SJames Wright   } else {
112d2714514SJames Wright     // Error checking
113d2714514SJames Wright     if (num_node_locs < faces[1] +1)
114d2714514SJames Wright       SETERRQ(comm, -1, "The y_node_locs_path has too few locations; "
115d2714514SJames Wright               "There are %d + 1 nodes, but only %d locations given",
116d2714514SJames Wright               faces[1]+1, num_node_locs);
117d2714514SJames Wright     if (num_node_locs > faces[1] +1) {
118d2714514SJames Wright       ierr = PetscPrintf(comm, "WARNING: y_node_locs_path has more locations (%d) "
119d2714514SJames Wright                          "than the mesh has nodes (%d). This maybe unintended.",
120d2714514SJames Wright                          num_node_locs, faces[1]+1); CHKERRQ(ierr);
121d2714514SJames Wright     }
122d2714514SJames Wright 
123d2714514SJames Wright     for (PetscInt i=0; i<ncoords; i++) {
124d2714514SJames Wright       // Determine which y-node we're at
125d2714514SJames Wright       PetscInt y_box_index = round(coords[i][1]/dybox);
126d2714514SJames Wright       coords[i][1] = (1 - ((coords[i][0] - domain_min[0])/domain_size[0])*angle_coeff)
127d2714514SJames Wright                      * node_locs[y_box_index];
12888626eedSJames Wright     }
12988626eedSJames Wright   }
13088626eedSJames Wright 
13188626eedSJames Wright   ierr = VecRestoreArray(vec_coords, &arr_coords); CHKERRQ(ierr);
13288626eedSJames Wright   ierr = DMSetCoordinatesLocal(dm, vec_coords); CHKERRQ(ierr);
13388626eedSJames Wright 
13488626eedSJames Wright   PetscFunctionReturn(0);
13588626eedSJames Wright }
13688626eedSJames Wright 
137a0add3c9SJed Brown PetscErrorCode NS_BLASIUS(ProblemData *problem, DM dm, void *ctx) {
13888626eedSJames Wright 
13988626eedSJames Wright   PetscInt ierr;
14088626eedSJames Wright   User      user    = *(User *)ctx;
14188626eedSJames Wright   MPI_Comm  comm    = PETSC_COMM_WORLD;
142ba6664aeSJames Wright   PetscBool use_stg = PETSC_FALSE;
143841e4c73SJed Brown   BlasiusContext blasius_ctx;
144841e4c73SJed Brown   NewtonianIdealGasContext newtonian_ig_ctx;
145841e4c73SJed Brown   CeedQFunctionContext blasius_context;
146841e4c73SJed Brown 
14788626eedSJames Wright   PetscFunctionBeginUser;
148a0add3c9SJed Brown   ierr = NS_NEWTONIAN_IG(problem, dm, ctx); CHKERRQ(ierr);
149841e4c73SJed Brown   ierr = PetscCalloc1(1, &blasius_ctx); CHKERRQ(ierr);
15088626eedSJames Wright 
15188626eedSJames Wright   // ------------------------------------------------------
15288626eedSJames Wright   //               SET UP Blasius
15388626eedSJames Wright   // ------------------------------------------------------
154841e4c73SJed Brown   CeedQFunctionContextDestroy(&problem->ics.qfunction_context);
15591e5af17SJed Brown   problem->ics.qfunction                       = ICsBlasius;
15691e5af17SJed Brown   problem->ics.qfunction_loc                   = ICsBlasius_loc;
157e334ad8fSJed Brown   problem->apply_inflow_jacobian.qfunction     = Blasius_Inflow_Jacobian;
158e334ad8fSJed Brown   problem->apply_inflow_jacobian.qfunction_loc = Blasius_Inflow_Jacobian_loc;
15988626eedSJames Wright 
16088626eedSJames Wright   CeedScalar Uinf   = 40;          // m/s
16188626eedSJames Wright   CeedScalar delta0 = 4.2e-4;      // m
16288626eedSJames Wright   CeedScalar theta0 = 288.;        // K
16388626eedSJames Wright   CeedScalar P0     = 1.01e5;      // Pa
164871db79fSKenneth E. Jansen   PetscBool  weakT  = PETSC_FALSE; // weak density or temperature
1657b8d3891SJames Wright   PetscReal  mesh_refine_height = 5.9e-4; // m
1667b8d3891SJames Wright   PetscReal  mesh_growth        = 1.08;   // [-]
1677b8d3891SJames Wright   PetscInt   mesh_Ndelta        = 45;     // [-]
1687b8d3891SJames Wright   PetscReal  mesh_top_angle     = 5;      // degrees
169d2714514SJames Wright   char mesh_ynodes_path[PETSC_MAX_PATH_LEN] = "";
17088626eedSJames Wright 
17188626eedSJames Wright   PetscOptionsBegin(comm, NULL, "Options for CHANNEL problem", NULL);
172871db79fSKenneth E. Jansen   ierr = PetscOptionsBool("-weakT", "Change from rho weak to T weak at inflow",
173871db79fSKenneth E. Jansen                           NULL, weakT, &weakT, NULL); CHKERRQ(ierr);
17488626eedSJames Wright   ierr = PetscOptionsScalar("-Uinf", "Velocity at boundary layer edge",
17588626eedSJames Wright                             NULL, Uinf, &Uinf, NULL); CHKERRQ(ierr);
17688626eedSJames Wright   ierr = PetscOptionsScalar("-delta0", "Boundary layer height at inflow",
17788626eedSJames Wright                             NULL, delta0, &delta0, NULL); CHKERRQ(ierr);
17888626eedSJames Wright   ierr = PetscOptionsScalar("-theta0", "Wall temperature",
17988626eedSJames Wright                             NULL, theta0, &theta0, NULL); CHKERRQ(ierr);
18088626eedSJames Wright   ierr = PetscOptionsScalar("-P0", "Pressure at outflow",
18188626eedSJames Wright                             NULL, P0, &P0, NULL); CHKERRQ(ierr);
1827b8d3891SJames Wright   ierr = PetscOptionsBoundedInt("-platemesh_Ndelta",
1837b8d3891SJames Wright                                 "Velocity at boundary layer edge",
1847b8d3891SJames Wright                                 NULL, mesh_Ndelta, &mesh_Ndelta, NULL, 1); CHKERRQ(ierr);
1857b8d3891SJames Wright   ierr = PetscOptionsScalar("-platemesh_refine_height",
18688626eedSJames Wright                             "Height of boundary layer mesh refinement",
1877b8d3891SJames Wright                             NULL, mesh_refine_height, &mesh_refine_height, NULL); CHKERRQ(ierr);
1887b8d3891SJames Wright   ierr = PetscOptionsScalar("-platemesh_growth",
18988626eedSJames Wright                             "Geometric growth rate of boundary layer mesh",
1907b8d3891SJames Wright                             NULL, mesh_growth, &mesh_growth, NULL); CHKERRQ(ierr);
1917b8d3891SJames Wright   ierr = PetscOptionsScalar("-platemesh_top_angle",
19288626eedSJames Wright                             "Geometric top_angle rate of boundary layer mesh",
1937b8d3891SJames Wright                             NULL, mesh_top_angle, &mesh_top_angle, NULL); CHKERRQ(ierr);
194d2714514SJames Wright   ierr = PetscOptionsString("-platemesh_y_node_locs_path",
195d2714514SJames Wright                             "Path to file with y node locations. "
196d2714514SJames Wright                             "If empty, will use the algorithmic mesh warping.", NULL,
197d2714514SJames Wright                             mesh_ynodes_path, mesh_ynodes_path,
198d2714514SJames Wright                             sizeof(mesh_ynodes_path), NULL); CHKERRQ(ierr);
199ba6664aeSJames Wright   ierr = PetscOptionsBool("-stg_use", "Use STG inflow boundary condition",
200ba6664aeSJames Wright                           NULL, use_stg, &use_stg, NULL); CHKERRQ(ierr);
20188626eedSJames Wright   PetscOptionsEnd();
20288626eedSJames Wright 
20388626eedSJames Wright   PetscScalar meter  = user->units->meter;
20488626eedSJames Wright   PetscScalar second = user->units->second;
20588626eedSJames Wright   PetscScalar Kelvin = user->units->Kelvin;
20688626eedSJames Wright   PetscScalar Pascal = user->units->Pascal;
20788626eedSJames Wright 
20888626eedSJames Wright   theta0 *= Kelvin;
20988626eedSJames Wright   P0     *= Pascal;
21088626eedSJames Wright   Uinf   *= meter / second;
21188626eedSJames Wright   delta0 *= meter;
21288626eedSJames Wright 
213d2714514SJames Wright   PetscReal *mesh_ynodes = NULL;
214d2714514SJames Wright   PetscInt  mesh_nynodes = 0;
215d2714514SJames Wright   if (strcmp(mesh_ynodes_path, "")) {
216d2714514SJames Wright     ierr = GetYNodeLocs(comm, mesh_ynodes_path, &mesh_ynodes, &mesh_nynodes);
21788626eedSJames Wright     CHKERRQ(ierr);
218d2714514SJames Wright   }
219d2714514SJames Wright   ierr = ModifyMesh(comm, dm, problem->dim, mesh_growth, mesh_Ndelta,
220d2714514SJames Wright                     mesh_refine_height, mesh_top_angle, mesh_ynodes,
221d2714514SJames Wright                     mesh_nynodes); CHKERRQ(ierr);
22288626eedSJames Wright 
223841e4c73SJed Brown   // Some properties depend on parameters from NewtonianIdealGas
224841e4c73SJed Brown   CeedQFunctionContextGetData(problem->apply_vol_rhs.qfunction_context,
225841e4c73SJed Brown                               CEED_MEM_HOST, &newtonian_ig_ctx);
22688626eedSJames Wright 
227ba6664aeSJames Wright   blasius_ctx->weakT         = weakT;
228841e4c73SJed Brown   blasius_ctx->Uinf          = Uinf;
229841e4c73SJed Brown   blasius_ctx->delta0        = delta0;
230841e4c73SJed Brown   blasius_ctx->theta0        = theta0;
231841e4c73SJed Brown   blasius_ctx->P0            = P0;
232*30e9fa81SJames Wright   newtonian_ig_ctx->P0       = P0;
233841e4c73SJed Brown   blasius_ctx->implicit      = user->phys->implicit;
234841e4c73SJed Brown   blasius_ctx->newtonian_ctx = *newtonian_ig_ctx;
235ba6664aeSJames Wright 
236f1122ed0SJames Wright   {
237f1122ed0SJames Wright     PetscReal domain_min[3];
238f1122ed0SJames Wright     ierr = DMGetBoundingBox(dm, domain_min, NULL); CHKERRQ(ierr);
239f1122ed0SJames Wright     blasius_ctx->x_inflow = domain_min[0];
240f1122ed0SJames Wright   }
241f1122ed0SJames Wright 
242841e4c73SJed Brown   CeedQFunctionContextRestoreData(problem->apply_vol_rhs.qfunction_context,
243841e4c73SJed Brown                                   &newtonian_ig_ctx);
24488626eedSJames Wright 
245841e4c73SJed Brown   CeedQFunctionContextCreate(user->ceed, &blasius_context);
246841e4c73SJed Brown   CeedQFunctionContextSetData(blasius_context, CEED_MEM_HOST,
24788626eedSJames Wright                               CEED_USE_POINTER,
248841e4c73SJed Brown                               sizeof(*blasius_ctx), blasius_ctx);
249841e4c73SJed Brown   CeedQFunctionContextSetDataDestroy(blasius_context, CEED_MEM_HOST,
250841e4c73SJed Brown                                      FreeContextPetsc);
25188626eedSJames Wright 
252841e4c73SJed Brown   problem->ics.qfunction_context = blasius_context;
253841e4c73SJed Brown   CeedQFunctionContextReferenceCopy(blasius_context,
254e334ad8fSJed Brown                                     &problem->apply_inflow_jacobian.qfunction_context);
255ba6664aeSJames Wright   if (use_stg) {
2564e139266SJames Wright     ierr = SetupSTG(comm, dm, problem, user, weakT, theta0, P0, mesh_ynodes,
2574e139266SJames Wright                     mesh_nynodes); CHKERRQ(ierr);
25865dd5cafSJames Wright   } else {
25965dd5cafSJames Wright     problem->apply_inflow.qfunction     = Blasius_Inflow;
26065dd5cafSJames Wright     problem->apply_inflow.qfunction_loc = Blasius_Inflow_loc;
26165dd5cafSJames Wright     CeedQFunctionContextReferenceCopy(blasius_context,
26265dd5cafSJames Wright                                       &problem->apply_inflow.qfunction_context);
263ba6664aeSJames Wright   }
2644e139266SJames Wright   ierr = PetscFree(mesh_ynodes); CHKERRQ(ierr);
26588626eedSJames Wright   PetscFunctionReturn(0);
26688626eedSJames Wright }
267