Skip to content
Snippets Groups Projects
moebiusstrip.hh 3.75 KiB
#ifndef DUNE_GFE_MOEBIUSSTRIP_GRIDFUNCTION_HH
#define DUNE_GFE_MOEBIUSSTRIP_GRIDFUNCTION_HH

#include <cmath>

#include <dune/common/fmatrix.hh>
#include <dune/common/fvector.hh>
#include <dune/curvedgrid/gridfunctions/analyticgridfunction.hh>

namespace Dune {

/// \brief Functor representing a Möbius strip in 3D, where the base circle is the circle in the x-y-plane with radius_ around 0,0,0
template <class T = double>
class MoebiusStripProjection
{
  static const int dim = 3;
  using Domain = FieldVector<T,dim>;
  using Jacobian = FieldMatrix<T,dim,dim>;

  T radius_;

public:
  MoebiusStripProjection (T radius)
    : radius_(radius)
  {}

  /// \brief Project the coordinate to the MS using closest point projection
  Domain operator() (const Domain& x) const
  {
    double nrm = std::sqrt(x[0]*x[0] + x[1]*x[1]);
    //center for the point x - it lies on the circle around (0,0,0) with radius radius_ in the x-y-plane
    Domain center{x[0] * radius_/nrm, x[1] * radius_/nrm, 0}; 
    double cosu = x[0]/nrm;
    double sinu = x[1]/nrm;
    double cosuhalf = std::sqrt((1+cosu)/2);
    cosuhalf *= (sinu < 0) ? (-1) : 1 ; // u goes from 0 to 2*pi, so cosuhalf is negative if sinu < 0, we multiply that here
    double sinuhalf = std::sqrt((1-cosu)/2); //u goes from 0 to 2*pi, so sinuhalf is always >= 0 

    // now calculate line from center to the new point
    // the direction is (cosuhalf*cosu,cosuhalf*sinu,sinuhalf), as can be seen from the formula to construct the MS, we normalize this vector
    Domain centerToPointOnMS{cosuhalf*cosu,cosuhalf*sinu,sinuhalf};
    centerToPointOnMS /= centerToPointOnMS.two_norm();

    Domain centerToX = center - x;
    // We need the length, let theta be the angle between the vector centerToX and center to centerToPointOnMS
    // then cos(theta) = centerToX*centerToPointOnMS/(len(centerToX)*len(centerToPointOnMS))
    // We want to project the point to MS, s.t. the angle between the MS and the projection is 90deg
    // Then, length = cos(theta) * len(centerToX) = centerToX*centerToPointOnMS/len(centerToPointOnMS) = centerToX*centerToPointOnMS
    double length = - centerToX * centerToPointOnMS;
    centerToPointOnMS *= length;
    return center + centerToPointOnMS;
  }


  /// \brief derivative of the projection to the MS
  friend auto derivative (const MoebiusStripProjection& moebiusStrip)
  {
    DUNE_THROW(NotImplemented,"The derivative of the projection to the Möbius strip is not implemented yet!");
    return [radius = moebiusStrip.radius_](const Domain& x)
    {
      Jacobian out;
      return out;
    };
  }

  /// \brief Normal Vector of the MS
  Domain normal (const Domain& x) const
  {
    using std::sqrt;
    Domain nVec = {x[0],x[1],0};
    double nrm = std::sqrt(x[0]*x[0] + x[1]*x[1]);
    double cosu = x[0]/nrm;
    double sinu = x[1]/nrm;
    double cosuhalf = std::sqrt((1+cosu)/2);
    cosuhalf *= (sinu < 0) ? (-1) : 1 ; // u goes from 0 to 2*pi, so cosuhalf is negative if sinu < 0, we multiply that here
    double sinuhalf = std::sqrt((1-cosu)/2);
    nVec[2] = (-1)*cosuhalf*(cosu*x[0]+sinu*x[1])/sinuhalf;
    nVec /= nVec.two_norm();
    return nVec;
  }

  /// \brief The mean curvature of the MS
  T mean_curvature (const Domain& /*x*/) const
  {
    DUNE_THROW(NotImplemented,"The mean curvature of the Möbius strip is not implemented yet!");
    return 1;
  }

  /// \brief The area of the MS
  T area () const
  {
    DUNE_THROW(NotImplemented,"The area of the Möbius strip is not implemented yet!");
    return 1;
  }
};

/// \brief construct a grid function representing the parametrization of a MS
template <class Grid, class T>
auto moebiusStripGridFunction (T radius)
{
  return analyticGridFunction<Grid>(MoebiusStripProjection<T>{radius});
}

} // end namespace Dune

#endif // DUNE_GFE_MOEBIUSSTRIP_GRIDFUNCTION_HH