LCOV - code coverage report
Current view: top level - src/utils - BSpline.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 53 56 94.6 %
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             : #include "BSpline.h"
      11             : #include "libMeshReducedNamespace.h"
      12             : #include "MooseError.h"
      13             : #include "SplineUtils.h"
      14             : #include "Conversion.h"
      15             : 
      16             : using namespace libMesh;
      17             : 
      18             : namespace Moose
      19             : {
      20         272 : BSpline::BSpline(const unsigned int degree,
      21             :                  const libMesh::Point & start_point,
      22             :                  const libMesh::Point & end_point,
      23             :                  const libMesh::RealVectorValue & start_direction,
      24             :                  const libMesh::RealVectorValue & end_direction,
      25             :                  const unsigned int cps_per_half,
      26         272 :                  const libMesh::Real sharpness)
      27         272 :   : _degree(degree),
      28         272 :     _start_point(start_point),
      29         272 :     _end_point(end_point),
      30         272 :     _start_dir(start_direction),
      31         272 :     _end_dir(end_direction),
      32         272 :     _cps_per_half(cps_per_half),
      33         272 :     _sharpness(sharpness),
      34         272 :     _control_points(createControlPoints()),
      35         272 :     _knot_vector(buildKnotVector())
      36             : {
      37         272 : }
      38             : 
      39             : libMesh::Point
      40        5996 : BSpline::getPoint(const libMesh::Real t) const
      41             : {
      42             :   mooseAssert((t >= 0) && (t <= 1), "t should be in [0, 1]. t=" + std::to_string(t));
      43        5996 :   libMesh::Point returnPoint(0, 0, 0);
      44        5996 :   if (t == 1.0)
      45         272 :     return _control_points.back();
      46       49068 :   for (const auto i : index_range(_control_points))
      47       43344 :     returnPoint += BSpline::CdBBasis(t, i, _degree) * _control_points[i];
      48             : 
      49        5724 :   return returnPoint;
      50             : }
      51             : 
      52             : std::vector<libMesh::Point>
      53         272 : BSpline::createControlPoints() const
      54             : {
      55         272 :   std::vector<libMesh::Point> cps;
      56             :   /// call SplineUtils function
      57         272 :   cps = SplineUtils::bSplineControlPoints(
      58         272 :       _start_point, _end_point, _start_dir, _end_dir, _cps_per_half, _sharpness);
      59         272 :   return cps;
      60           0 : }
      61             : 
      62             : std::vector<libMesh::Real>
      63         272 : BSpline::buildKnotVector() const
      64             : {
      65         272 :   std::vector<libMesh::Real> knot_vector(_degree, 0); // initialize bottom to zeros
      66             : 
      67         272 :   const unsigned int num_points_left = _control_points.size() + 1 - _degree;
      68         272 :   if (_control_points.size() < _degree + 1)
      69           0 :     mooseError("Number of control points must be greater than or equal to degree + 1!");
      70             : 
      71        1730 :   for (const auto i : make_range(num_points_left))
      72             :   {
      73        1458 :     knot_vector.push_back(i); // count up
      74             :     mooseAssert(i < _control_points.size(),
      75             :                 "i must be less than the number of control points because of 0 indexing.");
      76             :   }
      77             : 
      78             :   // Filling with int(degree + 1) 0s at the beginning and 1s at the end makes the B-spline go
      79             :   // through the start and end point Note: the "+1" comes from the loop above which starts at 0 and
      80             :   // ends at num_points_left - 1
      81        1078 :   for (const auto i : make_range(_degree))
      82             :   {
      83         806 :     libmesh_ignore(i);
      84         806 :     knot_vector.push_back(num_points_left - 1);
      85             :   }
      86             : 
      87             :   // Normalize values s.t. the the max value is 1.
      88             :   // This must be done to have a constant domain of t to be between 0 and 1 (inclusive).
      89             :   // NOTE: the spacing is uniform, making this a uniform BSpline
      90        3342 :   for (auto & value : knot_vector)
      91             :   {
      92        3070 :     value /= (num_points_left - 1);
      93             :     mooseAssert(value <= 1.0 && value >= 0.0,
      94             :                 "knot_vector must be normalized to 1 with minimum value 0.");
      95             :   }
      96             : 
      97         272 :   return knot_vector;
      98           0 : }
      99             : 
     100             : libMesh::Real
     101      638448 : BSpline::CdBBasis(const libMesh::Real t, const unsigned int i, const unsigned int j) const
     102             : {
     103             :   mooseAssert(i + 1 < _knot_vector.size(), "Out of bounds access in knot vector");
     104             :   mooseAssert(_knot_vector[i] <= _knot_vector[i + 1],
     105             :               "Knot vector is ill-formatted. Ensure values are increasing." +
     106             :                   Moose::stringify(_knot_vector));
     107      638448 :   if (j == 0)
     108      340896 :     return ((t < _knot_vector[i + 1]) && (_knot_vector[i] <= t)) ? 1 : 0;
     109             :   else
     110      297552 :     return BSpline::firstCoeff(t, i, j) * BSpline::CdBBasis(t, i, j - 1) +
     111      297552 :            BSpline::secondCoeff(t, i, j) * BSpline::CdBBasis(t, i + 1, j - 1);
     112             : }
     113             : 
     114             : libMesh::Real
     115      297552 : BSpline::firstCoeff(const libMesh::Real t, const unsigned int i, const unsigned int j) const
     116             : {
     117             :   mooseAssert(i + j < _knot_vector.size(), "Out of bounds access in knot vector");
     118      297552 :   const auto ti = _knot_vector[i];
     119      297552 :   const auto tij = _knot_vector[i + j];
     120      297552 :   if (ti != tij)
     121      203124 :     return (t - ti) / (tij - ti);
     122             :   else
     123       94428 :     return 0;
     124             : }
     125             : 
     126             : libMesh::Real
     127      297552 : BSpline::secondCoeff(const libMesh::Real t, const unsigned int i, const unsigned int j) const
     128             : {
     129             :   mooseAssert(i + j + 1 < _knot_vector.size(), "Out of bounds access in knot vector");
     130      297552 :   libMesh::Real ti1 = _knot_vector[i + 1];
     131      297552 :   libMesh::Real tij1 = _knot_vector[i + j + 1];
     132      297552 :   if (ti1 != tij1)
     133      203124 :     return (tij1 - t) / (tij1 - ti1);
     134             :   else
     135       94428 :     return 0;
     136             : }
     137             : }

Generated by: LCOV version 1.14