LCOV - code coverage report
Current view: top level - src/meshgenerators - BSplineCurveGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 126 137 92.0 %
Date: 2026-05-29 20:35:17 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "BSplineCurveGenerator.h"
      12             : #include "CastUniquePointer.h"
      13             : #include "MooseMeshUtils.h"
      14             : #include "BSpline.h"
      15             : #include "SplineUtils.h"
      16             : 
      17             : #include "libmesh/edge_edge2.h"
      18             : #include "libmesh/edge_edge3.h"
      19             : #include "libmesh/edge_edge4.h"
      20             : 
      21             : using namespace libMesh;
      22             : 
      23             : registerMooseObject("MooseApp", BSplineCurveGenerator);
      24             : 
      25             : InputParameters
      26        3632 : BSplineCurveGenerator::validParams()
      27             : {
      28        3632 :   InputParameters params = MeshGenerator::validParams();
      29       14528 :   MooseEnum edge_elem_type("EDGE2 EDGE3 EDGE4", "EDGE2");
      30             : 
      31             :   // Convenience parameters
      32       10896 :   params.addParam<SubdomainID>(
      33        7264 :       "new_subdomain_id", 1, "Subdomain ID to assign to the curve elements");
      34       14528 :   params.addParam<SubdomainName>("new_subdomain_name",
      35             :                                  "Subdomain name to assign to the curve elements");
      36             : 
      37             :   // Geometry parameters
      38       14528 :   params.addParam<Point>("start_point", "Starting (x,y,z) point for curve.");
      39       14528 :   params.addParam<Point>("end_point", "Ending (x,y,z) point for curve.");
      40       14528 :   params.addParam<RealVectorValue>("start_direction", "Direction vector of curve at start point.");
      41       14528 :   params.addParam<RealVectorValue>("end_direction", "Direction vector of curve at end point.");
      42       14528 :   params.addParamNamesToGroup("start_point end_point start_direction end_direction",
      43             :                               "Curve extremities input");
      44             : 
      45             :   // Alternative to start / end point
      46       14528 :   params.addParam<MeshGeneratorName>("start_mesh",
      47             :                                      "Meshgenerator providing the mesh to start spline from.");
      48       14528 :   params.addParam<BoundaryName>(
      49             :       "boundary_providing_start_point",
      50             :       "Boundary at whose centroid the spline should start. If the start_direction is not set, the "
      51             :       "starting direction is computed from a side-volume average of the side-vertex-average "
      52             :       "normals of the boundary sides");
      53       14528 :   params.addParam<MeshGeneratorName>("end_mesh",
      54             :                                      "Meshgenerator providing the mesh to end splne on.");
      55       14528 :   params.addParam<BoundaryName>(
      56             :       "boundary_providing_end_point",
      57             :       "Boundary at whose centroid the spline should end. If the end_direction is not set, the "
      58             :       "ending direction is computed from a side-volume average of the side-vertex-average normals "
      59             :       "of the boundary sides");
      60       14528 :   params.addParamNamesToGroup(
      61             :       "start_mesh end_mesh boundary_providing_start_point boundary_providing_end_point",
      62             :       "Curve extremities input");
      63             : 
      64             :   // Spline shape parameters
      65       14528 :   params.addParam<unsigned int>("degree", 3, "Degree of interpolating polynomial.");
      66       18160 :   params.addRangeCheckedParam<libMesh::Real>(
      67        7264 :       "sharpness", 0.6, "sharpness>0 & sharpness<=1", "Sharpness of curve bend.");
      68       10896 :   params.addParam<unsigned int>(
      69             :       "num_cps",
      70        7264 :       6,
      71             :       "Number of control points used to draw the curve. Minimum of degree+1 points are required.");
      72       14528 :   params.addParamNamesToGroup("degree sharpness num_cps", "Spline");
      73             : 
      74             :   // Discretization parameters
      75       14528 :   params.addParam<MooseEnum>(
      76             :       "edge_element_type", edge_elem_type, "Type of the EDGE elements to be generated.");
      77       21792 :   params.addRequiredRangeCheckedParam<unsigned int>(
      78             :       "num_elements", "num_elements>=1", "Numer of elements to be drawn. Must be at least 1.");
      79       14528 :   params.addParamNamesToGroup("edge_element_type num_elements", "Discretization");
      80             : 
      81        7264 :   params.addClassDescription(
      82             :       "This BSplineMeshGenerator object is designed to generate a mesh of a curve that consists of "
      83             :       "EDGE2, EDGE3, or EDGE4 elements drawn using an open uniform B-Spline.");
      84        7264 :   params.addParam<std::vector<BoundaryName>>("edge_nodesets",
      85        7264 :                                              std::vector<BoundaryName>(),
      86             :                                              "Nodeset name to give each edge of the spline curve");
      87             : 
      88        7264 :   return params;
      89        3632 : }
      90             : 
      91         287 : BSplineCurveGenerator::BSplineCurveGenerator(const InputParameters & parameters)
      92             :   : MeshGenerator(parameters),
      93         287 :     _new_subdomain_id(getParam<SubdomainID>("new_subdomain_id")),
      94         574 :     _degree(getParam<unsigned int>("degree")),
      95         574 :     _sharpness(getParam<libMesh::Real>("sharpness")),
      96         574 :     _num_cps(getParam<unsigned int>("num_cps")),
      97         574 :     _order((unsigned int)(getParam<MooseEnum>("edge_element_type")) + 1),
      98         574 :     _num_elements(getParam<unsigned int>("num_elements")),
      99         574 :     _node_set_boundaries(getParam<std::vector<BoundaryName>>("edge_nodesets")),
     100         574 :     _start_mesh_input(getMesh("start_mesh", true)),
     101        1148 :     _end_mesh_input(getMesh("end_mesh", true))
     102             : {
     103         287 :   if (_num_cps < _degree + 1)
     104           6 :     paramError("num_cps", "Number of control points must be at least degree+1.");
     105             : 
     106             :   // Check input parameters
     107         852 :   if (!isParamValid("start_point"))
     108             :   {
     109         228 :     if (!isParamValid("boundary_providing_start_point"))
     110           0 :       paramError("boundary_providing_start_point",
     111             :                  "boundary_providing_start_point must be specified if start_point is not");
     112         228 :     else if (!isParamValid("start_mesh"))
     113           0 :       paramError("start_mesh", "start_mesh must be specified if start_point is not.");
     114             :   }
     115             :   else
     116             :   {
     117        1040 :     if (isParamValid("boundary_providing_start_point") || isParamValid("start_mesh"))
     118           0 :       paramError("start_point",
     119             :                  "start_point and boundary_providing_start_point or start_mesh cannot be "
     120             :                  "simultaneously specified!");
     121         624 :     if (!isParamValid("start_direction"))
     122           0 :       paramError("start_direction",
     123             :                  "Starting direction must be specified if the 'start_point' is specified");
     124             :   }
     125         852 :   if (!isParamValid("end_point"))
     126             :   {
     127         228 :     if (!isParamValid("boundary_providing_end_point"))
     128           0 :       paramError("boundary_providing_end_point",
     129             :                  "boundary_providing_end_point must be specified if start_point is not");
     130         228 :     else if (!isParamValid("end_mesh"))
     131           0 :       paramError("end_mesh", "end_mesh must be specified if start_point is not.");
     132             :   }
     133             :   else
     134             :   {
     135        1040 :     if (isParamValid("boundary_providing_end_point") || isParamValid("end_mesh"))
     136           0 :       paramError("end_point",
     137             :                  "end_point and boundary_providing_end_point or end_mesh cannot be "
     138             :                  "simultaneously specified!");
     139         624 :     if (!isParamValid("end_direction"))
     140           0 :       paramError("end_direction",
     141             :                  "Ending direction must be specified if the 'end_point' is specified");
     142             :   }
     143         284 :   if (_node_set_boundaries.size() != 0 && _node_set_boundaries.size() != 2)
     144           0 :     paramError("edge_nodesets", "If specified, edge_nodesets must have exactly 2 entries.");
     145         284 : }
     146             : 
     147             : std::unique_ptr<MeshBase>
     148         272 : BSplineCurveGenerator::generate()
     149             : {
     150         272 :   if (_start_mesh_input)
     151          76 :     _start_mesh = std::move(_start_mesh_input);
     152         272 :   if (_end_mesh_input)
     153          76 :     _end_mesh = std::move(_end_mesh_input);
     154             : 
     155         272 :   const auto start_point = startPoint();
     156         272 :   const auto end_point = endPoint();
     157         272 :   const auto start_dir = startDirection();
     158         272 :   const auto end_dir = endDirection();
     159             : 
     160         272 :   auto mesh = buildReplicatedMesh(2);
     161             : 
     162             :   // determine number of control points needed
     163             :   unsigned int half_cps;
     164         272 :   if (_num_cps % 2 == 0)
     165         272 :     half_cps = _num_cps / 2;
     166             :   else
     167             :   {
     168           0 :     half_cps = (_num_cps - 1) / 2;
     169           0 :     mooseWarning("Need an even number of control points. `num_cps` has been decreased by 1.");
     170             :   }
     171             : 
     172             :   // generate points using BSpline functions/class
     173             :   std::vector<Point> control_points = SplineUtils::bSplineControlPoints(
     174         272 :       start_point, end_point, start_dir, end_dir, half_cps, _sharpness);
     175             : 
     176             :   // initialize BSpline class
     177             :   Moose::BSpline b_spline(
     178         266 :       _degree, start_point, end_point, start_dir, end_dir, half_cps, _sharpness);
     179             : 
     180             :   // discretize t and evaluate points, assemble into nodes inside loop
     181         266 :   const auto n_ts = _num_elements * _order + 1;
     182         266 :   std::vector<Node *> nodes(n_ts);
     183         266 :   std::vector<Point> eval_points;
     184        6238 :   for (const auto i : make_range(n_ts))
     185             :   {
     186             :     // n_ts-1 because max(t_current) must be 1.0
     187        5972 :     const auto t_current = ((Real)i / (Real)(n_ts - 1));
     188        5972 :     eval_points.push_back(b_spline.getPoint(t_current));
     189        5972 :     nodes[i] = mesh->add_point(eval_points.back(), i);
     190             :   }
     191             : 
     192             :   // create elements from points
     193        5222 :   for (const auto i : make_range(_num_elements))
     194             :   {
     195        4956 :     std::unique_ptr<Elem> new_elem;
     196        4956 :     switch (_order)
     197             :     {
     198        4456 :       case 1:
     199        4456 :         new_elem = std::make_unique<Edge2>();
     200        4456 :         break;
     201         250 :       case 2:
     202         250 :         new_elem = std::make_unique<Edge3>();
     203         250 :         new_elem->set_node(2, nodes[i * _order + 1]);
     204         250 :         break;
     205         250 :       default:
     206             :       {
     207         250 :         new_elem = std::make_unique<Edge4>();
     208         250 :         new_elem->set_node(2, nodes[i * _order + 1]);
     209         250 :         new_elem->set_node(3, nodes[i * _order + 2]);
     210             :       }
     211             :     }
     212             : 
     213        4956 :     new_elem->set_node(0, nodes[i * _order]);
     214             :     mooseAssert((i + 1) * _order < nodes.size(), "Out of bounds in nodes array");
     215        4956 :     new_elem->set_node(1, nodes[((i + 1) * _order)]);
     216             : 
     217        4956 :     new_elem->subdomain_id() = _new_subdomain_id;
     218        4956 :     mesh->add_elem(std::move(new_elem));
     219        4956 :   }
     220             : 
     221             :   // Add subdomain name if needed
     222         798 :   if (isParamValid("new_subdomain_name"))
     223          24 :     mesh->subdomain_name(_new_subdomain_id) = getParam<SubdomainName>("new_subdomain_name");
     224             : 
     225         266 :   if (_node_set_boundaries.size())
     226             :   {
     227             :     // Add boundary nodesets to boundary info
     228          40 :     BoundaryInfo & boundary_info = mesh->get_boundary_info();
     229          40 :     int i = 0;
     230         120 :     for (auto & side_name : _node_set_boundaries)
     231          80 :       boundary_info.nodeset_name(i++) = side_name;
     232             : 
     233          40 :     boundary_info.add_node(*nodes.begin(), boundary_info.get_id_by_name(_node_set_boundaries[0]));
     234          40 :     boundary_info.add_node(*(nodes.end() - 1),
     235          40 :                            boundary_info.get_id_by_name(_node_set_boundaries[1]));
     236             :   }
     237         532 :   return dynamic_pointer_cast<MeshBase>(mesh);
     238         266 : }
     239             : 
     240             : Point
     241         272 : BSplineCurveGenerator::startPoint() const
     242             : {
     243         816 :   if (isParamValid("start_point"))
     244         588 :     return getParam<Point>("start_point");
     245             :   else
     246         304 :     return MooseMeshUtils::boundaryCentroidCalculator(
     247         152 :         getParam<BoundaryName>("boundary_providing_start_point"), *_start_mesh);
     248             : }
     249             : 
     250             : Point
     251         272 : BSplineCurveGenerator::endPoint() const
     252             : {
     253         816 :   if (isParamValid("end_point"))
     254         588 :     return getParam<Point>("end_point");
     255             :   else
     256         304 :     return MooseMeshUtils::boundaryCentroidCalculator(
     257         152 :         getParam<BoundaryName>("boundary_providing_end_point"), *_end_mesh);
     258             : }
     259             : 
     260             : RealVectorValue
     261         272 : BSplineCurveGenerator::startDirection() const
     262             : {
     263         816 :   if (isParamValid("start_direction"))
     264         786 :     return getParam<RealVectorValue>("start_direction");
     265             :   else
     266          40 :     return MooseMeshUtils::boundaryWeightedNormal(
     267          20 :         getParam<BoundaryName>("boundary_providing_start_point"), *_start_mesh);
     268             : }
     269             : 
     270             : RealVectorValue
     271         272 : BSplineCurveGenerator::endDirection() const
     272             : {
     273         816 :   if (isParamValid("end_direction"))
     274         786 :     return getParam<RealVectorValue>("end_direction");
     275             :   else
     276          40 :     return MooseMeshUtils::boundaryWeightedNormal(
     277          20 :         getParam<BoundaryName>("boundary_providing_end_point"), *_end_mesh);
     278             : }

Generated by: LCOV version 1.14