LCOV - code coverage report
Current view: top level - src/transfers - NekMeshDeformation.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: be601f Lines: 135 142 95.1 %
Date: 2025-07-15 20:50:38 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /********************************************************************/
       2             : /*                  SOFTWARE COPYRIGHT NOTIFICATION                 */
       3             : /*                             Cardinal                             */
       4             : /*                                                                  */
       5             : /*                  (c) 2021 UChicago Argonne, LLC                  */
       6             : /*                        ALL RIGHTS RESERVED                       */
       7             : /*                                                                  */
       8             : /*                 Prepared by UChicago Argonne, LLC                */
       9             : /*               Under Contract No. DE-AC02-06CH11357               */
      10             : /*                With the U. S. Department of Energy               */
      11             : /*                                                                  */
      12             : /*             Prepared by Battelle Energy Alliance, LLC            */
      13             : /*               Under Contract No. DE-AC07-05ID14517               */
      14             : /*                With the U. S. Department of Energy               */
      15             : /*                                                                  */
      16             : /*                 See LICENSE for full restrictions                */
      17             : /********************************************************************/
      18             : 
      19             : #ifdef ENABLE_NEK_COUPLING
      20             : 
      21             : #include "NekMeshDeformation.h"
      22             : #include "DisplacedProblem.h"
      23             : 
      24             : registerMooseObject("CardinalApp", NekMeshDeformation);
      25             : 
      26             : extern nekrs::usrwrkIndices indices;
      27             : 
      28             : InputParameters
      29          86 : NekMeshDeformation::validParams()
      30             : {
      31          86 :   auto params = FieldTransferBase::validParams();
      32          86 :   params.addClassDescription("Reads/writes mesh deformation between NekRS and MOOSE.");
      33          86 :   return params;
      34           0 : }
      35             : 
      36          45 : NekMeshDeformation::NekMeshDeformation(const InputParameters & parameters)
      37          45 :   : FieldTransferBase(parameters)
      38             : {
      39          45 :   if (_direction == "to_nek" /* && nekrs::hasBlendingSolver() */)
      40             :   {
      41          45 :     if (_usrwrk_slot.size() != 3)
      42           0 :       paramError("usrwrk_slot", "For mesh deformation, 'usrwrk_slot' must be of length 3");
      43             : 
      44          45 :     addExternalVariable(_usrwrk_slot[0], _variable + "_x");
      45          45 :     addExternalVariable(_usrwrk_slot[1], _variable + "_y");
      46          90 :     addExternalVariable(_usrwrk_slot[2], _variable + "_z");
      47             : 
      48          45 :     indices.mesh_velocity_x = _usrwrk_slot[0] * nekrs::fieldOffset();
      49          45 :     indices.mesh_velocity_y = _usrwrk_slot[1] * nekrs::fieldOffset();
      50          45 :     indices.mesh_velocity_z = _usrwrk_slot[2] * nekrs::fieldOffset();
      51             :   }
      52             :   else
      53             :   {
      54             :     // technically, we do not need usrwrk_slot when not using the blending mesh solver,
      55             :     // since we directly apply the displacements to the entire mesh volume. However,
      56             :     // the base class is set up to require usrwrk_slot whenever we have displacements
      57             :     // being applied. Because the mesh deformation is not a widely used feature yet,
      58             :     // we ignore this annoyance. If someone wants to fix in future with a redesign
      59             :     // they are welcome to.
      60             :     // checkUnusedParam(parameters, "usrwrk_slot", "not using the blending mesh solver");
      61             :   }
      62             : 
      63          45 :   if (_direction == "from_nek")
      64           0 :     paramError("direction",
      65             :                "The NekMeshDeformation currently only supports transfers 'to_nek'; contact the "
      66             :                "Cardinal developer team if you require reading of NekRS mesh coordinates.");
      67             : 
      68          45 :   if (_app.actionWarehouse().displacedMesh() && !nekrs::hasMovingMesh())
      69           1 :     mooseWarning(
      70           1 :         "Your NekRSMesh has 'displacements', but '" + _nek_problem.casename() +
      71             :         ".par' does not have a\n"
      72             :         "solver in the [MESH] block! The displacements transferred to NekRS will be unused.");
      73             : 
      74          44 :   if (nekrs::hasMovingMesh() && _nek_mesh->exactMirror())
      75           0 :     mooseError("An exact mesh mirror is not yet implemented for the boundary mesh solver.");
      76             : 
      77          44 :   int n_per_surf = _nek_mesh->exactMirror() ? std::pow(_nek_mesh->nekNumQuadraturePoints1D(), 2.0)
      78          44 :                                             : _nek_mesh->numVerticesPerSurface();
      79          44 :   int n_per_vol = _nek_mesh->exactMirror() ? std::pow(_nek_mesh->nekNumQuadraturePoints1D(), 3.0)
      80          44 :                                            : _nek_mesh->numVerticesPerVolume();
      81             : 
      82          44 :   if (nekrs::hasMovingMesh())
      83             :   {
      84          44 :     int n_entries = _nek_mesh->volume() ? n_per_vol : n_per_surf;
      85          44 :     _displacement_x = (double *)calloc(n_entries, sizeof(double));
      86          44 :     _displacement_y = (double *)calloc(n_entries, sizeof(double));
      87          44 :     _displacement_z = (double *)calloc(n_entries, sizeof(double));
      88             : 
      89          44 :     if (nekrs::hasBlendingSolver())
      90          18 :       _mesh_velocity_elem = (double *)calloc(n_entries, sizeof(double));
      91             :   }
      92             : 
      93          44 :   auto boundary = _nek_mesh->boundary();
      94          44 :   if (!boundary && nekrs::hasBlendingSolver())
      95           2 :     mooseError("'" + _nek_problem.casename() +
      96             :                ".par' has a solver in the [MESH] block. This solver uses\n"
      97             :                "boundary displacement values from MOOSE to move the NekRS mesh. Please indicate\n"
      98             :                "the 'boundary' for which mesh motion is coupled from MOOSE to NekRS.");
      99             : 
     100          43 :   if (nekrs::hasBlendingSolver())
     101             :   {
     102             :     bool has_one_mv_bc = false;
     103          18 :     for (const auto & b : *boundary)
     104             :     {
     105          17 :       if (nekrs::isMovingMeshBoundary(b))
     106             :       {
     107             :         has_one_mv_bc = true;
     108             :         break;
     109             :       }
     110             :     }
     111             : 
     112          17 :     if (!has_one_mv_bc)
     113           1 :       mooseError("For boundary-coupled moving mesh problems, you need at least one "
     114           1 :                  "boundary in '" +
     115           1 :                  _nek_problem.casename() +
     116             :                  ".par'\nto be of the type 'codedFixedValue'"
     117             :                  " in the [MESH] block.");
     118             :   }
     119             : 
     120          42 :   if (!_nek_mesh->volume() && nekrs::hasUserMeshSolver())
     121           1 :     mooseError(
     122           1 :         "'" + _nek_problem.casename() +
     123             :         ".par' has 'solver = user' in the [MESH] block. With this solver,\n"
     124             :         "displacement values are sent to every GLL point in NekRS's volume. If you only are "
     125             :         "building\n"
     126             :         "a boundary mesh mirror, it's possible that some displacement values could result\n"
     127             :         "in negative Jacobians if a sideset moves beyond the bounds of an undeformed element.\n"
     128             :         "To eliminate this possibility, please enable 'volume = true' for NekRSMesh and send a\n"
     129             :         "whole-domain displacement to NekRS.");
     130             : 
     131          41 :   if (nekrs::hasBlendingSolver())
     132          16 :     _nek_mesh->initializePreviousDisplacements();
     133             : 
     134          41 :   if (nekrs::hasUserMeshSolver())
     135          25 :     _nek_mesh->saveInitialVolMesh();
     136          41 : }
     137             : 
     138          80 : NekMeshDeformation::~NekMeshDeformation()
     139             : {
     140          40 :   freePointer(_displacement_x);
     141          40 :   freePointer(_displacement_y);
     142          40 :   freePointer(_displacement_z);
     143          40 :   freePointer(_mesh_velocity_elem);
     144          80 : }
     145             : 
     146             : void
     147         904 : NekMeshDeformation::sendDataToNek()
     148             : {
     149         904 :   if (nekrs::hasUserMeshSolver())
     150         104 :     sendVolumeDeformationToNek();
     151         800 :   else if (nekrs::hasBlendingSolver())
     152         800 :     sendBoundaryDeformationToNek();
     153             :   else
     154           0 :     mooseError("Unhandled mesh solver case in NekMeshDeformation!");
     155             : 
     156         904 :   _nek_problem.getDisplacedProblem()->updateMesh();
     157         904 : }
     158             : 
     159             : void
     160         104 : NekMeshDeformation::sendVolumeDeformationToNek()
     161             : {
     162         104 :   _console << "Sending volume deformation to NekRS" << std::endl;
     163             : 
     164         104 :   auto d = nekrs::nondimensionalDivisor(field::x_displacement);
     165         104 :   auto a = nekrs::nondimensionalAdditive(field::x_displacement);
     166        6760 :   for (unsigned int e = 0; e < _nek_mesh->numVolumeElems(); e++)
     167             :   {
     168             :     // We can only write into the nekRS scratch space if that face is "owned" by the current process
     169        6656 :     if (nekrs::commRank() != _nek_mesh->volumeCoupling().processor_id(e))
     170        5632 :       continue;
     171             : 
     172        1024 :     _nek_problem.mapVolumeDataToNekVolume(
     173        1024 :         e, _variable_number[_variable + "_x"], d, a, &_displacement_x);
     174        1024 :     _nek_problem.writeVolumeSolution(
     175        1024 :         e, field::x_displacement, _displacement_x, &(_nek_mesh->nek_initial_x()));
     176             : 
     177        1024 :     _nek_problem.mapVolumeDataToNekVolume(
     178        1024 :         e, _variable_number[_variable + "_y"], d, a, &_displacement_y);
     179        1024 :     _nek_problem.writeVolumeSolution(
     180        1024 :         e, field::y_displacement, _displacement_y, &(_nek_mesh->nek_initial_y()));
     181             : 
     182        1024 :     _nek_problem.mapVolumeDataToNekVolume(
     183        1024 :         e, _variable_number[_variable + "_z"], d, a, &_displacement_z);
     184        1024 :     _nek_problem.writeVolumeSolution(
     185        1024 :         e, field::z_displacement, _displacement_z, &(_nek_mesh->nek_initial_z()));
     186             :   }
     187         104 : }
     188             : 
     189             : void
     190         800 : NekMeshDeformation::sendBoundaryDeformationToNek()
     191             : {
     192         800 :   _console << "Sending boundary deformation to NekRS..." << std::endl;
     193             : 
     194         800 :   auto d = nekrs::nondimensionalDivisor(field::x_displacement);
     195         800 :   auto a = nekrs::nondimensionalAdditive(field::x_displacement);
     196         800 :   if (!_nek_mesh->volume())
     197             :   {
     198      179600 :     for (unsigned int e = 0; e < _nek_mesh->numSurfaceElems(); e++)
     199             :     {
     200             :       // We can only write into the nekRS scratch space if that face is "owned" by the current
     201             :       // process
     202      179200 :       if (nekrs::commRank() != _nek_mesh->boundaryCoupling().processor_id(e))
     203      134400 :         continue;
     204             : 
     205       44800 :       _nek_problem.mapFaceDataToNekFace(
     206       44800 :           e, _variable_number[_variable + "_x"], d, a, &_displacement_x);
     207       44800 :       calculateMeshVelocity(e, field::mesh_velocity_x);
     208       44800 :       _nek_problem.writeBoundarySolution(e, field::mesh_velocity_x, _mesh_velocity_elem);
     209             : 
     210       44800 :       _nek_problem.mapFaceDataToNekFace(
     211       44800 :           e, _variable_number[_variable + "_y"], d, a, &_displacement_y);
     212       44800 :       calculateMeshVelocity(e, field::mesh_velocity_y);
     213       44800 :       _nek_problem.writeBoundarySolution(e, field::mesh_velocity_y, _mesh_velocity_elem);
     214             : 
     215       44800 :       _nek_problem.mapFaceDataToNekFace(
     216       44800 :           e, _variable_number[_variable + "_z"], d, a, &_displacement_z);
     217       44800 :       calculateMeshVelocity(e, field::mesh_velocity_z);
     218       44800 :       _nek_problem.writeBoundarySolution(e, field::mesh_velocity_z, _mesh_velocity_elem);
     219             :     }
     220             :   }
     221             :   else
     222             :   {
     223      538000 :     for (unsigned int e = 0; e < _nek_mesh->numVolumeElems(); ++e)
     224             :     {
     225             :       // We can only write into the nekRS scratch space if that face is "owned" by the current
     226             :       // process
     227      537600 :       if (nekrs::commRank() != _nek_mesh->volumeCoupling().processor_id(e))
     228      403200 :         continue;
     229             : 
     230      134400 :       _nek_problem.mapFaceDataToNekVolume(
     231      134400 :           e, _variable_number[_variable + "_x"], d, a, &_displacement_x);
     232      134400 :       calculateMeshVelocity(e, field::mesh_velocity_x);
     233      134400 :       _nek_problem.writeVolumeSolution(e, field::mesh_velocity_x, _mesh_velocity_elem);
     234             : 
     235      134400 :       _nek_problem.mapFaceDataToNekVolume(
     236      134400 :           e, _variable_number[_variable + "_y"], d, a, &_displacement_y);
     237      134400 :       calculateMeshVelocity(e, field::mesh_velocity_y);
     238      134400 :       _nek_problem.writeVolumeSolution(e, field::mesh_velocity_y, _mesh_velocity_elem);
     239             : 
     240      134400 :       _nek_problem.mapFaceDataToNekVolume(
     241      134400 :           e, _variable_number[_variable + "_z"], d, a, &_displacement_z);
     242      134400 :       calculateMeshVelocity(e, field::mesh_velocity_z);
     243      134400 :       _nek_problem.writeVolumeSolution(e, field::mesh_velocity_z, _mesh_velocity_elem);
     244             :     }
     245             :   }
     246         800 : }
     247             : 
     248             : void
     249      537600 : NekMeshDeformation::calculateMeshVelocity(int e, const field::NekWriteEnum & field)
     250             : {
     251             :   int len =
     252      537600 :       _nek_mesh->volume() ? _nek_mesh->numVerticesPerVolume() : _nek_mesh->numVerticesPerSurface();
     253             : 
     254      537600 :   double dt = _nek_problem.transientExecutioner()->getTimeStepper()->getCurrentDT();
     255             : 
     256             :   double *displacement = nullptr, *prev_disp = nullptr;
     257             :   field::NekWriteEnum disp_field;
     258             : 
     259      537600 :   switch (field)
     260             :   {
     261      179200 :     case field::mesh_velocity_x:
     262      179200 :       displacement = _displacement_x;
     263             :       prev_disp = _nek_mesh->prev_disp_x().data();
     264             :       disp_field = field::x_displacement;
     265      179200 :       break;
     266      179200 :     case field::mesh_velocity_y:
     267      179200 :       displacement = _displacement_y;
     268             :       prev_disp = _nek_mesh->prev_disp_y().data();
     269             :       disp_field = field::y_displacement;
     270      179200 :       break;
     271      179200 :     case field::mesh_velocity_z:
     272      179200 :       displacement = _displacement_z;
     273             :       prev_disp = _nek_mesh->prev_disp_z().data();
     274             :       disp_field = field::z_displacement;
     275      179200 :       break;
     276           0 :     default:
     277           0 :       mooseError("Unhandled NekWriteEnum in NekRSProblem::calculateMeshVelocity!\n");
     278             :   }
     279             : 
     280      537600 :   auto reference_v = nekrs::nondimensionalDivisor(field::velocity);
     281    12633600 :   for (int i = 0; i < len; i++)
     282    12096000 :     _mesh_velocity_elem[i] = (displacement[i] - prev_disp[(e * len) + i]) / dt / reference_v;
     283             : 
     284      537600 :   _nek_mesh->updateDisplacement(e, displacement, disp_field);
     285      537600 : }
     286             : 
     287             : #endif

Generated by: LCOV version 1.14