Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • osander/dune-gfe
  • lnebel/dune-gfe
  • spraetor/dune-gfe
3 results
Show changes
Showing
with 3774 additions and 3579 deletions
......@@ -27,7 +27,7 @@
#include <dune/gfe/parallel/vectorcommunicator.hh>
template <class Basis, class TargetSpace, class Assembler>
void RiemannianTrustRegionSolver<Basis, TargetSpace, Assembler>::
void Dune::GFE::RiemannianTrustRegionSolver<Basis, TargetSpace, Assembler>::
setup(const GridType& grid,
const Assembler* assembler,
const SolutionType& x,
......@@ -66,7 +66,7 @@ setup(const GridType& grid,
}
template <class Basis, class TargetSpace, class Assembler>
void RiemannianTrustRegionSolver<Basis,TargetSpace,Assembler>::
void Dune::GFE::RiemannianTrustRegionSolver<Basis,TargetSpace,Assembler>::
setup(const GridType& grid,
const Assembler* assembler,
const SolutionType& x,
......@@ -121,11 +121,11 @@ setup(const GridType& grid,
// Hack: the two-norm may not scale all that well, but it is fast!
auto baseNorm = std::make_shared<TwoNorm<CorrectionType> >();
auto baseSolver = std::make_shared<::LoopSolver<CorrectionType> >(baseSolverStep,
baseIterations,
baseTolerance,
baseNorm,
Solver::QUIET);
auto baseSolver = std::make_shared<Solvers::LoopSolver<CorrectionType> >(baseSolverStep,
baseIterations,
baseTolerance,
baseNorm,
Solver::QUIET);
#endif
#if HAVE_MPI
......@@ -179,11 +179,11 @@ setup(const GridType& grid,
#endif
h1SemiNorm_ = std::make_shared<H1SemiNorm<CorrectionType> >(A);
innerSolver_ = std::make_shared<::LoopSolver<CorrectionType> >(mmgStep,
innerIterations_,
innerTolerance_,
h1SemiNorm_,
Solver::QUIET);
innerSolver_ = std::make_shared<Solvers::LoopSolver<CorrectionType> >(mmgStep,
innerIterations_,
innerTolerance_,
h1SemiNorm_,
Solver::QUIET);
// //////////////////////////////////////////////////////////////////////////////////////
// Assemble a mass matrix to create a norm that's equivalent to the L2-norm
......@@ -204,8 +204,8 @@ setup(const GridType& grid,
// Write all intermediate solutions, if requested
if (instrumented_
&& dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get()))
dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())->historyBuffer_ = instrumentedPath_ + "/mgHistory";
&& dynamic_cast<Solvers::IterativeSolver<CorrectionType>*>(innerSolver_.get()))
dynamic_cast<Solvers::IterativeSolver<CorrectionType>*>(innerSolver_.get())->historyBuffer_ = instrumentedPath_ + "/mgHistory";
// ////////////////////////////////////////////////////////////
// Create Hessian matrix and its occupation structure
......@@ -248,7 +248,7 @@ setup(const GridType& grid,
#if HAVE_MPI
// If we are on more than 1 processors, join all local transfer matrices on rank 0,
// and construct a single global transfer operator there.
typedef Dune::GlobalP1Mapper<Dune::Functions::LagrangeBasis<typename Basis::GridView,1> > GlobalLeafP1Mapper;
typedef GFE::GlobalP1Mapper<Dune::Functions::LagrangeBasis<typename Basis::GridView,1> > GlobalLeafP1Mapper;
GlobalLeafP1Mapper p1Index(grid_->leafGridView());
typedef Dune::MultipleCodimMultipleGeomTypeMapper<typename GridType::LeafGridView> LeafP1LocalMapper;
......@@ -282,7 +282,7 @@ setup(const GridType& grid,
// If we are on more than 1 processors, join all local transfer matrices on rank 0,
// and construct a single global transfer operator there.
typedef Dune::Functions::LagrangeBasis<typename GridType::LevelGridView, 1> FEBasis;
typedef Dune::GlobalP1Mapper<FEBasis> GlobalLevelP1Mapper;
typedef GFE::GlobalP1Mapper<FEBasis> GlobalLevelP1Mapper;
GlobalLevelP1Mapper fineGUIndex(grid_->levelGridView(i+1));
GlobalLevelP1Mapper coarseGUIndex(grid_->levelGridView(i));
......@@ -328,15 +328,15 @@ setup(const GridType& grid,
template <class Basis, class TargetSpace, class Assembler>
void RiemannianTrustRegionSolver<Basis,TargetSpace,Assembler>::solve()
void Dune::GFE::RiemannianTrustRegionSolver<Basis,TargetSpace,Assembler>::solve()
{
int rank = grid_->comm().rank();
MonotoneMGStep<MatrixType,CorrectionType>* mgStep = nullptr; // Non-shared pointer -- the innerSolver keeps the ownership
// if the inner solver is a monotone multigrid set up a max-norm trust-region
if (dynamic_cast<LoopSolver<CorrectionType>*>(innerSolver_.get())) {
auto loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_);
if (dynamic_cast<Solvers::LoopSolver<CorrectionType>*>(innerSolver_.get())) {
auto loopSolver = std::dynamic_pointer_cast<Solvers::LoopSolver<CorrectionType> >(innerSolver_);
mgStep = dynamic_cast<MonotoneMGStep<MatrixType,CorrectionType>*>(&loopSolver->getIterationStep());
}
......
......@@ -20,164 +20,169 @@
#include <dune/gfe/parallel/mapperfactory.hh>
/** \brief Riemannian trust-region solver for geodesic finite-element problems */
template <class Basis, class TargetSpace, class Assembler = GeodesicFEAssembler<Basis,TargetSpace> >
class RiemannianTrustRegionSolver
: public IterativeSolver<std::vector<TargetSpace>,
Dune::BitSetVector<TargetSpace::TangentVector::dimension> >
namespace Dune::GFE
{
typedef typename Basis::GridView::Grid GridType;
const static int blocksize = TargetSpace::TangentVector::dimension;
const static int gridDim = GridType::dimension;
// Centralize the field type here
typedef double field_type;
// Some types that I need
typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType;
typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType;
typedef std::vector<TargetSpace> SolutionType;
#if HAVE_MPI
typedef typename Dune::GFE::MapperFactory<Basis>::GlobalMapper GlobalMapper;
typedef typename Dune::GFE::MapperFactory<Basis>::LocalMapper LocalMapper;
#endif
/** \brief Records information about the last run of the RiemannianTrustRegionSolver
*
* This is used primarily for unit testing.
*/
struct Statistics
/** \brief Riemannian trust-region solver for geodesic finite-element problems */
template <class Basis, class TargetSpace, class Assembler = GeodesicFEAssembler<Basis,TargetSpace> >
class RiemannianTrustRegionSolver
: public ::IterativeSolver<std::vector<TargetSpace>,
Dune::BitSetVector<TargetSpace::TangentVector::dimension> >
{
std::size_t finalIteration;
typedef typename Basis::GridView::Grid GridType;
field_type finalEnergy;
};
const static int blocksize = TargetSpace::TangentVector::dimension;
public:
const static int gridDim = GridType::dimension;
RiemannianTrustRegionSolver()
: IterativeSolver<std::vector<TargetSpace>, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL),
hessianMatrix_(nullptr), h1SemiNorm_(NULL)
{
std::fill(scaling_.begin(), scaling_.end(), 1.0);
}
/** \brief Set up the solver using a monotone multigrid method as the inner solver */
void setup(const GridType& grid,
const Assembler* assembler,
const SolutionType& x,
const Dune::BitSetVector<blocksize>& dirichletNodes,
double tolerance,
int maxTrustRegionSteps,
double initialTrustRegionRadius,
int multigridIterations,
double mgTolerance,
int mu,
int nu1,
int nu2,
int baseIterations,
double baseTolerance,
bool instrumented);
/** \brief Set up the solver using a monotone multigrid method as the inner solver */
void setup(const GridType& grid,
const Assembler* assembler,
const SolutionType& x,
const Dune::BitSetVector<blocksize>& dirichletNodes,
const Dune::ParameterTree& parameterSet);
void setScaling(const Dune::FieldVector<double,blocksize>& scaling)
{
scaling_ = scaling;
}
// Centralize the field type here
typedef double field_type;
void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes)
{
ignoreNodes_ = &ignoreNodes;
std::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_);
assert(loopSolver);
loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_;
}
void solve();
[[deprecated]]
void setInitialSolution(const SolutionType& x) {
x_ = x;
}
void setInitialIterate(const SolutionType& x) {
x_ = x;
}
SolutionType getSol() const {return x_;}
// Some types that I need
typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType;
typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType;
typedef std::vector<TargetSpace> SolutionType;
const Statistics& getStatistics() const {return statistics_;}
#if HAVE_MPI
typedef typename Dune::GFE::MapperFactory<Basis>::GlobalMapper GlobalMapper;
typedef typename Dune::GFE::MapperFactory<Basis>::LocalMapper LocalMapper;
#endif
protected:
/** \brief Records information about the last run of the RiemannianTrustRegionSolver
*
* This is used primarily for unit testing.
*/
struct Statistics
{
std::size_t finalIteration;
field_type finalEnergy;
};
public:
RiemannianTrustRegionSolver()
: ::IterativeSolver<std::vector<TargetSpace>, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL),
hessianMatrix_(nullptr), h1SemiNorm_(NULL)
{
std::fill(scaling_.begin(), scaling_.end(), 1.0);
}
/** \brief Set up the solver using a monotone multigrid method as the inner solver */
void setup(const GridType& grid,
const Assembler* assembler,
const SolutionType& x,
const Dune::BitSetVector<blocksize>& dirichletNodes,
double tolerance,
int maxTrustRegionSteps,
double initialTrustRegionRadius,
int multigridIterations,
double mgTolerance,
int mu,
int nu1,
int nu2,
int baseIterations,
double baseTolerance,
bool instrumented);
/** \brief Set up the solver using a monotone multigrid method as the inner solver */
void setup(const GridType& grid,
const Assembler* assembler,
const SolutionType& x,
const Dune::BitSetVector<blocksize>& dirichletNodes,
const Dune::ParameterTree& parameterSet);
void setScaling(const Dune::FieldVector<double,blocksize>& scaling)
{
scaling_ = scaling;
}
void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes)
{
ignoreNodes_ = &ignoreNodes;
std::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_);
assert(loopSolver);
loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_;
}
void solve();
[[deprecated]]
void setInitialSolution(const SolutionType& x) {
x_ = x;
}
void setInitialIterate(const SolutionType& x) {
x_ = x;
}
SolutionType getSol() const {return x_;}
const Statistics& getStatistics() const {return statistics_;}
protected:
#if HAVE_MPI
std::unique_ptr<GlobalMapper> globalMapper_;
std::unique_ptr<GlobalMapper> globalMapper_;
#endif
/** \brief The grid */
const GridType* grid_;
/** \brief The grid */
const GridType* grid_;
/** \brief The solution vector */
SolutionType x_;
/** \brief The solution vector */
SolutionType x_;
/** \brief The initial trust-region radius in the maximum-norm */
double initialTrustRegionRadius_;
/** \brief The initial trust-region radius in the maximum-norm */
double initialTrustRegionRadius_;
/** \brief Trust-region norm scaling */
Dune::FieldVector<double,blocksize> scaling_;
/** \brief Trust-region norm scaling */
Dune::FieldVector<double,blocksize> scaling_;
/** \brief Maximum number of trust-region steps */
std::size_t maxTrustRegionSteps_;
/** \brief Maximum number of trust-region steps */
std::size_t maxTrustRegionSteps_;
/** \brief Maximum number of multigrid iterations */
int innerIterations_;
/** \brief Maximum number of multigrid iterations */
int innerIterations_;
/** \brief Error tolerance of the multigrid QP solver */
double innerTolerance_;
/** \brief Error tolerance of the multigrid QP solver */
double innerTolerance_;
/** \brief Hessian matrix */
std::unique_ptr<MatrixType> hessianMatrix_;
/** \brief Hessian matrix */
std::unique_ptr<MatrixType> hessianMatrix_;
/** \brief The assembler for the material law */
const Assembler* assembler_;
/** \brief The assembler for the material law */
const Assembler* assembler_;
/** \brief The solver for the quadratic inner problems */
std::shared_ptr<Solver> innerSolver_;
/** \brief The solver for the quadratic inner problems */
std::shared_ptr<Solver> innerSolver_;
/** \brief Contains 'true' everywhere -- the trust-region is bounded */
Dune::BitSetVector<blocksize> hasObstacle_;
/** \brief Contains 'true' everywhere -- the trust-region is bounded */
Dune::BitSetVector<blocksize> hasObstacle_;
/** \brief The Dirichlet nodes */
const Dune::BitSetVector<blocksize>* ignoreNodes_;
/** \brief The Dirichlet nodes */
const Dune::BitSetVector<blocksize>* ignoreNodes_;
/** \brief The norm used to measure multigrid convergence */
std::shared_ptr<H1SemiNorm<CorrectionType> > h1SemiNorm_;
/** \brief The norm used to measure multigrid convergence */
std::shared_ptr<H1SemiNorm<CorrectionType> > h1SemiNorm_;
/** \brief An L2-norm, really. The H1SemiNorm class is badly named */
std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_;
/** \brief An L2-norm, really. The H1SemiNorm class is badly named */
std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_;
/** \brief If set to true we log convergence speed and other stuff */
bool instrumented_;
/** \brief If set to true we log convergence speed and other stuff */
bool instrumented_;
/** \brief Output path for instrumented log */
std::string instrumentedPath_ = "/tmp";
/** \brief Output path for instrumented log */
std::string instrumentedPath_ = "/tmp";
/** \brief Norm type used for stopping criterion (default is infinity norm) */
enum class ErrorNormType {infinity, H1semi} normType_ = ErrorNormType::infinity;
/** \brief Norm type used for stopping criterion (default is infinity norm) */
enum class ErrorNormType {infinity, H1semi} normType_ = ErrorNormType::infinity;
/** \brief Store information about solver runs for unit testing */
Statistics statistics_;
/** \brief Store information about solver runs for unit testing */
Statistics statistics_;
};
};
} // namespace Dune::GFE
#include "riemanniantrsolver.cc"
......
......@@ -15,222 +15,228 @@
#include <dune/gfe/spaces/realtuple.hh>
#include <dune/gfe/spaces/rotation.hh>
/** \brief A factory class that implements various ways to create rod configurations
*/
template <class GridView>
class RodFactory
namespace Dune::GFE
{
static_assert(GridView::dimensionworld==1, "RodFactory is only implemented for grids in a 1d world");
public:
RodFactory(const GridView& gridView)
: gridView_(gridView)
{}
/** \brief Make a straight, unsheared rod from two given endpoints
\param[out] rod The new rod
\param[in] n The number of vertices
/** \brief A factory class that implements various ways to create rod configurations
*/
template <int dim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> > >& rod,
const Dune::FieldVector<double,3>& beginning, const Dune::FieldVector<double,3>& end)
{
// Compute the correct orientation
Rotation<double,dim> orientation = Rotation<double,dim>::identity();
Dune::FieldVector<double,3> zAxis(0);
zAxis[2] = 1;
Dune::FieldVector<double,3> axis = MatrixVector::crossProduct(Dune::FieldVector<double,3>(end-beginning), zAxis);
if (axis.two_norm() != 0)
axis /= -axis.two_norm();
Dune::FieldVector<double,3> d3 = end-beginning;
d3 /= d3.two_norm();
double angle = std::acos(zAxis * d3);
if (angle != 0)
orientation = Rotation<double,3>(axis, angle);
template <class GridView>
class RodFactory
{
static_assert(GridView::dimensionworld==1, "RodFactory is only implemented for grids in a 1d world");
// Set the values
create(rod,
Dune::GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> > (beginning,orientation),
Dune::GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> > (end,orientation));
}
public:
RodFactory(const GridView& gridView)
: gridView_(gridView)
{}
/** \brief Make a rod by interpolating between two end configurations
/** \brief Make a straight, unsheared rod from two given endpoints
\param[out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & beginning,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & end)
{
\param[out] rod The new rod
\param[in] n The number of vertices
*/
template <int dim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> > >& rod,
const Dune::FieldVector<double,3>& beginning, const Dune::FieldVector<double,3>& end)
{
// Compute the correct orientation
Rotation<double,dim> orientation = Rotation<double,dim>::identity();
static const int dim = GridView::dimension; // de facto: 1
Dune::FieldVector<double,3> zAxis(0);
zAxis[2] = 1;
Dune::FieldVector<double,3> axis = MatrixVector::crossProduct(Dune::FieldVector<double,3>(end-beginning), zAxis);
if (axis.two_norm() != 0)
axis /= -axis.two_norm();
//////////////////////////////////////////////////////////////////////////////////////////////
// Get smallest and largest coordinate, in order to create an arc-length parametrization
//////////////////////////////////////////////////////////////////////////////////////////////
Dune::FieldVector<double,3> d3 = end-beginning;
d3 /= d3.two_norm();
typename GridView::template Codim<dim>::Iterator vIt = gridView_.template begin<dim>();
typename GridView::template Codim<dim>::Iterator vEndIt = gridView_.template end<dim>();
double angle = std::acos(zAxis * d3);
double min = std::numeric_limits<double>::max();
double max = -std::numeric_limits<double>::max();
if (angle != 0)
orientation = Rotation<double,3>(axis, angle);
for (; vIt != vEndIt; ++vIt) {
min = std::min(min, vIt->geometry().corner(0)[0]);
max = std::max(max, vIt->geometry().corner(0)[0]);
// Set the values
create(rod,
Dune::GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> > (beginning,orientation),
Dune::GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> > (end,orientation));
}
////////////////////////////////////////////////////////////////////////////////////
// Interpolate according to arc-length
////////////////////////////////////////////////////////////////////////////////////
rod.resize(gridView_.size(dim));
/** \brief Make a rod by interpolating between two end configurations
for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) {
int idx = gridView_.indexSet().index(*vIt);
Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min);
\param[out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & beginning,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & end)
{
for (int i=0; i<3; i++)
rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i];
rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local);
}
}
static const int dim = GridView::dimension; // de facto: 1
/** \brief Make a rod by setting each entry to the same value
//////////////////////////////////////////////////////////////////////////////////////////////
// Get smallest and largest coordinate, in order to create an arc-length parametrization
//////////////////////////////////////////////////////////////////////////////////////////////
\param[out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & value)
{
rod.resize(gridView_.size(1));
std::fill(rod.begin(), rod.end(), value);
}
typename GridView::template Codim<dim>::Iterator vIt = gridView_.template begin<dim>();
typename GridView::template Codim<dim>::Iterator vEndIt = gridView_.template end<dim>();
/** \brief Make a rod by linearly interpolating between the end values
double min = std::numeric_limits<double>::max();
double max = -std::numeric_limits<double>::max();
\note The end values are expected to be in the input container!
\param[in,out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod)
{
static const int dim = GridView::dimension; // de facto: 1
assert(gridView_.size(dim)==rod.size());
for (; vIt != vEndIt; ++vIt) {
min = std::min(min, vIt->geometry().corner(0)[0]);
max = std::max(max, vIt->geometry().corner(0)[0]);
}
//////////////////////////////////////////////////////////////////////////////////////////////
// Get smallest and largest coordinate, in order to create an arc-length parametrization
//////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Interpolate according to arc-length
////////////////////////////////////////////////////////////////////////////////////
typename GridView::template Codim<dim>::Iterator vIt = gridView_.template begin<dim>();
typename GridView::template Codim<dim>::Iterator vEndIt = gridView_.template end<dim>();
rod.resize(gridView_.size(dim));
double min = std::numeric_limits<double>::max();
double max = -std::numeric_limits<double>::max();
Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > beginning, end;
for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) {
int idx = gridView_.indexSet().index(*vIt);
Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min);
for (; vIt != vEndIt; ++vIt) {
if (vIt->geometry().corner(0)[0] < min) {
min = vIt->geometry().corner(0)[0];
beginning = rod[gridView_.indexSet().index(*vIt)];
}
if (vIt->geometry().corner(0)[0] > max) {
max = vIt->geometry().corner(0)[0];
end = rod[gridView_.indexSet().index(*vIt)];
for (int i=0; i<3; i++)
rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i];
rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local);
}
}
////////////////////////////////////////////////////////////////////////////////////
// Interpolate according to arc-length
////////////////////////////////////////////////////////////////////////////////////
rod.resize(gridView_.size(dim));
/** \brief Make a rod by setting each entry to the same value
for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) {
int idx = gridView_.indexSet().index(*vIt);
Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min);
for (int i=0; i<3; i++)
rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i];
rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local);
\param[out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & value)
{
rod.resize(gridView_.size(1));
std::fill(rod.begin(), rod.end(), value);
}
}
/** \brief Make a rod by linearly interpolating between the end values
\note The end values are expected to be in the input container!
\param[in,out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod)
{
static const int dim = GridView::dimension; // de facto: 1
assert(gridView_.size(dim)==rod.size());
//////////////////////////////////////////////////////////////////////////////////////////////
// Get smallest and largest coordinate, in order to create an arc-length parametrization
//////////////////////////////////////////////////////////////////////////////////////////////
typename GridView::template Codim<dim>::Iterator vIt = gridView_.template begin<dim>();
typename GridView::template Codim<dim>::Iterator vEndIt = gridView_.template end<dim>();
double min = std::numeric_limits<double>::max();
double max = -std::numeric_limits<double>::max();
Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > beginning, end;
for (; vIt != vEndIt; ++vIt) {
if (vIt->geometry().corner(0)[0] < min) {
min = vIt->geometry().corner(0)[0];
beginning = rod[gridView_.indexSet().index(*vIt)];
}
if (vIt->geometry().corner(0)[0] > max) {
max = vIt->geometry().corner(0)[0];
end = rod[gridView_.indexSet().index(*vIt)];
}
}
/** \brief Make a rod solving a static Dirichlet problem
////////////////////////////////////////////////////////////////////////////////////
// Interpolate according to arc-length
////////////////////////////////////////////////////////////////////////////////////
\param rod The configuration to be computed
\param radius The rod's radius
\param E The rod's elastic modulus
\param nu The rod's Poisson modulus
\param beginning The prescribed Dirichlet values
\param end The prescribed Dirichlet values
\param[out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod,
double radius, double E, double nu,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & beginning,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & end)
{
rod.resize(gridView_.size(dim));
// Make Dirichlet bitfields for the rods as well
Dune::BitSetVector<6> rodDirichletNodes(gridView_.size(GridView::dimension),false);
for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) {
int idx = gridView_.indexSet().index(*vIt);
Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min);
for (int j=0; j<6; j++) {
rodDirichletNodes[0][j] = true;
rodDirichletNodes.back()[j] = true;
for (int i=0; i<3; i++)
rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i];
rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local);
}
}
// Create local assembler for the static elastic problem
RodLocalStiffness<GridView,double> rodLocalStiffness(gridView_, radius*radius*M_PI,
std::pow(radius,4) * 0.25* M_PI, std::pow(radius,4) * 0.25* M_PI, E, nu);
typedef Dune::Functions::PQKNodalBasis<GridView,1> RodP1Basis;
RodP1Basis p1Basis(gridView_);
RodAssembler<RodP1Basis,spaceDim> assembler(p1Basis, &rodLocalStiffness);
/** \brief Make a rod solving a static Dirichlet problem
\param rod The configuration to be computed
\param radius The rod's radius
\param E The rod's elastic modulus
\param nu The rod's Poisson modulus
\param beginning The prescribed Dirichlet values
\param end The prescribed Dirichlet values
\param[out] rod The new rod
*/
template <int spaceDim>
void create(std::vector<Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > >& rod,
double radius, double E, double nu,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & beginning,
const Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > & end)
{
// Make Dirichlet bitfields for the rods as well
Dune::BitSetVector<6> rodDirichletNodes(gridView_.size(GridView::dimension),false);
for (int j=0; j<6; j++) {
rodDirichletNodes[0][j] = true;
rodDirichletNodes.back()[j] = true;
}
// Create local assembler for the static elastic problem
RodLocalStiffness<GridView,double> rodLocalStiffness(gridView_, radius*radius*M_PI,
std::pow(radius,4) * 0.25* M_PI, std::pow(radius,4) * 0.25* M_PI, E, nu);
// Create initial iterate using the straight rod interpolation method
create(rod, beginning.r, end.r);
typedef Dune::Functions::PQKNodalBasis<GridView,1> RodP1Basis;
RodP1Basis p1Basis(gridView_);
RodAssembler<RodP1Basis,spaceDim> assembler(p1Basis, &rodLocalStiffness);
// Set reference configuration
rodLocalStiffness.setReferenceConfiguration(rod);
// Create initial iterate using the straight rod interpolation method
create(rod, beginning.r, end.r);
// Set Dirichlet values
rod[0] = beginning;
rod.back() = end;
// Set reference configuration
rodLocalStiffness.setReferenceConfiguration(rod);
// Trust--Region solver
RiemannianTrustRegionSolver<RodP1Basis, Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > > rodSolver;
rodSolver.setup(gridView_.grid(), &assembler, rod,
rodDirichletNodes,
1e-10, 100, // TR tolerance and iterations
20, // init TR radius
200, 1e-00, 1, 3, 3, // Multigrid parameters
100, 1e-8 , false); // base solver parameters
// Set Dirichlet values
rod[0] = beginning;
rod.back() = end;
rodSolver.verbosity_ = NumProc::QUIET;
// Trust--Region solver
RiemannianTrustRegionSolver<RodP1Basis, Dune::GFE::ProductManifold<RealTuple<double,spaceDim>,Rotation<double,spaceDim> > > rodSolver;
rodSolver.setup(gridView_.grid(), &assembler, rod,
rodDirichletNodes,
1e-10, 100, // TR tolerance and iterations
20, // init TR radius
200, 1e-00, 1, 3, 3, // Multigrid parameters
100, 1e-8 , false); // base solver parameters
rodSolver.solve();
rodSolver.verbosity_ = NumProc::QUIET;
rod = rodSolver.getSol();
rodSolver.solve();
rod = rodSolver.getSol();
}
}
private:
private:
const GridView gridView_;
};
const GridView gridView_;
};
} // namespace Dune::GFE
#endif
#ifndef DUNE_SKEW_MATRIX_HH
#define DUNE_SKEW_MATRIX_HH
/** \brief Static dense skew-symmetric matrix */
template <class T, int N>
class SkewMatrix
{};
/** \brief Static dense skew-symmetric 3x3 matrix */
template <class T>
class SkewMatrix<T,3>
namespace Dune::GFE
{
public:
/** \brief Default constructor -- does nothing */
SkewMatrix()
{}
/** \brief Constructor from an axial vector */
explicit SkewMatrix(const Dune::FieldVector<T,3>& v)
: data_(v)
{}
/** \brief Constructor from a general matrix */
explicit SkewMatrix(const Dune::FieldMatrix<T,3,3>& m)
{
data_ = {m[2][1], m[0][2], m[1][0]};
}
SkewMatrix(T v)
: data_(v)
{}
SkewMatrix<T,3>& operator*=(const T& a)
{
data_ *= a;
return *this;
}
Dune::FieldVector<T,3>& axial()
{
return data_;
}
const Dune::FieldVector<T,3>& axial() const
{
return data_;
}
typedef typename Dune::FieldVector<T,3>::Iterator Iterator;
//! begin iterator
Iterator begin ()
{
return Iterator(data_,0);
}
//! end iterator
Iterator end ()
{
return Iterator(data_,3);
}
/** \brief Static dense skew-symmetric matrix */
template <class T, int N>
class SkewMatrix
{};
typedef typename Dune::FieldVector<T,3>::ConstIterator ConstIterator;
//! begin iterator
ConstIterator begin () const
{
return ConstIterator(data_,0);
}
//! end iterator
ConstIterator end () const
{
return ConstIterator(data_,3);
}
/** \brief Embed the skew-symmetric matrix in R^3x3 */
Dune::FieldMatrix<T,3,3> toMatrix() const
/** \brief Static dense skew-symmetric 3x3 matrix */
template <class T>
class SkewMatrix<T,3>
{
Dune::FieldMatrix<T,3,3> mat;
mat = 0;
mat[0][1] = -data_[2];
mat[1][0] = data_[2];
mat[0][2] = data_[1];
mat[2][0] = -data_[1];
mat[1][2] = -data_[0];
mat[2][1] = data_[0];
return mat;
}
private:
// we store the axial vector
Dune::FieldVector<T,3> data_;
};
public:
/** \brief Default constructor -- does nothing */
SkewMatrix()
{}
/** \brief Constructor from an axial vector */
explicit SkewMatrix(const Dune::FieldVector<T,3>& v)
: data_(v)
{}
/** \brief Constructor from a general matrix */
explicit SkewMatrix(const Dune::FieldMatrix<T,3,3>& m)
{
data_ = {m[2][1], m[0][2], m[1][0]};
}
SkewMatrix(T v)
: data_(v)
{}
SkewMatrix<T,3>& operator*=(const T& a)
{
data_ *= a;
return *this;
}
Dune::FieldVector<T,3>& axial()
{
return data_;
}
const Dune::FieldVector<T,3>& axial() const
{
return data_;
}
typedef typename Dune::FieldVector<T,3>::Iterator Iterator;
//! begin iterator
Iterator begin ()
{
return Iterator(data_,0);
}
//! end iterator
Iterator end ()
{
return Iterator(data_,3);
}
typedef typename Dune::FieldVector<T,3>::ConstIterator ConstIterator;
//! begin iterator
ConstIterator begin () const
{
return ConstIterator(data_,0);
}
//! end iterator
ConstIterator end () const
{
return ConstIterator(data_,3);
}
/** \brief Embed the skew-symmetric matrix in R^3x3 */
Dune::FieldMatrix<T,3,3> toMatrix() const
{
Dune::FieldMatrix<T,3,3> mat;
mat = 0;
mat[0][1] = -data_[2];
mat[1][0] = data_[2];
mat[0][2] = data_[1];
mat[2][0] = -data_[1];
mat[1][2] = -data_[0];
mat[2][1] = data_[0];
return mat;
}
private:
// we store the axial vector
Dune::FieldVector<T,3> data_;
};
} // namespace Dune::GFE
#endif
......@@ -9,391 +9,397 @@
#include <dune/gfe/tensor3.hh>
/** \brief A point in the hyperbolic half-space H^N
\tparam N Dimension of the hyperbolic half-space
\tparam T The type used for individual coordinates
*/
template <class T, int N>
class HyperbolicHalfspacePoint
namespace Dune::GFE
{
static_assert(N>=2, "A hyperbolic half-space needs to be at least two-dimensional!");
/** \brief Compute the derivative of arccosh^2 without getting unstable for x close to 1 */
static T derivativeOfArcCosHSquared(const T& x) {
const T eps = 1e-4;
if (x < 1+eps) { // regular expression is unstable, use the series expansion instead
return 2 - 2*(x-1)/3 + 4/15*(x-1)*(x-1);
} else
return 2*std::acosh(x) / std::sqrt(x*x-1);
}
/** \brief Compute the second derivative of arccosh^2 without getting unstable for x close to 1 */
static T secondDerivativeOfArcCosHSquared(const T& x) {
const T eps = 1e-4;
if (x < 1+eps) { // regular expression is unstable, use the series expansion instead
return -2.0/3 + 8*(x-1)/15;
} else
return 2/(x*x-1) - 2*x*std::acosh(x) / std::pow(x*x-1,1.5);
}
/** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */
static T thirdDerivativeOfArcCosHSquared(const T& x) {
const T eps = 1e-4;
if (x < 1+eps) { // regular expression is unstable, use the series expansion instead
return 8.0/15 - 24*(x-1)/35;
} else {
T d = x*x-1;
return -6*x/(d*d) + (4*x*x+2)*std::acosh(x)/(std::pow(d,2.5));
}
}
/** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
/** \brief A point in the hyperbolic half-space H^N
\tparam N Dimension of the hyperbolic half-space
\tparam T The type used for individual coordinates
*/
static Dune::FieldVector<T,N> computeDFdp(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared)
template <class T, int N>
class HyperbolicHalfspacePoint
{
Dune::FieldVector<T,N> result;
static_assert(N>=2, "A hyperbolic half-space needs to be at least two-dimensional!");
/** \brief Compute the derivative of arccosh^2 without getting unstable for x close to 1 */
static T derivativeOfArcCosHSquared(const T& x) {
const T eps = 1e-4;
if (x < 1+eps) { // regular expression is unstable, use the series expansion instead
return 2 - 2*(x-1)/3 + 4/15*(x-1)*(x-1);
} else
return 2*std::acosh(x) / std::sqrt(x*x-1);
}
for (size_t i=0; i<N-1; i++)
result[i] = ( p.data_[i] - q.data_[i] ) / (q.data_[N-1] * p.data_[N-1]);
/** \brief Compute the second derivative of arccosh^2 without getting unstable for x close to 1 */
static T secondDerivativeOfArcCosHSquared(const T& x) {
const T eps = 1e-4;
if (x < 1+eps) { // regular expression is unstable, use the series expansion instead
return -2.0/3 + 8*(x-1)/15;
} else
return 2/(x*x-1) - 2*x*std::acosh(x) / std::pow(x*x-1,1.5);
}
result[N-1] = - diffNormSquared / (2*p.data_[N-1]*p.data_[N-1]*q.data_[N-1]) + (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]);
/** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */
static T thirdDerivativeOfArcCosHSquared(const T& x) {
const T eps = 1e-4;
if (x < 1+eps) { // regular expression is unstable, use the series expansion instead
return 8.0/15 - 24*(x-1)/35;
} else {
T d = x*x-1;
return -6*x/(d*d) + (4*x*x+2)*std::acosh(x)/(std::pow(d,2.5));
}
}
return result;
}
/** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldVector<T,N> computeDFdp(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared)
{
Dune::FieldVector<T,N> result;
/** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldVector<T,N> computeDFdq(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared)
{
Dune::FieldVector<T,N> result;
for (size_t i=0; i<N-1; i++)
result[i] = ( p.data_[i] - q.data_[i] ) / (q.data_[N-1] * p.data_[N-1]);
for (size_t i=0; i<N-1; i++)
result[i] = ( q.data_[i] - p.data_[i] ) / (q.data_[N-1] * p.data_[N-1]);
result[N-1] = - diffNormSquared / (2*p.data_[N-1]*p.data_[N-1]*q.data_[N-1]) + (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]);
result[N-1] = - diffNormSquared / (2*p.data_[N-1]*q.data_[N-1]*q.data_[N-1]) - (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]);
return result;
}
return result;
}
/** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldVector<T,N> computeDFdq(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared)
{
Dune::FieldVector<T,N> result;
/** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p and q
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldMatrix<T,N,N> computeDFdpdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared)
{
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
for (size_t i=0; i<N-1; i++)
result[i] = ( q.data_[i] - p.data_[i] ) / (q.data_[N-1] * p.data_[N-1]);
result[N-1] = - diffNormSquared / (2*p.data_[N-1]*q.data_[N-1]*q.data_[N-1]) - (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]);
Dune::FieldMatrix<T,N,N> dFdpdq;
return result;
}
for (size_t i=0; i<N; i++) {
/** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p and q
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldMatrix<T,N,N> computeDFdpdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared)
{
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
for (size_t j=0; j<N; j++) {
Dune::FieldMatrix<T,N,N> dFdpdq;
if (i!=N-1 and j!=N-1) {
for (size_t i=0; i<N; i++) {
dFdpdq[i][j] = -(i==j) / (p[N-1]*q[N-1]);
for (size_t j=0; j<N; j++) {
} else if (i!=N-1 and j==N-1) {
if (i!=N-1 and j!=N-1) {
dFdpdq[i][j] = -(p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]);
dFdpdq[i][j] = -(i==j) / (p[N-1]*q[N-1]);
} else if (i==N-1 and j!=N-1) {
} else if (i!=N-1 and j==N-1) {
dFdpdq[i][j] = (p[j] - q[j]) / (p[N-1]*p[N-1]*q[N-1]);
dFdpdq[i][j] = -(p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i==N-1 and j==N-1) {
} else if (i==N-1 and j!=N-1) {
dFdpdq[i][j] = -1/(p[N-1]*q[N-1])
- (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1])
+ (p[N-1]-q[N-1]) / (p[N-1]*p[N-1]*q[N-1]) + diffNormSquared / (2*p[N-1]*p[N-1]*q[N-1]*q[N-1]);
dFdpdq[i][j] = (p[j] - q[j]) / (p[N-1]*p[N-1]*q[N-1]);
} else if (i==N-1 and j==N-1) {
dFdpdq[i][j] = -1/(p[N-1]*q[N-1])
- (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1])
+ (p[N-1]-q[N-1]) / (p[N-1]*p[N-1]*q[N-1]) + diffNormSquared / (2*p[N-1]*p[N-1]*q[N-1]*q[N-1]);
}
}
}
return dFdpdq;
}
return dFdpdq;
}
/** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldMatrix<T,N,N> computeDFdqdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared)
{
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
/** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q
\param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons
*/
static Dune::FieldMatrix<T,N,N> computeDFdqdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared)
{
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
Dune::FieldMatrix<T,N,N> dFdqdq;
Dune::FieldMatrix<T,N,N> dFdqdq;
for (size_t i=0; i<N; i++) {
for (size_t i=0; i<N; i++) {
for (size_t j=0; j<N; j++) {
for (size_t j=0; j<N; j++) {
if (i!=N-1 and j!=N-1) {
if (i!=N-1 and j!=N-1) {
dFdqdq[i][j] = (i==j) / (p[N-1]*q[N-1]);
dFdqdq[i][j] = (i==j) / (p[N-1]*q[N-1]);
} else if (i!=N-1 and j==N-1) {
} else if (i!=N-1 and j==N-1) {
dFdqdq[i][j] = (p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]);
dFdqdq[i][j] = (p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i==N-1 and j!=N-1) {
} else if (i==N-1 and j!=N-1) {
dFdqdq[i][j] = (p[j] - q[j]) / (p[N-1]*q[N-1]*q[N-1]);
dFdqdq[i][j] = (p[j] - q[j]) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i==N-1 and j==N-1) {
} else if (i==N-1 and j==N-1) {
dFdqdq[i][j] = 1/(q[N-1]*q[N-1]) + (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1]) + diffNormSquared / (p[N-1]*q[N-1]*q[N-1]*q[N-1]);
dFdqdq[i][j] = 1/(q[N-1]*q[N-1]) + (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1]) + diffNormSquared / (p[N-1]*q[N-1]*q[N-1]*q[N-1]);
}
}
}
return dFdqdq;
}
return dFdqdq;
}
public:
public:
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,N> CoordinateType;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,N> CoordinateType;
/** \brief Dimension of the manifold */
static const int dim = N;
/** \brief Dimension of the manifold */
static const int dim = N;
/** \brief Dimension of the Euclidean space the manifold is embedded in */
static const int embeddedDim = N;
/** \brief Dimension of the Euclidean space the manifold is embedded in */
static const int embeddedDim = N;
/** \brief Type of a tangent vector in local coordinates */
typedef Dune::FieldVector<T,N> TangentVector;
/** \brief Type of a tangent vector in local coordinates */
typedef Dune::FieldVector<T,N> TangentVector;
/** \brief Type of a tangent vector in the embedding space */
typedef Dune::FieldVector<T,N> EmbeddedTangentVector;
/** \brief Type of a tangent vector in the embedding space */
typedef Dune::FieldVector<T,N> EmbeddedTangentVector;
/** \brief The global convexity radius of the hyberbolic plane */
static constexpr T convexityRadius = std::numeric_limits<T>::infinity();
/** \brief The global convexity radius of the hyberbolic plane */
static constexpr T convexityRadius = std::numeric_limits<T>::infinity();
/** \brief Default constructor */
HyperbolicHalfspacePoint()
{}
/** \brief Default constructor */
HyperbolicHalfspacePoint()
{}
/** \brief Constructor from a vector. The vector gets normalized */
HyperbolicHalfspacePoint(const Dune::FieldVector<T,N>& vector)
: data_(vector)
{
assert(vector[N-1]>0);
}
/** \brief Constructor from a vector. The vector gets normalized */
HyperbolicHalfspacePoint(const Dune::FieldVector<T,N>& vector)
: data_(vector)
{
assert(vector[N-1]>0);
}
/** \brief Constructor from an array. The array gets normalized */
HyperbolicHalfspacePoint(const std::array<T,N>& vector)
{
assert(vector.back()>0);
for (int i=0; i<N; i++)
data_[i] = vector[i];
}
/** \brief Constructor from an array. The array gets normalized */
HyperbolicHalfspacePoint(const std::array<T,N>& vector)
{
assert(vector.back()>0);
for (int i=0; i<N; i++)
data_[i] = vector[i];
}
/** \brief The exponential map */
static HyperbolicHalfspacePoint exp(const HyperbolicHalfspacePoint& p, const TangentVector& v) {
/** \brief The exponential map */
static HyperbolicHalfspacePoint exp(const HyperbolicHalfspacePoint& p, const TangentVector& v) {
assert (N==2);
assert (N==2);
T vNorm = v.two_norm();
T vNorm = v.two_norm();
// we compute geodesics by applying an isometry to a fixed unit-speed geodesic.
// Hence we need a unit velocity vector.
if (vNorm <= 0)
return p;
// we compute geodesics by applying an isometry to a fixed unit-speed geodesic.
// Hence we need a unit velocity vector.
if (vNorm <= 0)
return p;
TangentVector vUnit = v;
vUnit /= vNorm;
TangentVector vUnit = v;
vUnit /= vNorm;
// Compute the coefficients a,b,c,d of the Moebius transform that transforms
// the unit speed upward geodesic to the one through p with direction vUnit.
// We expect the Moebius transform to be an isometry, i.e. ad-bc = 1.
T cc = 1/(2*p.data_[N-1]) - vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]);
T dd = 1/(2*p.data_[N-1]) + vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]);
T ac = vUnit[0] / (2*p.data_[N-1]) + p.data_[0]*cc;
T bd = p.data_[0] / p.data_[N-1] - ac;
// Compute the coefficients a,b,c,d of the Moebius transform that transforms
// the unit speed upward geodesic to the one through p with direction vUnit.
// We expect the Moebius transform to be an isometry, i.e. ad-bc = 1.
T cc = 1/(2*p.data_[N-1]) - vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]);
T dd = 1/(2*p.data_[N-1]) + vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]);
T ac = vUnit[0] / (2*p.data_[N-1]) + p.data_[0]*cc;
T bd = p.data_[0] / p.data_[N-1] - ac;
HyperbolicHalfspacePoint result;
HyperbolicHalfspacePoint result;
// vertical part
result.data_[1] = std::exp(vNorm) / (cc*std::exp(2*vNorm) + dd);
// vertical part
result.data_[1] = std::exp(vNorm) / (cc*std::exp(2*vNorm) + dd);
// horizontal part
result.data_[0] = (ac*std::exp(2*vNorm) + bd) / (cc*std::exp(2*vNorm) + dd);
// horizontal part
result.data_[0] = (ac*std::exp(2*vNorm) + bd) / (cc*std::exp(2*vNorm) + dd);
return result;
}
return result;
}
/** \brief Hyperbolic distance between two points
*
* \f dist(a,b) = arccosh ( 1 + ||a-b||^2 / (2a_n b_n) \f
*/
static T distance(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) {
/** \brief Hyperbolic distance between two points
*
* \f dist(a,b) = arccosh ( 1 + ||a-b||^2 / (2a_n b_n) \f
*/
static T distance(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) {
T result(0);
T result(0);
for (size_t i=0; i<N; i++)
result += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]);
for (size_t i=0; i<N; i++)
result += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]);
return std::acosh(1 + result / (2*a.data_[N-1]*b.data_[N-1]));
}
return std::acosh(1 + result / (2*a.data_[N-1]*b.data_[N-1]));
}
/** \brief Compute the gradient of the squared distance function keeping the first argument fixed
/** \brief Compute the gradient of the squared distance function keeping the first argument fixed
Unlike the distance itself the squared distance is differentiable at zero
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) {
Unlike the distance itself the squared distance is differentiable at zero
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) {
T diffNormSquared(0);
T diffNormSquared(0);
for (size_t i=0; i<N; i++)
diffNormSquared += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]);
for (size_t i=0; i<N; i++)
diffNormSquared += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]);
TangentVector result = computeDFdq(a,b,diffNormSquared);
TangentVector result = computeDFdq(a,b,diffNormSquared);
T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]);
T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]);
result *= derivativeOfArcCosHSquared(x);
result *= derivativeOfArcCosHSquared(x);
return result;
}
return result;
}
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) {
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) {
T diffNormSquared = (a.data_-b.data_).two_norm2();
T diffNormSquared = (a.data_-b.data_).two_norm2();
// Compute first derivative of F
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute first derivative of F
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared);
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared);
//
T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
//
T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
// Sum it all together
Dune::FieldMatrix<T,N,N> result;
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
result[i][j] = alphaPrimePrime * dFdq[i] * dFdq[j] + alphaPrime * dFdqdq[i][j];
// Sum it all together
Dune::FieldMatrix<T,N,N> result;
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
result[i][j] = alphaPrimePrime * dFdq[i] * dFdq[j] + alphaPrime * dFdqdq[i][j];
return result;
}
return result;
}
/** \brief Compute the mixed second derivative \partial d^2 / \partial da db
/** \brief Compute the mixed second derivative \partial d^2 / \partial da db
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b)
{
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b)
{
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
T diffNormSquared = (p-q).two_norm2();
T diffNormSquared = (p-q).two_norm2();
// Compute first derivatives of F with respect to p and q
Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared);
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute first derivatives of F with respect to p and q
Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared);
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared);
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared);
//
T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
//
T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
// Sum it all together
Dune::FieldMatrix<T,N,N> result;
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
result[i][j] = alphaPrimePrime * dFdp[i] * dFdq[j] + alphaPrime * dFdpdq[i][j];
// Sum it all together
Dune::FieldMatrix<T,N,N> result;
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
result[i][j] = alphaPrimePrime * dFdp[i] * dFdq[j] + alphaPrime * dFdpdq[i][j];
return result;
}
return result;
}
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b)
{
Tensor3<T,N,N,N> result;
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b)
{
Tensor3<T,N,N,N> result;
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
T diffNormSquared = (p-q).two_norm2();
T diffNormSquared = (p-q).two_norm2();
// Compute first derivative of F
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute first derivative of F
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared);
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared);
// Compute third derivatives of F
Tensor3<T,N,N,N> dFdqdqdq;
// Compute third derivatives of F
Tensor3<T,N,N,N> dFdqdqdq;
for (size_t i=0; i<N; i++) {
for (size_t i=0; i<N; i++) {
for (size_t j=0; j<N; j++) {
for (size_t j=0; j<N; j++) {
for (size_t k=0; k<N; k++) {
for (size_t k=0; k<N; k++) {
if (i!=N-1 and j!=N-1 and k!=N-1) {
if (i!=N-1 and j!=N-1 and k!=N-1) {
dFdqdqdq[i][j][k] = 0;
dFdqdqdq[i][j][k] = 0;
} else if (i!=N-1 and j!=N-1 and k==N-1) {
} else if (i!=N-1 and j!=N-1 and k==N-1) {
dFdqdqdq[i][j][k] = -(i==j) / (p[N-1]*q[N-1]*q[N-1]);
dFdqdqdq[i][j][k] = -(i==j) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i!=N-1 and j==N-1 and k!=N-1) {
} else if (i!=N-1 and j==N-1 and k!=N-1) {
dFdqdqdq[i][j][k] = -(i==k) / (p[N-1]*q[N-1]*q[N-1]);
dFdqdqdq[i][j][k] = -(i==k) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i!=N-1 and j==N-1 and k==N-1) {
} else if (i!=N-1 and j==N-1 and k==N-1) {
dFdqdqdq[i][j][k] = -2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3));
dFdqdqdq[i][j][k] = -2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3));
} else if (i==N-1 and j!=N-1 and k!=N-1) {
} else if (i==N-1 and j!=N-1 and k!=N-1) {
dFdqdqdq[i][j][k] = - (j==k) / (p[N-1]*q[N-1]*q[N-1]);
dFdqdqdq[i][j][k] = - (j==k) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i==N-1 and j!=N-1 and k==N-1) {
} else if (i==N-1 and j!=N-1 and k==N-1) {
dFdqdqdq[i][j][k] = -2*(p[j] - q[j]) / (p[N-1]*Dune::power(q[N-1],3));
dFdqdqdq[i][j][k] = -2*(p[j] - q[j]) / (p[N-1]*Dune::power(q[N-1],3));
} else if (i==N-1 and j==N-1 and k!=N-1) {
} else if (i==N-1 and j==N-1 and k!=N-1) {
dFdqdqdq[i][j][k] = -2*(p[k] - q[k]) / (p[N-1]*Dune::power(q[N-1],3));
dFdqdqdq[i][j][k] = -2*(p[k] - q[k]) / (p[N-1]*Dune::power(q[N-1],3));
} else if (i==N-1 and j==N-1 and k==N-1) {
} else if (i==N-1 and j==N-1 and k==N-1) {
dFdqdqdq[i][j][k] = -2/Dune::power(q[N-1],3) - 1/(p[N-1]*q[N-1]*q[N-1]) - 4*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3))
- 3*diffNormSquared / (p[N-1]*Dune::power(q[N-1],4));
dFdqdqdq[i][j][k] = -2/Dune::power(q[N-1],3) - 1/(p[N-1]*q[N-1]*q[N-1]) - 4*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3))
- 3*diffNormSquared / (p[N-1]*Dune::power(q[N-1],4));
}
}
......@@ -401,89 +407,89 @@ public:
}
//
T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x);
// Sum it all together
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
for (size_t k=0; k<N; k++)
result[i][j][k] = alphaPrimePrimePrime * dFdq[i] * dFdq[j] * dFdq[k]
+ alphaPrimePrime * (dFdqdq[i][j] * dFdq[k] + dFdqdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdq[i])
+ alphaPrime * dFdqdqdq[i][j][k];
return result;
}
//
T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x);
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b)
{
Tensor3<T,N,N,N> result;
// Sum it all together
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
for (size_t k=0; k<N; k++)
result[i][j][k] = alphaPrimePrimePrime * dFdq[i] * dFdq[j] * dFdq[k]
+ alphaPrimePrime * (dFdqdq[i][j] * dFdq[k] + dFdqdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdq[i])
+ alphaPrime * dFdqdqdq[i][j][k];
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
return result;
}
T diffNormSquared = (p-q).two_norm2();
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b)
{
Tensor3<T,N,N,N> result;
// Compute first derivatives of F with respect to p and q
Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared);
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// abbreviate notation
const Dune::FieldVector<T,N>& p = a.data_;
const Dune::FieldVector<T,N>& q = b.data_;
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared);
T diffNormSquared = (p-q).two_norm2();
Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared);
// Compute first derivatives of F with respect to p and q
Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared);
Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared);
// Compute third derivatives of F
Tensor3<T,N,N,N> dFdpdqdq;
// Compute second derivatives of F
Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared);
for (size_t i=0; i<N; i++) {
Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared);
for (size_t j=0; j<N; j++) {
// Compute third derivatives of F
Tensor3<T,N,N,N> dFdpdqdq;
for (size_t k=0; k<N; k++) {
for (size_t i=0; i<N; i++) {
if (i!=N-1 and j!=N-1 and k!=N-1) {
for (size_t j=0; j<N; j++) {
dFdpdqdq[i][j][k] = 0;
for (size_t k=0; k<N; k++) {
} else if (i!=N-1 and j!=N-1 and k==N-1) {
if (i!=N-1 and j!=N-1 and k!=N-1) {
dFdpdqdq[i][j][k] = (i==j) / (p[N-1]*q[N-1]*q[N-1]);
dFdpdqdq[i][j][k] = 0;
} else if (i!=N-1 and j==N-1 and k!=N-1) {
} else if (i!=N-1 and j!=N-1 and k==N-1) {
dFdpdqdq[i][j][k] = (i==k) / (p[N-1]*q[N-1]*q[N-1]);
dFdpdqdq[i][j][k] = (i==j) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i!=N-1 and j==N-1 and k==N-1) {
} else if (i!=N-1 and j==N-1 and k!=N-1) {
dFdpdqdq[i][j][k] = 2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3));
dFdpdqdq[i][j][k] = (i==k) / (p[N-1]*q[N-1]*q[N-1]);
} else if (i==N-1 and j!=N-1 and k!=N-1) {
} else if (i!=N-1 and j==N-1 and k==N-1) {
dFdpdqdq[i][j][k] = -(j==k) / (p[N-1]*p[N-1]*q[N-1]);
dFdpdqdq[i][j][k] = 2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3));
} else if (i==N-1 and j!=N-1 and k==N-1) {
} else if (i==N-1 and j!=N-1 and k!=N-1) {
dFdpdqdq[i][j][k] = -(p[j] - q[j]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2));
dFdpdqdq[i][j][k] = -(j==k) / (p[N-1]*p[N-1]*q[N-1]);
} else if (i==N-1 and j==N-1 and k!=N-1) {
} else if (i==N-1 and j!=N-1 and k==N-1) {
dFdpdqdq[i][j][k] = -(p[k] - q[k]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2));
dFdpdqdq[i][j][k] = -(p[j] - q[j]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2));
} else if (i==N-1 and j==N-1 and k==N-1) {
} else if (i==N-1 and j==N-1 and k!=N-1) {
dFdpdqdq[i][j][k] = 1.0/(p[N-1]*q[N-1]*q[N-1])
+ 2*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3))
- (p[N-1]-q[N-1])/(p[N-1]*p[N-1]*q[N-1]*q[N-1])
- diffNormSquared / (p[N-1]*p[N-1]*Dune::power(q[N-1],3));
dFdpdqdq[i][j][k] = -(p[k] - q[k]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2));
} else if (i==N-1 and j==N-1 and k==N-1) {
dFdpdqdq[i][j][k] = 1.0/(p[N-1]*q[N-1]*q[N-1])
+ 2*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3))
- (p[N-1]-q[N-1])/(p[N-1]*p[N-1]*q[N-1]*q[N-1])
- diffNormSquared / (p[N-1]*p[N-1]*Dune::power(q[N-1],3));
}
}
......@@ -491,62 +497,62 @@ public:
}
}
//
T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x);
//
T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]);
T alphaPrime = derivativeOfArcCosHSquared(x);
T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x);
T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x);
// Sum it all together
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
for (size_t k=0; k<N; k++)
result[i][j][k] = alphaPrimePrimePrime * dFdp[i] * dFdq[j] * dFdq[k]
+ alphaPrimePrime * (dFdpdq[i][j] * dFdq[k] + dFdpdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdp[i])
+ alphaPrime * dFdpdqdq[i][j][k];
// Sum it all together
for (size_t i=0; i<N; i++)
for (size_t j=0; j<N; j++)
for (size_t k=0; k<N; k++)
result[i][j][k] = alphaPrimePrimePrime * dFdp[i] * dFdq[j] * dFdq[k]
+ alphaPrimePrime * (dFdpdq[i][j] * dFdq[k] + dFdpdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdp[i])
+ alphaPrime * dFdpdqdq[i][j][k];
return result;
return result;
}
}
/** \brief Project tangent vector of R^n onto the tangent space. For H^m this is the identity */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
return v;
}
/** \brief Project tangent vector of R^n onto the tangent space. For H^m this is the identity */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
return v;
}
/** \brief The global coordinates, if you really want them */
const CoordinateType& globalCoordinates() const {
return data_;
}
/** \brief The global coordinates, if you really want them */
const CoordinateType& globalCoordinates() const {
return data_;
}
/** \brief Compute an orthonormal basis of the tangent space of H^N.
*/
Dune::FieldMatrix<T,N,N> orthonormalFrame() const {
/** \brief Compute an orthonormal basis of the tangent space of H^N.
*/
Dune::FieldMatrix<T,N,N> orthonormalFrame() const {
Dune::ScaledIdentityMatrix<T,N> result( data_[N-1] );
Dune::ScaledIdentityMatrix<T,N> result( data_[N-1] );
return Dune::FieldMatrix<T,N,N>(result);
}
return Dune::FieldMatrix<T,N,N>(result);
}
/** \brief Scalar product of two tangent vectors */
T metric(const TangentVector& v, const TangentVector& w) const
{
return v*w/(data_[N-1]*data_[N-1]);
}
/** \brief Scalar product of two tangent vectors */
T metric(const TangentVector& v, const TangentVector& w) const
{
return v*w/(data_[N-1]*data_[N-1]);
}
/** \brief Write unit vector object to output stream */
friend std::ostream& operator<< (std::ostream& s, const HyperbolicHalfspacePoint& p)
{
return s << p.data_;
}
/** \brief Write unit vector object to output stream */
friend std::ostream& operator<< (std::ostream& s, const HyperbolicHalfspacePoint& p)
{
return s << p.data_;
}
private:
private:
Dune::FieldVector<T,N> data_;
};
Dune::FieldVector<T,N> data_;
};
} // namespace Dune::GFE
#endif
......@@ -5,176 +5,182 @@
#include <dune/gfe/skewmatrix.hh>
/** \brief An orthogonal \f$ n \times n \f$ matrix
* \tparam T Type of the matrix entries
* \tparam N Space dimension
*/
template <class T, int N>
class OrthogonalMatrix
{
public:
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief Dimension of the manifold formed by the orthogonal matrices */
static const int dim = N*(N-1)/2;
/** \brief Coordinates are embedded into a Euclidean space of N x N - matrices */
static const int embeddedDim = N*N;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,embeddedDim> CoordinateType;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */
typedef Dune::FieldVector<T,dim> TangentVector;
/** \brief A tangent vector as a vector in the surrounding coordinate space */
typedef Dune::FieldVector<T,embeddedDim> EmbeddedTangentVector;
/** \brief Default constructor -- leaves the matrix uninitialized */
OrthogonalMatrix()
{}
/** \brief Constructor from a general matrix
*
* It is not checked whether the matrix is really orthogonal
*/
explicit OrthogonalMatrix(const Dune::FieldMatrix<T,N,N>& matrix)
: data_(matrix)
{}
namespace Dune::GFE
{
/** \brief Copy constructor
*
* It is not checked whether the matrix is really orthogonal
/** \brief An orthogonal \f$ n \times n \f$ matrix
* \tparam T Type of the matrix entries
* \tparam N Space dimension
*/
explicit OrthogonalMatrix(const CoordinateType& other)
template <class T, int N>
class OrthogonalMatrix
{
DUNE_THROW(Dune::NotImplemented, "constructor");
}
public:
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief Dimension of the manifold formed by the orthogonal matrices */
static const int dim = N*(N-1)/2;
/** \brief Coordinates are embedded into a Euclidean space of N x N - matrices */
static const int embeddedDim = N*N;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,embeddedDim> CoordinateType;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */
typedef Dune::FieldVector<T,dim> TangentVector;
/** \brief A tangent vector as a vector in the surrounding coordinate space */
typedef Dune::FieldVector<T,embeddedDim> EmbeddedTangentVector;
/** \brief Default constructor -- leaves the matrix uninitialized */
OrthogonalMatrix()
{}
/** \brief Constructor from a general matrix
*
* It is not checked whether the matrix is really orthogonal
*/
explicit OrthogonalMatrix(const Dune::FieldMatrix<T,N,N>& matrix)
: data_(matrix)
{}
/** \brief Copy constructor
*
* It is not checked whether the matrix is really orthogonal
*/
explicit OrthogonalMatrix(const CoordinateType& other)
{
DUNE_THROW(Dune::NotImplemented, "constructor");
}
/** \brief Const access to the matrix entries */
const Dune::FieldMatrix<T,N,N>& data() const
{
return data_;
}
/** \brief Project the input matrix onto the tangent space at "this"
*
* See Absil, Mahoney, Sepulche, Example 5.3.2 for the formula
* \f[ P_X \xi = X \operatorname{skew} (X^T \xi) \f]
*
*/
Dune::FieldMatrix<T,N,N> projectOntoTangentSpace(const Dune::FieldMatrix<T,N,N>& matrix) const
{
// rename to get the same notation as Absil & Co
const Dune::FieldMatrix<T,N,N>& X = data_;
// X^T \xi
Dune::FieldMatrix<T,N,N> XTxi(0);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++)
XTxi[i][j] += X[k][i] * matrix[k][j];
// skew (X^T \xi)
Dune::FieldMatrix<T,N,N> skewXTxi(0);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
skewXTxi[i][j] = 0.5 * ( XTxi[i][j] - XTxi[j][i] );
// X skew (X^T \xi)
Dune::FieldMatrix<T,N,N> result(0);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++)
result[i][j] += X[i][k] * skewXTxi[k][j];
return result;
}
/** \brief Rebind the OrthogonalMatrix to another coordinate type */
template<class U>
struct rebind
{
typedef OrthogonalMatrix<U,N> other;
};
OrthogonalMatrix& operator= (const CoordinateType& other)
{
std::size_t idx = 0;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
data_[i][j] = other[idx++];
return *this;
}
/** \brief The exponential map from a given point $p \in SO(n)$.
\param v A tangent vector.
*/
static OrthogonalMatrix<T,3> exp(const OrthogonalMatrix<T,N>& p, const TangentVector& v)
{
DUNE_THROW(Dune::NotImplemented, "exp");
}
/** \brief Return the actual matrix */
void matrix(Dune::FieldMatrix<T,N,N>& m) const
{
m = data_;
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const
{
DUNE_THROW(Dune::NotImplemented, "projectOntoNormalSpace");
}
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const
{
EmbeddedTangentVector result;
DUNE_THROW(Dune::NotImplemented, "weingarten");
return result;
}
static OrthogonalMatrix<T,N> projectOnto(const CoordinateType& p)
{
DUNE_THROW(Dune::NotImplemented, "projectOnto");
}
// TODO return type is wrong
static Dune::FieldMatrix<T,1,1> derivativeOfProjection(const CoordinateType& p)
{
Dune::FieldMatrix<T,1,1> result;
return result;
}
/** \brief The global coordinates, if you really want them */
CoordinateType globalCoordinates() const
{
CoordinateType result;
std::size_t idx = 0;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[idx++] = data_[i][j];
return result;
}
/** \brief Compute an orthonormal basis of the tangent space of SO(n). */
Dune::FieldMatrix<T,dim,embeddedDim> orthonormalFrame() const
{
Dune::FieldMatrix<T,dim,embeddedDim> result;
DUNE_THROW(Dune::NotImplemented, "orthonormalFrame");
return result;
}
private:
/** \brief The actual data */
Dune::FieldMatrix<T,N,N> data_;
/** \brief Const access to the matrix entries */
const Dune::FieldMatrix<T,N,N>& data() const
{
return data_;
}
/** \brief Project the input matrix onto the tangent space at "this"
*
* See Absil, Mahoney, Sepulche, Example 5.3.2 for the formula
* \f[ P_X \xi = X \operatorname{skew} (X^T \xi) \f]
*
*/
Dune::FieldMatrix<T,N,N> projectOntoTangentSpace(const Dune::FieldMatrix<T,N,N>& matrix) const
{
// rename to get the same notation as Absil & Co
const Dune::FieldMatrix<T,N,N>& X = data_;
// X^T \xi
Dune::FieldMatrix<T,N,N> XTxi(0);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++)
XTxi[i][j] += X[k][i] * matrix[k][j];
// skew (X^T \xi)
Dune::FieldMatrix<T,N,N> skewXTxi(0);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
skewXTxi[i][j] = 0.5 * ( XTxi[i][j] - XTxi[j][i] );
// X skew (X^T \xi)
Dune::FieldMatrix<T,N,N> result(0);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++)
result[i][j] += X[i][k] * skewXTxi[k][j];
return result;
}
/** \brief Rebind the OrthogonalMatrix to another coordinate type */
template<class U>
struct rebind
{
typedef OrthogonalMatrix<U,N> other;
};
OrthogonalMatrix& operator= (const CoordinateType& other)
{
std::size_t idx = 0;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
data_[i][j] = other[idx++];
return *this;
}
/** \brief The exponential map from a given point $p \in SO(n)$.
\param v A tangent vector.
*/
static OrthogonalMatrix<T,3> exp(const OrthogonalMatrix<T,N>& p, const TangentVector& v)
{
DUNE_THROW(Dune::NotImplemented, "exp");
}
/** \brief Return the actual matrix */
void matrix(Dune::FieldMatrix<T,N,N>& m) const
{
m = data_;
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const
{
DUNE_THROW(Dune::NotImplemented, "projectOntoNormalSpace");
}
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const
{
EmbeddedTangentVector result;
DUNE_THROW(Dune::NotImplemented, "weingarten");
return result;
}
static OrthogonalMatrix<T,N> projectOnto(const CoordinateType& p)
{
DUNE_THROW(Dune::NotImplemented, "projectOnto");
}
// TODO return type is wrong
static Dune::FieldMatrix<T,1,1> derivativeOfProjection(const CoordinateType& p)
{
Dune::FieldMatrix<T,1,1> result;
return result;
}
/** \brief The global coordinates, if you really want them */
CoordinateType globalCoordinates() const
{
CoordinateType result;
std::size_t idx = 0;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[idx++] = data_[i][j];
return result;
}
/** \brief Compute an orthonormal basis of the tangent space of SO(n). */
Dune::FieldMatrix<T,dim,embeddedDim> orthonormalFrame() const
{
Dune::FieldMatrix<T,dim,embeddedDim> result;
DUNE_THROW(Dune::NotImplemented, "orthonormalFrame");
return result;
}
private:
/** \brief The actual data */
Dune::FieldMatrix<T,N,N> data_;
};
} // namespace Dune::GFE
#endif // DUNE_GFE_SPACES_ORTHOGONALMATRIX_HH
......@@ -208,7 +208,7 @@ namespace Dune::GFE
static auto secondDerivativeOfDistanceSquaredWRTSecondArgument(const ProductManifold& a,
const ProductManifold& b)
{
Dune::SymmetricMatrix<field_type,embeddedDim> result;
SymmetricMatrix<field_type,embeddedDim> result;
auto secDerivOfDistSqWRTSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt)
{
auto& deriv = std::get<0>(argsTuple);
......
......@@ -7,165 +7,171 @@
#include <dune/gfe/tensor3.hh>
template <class T>
class Quaternion : public Dune::FieldVector<T,4>
namespace Dune::GFE
{
public:
/** \brief Default constructor */
Quaternion() {}
/** \brief Constructor with the four components */
Quaternion(const T& a, const T& b, const T& c, const T& d) {
(*this)[0] = a;
(*this)[1] = b;
(*this)[2] = c;
(*this)[3] = d;
}
/** \brief Constructor from a single scalar */
Quaternion(const T& a) : Dune::FieldVector<T,4>(a) {}
/** \brief Copy constructor */
Quaternion(const Dune::FieldVector<T,4>& other) : Dune::FieldVector<T,4>(other) {}
/** \brief Assignment from a scalar */
Quaternion<T>& operator=(const T& v) {
for (int i=0; i<4; i++)
(*this)[i] = v;
return (*this);
}
/** \brief Return the identity element */
static Quaternion<T> identity() {
Quaternion<T> id;
id[0] = 0;
id[1] = 0;
id[2] = 0;
id[3] = 1;
return id;
}
/** \brief Right quaternion multiplication */
Quaternion<T> mult(const Quaternion<T>& other) const {
Quaternion<T> q;
q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3];
q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3];
q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3];
q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3];
return q;
}
/** \brief Return the triple of director vectors represented by a unit quaternion
The formulas are taken from Dichmann, Li, Maddocks, (2.6.4), (2.6.5), (2.6.6)
*/
Dune::FieldVector<T,3> director(int i) const {
Dune::FieldVector<T,3> d;
const Dune::FieldVector<T,4>& q = *this; // simpler notation
if (i==0) {
d[0] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
d[1] = 2 * (q[0]*q[1] + q[2]*q[3]);
d[2] = 2 * (q[0]*q[2] - q[1]*q[3]);
} else if (i==1) {
d[0] = 2 * (q[0]*q[1] - q[2]*q[3]);
d[1] = -q[0]*q[0] + q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
d[2] = 2 * (q[1]*q[2] + q[0]*q[3]);
} else if (i==2) {
d[0] = 2 * (q[0]*q[2] + q[1]*q[3]);
d[1] = 2 * (q[1]*q[2] - q[0]*q[3]);
d[2] = -q[0]*q[0] - q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
} else
DUNE_THROW(Dune::Exception, "Nonexisting director " << i << " requested!");
return d;
}
/** \brief Turn quaternion into a unit quaternion by dividing by its Euclidean norm */
void normalize() {
(*this) /= this->two_norm();
}
Dune::FieldVector<double,3> rotate(const Dune::FieldVector<double,3>& v) const {
Dune::FieldVector<double,3> result;
Dune::FieldVector<double,3> d0 = director(0);
Dune::FieldVector<double,3> d1 = director(1);
Dune::FieldVector<double,3> d2 = director(2);
for (int i=0; i<3; i++)
result[i] = v[0]*d0[i] + v[1]*d1[i] + v[2]*d2[i];
return result;
}
/** \brief Conjugate the quaternion */
void conjugate() {
(*this)[0] *= -1;
(*this)[1] *= -1;
(*this)[2] *= -1;
}
/** \brief Invert the quaternion */
void invert() {
conjugate();
(*this) /= this->two_norm2();
}
/** \brief Yield the inverse quaternion */
Quaternion<T> inverse() const {
Quaternion<T> result = *this;
result.invert();
return result;
}
/** \brief Create three vectors which form an orthonormal basis of \mathbb{H} together
with this one.
This is used to compute the strain in rod problems.
See: Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in
Rod Mechanics', page 83
*/
Quaternion<T> B(int m) const {
assert(m>=0 && m<3);
Quaternion<T> r;
if (m==0) {
r[0] = (*this)[3];
r[1] = (*this)[2];
r[2] = -(*this)[1];
r[3] = -(*this)[0];
} else if (m==1) {
r[0] = -(*this)[2];
r[1] = (*this)[3];
r[2] = (*this)[0];
r[3] = -(*this)[1];
} else {
r[0] = (*this)[1];
r[1] = -(*this)[0];
r[2] = (*this)[3];
r[3] = -(*this)[2];
template <class T>
class Quaternion : public Dune::FieldVector<T,4>
{
public:
/** \brief Default constructor */
Quaternion() {}
/** \brief Constructor with the four components */
Quaternion(const T& a, const T& b, const T& c, const T& d) {
(*this)[0] = a;
(*this)[1] = b;
(*this)[2] = c;
(*this)[3] = d;
}
/** \brief Constructor from a single scalar */
Quaternion(const T& a) : Dune::FieldVector<T,4>(a) {}
/** \brief Copy constructor */
Quaternion(const Dune::FieldVector<T,4>& other) : Dune::FieldVector<T,4>(other) {}
/** \brief Assignment from a scalar */
Quaternion<T>& operator=(const T& v) {
for (int i=0; i<4; i++)
(*this)[i] = v;
return (*this);
}
/** \brief Return the identity element */
static Quaternion<T> identity() {
Quaternion<T> id;
id[0] = 0;
id[1] = 0;
id[2] = 0;
id[3] = 1;
return id;
}
/** \brief Right quaternion multiplication */
Quaternion<T> mult(const Quaternion<T>& other) const {
Quaternion<T> q;
q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3];
q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3];
q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3];
q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3];
return q;
}
/** \brief Return the triple of director vectors represented by a unit quaternion
The formulas are taken from Dichmann, Li, Maddocks, (2.6.4), (2.6.5), (2.6.6)
*/
Dune::FieldVector<T,3> director(int i) const {
Dune::FieldVector<T,3> d;
const Dune::FieldVector<T,4>& q = *this; // simpler notation
if (i==0) {
d[0] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
d[1] = 2 * (q[0]*q[1] + q[2]*q[3]);
d[2] = 2 * (q[0]*q[2] - q[1]*q[3]);
} else if (i==1) {
d[0] = 2 * (q[0]*q[1] - q[2]*q[3]);
d[1] = -q[0]*q[0] + q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
d[2] = 2 * (q[1]*q[2] + q[0]*q[3]);
} else if (i==2) {
d[0] = 2 * (q[0]*q[2] + q[1]*q[3]);
d[1] = 2 * (q[1]*q[2] - q[0]*q[3]);
d[2] = -q[0]*q[0] - q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
} else
DUNE_THROW(Dune::Exception, "Nonexisting director " << i << " requested!");
return d;
}
/** \brief Turn quaternion into a unit quaternion by dividing by its Euclidean norm */
void normalize() {
(*this) /= this->two_norm();
}
return r;
}
Dune::FieldVector<double,3> rotate(const Dune::FieldVector<double,3>& v) const {
Dune::FieldVector<double,3> result;
Dune::FieldVector<double,3> d0 = director(0);
Dune::FieldVector<double,3> d1 = director(1);
Dune::FieldVector<double,3> d2 = director(2);
for (int i=0; i<3; i++)
result[i] = v[0]*d0[i] + v[1]*d1[i] + v[2]*d2[i];
return result;
}
/** \brief Conjugate the quaternion */
void conjugate() {
(*this)[0] *= -1;
(*this)[1] *= -1;
(*this)[2] *= -1;
}
/** \brief Invert the quaternion */
void invert() {
conjugate();
(*this) /= this->two_norm2();
}
/** \brief Yield the inverse quaternion */
Quaternion<T> inverse() const {
Quaternion<T> result = *this;
result.invert();
return result;
}
/** \brief Create three vectors which form an orthonormal basis of \mathbb{H} together
with this one.
This is used to compute the strain in rod problems.
See: Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in
Rod Mechanics', page 83
*/
Quaternion<T> B(int m) const {
assert(m>=0 && m<3);
Quaternion<T> r;
if (m==0) {
r[0] = (*this)[3];
r[1] = (*this)[2];
r[2] = -(*this)[1];
r[3] = -(*this)[0];
} else if (m==1) {
r[0] = -(*this)[2];
r[1] = (*this)[3];
r[2] = (*this)[0];
r[3] = -(*this)[1];
} else {
r[0] = (*this)[1];
r[1] = -(*this)[0];
r[2] = (*this)[3];
r[3] = -(*this)[2];
}
return r;
}
};
};
} // namespace Dune::GFE
namespace Dune
{
/** \brief Specizalization needed to allow certain forms of matrix--quaternion multiplications */
template< class T >
struct FieldTraits< Quaternion<T> >
struct FieldTraits< Dune::GFE::Quaternion<T> >
{
typedef typename FieldTraits<T>::field_type field_type;
typedef typename FieldTraits<T>::real_type real_type;
......
......@@ -9,249 +9,254 @@
#include <dune/gfe/symmetricmatrix.hh>
/** \brief Implement a tuple of real numbers as a Riemannian manifold
Currently this class only exists for testing purposes.
*/
template <class T, int N>
class RealTuple
namespace Dune::GFE
{
public:
typedef T ctype;
typedef T field_type;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,N> CoordinateType;
/** \brief Dimension of the manifold formed by unit vectors */
static const int dim = N;
/** \brief Dimension of the Euclidean space the manifold is embedded in */
static const int embeddedDim = N;
typedef Dune::FieldVector<T,N> EmbeddedTangentVector;
typedef Dune::FieldVector<T,N> TangentVector;
/** \brief The global convexity radius of the Euclidean space */
static constexpr double convexityRadius = std::numeric_limits<double>::infinity();
/** \brief Implement a tuple of real numbers as a Riemannian manifold
/** \brief The return type of the derivativeOfProjection method */
typedef Dune::ScaledIdentityMatrix<T, N> DerivativeOfProjection;
/** \brief Default constructor */
RealTuple()
{}
Currently this class only exists for testing purposes.
*/
/** \brief Construction from a scalar */
RealTuple(T v)
template <class T, int N>
class RealTuple
{
data_ = v;
}
/** \brief Copy constructor */
RealTuple(const RealTuple<T,N>& other)
: data_(other.data_)
{}
/** \brief Constructor from FieldVector*/
RealTuple(const Dune::FieldVector<T,N>& other)
: data_(other)
{}
RealTuple& operator=(const Dune::FieldVector<T,N>& other) {
data_ = other;
return *this;
}
RealTuple& operator-=(const Dune::FieldVector<T,N>& other) {
data_ -= other;
return *this;
}
template <class T2>
RealTuple& operator-=(const RealTuple<T2,N>& other) {
data_ -= other.data_;
return *this;
}
/** \brief Assignment from RealTuple with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
RealTuple& operator <<= (const RealTuple<T2,N>& other) {
for (size_t i=0; i<N; i++)
data_[i] <<= other.data_[i];
return *this;
}
/** \brief Const random-access operator*/
T operator[] (const size_t indexVariable ) const {
return data_[indexVariable];
}
/** \brief Rebind the RealTuple to another coordinate type */
template<class U>
struct rebind
{
typedef RealTuple<U,N> other;
};
/** \brief The exponention map */
static RealTuple exp(const RealTuple& p, const TangentVector& v) {
return RealTuple(p.data_+v);
}
/** \brief Geodesic distance between two points
Simply the Euclidean distance */
static T distance(const RealTuple& a, const RealTuple& b) {
return log(a.data_,b.data_).two_norm();
}
/** \brief The logarithmic map
*
* \result A vector in the tangent space of a, viz: b-a
* */
static auto log(const RealTuple& a, const RealTuple& b) {
return b.data_ - a.data_;
}
public:
typedef T ctype;
typedef T field_type;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,N> CoordinateType;
/** \brief Dimension of the manifold formed by unit vectors */
static const int dim = N;
/** \brief Dimension of the Euclidean space the manifold is embedded in */
static const int embeddedDim = N;
typedef Dune::FieldVector<T,N> EmbeddedTangentVector;
typedef Dune::FieldVector<T,N> TangentVector;
/** \brief The global convexity radius of the Euclidean space */
static constexpr double convexityRadius = std::numeric_limits<double>::infinity();
/** \brief The return type of the derivativeOfProjection method */
typedef Dune::ScaledIdentityMatrix<T, N> DerivativeOfProjection;
/** \brief Default constructor */
RealTuple()
{}
/** \brief Construction from a scalar */
RealTuple(T v)
{
data_ = v;
}
/** \brief Copy constructor */
RealTuple(const RealTuple<T,N>& other)
: data_(other.data_)
{}
/** \brief Constructor from FieldVector*/
RealTuple(const Dune::FieldVector<T,N>& other)
: data_(other)
{}
RealTuple& operator=(const Dune::FieldVector<T,N>& other) {
data_ = other;
return *this;
}
RealTuple& operator-=(const Dune::FieldVector<T,N>& other) {
data_ -= other;
return *this;
}
template <class T2>
RealTuple& operator-=(const RealTuple<T2,N>& other) {
data_ -= other.data_;
return *this;
}
/** \brief Assignment from RealTuple with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
RealTuple& operator <<= (const RealTuple<T2,N>& other) {
for (size_t i=0; i<N; i++)
data_[i] <<= other.data_[i];
return *this;
}
/** \brief Const random-access operator*/
T operator[] (const size_t indexVariable ) const {
return data_[indexVariable];
}
/** \brief Rebind the RealTuple to another coordinate type */
template<class U>
struct rebind
{
typedef RealTuple<U,N> other;
};
/** \brief The exponention map */
static RealTuple exp(const RealTuple& p, const TangentVector& v) {
return RealTuple(p.data_+v);
}
/** \brief Geodesic distance between two points
Simply the Euclidean distance */
static T distance(const RealTuple& a, const RealTuple& b) {
return log(a.data_,b.data_).two_norm();
}
/** \brief The logarithmic map
*
* \result A vector in the tangent space of a, viz: b-a
* */
static auto log(const RealTuple& a, const RealTuple& b) {
return b.data_ - a.data_;
}
#if ADOLC_ADOUBLE_H
/** \brief Geodesic distance squared between two points
Simply the Euclidean distance squared */
static adouble distanceSquared(const RealTuple<double,N>& a, const RealTuple<adouble,N>& b) {
adouble result(0.0);
for (int i=0; i<N; i++)
result += (a.globalCoordinates()[i] - b.globalCoordinates()[i]) * (a.globalCoordinates()[i] - b.globalCoordinates()[i]);
return result;
}
/** \brief Geodesic distance squared between two points
Simply the Euclidean distance squared */
static adouble distanceSquared(const RealTuple<double,N>& a, const RealTuple<adouble,N>& b) {
adouble result(0.0);
for (int i=0; i<N; i++)
result += (a.globalCoordinates()[i] - b.globalCoordinates()[i]) * (a.globalCoordinates()[i] - b.globalCoordinates()[i]);
return result;
}
#endif
/** \brief Compute the gradient of the squared distance function keeping the first argument fixed
/** \brief Compute the gradient of the squared distance function keeping the first argument fixed
Unlike the distance itself the squared distance is differentiable at zero
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) {
EmbeddedTangentVector result = a.data_;
result -= b.data_;
result *= -2;
return result;
}
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed
Unlike the distance itself the squared distance is differentiable at zero
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) {
EmbeddedTangentVector result = a.data_;
result -= b.data_;
result *= -2;
return result;
}
Unlike the distance itself the squared distance is differentiable at zero
*/
static SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) {
SymmetricMatrix<T,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<=i; j++)
result(i,j) = 2*(i==j);
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed
return result;
}
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) {
/** \brief Compute the mixed second derivate \partial d^2 / \partial da db
Dune::SymmetricMatrix<T,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<=i; j++)
result(i,j) = 2*(i==j);
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RealTuple& a, const RealTuple& b) {
return result;
}
Dune::FieldMatrix<T,N,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = -2*(i==j);
/** \brief Compute the mixed second derivate \partial d^2 / \partial da db
return result;
}
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RealTuple& a, const RealTuple& b) {
/** \brief Compute the mixed third derivative \partial d^3 / \partial db^3
Dune::FieldMatrix<T,N,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = -2*(i==j);
The result is the constant zero-tensor.
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) {
return Tensor3<T,N,N,N>(0);
}
return result;
}
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
/** \brief Compute the mixed third derivative \partial d^3 / \partial db^3
The result is the constant zero-tensor.
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RealTuple& a, const RealTuple& b) {
return Tensor3<T,N,N,N>(0);
}
The result is the constant zero-tensor.
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) {
return Tensor3<T,N,N,N>(0);
}
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
return v;
}
The result is the constant zero-tensor.
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RealTuple& a, const RealTuple& b) {
return Tensor3<T,N,N,N>(0);
}
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
return v;
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
return EmbeddedTangentVector(0);
}
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
return EmbeddedTangentVector(0);
}
/** \brief Projection from the embedding space onto the manifold
*
* For RealTuples this is simply the identity map
*/
static RealTuple<T,N> projectOnto(const CoordinateType& p)
{
return RealTuple<T,N>(p);
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
return EmbeddedTangentVector(0);
}
/** \brief Derivative of the projection from the embedding space onto the manifold
*
* For RealTuples this is simply the identity
*/
static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p)
{
return Dune::ScaledIdentityMatrix<T,N>(1.0);
}
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
return EmbeddedTangentVector(0);
}
/** \brief The global coordinates, if you really want them */
const Dune::FieldVector<T,N>& globalCoordinates() const {
return data_;
}
/** \brief Projection from the embedding space onto the manifold
*
* For RealTuples this is simply the identity map
*/
static RealTuple<T,N> projectOnto(const CoordinateType& p)
{
return RealTuple<T,N>(p);
}
Dune::FieldVector<T,N>& globalCoordinates() {
return data_;
}
/** \brief Derivative of the projection from the embedding space onto the manifold
*
* For RealTuples this is simply the identity
*/
static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p)
{
return Dune::ScaledIdentityMatrix<T,N>(1.0);
}
/** \brief The global coordinates, if you really want them */
const Dune::FieldVector<T,N>& globalCoordinates() const {
return data_;
}
/** \brief Compute an orthonormal basis of the tangent space of R^n.
Dune::FieldVector<T,N>& globalCoordinates() {
return data_;
}
In general this frame field, may of course not be continuous, but for RealTuples it is.
*/
Dune::FieldMatrix<T,N,N> orthonormalFrame() const {
Dune::FieldMatrix<T,N,N> result;
/** \brief Compute an orthonormal basis of the tangent space of R^n.
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = (i==j);
return result;
}
In general this frame field, may of course not be continuous, but for RealTuples it is.
*/
Dune::FieldMatrix<T,N,N> orthonormalFrame() const {
Dune::FieldMatrix<T,N,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = (i==j);
return result;
}
/** \brief Write LocalKey object to output stream */
friend std::ostream& operator<< (std::ostream& s, const RealTuple& realTuple)
{
return s << realTuple.data_;
}
/** \brief Write LocalKey object to output stream */
friend std::ostream& operator<< (std::ostream& s, const RealTuple& realTuple)
{
return s << realTuple.data_;
}
private:
private:
Dune::FieldVector<T,N> data_;
Dune::FieldVector<T,N> data_;
};
};
} // namespace Dune::GFE
#endif
......@@ -18,454 +18,460 @@
#include <dune/gfe/spaces/quaternion.hh>
#include <dune/gfe/spaces/unitvector.hh>
template <class T, int dim>
class Rotation
{};
/** \brief Specialization for dim==2
\tparam T The type used for coordinates
*/
template <class T>
class Rotation<T,2>
namespace Dune::GFE
{
public:
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief Dimension of the manifold formed by the 2d rotations */
static const int dim = 1;
template <class T, int dim>
class Rotation
{};
/** \brief Coordinates are embedded in the euclidean space */
static const int embeddedDim = 1;
/** \brief Specialization for dim==2
\tparam T The type used for coordinates
*/
template <class T>
class Rotation<T,2>
{
public:
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */
typedef Dune::FieldVector<T,1> TangentVector;
/** \brief Dimension of the manifold formed by the 2d rotations */
static const int dim = 1;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix
/** \brief Coordinates are embedded in the euclidean space */
static const int embeddedDim = 1;
This vector is not really embedded in anything. I have to make my notation more consistent! */
typedef Dune::FieldVector<T,1> EmbeddedTangentVector;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */
typedef Dune::FieldVector<T,1> TangentVector;
/** \brief The global convexity radius of the rotation group */
static constexpr double convexityRadius = 0.5 * M_PI;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix
/** \brief Default constructor, create the identity rotation */
Rotation()
: angle_(0)
{}
This vector is not really embedded in anything. I have to make my notation more consistent! */
typedef Dune::FieldVector<T,1> EmbeddedTangentVector;
Rotation(const T& angle)
: angle_(angle)
{}
/** \brief The global convexity radius of the rotation group */
static constexpr double convexityRadius = 0.5 * M_PI;
/** \brief Return the identity element */
static Rotation<T,2> identity() {
// Default constructor creates an identity
Rotation<T,2> id;
return id;
}
/** \brief Default constructor, create the identity rotation */
Rotation()
: angle_(0)
{}
static T distance(const Rotation<T,2>& a, const Rotation<T,2>& b) {
T dist = a.angle_ - b.angle_;
while (dist < 0)
dist += 2*M_PI;
while (dist > 2*M_PI)
dist -= 2*M_PI;
Rotation(const T& angle)
: angle_(angle)
{}
return (dist <= M_PI) ? dist : 2*M_PI - dist;
}
/** \brief Return the identity element */
static Rotation<T,2> identity() {
// Default constructor creates an identity
Rotation<T,2> id;
return id;
}
/** \brief The exponential map from a given point $p \in SO(3)$. */
static Rotation<T,2> exp(const Rotation<T,2>& p, const TangentVector& v) {
Rotation<T,2> result = p;
result.angle_ += v;
return result;
}
static T distance(const Rotation<T,2>& a, const Rotation<T,2>& b) {
T dist = a.angle_ - b.angle_;
while (dist < 0)
dist += 2*M_PI;
while (dist > 2*M_PI)
dist -= 2*M_PI;
/** \brief The exponential map from \f$ \mathfrak{so}(2) \f$ to \f$ SO(2) \f$
*/
static Rotation<T,2> exp(const Dune::FieldVector<T,1>& v) {
Rotation<T,2> result;
result.angle_ = v[0];
return result;
}
return (dist <= M_PI) ? dist : 2*M_PI - dist;
}
static TangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a,
const Rotation<T,2>& b) {
// This assertion is here to remind me of the following laziness:
// The difference has to be computed modulo 2\pi
assert( std::abs(a.angle_ - b.angle_) <= M_PI );
return -2 * (a.angle_ - b.angle_);
}
/** \brief The exponential map from a given point $p \in SO(3)$. */
static Rotation<T,2> exp(const Rotation<T,2>& p, const TangentVector& v) {
Rotation<T,2> result = p;
result.angle_ += v;
return result;
}
static Dune::FieldMatrix<T,1,1> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a,
const Rotation<T,2>& b) {
return 2;
}
/** \brief The exponential map from \f$ \mathfrak{so}(2) \f$ to \f$ SO(2) \f$
*/
static Rotation<T,2> exp(const Dune::FieldVector<T,1>& v) {
Rotation<T,2> result;
result.angle_ = v[0];
return result;
}
/** \brief Right multiplication */
Rotation<T,2> mult(const Rotation<T,2>& other) const {
Rotation<T,2> q = *this;
q.angle_ += other.angle_;
return q;
}
static TangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a,
const Rotation<T,2>& b) {
// This assertion is here to remind me of the following laziness:
// The difference has to be computed modulo 2\pi
assert( std::abs(a.angle_ - b.angle_) <= M_PI );
return -2 * (a.angle_ - b.angle_);
}
/** \brief Compute an orthonormal basis of the tangent space of SO(3).
static Dune::FieldMatrix<T,1,1> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a,
const Rotation<T,2>& b) {
return 2;
}
This basis is of course not globally continuous.
*/
Dune::FieldMatrix<T,1,1> orthonormalFrame() const {
return Dune::FieldMatrix<T,1,1>(1);
}
/** \brief Right multiplication */
Rotation<T,2> mult(const Rotation<T,2>& other) const {
Rotation<T,2> q = *this;
q.angle_ += other.angle_;
return q;
}
//private:
/** \brief Compute an orthonormal basis of the tangent space of SO(3).
// We store the rotation as an angle
T angle_;
};
This basis is of course not globally continuous.
*/
Dune::FieldMatrix<T,1,1> orthonormalFrame() const {
return Dune::FieldMatrix<T,1,1>(1);
}
//! Send configuration to output stream
template <class T>
std::ostream& operator<< (std::ostream& s, const Rotation<T,2>& c)
{
return s << "[" << c.angle_ << " (" << std::sin(c.angle_) << " " << std::cos(c.angle_) << ") ]";
}
//private:
// We store the rotation as an angle
T angle_;
};
/** \brief Specialization for dim==3
//! Send configuration to output stream
template <class T>
std::ostream& operator<< (std::ostream& s, const Rotation<T,2>& c)
{
return s << "[" << c.angle_ << " (" << std::sin(c.angle_) << " " << std::cos(c.angle_) << ") ]";
}
Uses unit quaternion coordinates.
*/
template <class T>
class Rotation<T,3> : public Quaternion<T>
{
/** \brief Computes sin(x/2) / x without getting unstable for small x */
static T sincHalf(const T& x) {
using std::sin;
return (x < 1e-1) ? 0.5 - (x*x/48) + Dune::power(x,4)/3840 - Dune::power(x,6)/645120 : sin(x/2)/x;
}
/** \brief Specialization for dim==3
/** \brief Computes sin(sqrt(x)/2) / sqrt(x) without getting unstable for small x
*
* Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0,
* which ADOL-C doesn't like.
Uses unit quaternion coordinates.
*/
static T sincHalfOfSquare(const T& x) {
using std::sin;
using std::sqrt;
return (x < 1e-15) ? 0.5 - (x/48) + (x*x)/(120*32) : sin(sqrt(x)/2)/sqrt(x);
}
template <class T>
class Rotation<T,3> : public Quaternion<T>
{
/** \brief Computes sin(sqrt(x)) / sqrt(x) without getting unstable for small x
*
* Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0,
* which ADOL-C doesn't like.
*/
static T sincOfSquare(const T& x) {
using std::sin;
using std::sqrt;
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
return (x < 1e-2) ?
1-x/6
+x*x/120
-Dune::power(x,3)/5040
+Dune::power(x,4)/362880
-Dune::power(x,5)/39916800
+Dune::power(x,6)/6227020800
-Dune::power(x,7)/1307674368000: sin(sqrt(x))/sqrt(x);
}
/** \brief Computes sin(x/2) / x without getting unstable for small x */
static T sincHalf(const T& x) {
using std::sin;
return (x < 1e-1) ? 0.5 - (x*x/48) + Dune::power(x,4)/3840 - Dune::power(x,6)/645120 : sin(x/2)/x;
}
public:
/** \brief Computes sin(sqrt(x)/2) / sqrt(x) without getting unstable for small x
*
* Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0,
* which ADOL-C doesn't like.
*/
static T sincHalfOfSquare(const T& x) {
using std::sin;
using std::sqrt;
return (x < 1e-15) ? 0.5 - (x/48) + (x*x)/(120*32) : sin(sqrt(x)/2)/sqrt(x);
}
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief Computes sin(sqrt(x)) / sqrt(x) without getting unstable for small x
*
* Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0,
* which ADOL-C doesn't like.
*/
static T sincOfSquare(const T& x) {
using std::sin;
using std::sqrt;
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
return (x < 1e-2) ?
1-x/6
+x*x/120
-Dune::power(x,3)/5040
+Dune::power(x,4)/362880
-Dune::power(x,5)/39916800
+Dune::power(x,6)/6227020800
-Dune::power(x,7)/1307674368000: sin(sqrt(x))/sqrt(x);
}
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,4> CoordinateType;
public:
/** \brief Dimension of the manifold formed by the 3d rotations */
static const int dim = 3;
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief Coordinates are embedded into a four-dimension Euclidean space */
static const int embeddedDim = 4;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,4> CoordinateType;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */
typedef Dune::FieldVector<T,3> TangentVector;
/** \brief Dimension of the manifold formed by the 3d rotations */
static const int dim = 3;
/** \brief A tangent vector as a vector in the surrounding coordinate space */
typedef Quaternion<T> EmbeddedTangentVector;
/** \brief Coordinates are embedded into a four-dimension Euclidean space */
static const int embeddedDim = 4;
/** \brief The global convexity radius of the rotation group */
static constexpr double convexityRadius = 0.5 * M_PI;
/** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */
typedef Dune::FieldVector<T,3> TangentVector;
/** \brief Default constructor creates the identity element */
Rotation()
: Quaternion<T>(0,0,0,1)
{}
/** \brief A tangent vector as a vector in the surrounding coordinate space */
typedef Quaternion<T> EmbeddedTangentVector;
explicit Rotation(const std::array<T,4>& c)
{
for (int i=0; i<4; i++)
(*this)[i] = c[i];
/** \brief The global convexity radius of the rotation group */
static constexpr double convexityRadius = 0.5 * M_PI;
*this /= this->two_norm();
}
/** \brief Default constructor creates the identity element */
Rotation()
: Quaternion<T>(0,0,0,1)
{}
explicit Rotation(const Dune::FieldVector<T,4>& c)
: Quaternion<T>(c)
{
*this /= this->two_norm();
}
explicit Rotation(const std::array<T,4>& c)
{
for (int i=0; i<4; i++)
(*this)[i] = c[i];
Rotation(Dune::FieldVector<T,3> axis, T angle)
{
axis /= axis.two_norm();
axis *= std::sin(angle/2);
(*this)[0] = axis[0];
(*this)[1] = axis[1];
(*this)[2] = axis[2];
(*this)[3] = std::cos(angle/2);
}
*this /= this->two_norm();
}
/** \brief Rebind the Rotation to another coordinate type */
template<class U>
struct rebind
{
typedef Rotation<U,3> other;
};
explicit Rotation(const Dune::FieldVector<T,4>& c)
: Quaternion<T>(c)
{
*this /= this->two_norm();
}
Rotation& operator= (const Dune::FieldVector<T,4>& other)
{
for (int i=0; i<4; i++)
(*this)[i] = other[i];
*this /= this->two_norm();
return *this;
}
Rotation(Dune::FieldVector<T,3> axis, T angle)
{
axis /= axis.two_norm();
axis *= std::sin(angle/2);
(*this)[0] = axis[0];
(*this)[1] = axis[1];
(*this)[2] = axis[2];
(*this)[3] = std::cos(angle/2);
}
/** \brief Assignment from Rotation with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
Rotation& operator <<= (const Rotation<T2,3>& other) {
for (int i=0; i<4; i++)
(*this)[i] <<= other[i];
return *this;
}
/** \brief Rebind the Rotation to another coordinate type */
template<class U>
struct rebind
{
typedef Rotation<U,3> other;
};
/** \brief Return the identity element */
static Rotation<T,3> identity() {
// Default constructor creates an identity
Rotation<T,3> id;
return id;
}
Rotation& operator= (const Dune::FieldVector<T,4>& other)
{
for (int i=0; i<4; i++)
(*this)[i] = other[i];
*this /= this->two_norm();
return *this;
}
/** \brief Right multiplication */
Rotation<T,3> mult(const Rotation<T,3>& other) const {
Rotation<T,3> q;
q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3];
q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3];
q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3];
q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3];
/** \brief Assignment from Rotation with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
Rotation& operator <<= (const Rotation<T2,3>& other) {
for (int i=0; i<4; i++)
(*this)[i] <<= other[i];
return *this;
}
return q;
}
/** \brief Return the identity element */
static Rotation<T,3> identity() {
// Default constructor creates an identity
Rotation<T,3> id;
return id;
}
/** \brief The exponential map from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$
*/
static Rotation<T,3> exp(const SkewMatrix<T,3>& v) {
using std::cos;
using std::sqrt;
/** \brief Right multiplication */
Rotation<T,3> mult(const Rotation<T,3>& other) const {
Rotation<T,3> q;
q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3];
q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3];
q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3];
q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3];
return q;
}
Rotation<T,3> q;
/** \brief The exponential map from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$
*/
static Rotation<T,3> exp(const SkewMatrix<T,3>& v) {
using std::cos;
using std::sqrt;
Dune::FieldVector<T,3> vAxial = v.axial();
T normV2 = vAxial.two_norm2();
Rotation<T,3> q;
// Stabilization for small |v|
T sin = sincHalfOfSquare(normV2);
Dune::FieldVector<T,3> vAxial = v.axial();
T normV2 = vAxial.two_norm2();
assert(!std::isnan(sin));
// Stabilization for small |v|
T sin = sincHalfOfSquare(normV2);
q[0] = sin * vAxial[0];
q[1] = sin * vAxial[1];
q[2] = sin * vAxial[2];
assert(!std::isnan(sin));
// The series expansion of cos(x) at x=0 is
// 1 - x*x/2 + x*x*x*x/24 - ...
// hence the series of cos(x/2) is
// 1 - x*x/8 + x*x*x*x/384 - ...
q[3] = (normV2 < 1e-4)
q[0] = sin * vAxial[0];
q[1] = sin * vAxial[1];
q[2] = sin * vAxial[2];
// The series expansion of cos(x) at x=0 is
// 1 - x*x/2 + x*x*x*x/24 - ...
// hence the series of cos(x/2) is
// 1 - x*x/8 + x*x*x*x/384 - ...
q[3] = (normV2 < 1e-4)
? 1 - normV2/8 + normV2*normV2 / 384-Dune::power(normV2,3)/46080 + Dune::power(normV2,4)/10321920
: cos(sqrt(normV2)/2);
return q;
}
return q;
}
/** \brief The exponential map from a given point $p \in SO(3)$.
/** \brief The exponential map from a given point $p \in SO(3)$.
* \param v A tangent vector *at the identity*!
*/
static Rotation<T,3> exp(const Rotation<T,3>& p, const SkewMatrix<T,3>& v) {
Rotation<T,3> corr = exp(v);
return p.mult(corr);
}
* \param v A tangent vector *at the identity*!
*/
static Rotation<T,3> exp(const Rotation<T,3>& p, const SkewMatrix<T,3>& v) {
Rotation<T,3> corr = exp(v);
return p.mult(corr);
}
/** \brief The exponential map from a given point $p \in SO(3)$.
/** \brief The exponential map from a given point $p \in SO(3)$.
There may be a more direct way to implement this
There may be a more direct way to implement this
\param v A tangent vector in quaternion coordinates
*/
static Rotation<T,3> exp(const Rotation<T,3>& p, const Dune::FieldVector<T,4>& v) {
\param v A tangent vector in quaternion coordinates
*/
static Rotation<T,3> exp(const Rotation<T,3>& p, const Dune::FieldVector<T,4>& v) {
using std::abs;
using std::cos;
using std::sqrt;
assert( abs(p*v) < 1e-8 );
using std::abs;
using std::cos;
using std::sqrt;
assert( abs(p*v) < 1e-8 );
const T norm2 = v.two_norm2();
const T norm2 = v.two_norm2();
Rotation<T,3> result = p;
Rotation<T,3> result = p;
// The series expansion of cos(x) at x=0 is
// 1 - x*x/2 + x*x*x*x/24 - ...
T cosValue = (norm2 < 1e-5)
// The series expansion of cos(x) at x=0 is
// 1 - x*x/2 + x*x*x*x/24 - ...
T cosValue = (norm2 < 1e-5)
? 1 - norm2/2 + norm2*norm2 / 24 - Dune::power(norm2,3)/720+Dune::power(norm2,4)/40320
: cos(sqrt(norm2));
result *= cosValue;
result *= cosValue;
result.axpy(sincOfSquare(norm2), v);
return result;
}
result.axpy(sincOfSquare(norm2), v);
return result;
}
/** \brief The exponential map from a given point $p \in SO(3)$.
/** \brief The exponential map from a given point $p \in SO(3)$.
\param v A tangent vector.
*/
static Rotation<T,3> exp(const Rotation<T,3>& p, const TangentVector& v) {
\param v A tangent vector.
*/
static Rotation<T,3> exp(const Rotation<T,3>& p, const TangentVector& v) {
// embedded tangent vector
Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame();
Quaternion<T> embeddedTangent;
basis.mtv(v, embeddedTangent);
// embedded tangent vector
Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame();
Quaternion<T> embeddedTangent;
basis.mtv(v, embeddedTangent);
return exp(p,embeddedTangent);
return exp(p,embeddedTangent);
}
}
/** \brief Compute tangent vector from given basepoint and skew symmetric matrix. */
static TangentVector skewToTangentVector(const Rotation<T,3>& p, const SkewMatrix<T,3>& v ) {
/** \brief Compute tangent vector from given basepoint and skew symmetric matrix. */
static TangentVector skewToTangentVector(const Rotation<T,3>& p, const SkewMatrix<T,3>& v ) {
// embedded tangent vector at identity
Quaternion<T> vAtIdentity(0);
vAtIdentity[0] = 0.5*v.axial()[0];
vAtIdentity[1] = 0.5*v.axial()[1];
vAtIdentity[2] = 0.5*v.axial()[2];
// embedded tangent vector at identity
Quaternion<T> vAtIdentity(0);
vAtIdentity[0] = 0.5*v.axial()[0];
vAtIdentity[1] = 0.5*v.axial()[1];
vAtIdentity[2] = 0.5*v.axial()[2];
// multiply with base point to get real embedded tangent vector
Quaternion<T> vQuat = ((Quaternion<T>)p).mult(vAtIdentity);
// multiply with base point to get real embedded tangent vector
Quaternion<T> vQuat = ((Quaternion<T>)p).mult(vAtIdentity);
//get basis of the tangent space
Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame();
//get basis of the tangent space
Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame();
// transform coordinates
TangentVector tang;
basis.mv(vQuat,tang);
// transform coordinates
TangentVector tang;
basis.mv(vQuat,tang);
return tang;
}
return tang;
}
/** \brief Compute skew matrix from given basepoint and tangent vector. */
static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const TangentVector& tangent) {
/** \brief Compute skew matrix from given basepoint and tangent vector. */
static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const TangentVector& tangent) {
// embedded tangent vector
Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame();
Quaternion<T> embeddedTangent;
basis.mtv(tangent, embeddedTangent);
// embedded tangent vector
Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame();
Quaternion<T> embeddedTangent;
basis.mtv(tangent, embeddedTangent);
return tangentToSkew(p,embeddedTangent);
}
return tangentToSkew(p,embeddedTangent);
}
/** \brief Compute skew matrix from given basepoint and an embedded tangent vector. */
static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const EmbeddedTangentVector& q) {
/** \brief Compute skew matrix from given basepoint and an embedded tangent vector. */
static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const EmbeddedTangentVector& q) {
// left multiplication by the inverse base point yields a tangent vector at the identity
Quaternion<T> vAtIdentity = p.inverse().mult(q);
assert( std::abs(vAtIdentity[3]) < 1e-8 );
// left multiplication by the inverse base point yields a tangent vector at the identity
Quaternion<T> vAtIdentity = p.inverse().mult(q);
assert( std::abs(vAtIdentity[3]) < 1e-8 );
SkewMatrix<T,3> skew;
skew.axial()[0] = 2*vAtIdentity[0];
skew.axial()[1] = 2*vAtIdentity[1];
skew.axial()[2] = 2*vAtIdentity[2];
SkewMatrix<T,3> skew;
skew.axial()[0] = 2*vAtIdentity[0];
skew.axial()[1] = 2*vAtIdentity[1];
skew.axial()[2] = 2*vAtIdentity[2];
return skew;
}
return skew;
}
/** \brief Derivative of the exponential map at the identity
*
* The exponential map at the identity is a map from a neighborhood of zero to the neighborhood of the identity rotation.
*
* \param v Where to evaluate the derivative of the (exponential map at the identity)
*/
static Dune::FieldMatrix<T,4,3> Dexp(const SkewMatrix<T,3>& v) {
/** \brief Derivative of the exponential map at the identity
*
* The exponential map at the identity is a map from a neighborhood of zero to the neighborhood of the identity rotation.
*
* \param v Where to evaluate the derivative of the (exponential map at the identity)
*/
static Dune::FieldMatrix<T,4,3> Dexp(const SkewMatrix<T,3>& v) {
using std::cos;
using std::cos;
Dune::FieldMatrix<T,4,3> result(0);
Dune::FieldVector<T,3> vAxial = v.axial();
T norm = vAxial.two_norm();
Dune::FieldMatrix<T,4,3> result(0);
Dune::FieldVector<T,3> vAxial = v.axial();
T norm = vAxial.two_norm();
for (int i=0; i<3; i++) {
for (int i=0; i<3; i++) {
for (int m=0; m<3; m++) {
for (int m=0; m<3; m++) {
result[m][i] = (norm<1e-10)
/** \todo Isn't there a better way to implement this stably? */
result[m][i] = (norm<1e-10)
/** \todo Isn't there a better way to implement this stably? */
? T(0.5) * (i==m)
: 0.5 * cos(norm/2) * vAxial[i] * vAxial[m] / (norm*norm) + sincHalf(norm) * ( (i==m) - vAxial[i]*vAxial[m]/(norm*norm));
}
}
result[3][i] = - 0.5 * sincHalf(norm) * vAxial[i];
result[3][i] = - 0.5 * sincHalf(norm) * vAxial[i];
}
return result;
}
return result;
}
static void DDexp(const Dune::FieldVector<T,3>& v,
std::array<Dune::FieldMatrix<T,3,3>, 4>& result) {
static void DDexp(const Dune::FieldVector<T,3>& v,
std::array<Dune::FieldMatrix<T,3,3>, 4>& result) {
using std::cos;
using std::sin;
using std::cos;
using std::sin;
T norm = v.two_norm();
if (norm<=1e-10) {
T norm = v.two_norm();
if (norm<=1e-10) {
for (int m=0; m<4; m++)
result[m] = 0;
for (int m=0; m<4; m++)
result[m] = 0;
for (int i=0; i<3; i++)
result[3][i][i] = -0.25;
for (int i=0; i<3; i++)
result[3][i][i] = -0.25;
} else {
} else {
for (int i=0; i<3; i++) {
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
for (int j=0; j<3; j++) {
for (int m=0; m<3; m++) {
for (int m=0; m<3; m++) {
result[m][i][j] = -0.25*sin(norm/2)*v[i]*v[j]*v[m]/(norm*norm*norm)
+ ((i==j)*v[m] + (j==m)*v[i] + (i==m)*v[j] - 3*v[i]*v[j]*v[m]/(norm*norm))
* (0.5*cos(norm/2) - sincHalf(norm)) / (norm*norm);
result[m][i][j] = -0.25*sin(norm/2)*v[i]*v[j]*v[m]/(norm*norm*norm)
+ ((i==j)*v[m] + (j==m)*v[i] + (i==m)*v[j] - 3*v[i]*v[j]*v[m]/(norm*norm))
* (0.5*cos(norm/2) - sincHalf(norm)) / (norm*norm);
}
}
result[3][i][j] = -0.5/(norm*norm)
* ( 0.5*cos(norm/2)*v[i]*v[j] + sin(norm/2) * ((i==j)*norm - v[i]*v[j]/norm));
result[3][i][j] = -0.5/(norm*norm)
* ( 0.5*cos(norm/2)*v[i]*v[j] + sin(norm/2) * ((i==j)*norm - v[i]*v[j]/norm));
}
}
......@@ -473,779 +479,802 @@ public:
}
}
/** \brief The inverse of the exponential map */
static Dune::FieldVector<T,3> expInv(const Rotation<T,3>& q) {
// Compute v = exp^{-1} q
// Due to numerical dirt, q[3] may be larger than 1.
// In that case, use 1 instead of q[3].
Dune::FieldVector<T,3> v;
if (q[3] > 1.0) {
/** \brief The inverse of the exponential map */
static Dune::FieldVector<T,3> expInv(const Rotation<T,3>& q) {
// Compute v = exp^{-1} q
// Due to numerical dirt, q[3] may be larger than 1.
// In that case, use 1 instead of q[3].
Dune::FieldVector<T,3> v;
if (q[3] > 1.0) {
v = 0;
v = 0;
} else {
} else {
using std::acos;
T invSinc = 1/sincHalf(2*acos(q[3]));
using std::acos;
T invSinc = 1/sincHalf(2*acos(q[3]));
v[0] = q[0] * invSinc;
v[1] = q[1] * invSinc;
v[2] = q[2] * invSinc;
v[0] = q[0] * invSinc;
v[1] = q[1] * invSinc;
v[2] = q[2] * invSinc;
}
return v;
}
return v;
}
/** \brief The derivative of the inverse of the exponential map, evaluated at q */
static Dune::FieldMatrix<T,3,4> DexpInv(const Rotation<T,3>& q) {
/** \brief The derivative of the inverse of the exponential map, evaluated at q */
static Dune::FieldMatrix<T,3,4> DexpInv(const Rotation<T,3>& q) {
// Compute v = exp^{-1} q
Dune::FieldVector<T,3> v = expInv(q);
// Compute v = exp^{-1} q
Dune::FieldVector<T,3> v = expInv(q);
// The derivative of exp at v
Dune::FieldMatrix<T,4,3> A = Dexp(SkewMatrix<T,3>(v));
// The derivative of exp at v
Dune::FieldMatrix<T,4,3> A = Dexp(SkewMatrix<T,3>(v));
// Compute the Moore-Penrose pseudo inverse A^+ = (A^T A)^{-1} A^T
Dune::FieldMatrix<T,3,3> ATA;
// Compute the Moore-Penrose pseudo inverse A^+ = (A^T A)^{-1} A^T
Dune::FieldMatrix<T,3,3> ATA;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++) {
ATA[i][j] = 0;
for (int k=0; k<4; k++)
ATA[i][j] += A[k][i] * A[k][j];
}
for (int i=0; i<3; i++)
for (int j=0; j<3; j++) {
ATA[i][j] = 0;
for (int k=0; k<4; k++)
ATA[i][j] += A[k][i] * A[k][j];
}
ATA.invert();
ATA.invert();
Dune::FieldMatrix<T,3,4> APseudoInv;
for (int i=0; i<3; i++)
for (int j=0; j<4; j++) {
APseudoInv[i][j] = 0;
for (int k=0; k<3; k++)
APseudoInv[i][j] += ATA[i][k] * A[j][k];
}
Dune::FieldMatrix<T,3,4> APseudoInv;
for (int i=0; i<3; i++)
for (int j=0; j<4; j++) {
APseudoInv[i][j] = 0;
for (int k=0; k<3; k++)
APseudoInv[i][j] += ATA[i][k] * A[j][k];
}
return APseudoInv;
}
return APseudoInv;
}
/** \brief The cayley mapping from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$.
*
* The formula is taken from 'J.C.Simo, N.Tarnom, M.Doblare - Non-linear dynamics of
* three-dimensional rods:Exact energy and momentum conserving algorithms'
* (but the corrected version with 0.25 instead of 0.5 in the denominator)
*/
static Rotation<T,3> cayley(const SkewMatrix<T,3>& s) {
Rotation<T,3> q;
/** \brief The cayley mapping from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$.
*
* The formula is taken from 'J.C.Simo, N.Tarnom, M.Doblare - Non-linear dynamics of
* three-dimensional rods:Exact energy and momentum conserving algorithms'
* (but the corrected version with 0.25 instead of 0.5 in the denominator)
*/
static Rotation<T,3> cayley(const SkewMatrix<T,3>& s) {
Rotation<T,3> q;
Dune::FieldVector<T,3> vAxial = s.axial();
T norm = 0.25*vAxial.two_norm2() + 1;
Dune::FieldMatrix<T,3,3> mat = s.toMatrix();
mat *= 0.5;
Dune::FieldMatrix<T,3,3> skewSquare = mat;
skewSquare.rightmultiply(mat);
mat += skewSquare;
mat *= 2/norm;
Dune::FieldVector<T,3> vAxial = s.axial();
T norm = 0.25*vAxial.two_norm2() + 1;
for (int i=0; i<3; i++)
mat[i][i] += 1;
Dune::FieldMatrix<T,3,3> mat = s.toMatrix();
mat *= 0.5;
Dune::FieldMatrix<T,3,3> skewSquare = mat;
skewSquare.rightmultiply(mat);
mat += skewSquare;
mat *= 2/norm;
q.set(mat);
return q;
}
for (int i=0; i<3; i++)
mat[i][i] += 1;
/** \brief The inverse of the Cayley mapping.
*
* The formula is taken from J.M.Selig - Cayley Maps for SE(3).
*/
static SkewMatrix<T,3> cayleyInv(const Rotation<T,3> q) {
q.set(mat);
return q;
}
Dune::FieldMatrix<T,3,3> mat;
/** \brief The inverse of the Cayley mapping.
*
* The formula is taken from J.M.Selig - Cayley Maps for SE(3).
*/
static SkewMatrix<T,3> cayleyInv(const Rotation<T,3> q) {
// compute the trace of the rotation matrix
T trace = -q[0]*q[0] -q[1]*q[1] -q[2]*q[2]+3*q[3]*q[3];
Dune::FieldMatrix<T,3,3> mat;
if ( (trace+1)>1e-6 || (trace+1)<-1e-6) { // if this term doesn't vanish we can use a direct formula
// compute the trace of the rotation matrix
T trace = -q[0]*q[0] -q[1]*q[1] -q[2]*q[2]+3*q[3]*q[3];
q.matrix(mat);
Dune::FieldMatrix<T,3,3> matT;
Rotation<T,3>(q.inverse()).matrix(matT);
mat -= matT;
mat *= 2/(1+trace);
}
else { // use the formula that involves the computation of an inverse
Dune::FieldMatrix<T,3,3> inv;
q.matrix(inv);
Dune::FieldMatrix<T,3,3> notInv = inv;
for (int i=0; i<3; i++) {
inv[i][i] +=1;
notInv[i][i] -=1;
}
inv.invert();
mat = notInv.leftmultiply(inv);
mat *= 2;
}
if ( (trace+1)>1e-6 || (trace+1)<-1e-6) { // if this term doesn't vanish we can use a direct formula
// result is a skew symmetric matrix
SkewMatrix<T,3> res;
res.axial()[0] = mat[2][1];
res.axial()[1] = mat[0][2];
res.axial()[2] = mat[1][0];
q.matrix(mat);
Dune::FieldMatrix<T,3,3> matT;
Rotation<T,3>(q.inverse()).matrix(matT);
mat -= matT;
mat *= 2/(1+trace);
}
else { // use the formula that involves the computation of an inverse
Dune::FieldMatrix<T,3,3> inv;
q.matrix(inv);
Dune::FieldMatrix<T,3,3> notInv = inv;
return res;
for (int i=0; i<3; i++) {
inv[i][i] +=1;
notInv[i][i] -=1;
}
inv.invert();
mat = notInv.leftmultiply(inv);
mat *= 2;
}
// result is a skew symmetric matrix
SkewMatrix<T,3> res;
res.axial()[0] = mat[2][1];
res.axial()[1] = mat[0][2];
res.axial()[2] = mat[1][0];
static T distance(const Rotation<T,3>& a, const Rotation<T,3>& b) {
return res;
// Distance in the unit quaternions: 2*arccos( ((a^{-1) b)_3 )
// But note that (a^{-1}b)_3 is actually <a,b> (in R^4)
T sp = a.globalCoordinates() * b.globalCoordinates();
}
// Scalar product may be larger than 1.0, due to numerical dirt
using std::acos;
using std::min;
T dist = 2*acos( min(sp,T(1.0)) );
static T distance(const Rotation<T,3>& a, const Rotation<T,3>& b) {
// Make sure we do the right thing if a and b are not in the same sheet
// of the double covering of the unit quaternions over SO(3)
return (dist>=M_PI) ? (2*M_PI - dist) : dist;
}
// Distance in the unit quaternions: 2*arccos( ((a^{-1) b)_3 )
// But note that (a^{-1}b)_3 is actually <a,b> (in R^4)
T sp = a.globalCoordinates() * b.globalCoordinates();
/** \brief Compute the vector in T_aSO(3) that is mapped by the exponential map
to the geodesic from a to b
*/
static EmbeddedTangentVector log(const Rotation<T,3>& a, const Rotation<T,3>& b) {
// Scalar product may be larger than 1.0, due to numerical dirt
using std::acos;
using std::min;
T dist = 2*acos( min(sp,T(1.0)) );
// embedded tangent vector at identity
Quaternion<T> v;
// Make sure we do the right thing if a and b are not in the same sheet
// of the double covering of the unit quaternions over SO(3)
return (dist>=M_PI) ? (2*M_PI - dist) : dist;
}
Quaternion<T> diff = a;
diff.invert();
diff = diff.mult(b);
/** \brief Compute the vector in T_aSO(3) that is mapped by the exponential map
to the geodesic from a to b
*/
static EmbeddedTangentVector log(const Rotation<T,3>& a, const Rotation<T,3>& b) {
// embedded tangent vector at identity
Quaternion<T> v;
// Compute the geodesical distance between a and b on SO(3)
// Due to numerical dirt, diff[3] may be larger than 1.
// In that case, use 1 instead of diff[3].
if (diff[3] > 1.0) {
Quaternion<T> diff = a;
diff.invert();
diff = diff.mult(b);
v = 0;
// Compute the geodesical distance between a and b on SO(3)
// Due to numerical dirt, diff[3] may be larger than 1.
// In that case, use 1 instead of diff[3].
if (diff[3] > 1.0) {
} else {
v = 0;
// TODO: ADOL-C does not like this part of the code,
// because arccos is not differentiable at -1 and 1.
// (Even though the overall 'log' function is differentiable.)
using std::acos;
T dist = 2*acos( diff[3] );
} else {
// Make sure we do the right thing if a and b are not in the same sheet
// of the double covering of the unit quaternions over SO(3)
if (dist>=M_PI) {
dist = 2*M_PI - dist;
diff *= -1;
}
// TODO: ADOL-C does not like this part of the code,
// because arccos is not differentiable at -1 and 1.
// (Even though the overall 'log' function is differentiable.)
using std::acos;
T dist = 2*acos( diff[3] );
T invSinc = 1/sincHalf(dist);
// Make sure we do the right thing if a and b are not in the same sheet
// of the double covering of the unit quaternions over SO(3)
if (dist>=M_PI) {
dist = 2*M_PI - dist;
diff *= -1;
// Compute log on T_a SO(3)
v[0] = 0.5 * diff[0] * invSinc;
v[1] = 0.5 * diff[1] * invSinc;
v[2] = 0.5 * diff[2] * invSinc;
v[3] = 0;
}
T invSinc = 1/sincHalf(dist);
// Compute log on T_a SO(3)
v[0] = 0.5 * diff[0] * invSinc;
v[1] = 0.5 * diff[1] * invSinc;
v[2] = 0.5 * diff[2] * invSinc;
v[3] = 0;
// multiply with base point to get real embedded tangent vector
return ((Quaternion<T>)a).mult(v);
}
// multiply with base point to get real embedded tangent vector
return ((Quaternion<T>)a).mult(v);
}
/** \brief Compute the derivatives of the director vectors with respect to the quaternion coordinates
*
* Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$.
* Then the return value of this method is
* \f[ A_{ijk} = \frac{\partial d_{i,j}}{\partial q_k} \f]
*/
void getFirstDerivativesOfDirectors(Tensor3<T,3, 3, 4>& dd_dq) const
{
const Quaternion<T>& q = (*this);
dd_dq[0][0] = { 2*q[0], -2*q[1], -2*q[2], 2*q[3]};
dd_dq[0][1] = { 2*q[1], 2*q[0], 2*q[3], 2*q[2]};
dd_dq[0][2] = { 2*q[2], -2*q[3], 2*q[0], -2*q[1]};
/** \brief Compute the derivatives of the director vectors with respect to the quaternion coordinates
*
* Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$.
* Then the return value of this method is
* \f[ A_{ijk} = \frac{\partial d_{i,j}}{\partial q_k} \f]
*/
void getFirstDerivativesOfDirectors(Tensor3<T,3, 3, 4>& dd_dq) const
{
const Quaternion<T>& q = (*this);
dd_dq[1][0] = { 2*q[1], 2*q[0], -2*q[3], -2*q[2]};
dd_dq[1][1] = {-2*q[0], 2*q[1], -2*q[2], 2*q[3]};
dd_dq[1][2] = { 2*q[3], 2*q[2], 2*q[1], 2*q[0]};
dd_dq[0][0] = { 2*q[0], -2*q[1], -2*q[2], 2*q[3]};
dd_dq[0][1] = { 2*q[1], 2*q[0], 2*q[3], 2*q[2]};
dd_dq[0][2] = { 2*q[2], -2*q[3], 2*q[0], -2*q[1]};
dd_dq[2][0] = { 2*q[2], 2*q[3], 2*q[0], 2*q[1]};
dd_dq[2][1] = {-2*q[3], 2*q[2], 2*q[1], -2*q[0]};
dd_dq[2][2] = {-2*q[0], -2*q[1], 2*q[2], 2*q[3]};
dd_dq[1][0] = { 2*q[1], 2*q[0], -2*q[3], -2*q[2]};
dd_dq[1][1] = {-2*q[0], 2*q[1], -2*q[2], 2*q[3]};
dd_dq[1][2] = { 2*q[3], 2*q[2], 2*q[1], 2*q[0]};
}
dd_dq[2][0] = { 2*q[2], 2*q[3], 2*q[0], 2*q[1]};
dd_dq[2][1] = {-2*q[3], 2*q[2], 2*q[1], -2*q[0]};
dd_dq[2][2] = {-2*q[0], -2*q[1], 2*q[2], 2*q[3]};
/** \brief Compute the second derivatives of the director vectors with respect to the quaternion coordinates
*
* Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$.
* Then the return value of this method is
* \f[ A_{ijkl} = \frac{\partial^2 d_{i,j}}{\partial q_k \partial q_l} \f]
*/
static void getSecondDerivativesOfDirectors(std::array<Tensor3<T,3, 4, 4>, 3>& dd_dq_dq)
{
for (int i=0; i<3; i++)
dd_dq_dq[i] = T(0);
}
dd_dq_dq[0][0][0][0] = 2; dd_dq_dq[0][0][1][1] = -2; dd_dq_dq[0][0][2][2] = -2; dd_dq_dq[0][0][3][3] = 2;
dd_dq_dq[0][1][0][1] = 2; dd_dq_dq[0][1][1][0] = 2; dd_dq_dq[0][1][2][3] = 2; dd_dq_dq[0][1][3][2] = 2;
dd_dq_dq[0][2][0][2] = 2; dd_dq_dq[0][2][1][3] = -2; dd_dq_dq[0][2][2][0] = 2; dd_dq_dq[0][2][3][1] = -2;
/** \brief Compute the second derivatives of the director vectors with respect to the quaternion coordinates
*
* Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$.
* Then the return value of this method is
* \f[ A_{ijkl} = \frac{\partial^2 d_{i,j}}{\partial q_k \partial q_l} \f]
*/
static void getSecondDerivativesOfDirectors(std::array<Tensor3<T,3, 4, 4>, 3>& dd_dq_dq)
{
for (int i=0; i<3; i++)
dd_dq_dq[i] = T(0);
dd_dq_dq[1][0][0][1] = 2; dd_dq_dq[1][0][1][0] = 2; dd_dq_dq[1][0][2][3] = -2; dd_dq_dq[1][0][3][2] = -2;
dd_dq_dq[1][1][0][0] = -2; dd_dq_dq[1][1][1][1] = 2; dd_dq_dq[1][1][2][2] = -2; dd_dq_dq[1][1][3][3] = 2;
dd_dq_dq[1][2][0][3] = 2; dd_dq_dq[1][2][1][2] = 2; dd_dq_dq[1][2][2][1] = 2; dd_dq_dq[1][2][3][0] = 2;
dd_dq_dq[0][0][0][0] = 2; dd_dq_dq[0][0][1][1] = -2; dd_dq_dq[0][0][2][2] = -2; dd_dq_dq[0][0][3][3] = 2;
dd_dq_dq[0][1][0][1] = 2; dd_dq_dq[0][1][1][0] = 2; dd_dq_dq[0][1][2][3] = 2; dd_dq_dq[0][1][3][2] = 2;
dd_dq_dq[0][2][0][2] = 2; dd_dq_dq[0][2][1][3] = -2; dd_dq_dq[0][2][2][0] = 2; dd_dq_dq[0][2][3][1] = -2;
dd_dq_dq[2][0][0][2] = 2; dd_dq_dq[2][0][1][3] = 2; dd_dq_dq[2][0][2][0] = 2; dd_dq_dq[2][0][3][1] = 2;
dd_dq_dq[2][1][0][3] = -2; dd_dq_dq[2][1][1][2] = 2; dd_dq_dq[2][1][2][1] = 2; dd_dq_dq[2][1][3][0] = -2;
dd_dq_dq[2][2][0][0] = -2; dd_dq_dq[2][2][1][1] = -2; dd_dq_dq[2][2][2][2] = 2; dd_dq_dq[2][2][3][3] = 2;
dd_dq_dq[1][0][0][1] = 2; dd_dq_dq[1][0][1][0] = 2; dd_dq_dq[1][0][2][3] = -2; dd_dq_dq[1][0][3][2] = -2;
dd_dq_dq[1][1][0][0] = -2; dd_dq_dq[1][1][1][1] = 2; dd_dq_dq[1][1][2][2] = -2; dd_dq_dq[1][1][3][3] = 2;
dd_dq_dq[1][2][0][3] = 2; dd_dq_dq[1][2][1][2] = 2; dd_dq_dq[1][2][2][1] = 2; dd_dq_dq[1][2][3][0] = 2;
}
dd_dq_dq[2][0][0][2] = 2; dd_dq_dq[2][0][1][3] = 2; dd_dq_dq[2][0][2][0] = 2; dd_dq_dq[2][0][3][1] = 2;
dd_dq_dq[2][1][0][3] = -2; dd_dq_dq[2][1][1][2] = 2; dd_dq_dq[2][1][2][1] = 2; dd_dq_dq[2][1][3][0] = -2;
dd_dq_dq[2][2][0][0] = -2; dd_dq_dq[2][2][1][1] = -2; dd_dq_dq[2][2][2][2] = 2; dd_dq_dq[2][2][3][3] = 2;
/** \brief Transform tangent vectors from quaternion representation to matrix representation
*
* This class represents rotations as unit quaternions, and therefore variations of rotations
* (i.e., tangent vector) are represented in quaternion space, too. However, some applications
* require the tangent vectors as matrices. To obtain matrix coordinates we use the
* chain rule, which means that we have to multiply the given derivative with
* the derivative of the embedding of the unit quaternion into the space of 3x3 matrices.
* This second derivative is almost given by the method getFirstDerivativesOfDirectors.
* However, since the directors of a given unit quaternion are the _columns_ of the
* corresponding orthogonal matrix, we need to invert the i and j indices
*
* As a typical GFE assembler will require this for several tangent vectors at once,
* the implementation here is a vector one: It allows to treat several tangent vectors
* together, which have to come as the columns of the `derivative` parameter.
*
* So, if I am not mistaken, result[i][j][k] contains \partial R_ij / \partial k
*
* \param derivative Tangent vector in quaternion coordinates
* \returns DR Tangent vector in matrix coordinates
*/
template <int blocksize>
Tensor3<T,3,3,blocksize> quaternionTangentToMatrixTangent(const Dune::FieldMatrix<T,4,blocksize>& derivative) const
{
Tensor3<T,3,3,blocksize> result = T(0);
Tensor3<T,3 , 3, 4> dd_dq;
getFirstDerivativesOfDirectors(dd_dq);
}
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<blocksize; k++)
for (int l=0; l<4; l++)
result[i][j][k] += dd_dq[j][i][l] * derivative[l][k];
/** \brief Transform tangent vectors from quaternion representation to matrix representation
*
* This class represents rotations as unit quaternions, and therefore variations of rotations
* (i.e., tangent vector) are represented in quaternion space, too. However, some applications
* require the tangent vectors as matrices. To obtain matrix coordinates we use the
* chain rule, which means that we have to multiply the given derivative with
* the derivative of the embedding of the unit quaternion into the space of 3x3 matrices.
* This second derivative is almost given by the method getFirstDerivativesOfDirectors.
* However, since the directors of a given unit quaternion are the _columns_ of the
* corresponding orthogonal matrix, we need to invert the i and j indices
*
* As a typical GFE assembler will require this for several tangent vectors at once,
* the implementation here is a vector one: It allows to treat several tangent vectors
* together, which have to come as the columns of the `derivative` parameter.
*
* So, if I am not mistaken, result[i][j][k] contains \partial R_ij / \partial k
*
* \param derivative Tangent vector in quaternion coordinates
* \returns DR Tangent vector in matrix coordinates
*/
template <int blocksize>
Tensor3<T,3,3,blocksize> quaternionTangentToMatrixTangent(const Dune::FieldMatrix<T,4,blocksize>& derivative) const
{
Tensor3<T,3,3,blocksize> result = T(0);
return result;
}
Tensor3<T,3 , 3, 4> dd_dq;
getFirstDerivativesOfDirectors(dd_dq);
/** \brief Compute the derivative of the squared distance function with respect to the second argument
*
* The squared distance function is
* \f[ 4 \arccos^2 (|<p,q>|) \f]
* Deriving this with respect to the second coordinate gives
* \f[ 4 (\arccos^2)'(x)|_{x = |<p,q>|} * P_qp \f]
* The whole thing has to be multiplied by -1 if \f$ <p,q> \f$ is negative
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p,
const Rotation<T,3>& q) {
T sp = p.globalCoordinates() * q.globalCoordinates();
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<blocksize; k++)
for (int l=0; l<4; l++)
result[i][j][k] += dd_dq[j][i][l] * derivative[l][k];
// Compute the projection of p onto the tangent space of q
EmbeddedTangentVector result = p;
result.axpy(-1*(q*p), q);
return result;
}
// The ternary operator comes from the derivative of the absolute value function
using std::abs;
result *= 4 * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ( (sp<0) ? -1 : 1 );
/** \brief Compute the derivative of the squared distance function with respect to the second argument
*
* The squared distance function is
* \f[ 4 \arccos^2 (|<p,q>|) \f]
* Deriving this with respect to the second coordinate gives
* \f[ 4 (\arccos^2)'(x)|_{x = |<p,q>|} * P_qp \f]
* The whole thing has to be multiplied by -1 if \f$ <p,q> \f$ is negative
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p,
const Rotation<T,3>& q) {
T sp = p.globalCoordinates() * q.globalCoordinates();
// Compute the projection of p onto the tangent space of q
EmbeddedTangentVector result = p;
result.axpy(-1*(q*p), q);
// The ternary operator comes from the derivative of the absolute value function
using std::abs;
result *= 4 * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ( (sp<0) ? -1 : 1 );
return result;
}
return result;
}
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */
static SymmetricMatrix<T,4> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */
static Dune::SymmetricMatrix<T,4> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
T sp = p.globalCoordinates() * q.globalCoordinates();
T sp = p.globalCoordinates() * q.globalCoordinates();
EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
SymmetricMatrix<T,4> A;
for (int i=0; i<4; i++)
for (int j=0; j<=i; j++)
A(i,j) = pProjected[i]*pProjected[j];
Dune::SymmetricMatrix<T,4> A;
for (int i=0; i<4; i++)
for (int j=0; j<=i; j++)
A(i,j) = pProjected[i]*pProjected[j];
using std::abs;
A *= 4*UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp));
using std::abs;
A *= 4*UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp));
// Compute matrix B (see notes)
SymmetricMatrix<T,4> Pq;
for (int i=0; i<4; i++)
for (int j=0; j<=i; j++)
Pq(i,j) = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
// Compute matrix B (see notes)
Dune::SymmetricMatrix<T,4> Pq;
for (int i=0; i<4; i++)
for (int j=0; j<=i; j++)
Pq(i,j) = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
// Bring it all together
A.axpy(-4* ((sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp))*sp, Pq);
// Bring it all together
A.axpy(-4* ((sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp))*sp, Pq);
return A;
}
return A;
}
/** \brief Compute the mixed second derivate \partial d^2 / \partial dp dq */
static Dune::FieldMatrix<T,4,4> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
/** \brief Compute the mixed second derivate \partial d^2 / \partial dp dq */
static Dune::FieldMatrix<T,4,4> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
T sp = p.globalCoordinates() * q.globalCoordinates();
T sp = p.globalCoordinates() * q.globalCoordinates();
// Compute vector A (see notes)
Dune::FieldMatrix<T,1,4> row;
row[0] = q.projectOntoTangentSpace(p.globalCoordinates());
// Compute vector A (see notes)
Dune::FieldMatrix<T,1,4> row;
row[0] = q.projectOntoTangentSpace(p.globalCoordinates());
EmbeddedTangentVector tmp = p.projectOntoTangentSpace(q.globalCoordinates());
Dune::FieldMatrix<T,4,1> column;
for (int i=0; i<4; i++) // turn row vector into column vector
column[i] = tmp[i];
EmbeddedTangentVector tmp = p.projectOntoTangentSpace(q.globalCoordinates());
Dune::FieldMatrix<T,4,1> column;
for (int i=0; i<4; i++) // turn row vector into column vector
column[i] = tmp[i];
Dune::FieldMatrix<T,4,4> A;
// A = row * column
Dune::FMatrixHelp::multMatrix(column,row,A);
using std::abs;
A *= 4 * UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp));
Dune::FieldMatrix<T,4,4> A;
// A = row * column
Dune::FMatrixHelp::multMatrix(column,row,A);
using std::abs;
A *= 4 * UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp));
// Compute matrix B (see notes)
Dune::FieldMatrix<T,4,4> Pp, Pq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) {
Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j];
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
}
// Compute matrix B (see notes)
Dune::FieldMatrix<T,4,4> Pp, Pq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) {
Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j];
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
}
Dune::FieldMatrix<T,4,4> B;
Dune::FMatrixHelp::multMatrix(Pp,Pq,B);
Dune::FieldMatrix<T,4,4> B;
Dune::FMatrixHelp::multMatrix(Pp,Pq,B);
// Bring it all together
A.axpy(4 * ( (sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)), B);
// Bring it all together
A.axpy(4 * ( (sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)), B);
return A;
}
return A;
}
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
Tensor3<T,4,4,4> result;
Tensor3<T,4,4,4> result;
T sp = p.globalCoordinates() * q.globalCoordinates();
T sp = p.globalCoordinates() * q.globalCoordinates();
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,4,4> Pq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,4,4> Pq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
double plusMinus = (sp < 0) ? -1 : 1;
double plusMinus = (sp < 0) ? -1 : 1;
using std::abs;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
for (int k=0; k<4; k++) {
using std::abs;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
for (int k=0; k<4; k++) {
result[i][j][k] = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * pProjected[j] * pProjected[k]
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k]
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j]
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * Pq[j][k] * sp
+ plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp
- plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * p.globalCoordinates()[i] * Pq[j][k];
}
result[i][j][k] = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * pProjected[j] * pProjected[k]
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k]
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j]
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * Pq[j][k] * sp
+ plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp
- plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * p.globalCoordinates()[i] * Pq[j][k];
}
Pq *= 4;
result = Pq * result;
Pq *= 4;
result = Pq * result;
return result;
}
return result;
}
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const Rotation<T,3>& p, const Rotation<T,3>& q) {
Tensor3<T,4,4,4> result;
Tensor3<T,4,4,4> result;
T sp = p.globalCoordinates() * q.globalCoordinates();
T sp = p.globalCoordinates() * q.globalCoordinates();
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,4,4> Pp, Pq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) {
Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j];
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
}
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,4,4> Pp, Pq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) {
Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j];
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
}
EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
EmbeddedTangentVector qProjected = p.projectOntoTangentSpace(q.globalCoordinates());
EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
EmbeddedTangentVector qProjected = p.projectOntoTangentSpace(q.globalCoordinates());
Tensor3<T,4,4,4> derivativeOfPqOTimesPq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
for (int k=0; k<4; k++) {
derivativeOfPqOTimesPq[i][j][k] = 0;
for (int l=0; l<4; l++)
derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]);
}
Tensor3<T,4,4,4> derivativeOfPqOTimesPq;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
for (int k=0; k<4; k++) {
derivativeOfPqOTimesPq[i][j][k] = 0;
for (int l=0; l<4; l++)
derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]);
}
double plusMinus = (sp < 0) ? -1 : 1;
double plusMinus = (sp < 0) ? -1 : 1;
using std::abs;
result = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,pProjected,pProjected)
+ UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * derivativeOfPqOTimesPq
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * sp * Tensor3<T,4,4,4>::product(qProjected,Pq)
- plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,Pq);
using std::abs;
result = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,pProjected,pProjected)
+ UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * derivativeOfPqOTimesPq
- UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * sp * Tensor3<T,4,4,4>::product(qProjected,Pq)
- plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,Pq);
result *= 4;
result *= 4;
return result;
return result;
}
}
/** \brief Interpolate between two rotations */
static Rotation<T,3> interpolate(const Rotation<T,3>& a, const Rotation<T,3>& b, T omega) {
/** \brief Interpolate between two rotations */
static Rotation<T,3> interpolate(const Rotation<T,3>& a, const Rotation<T,3>& b, T omega) {
// Compute log on T_a SO(3)
EmbeddedTangentVector v = log(a,b);
return exp(a, omega*v);
}
// Compute log on T_a SO(3)
EmbeddedTangentVector v = log(a,b);
return exp(a, omega*v);
}
/** \brief Interpolate between two rotations
\param omega must be between 0 and 1
*/
static Quaternion<T> interpolateDerivative(const Rotation<T,3>& a, const Rotation<T,3>& b,
T omega) {
Quaternion<T> result(0);
/** \brief Interpolate between two rotations
\param omega must be between 0 and 1
*/
static Quaternion<T> interpolateDerivative(const Rotation<T,3>& a, const Rotation<T,3>& b,
T omega) {
Quaternion<T> result(0);
// Compute log on T_a SO(3)
SkewMatrix<T,3> xi = log(a,b);
// Compute log on T_a SO(3)
SkewMatrix<T,3> xi = log(a,b);
SkewMatrix<T,3> v = xi;
v *= omega;
SkewMatrix<T,3> v = xi;
v *= omega;
// //////////////////////////////////////////////////////////////
// v now contains the derivative at 'a'. The derivative at
// the requested site is v pushed forward by Dexp.
// /////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////
// v now contains the derivative at 'a'. The derivative at
// the requested site is v pushed forward by Dexp.
// /////////////////////////////////////////////////////////////
Dune::FieldMatrix<T,4,3> diffExp = Dexp(v);
Dune::FieldMatrix<T,4,3> diffExp = Dexp(v);
diffExp.umv(xi.axial(),result);
diffExp.umv(xi.axial(),result);
return a.Quaternion<T>::mult(result);
}
return a.Quaternion<T>::mult(result);
}
/** \brief Return the corresponding orthogonal matrix */
void matrix(Dune::FieldMatrix<T,3,3>& m) const {
/** \brief Return the corresponding orthogonal matrix */
void matrix(Dune::FieldMatrix<T,3,3>& m) const {
m[0][0] = (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3];
m[0][1] = 2 * ( (*this)[0]*(*this)[1] - (*this)[2]*(*this)[3] );
m[0][2] = 2 * ( (*this)[0]*(*this)[2] + (*this)[1]*(*this)[3] );
m[0][0] = (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3];
m[0][1] = 2 * ( (*this)[0]*(*this)[1] - (*this)[2]*(*this)[3] );
m[0][2] = 2 * ( (*this)[0]*(*this)[2] + (*this)[1]*(*this)[3] );
m[1][0] = 2 * ( (*this)[0]*(*this)[1] + (*this)[2]*(*this)[3] );
m[1][1] = - (*this)[0]*(*this)[0] + (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3];
m[1][2] = 2 * ( -(*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] );
m[1][0] = 2 * ( (*this)[0]*(*this)[1] + (*this)[2]*(*this)[3] );
m[1][1] = - (*this)[0]*(*this)[0] + (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3];
m[1][2] = 2 * ( -(*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] );
m[2][0] = 2 * ( (*this)[0]*(*this)[2] - (*this)[1]*(*this)[3] );
m[2][1] = 2 * ( (*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] );
m[2][2] = - (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] + (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3];
m[2][0] = 2 * ( (*this)[0]*(*this)[2] - (*this)[1]*(*this)[3] );
m[2][1] = 2 * ( (*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] );
m[2][2] = - (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] + (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3];
}
}
/** \brief Derivative of the map from unit quaternions to orthogonal matrices
*/
static Tensor3<T,3,3,4> derivativeOfQuaternionToMatrix(const Dune::FieldVector<T,4>& p)
{
Tensor3<T,3,3,4> result;
/** \brief Derivative of the map from unit quaternions to orthogonal matrices
*/
static Tensor3<T,3,3,4> derivativeOfQuaternionToMatrix(const Dune::FieldVector<T,4>& p)
{
Tensor3<T,3,3,4> result;
result[0][0] = { 2*p[0], -2*p[1], -2*p[2], 2*p[3]};
result[0][1] = { 2*p[1], 2*p[0], -2*p[3], -2*p[2]};
result[0][2] = { 2*p[2], 2*p[3], 2*p[0], 2*p[1]};
result[0][0] = { 2*p[0], -2*p[1], -2*p[2], 2*p[3]};
result[0][1] = { 2*p[1], 2*p[0], -2*p[3], -2*p[2]};
result[0][2] = { 2*p[2], 2*p[3], 2*p[0], 2*p[1]};
result[1][0] = { 2*p[1], 2*p[0], 2*p[3], 2*p[2]};
result[1][1] = {-2*p[0], 2*p[1], -2*p[2], 2*p[3]};
result[1][2] = {-2*p[3], 2*p[2], 2*p[1], -2*p[0]};
result[1][0] = { 2*p[1], 2*p[0], 2*p[3], 2*p[2]};
result[1][1] = {-2*p[0], 2*p[1], -2*p[2], 2*p[3]};
result[1][2] = {-2*p[3], 2*p[2], 2*p[1], -2*p[0]};
result[2][0] = { 2*p[2], -2*p[3], 2*p[0], -2*p[1]};
result[2][1] = { 2*p[3], 2*p[2], 2*p[1], 2*p[0]};
result[2][2] = {-2*p[0], -2*p[1], 2*p[2], 2*p[3]};
result[2][0] = { 2*p[2], -2*p[3], 2*p[0], -2*p[1]};
result[2][1] = { 2*p[3], 2*p[2], 2*p[1], 2*p[0]};
result[2][2] = {-2*p[0], -2*p[1], 2*p[2], 2*p[3]};
return result;
}
return result;
}
/** \brief Set rotation from orthogonal matrix
/** \brief Set rotation from orthogonal matrix
We tacitly assume that the matrix really is orthogonal */
void set(const Dune::FieldMatrix<T,3,3>& m) {
We tacitly assume that the matrix really is orthogonal */
void set(const Dune::FieldMatrix<T,3,3>& m) {
using std::sqrt;
using std::sqrt;
// Easier writing
Dune::FieldVector<T,4>& p = (*this);
// The following equations for the derivation of a unit quaternion from a rotation
// matrix comes from 'E. Salamin, Application of Quaternions to Computation with
// Rotations, Technical Report, Stanford, 1974'
// Easier writing
Dune::FieldVector<T,4>& p = (*this);
// The following equations for the derivation of a unit quaternion from a rotation
// matrix comes from 'E. Salamin, Application of Quaternions to Computation with
// Rotations, Technical Report, Stanford, 1974'
p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4;
p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4;
p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4;
p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4;
p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4;
p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4;
p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4;
p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4;
// avoid rounding problems
if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3]) {
// avoid rounding problems
if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3]) {
p[0] = sqrt(p[0]);
p[0] = sqrt(p[0]);
// r_x r_y = (R_12 + R_21) / 4
p[1] = (m[0][1] + m[1][0]) / 4 / p[0];
// r_x r_y = (R_12 + R_21) / 4
p[1] = (m[0][1] + m[1][0]) / 4 / p[0];
// r_x r_z = (R_13 + R_31) / 4
p[2] = (m[0][2] + m[2][0]) / 4 / p[0];
// r_x r_z = (R_13 + R_31) / 4
p[2] = (m[0][2] + m[2][0]) / 4 / p[0];
// r_0 r_x = (R_32 - R_23) / 4
p[3] = (m[2][1] - m[1][2]) / 4 / p[0];
// r_0 r_x = (R_32 - R_23) / 4
p[3] = (m[2][1] - m[1][2]) / 4 / p[0];
} else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3]) {
} else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3]) {
p[1] = sqrt(p[1]);
p[1] = sqrt(p[1]);
// r_x r_y = (R_12 + R_21) / 4
p[0] = (m[0][1] + m[1][0]) / 4 / p[1];
// r_x r_y = (R_12 + R_21) / 4
p[0] = (m[0][1] + m[1][0]) / 4 / p[1];
// r_y r_z = (R_23 + R_32) / 4
p[2] = (m[1][2] + m[2][1]) / 4 / p[1];
// r_y r_z = (R_23 + R_32) / 4
p[2] = (m[1][2] + m[2][1]) / 4 / p[1];
// r_0 r_y = (R_13 - R_31) / 4
p[3] = (m[0][2] - m[2][0]) / 4 / p[1];
// r_0 r_y = (R_13 - R_31) / 4
p[3] = (m[0][2] - m[2][0]) / 4 / p[1];
} else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3]) {
} else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3]) {
p[2] = sqrt(p[2]);
p[2] = sqrt(p[2]);
// r_x r_z = (R_13 + R_31) / 4
p[0] = (m[0][2] + m[2][0]) / 4 / p[2];
// r_x r_z = (R_13 + R_31) / 4
p[0] = (m[0][2] + m[2][0]) / 4 / p[2];
// r_y r_z = (R_23 + R_32) / 4
p[1] = (m[1][2] + m[2][1]) / 4 / p[2];
// r_y r_z = (R_23 + R_32) / 4
p[1] = (m[1][2] + m[2][1]) / 4 / p[2];
// r_0 r_z = (R_21 - R_12) / 4
p[3] = (m[1][0] - m[0][1]) / 4 / p[2];
// r_0 r_z = (R_21 - R_12) / 4
p[3] = (m[1][0] - m[0][1]) / 4 / p[2];
} else {
} else {
p[3] = sqrt(p[3]);
p[3] = sqrt(p[3]);
// r_0 r_x = (R_32 - R_23) / 4
p[0] = (m[2][1] - m[1][2]) / 4 / p[3];
// r_0 r_x = (R_32 - R_23) / 4
p[0] = (m[2][1] - m[1][2]) / 4 / p[3];
// r_0 r_y = (R_13 - R_31) / 4
p[1] = (m[0][2] - m[2][0]) / 4 / p[3];
// r_0 r_y = (R_13 - R_31) / 4
p[1] = (m[0][2] - m[2][0]) / 4 / p[3];
// r_0 r_z = (R_21 - R_12) / 4
p[2] = (m[1][0] - m[0][1]) / 4 / p[3];
// r_0 r_z = (R_21 - R_12) / 4
p[2] = (m[1][0] - m[0][1]) / 4 / p[3];
}
}
}
/** \brief Derivative of the map from orthogonal matrices to unit quaternions
/** \brief Derivative of the map from orthogonal matrices to unit quaternions
We tacitly assume that the matrix really is orthogonal */
static Tensor3<T,4,3,3> derivativeOfMatrixToQuaternion(const Dune::FieldMatrix<T,3,3>& m)
{
using std::pow;
using std::sqrt;
We tacitly assume that the matrix really is orthogonal */
static Tensor3<T,4,3,3> derivativeOfMatrixToQuaternion(const Dune::FieldMatrix<T,3,3>& m)
{
using std::pow;
using std::sqrt;
Tensor3<T,4,3,3> result;
Tensor3<T,4,3,3> result;
Dune::FieldVector<T,4> p;
Dune::FieldVector<T,4> p;
// The following equations for the derivation of a unit quaternion from a rotation
// matrix comes from 'E. Salamin, Application of Quaternions to Computation with
// Rotations, Technical Report, Stanford, 1974'
// The following equations for the derivation of a unit quaternion from a rotation
// matrix comes from 'E. Salamin, Application of Quaternions to Computation with
// Rotations, Technical Report, Stanford, 1974'
p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4;
p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4;
p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4;
p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4;
p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4;
p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4;
p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4;
p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4;
// avoid rounding problems
if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3])
{
result[0] = {{1,0,0},{0,-1,0},{0,0,-1}};
result[0] *= 1.0/(8.0*sqrt(p[0]));
// avoid rounding problems
if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3])
{
result[0] = {{1,0,0},{0,-1,0},{0,0,-1}};
result[0] *= 1.0/(8.0*sqrt(p[0]));
T denom = 32 * pow(p[0],1.5);
T offDiag = 1.0/(4*sqrt(p[0]));
T denom = 32 * pow(p[0],1.5);
T offDiag = 1.0/(4*sqrt(p[0]));
result[1] = { {-(m[0][1]+m[1][0]) / denom, offDiag, 0},
{offDiag, (m[0][1]+m[1][0]) / denom, 0},
{0, 0, (m[0][1]+m[1][0]) / denom}};
result[1] = { {-(m[0][1]+m[1][0]) / denom, offDiag, 0},
{offDiag, (m[0][1]+m[1][0]) / denom, 0},
{0, 0, (m[0][1]+m[1][0]) / denom}};
result[2] = { {-(m[0][2]+m[2][0]) / denom, 0, offDiag},
{0, (m[0][2]+m[2][0]) / denom, 0},
{offDiag, 0, (m[0][2]+m[2][0]) / denom}};
result[2] = { {-(m[0][2]+m[2][0]) / denom, 0, offDiag},
{0, (m[0][2]+m[2][0]) / denom, 0},
{offDiag, 0, (m[0][2]+m[2][0]) / denom}};
result[3] = { {-(m[2][1]-m[1][2]) / denom, 0, 0},
{0, (m[2][1]-m[1][2]) / denom, -offDiag},
{0, offDiag, (m[2][1]-m[1][2]) / denom}};
}
else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3])
{
result[1] = {{-1,0,0},{0,1,0},{0,0,-1}};
result[1] *= 1.0/(8.0*sqrt(p[1]));
result[3] = { {-(m[2][1]-m[1][2]) / denom, 0, 0},
{0, (m[2][1]-m[1][2]) / denom, -offDiag},
{0, offDiag, (m[2][1]-m[1][2]) / denom}};
}
else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3])
{
result[1] = {{-1,0,0},{0,1,0},{0,0,-1}};
result[1] *= 1.0/(8.0*sqrt(p[1]));
T denom = 32 * pow(p[1],1.5);
T offDiag = 1.0/(4*sqrt(p[1]));
T denom = 32 * pow(p[1],1.5);
T offDiag = 1.0/(4*sqrt(p[1]));
result[0] = { {(m[0][1]+m[1][0]) / denom, offDiag, 0},
{offDiag, -(m[0][1]+m[1][0]) / denom, 0},
{0, 0, (m[0][1]+m[1][0]) / denom}};
result[0] = { {(m[0][1]+m[1][0]) / denom, offDiag, 0},
{offDiag, -(m[0][1]+m[1][0]) / denom, 0},
{0, 0, (m[0][1]+m[1][0]) / denom}};
result[2] = { {(m[1][2]+m[2][1]) / denom, 0 , 0},
{0, -(m[1][2]+m[2][1]) / denom, offDiag},
{0, offDiag, (m[1][2]+m[2][1]) / denom}};
result[2] = { {(m[1][2]+m[2][1]) / denom, 0 , 0},
{0, -(m[1][2]+m[2][1]) / denom, offDiag},
{0, offDiag, (m[1][2]+m[2][1]) / denom}};
result[3] = { {(m[0][2]-m[2][0]) / denom, 0, offDiag},
{0, -(m[0][2]-m[2][0]) / denom, 0},
{-offDiag, 0, (m[0][2]-m[2][0]) / denom}};
}
else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3])
{
result[2] = {{-1,0,0},{0,-1,0},{0,0,1}};
result[2] *= 1.0/(8.0*sqrt(p[2]));
result[3] = { {(m[0][2]-m[2][0]) / denom, 0, offDiag},
{0, -(m[0][2]-m[2][0]) / denom, 0},
{-offDiag, 0, (m[0][2]-m[2][0]) / denom}};
}
else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3])
{
result[2] = {{-1,0,0},{0,-1,0},{0,0,1}};
result[2] *= 1.0/(8.0*sqrt(p[2]));
T denom = 32 * pow(p[2],1.5);
T offDiag = 1.0/(4*sqrt(p[2]));
T denom = 32 * pow(p[2],1.5);
T offDiag = 1.0/(4*sqrt(p[2]));
result[0] = { {(m[0][2]+m[2][0]) / denom, 0, offDiag},
{0, (m[0][2]+m[2][0]) / denom, 0},
{offDiag, 0, -(m[0][2]+m[2][0]) / denom}};
result[0] = { {(m[0][2]+m[2][0]) / denom, 0, offDiag},
{0, (m[0][2]+m[2][0]) / denom, 0},
{offDiag, 0, -(m[0][2]+m[2][0]) / denom}};
result[1] = { {(m[1][2]+m[2][1]) / denom, 0 , 0},
{0, (m[1][2]+m[2][1]) / denom, offDiag},
{0, offDiag, -(m[1][2]+m[2][1]) / denom}};
result[1] = { {(m[1][2]+m[2][1]) / denom, 0 , 0},
{0, (m[1][2]+m[2][1]) / denom, offDiag},
{0, offDiag, -(m[1][2]+m[2][1]) / denom}};
result[3] = { {(m[1][0]-m[0][1]) / denom, -offDiag, 0},
{offDiag, (m[1][0]-m[0][1]) / denom, 0},
{0, 0, -(m[1][0]-m[0][1]) / denom}};
}
else
{
result[3] = {{1,0,0},{0,1,0},{0,0,1}};
result[3] *= 1.0/(8.0*sqrt(p[3]));
result[3] = { {(m[1][0]-m[0][1]) / denom, -offDiag, 0},
{offDiag, (m[1][0]-m[0][1]) / denom, 0},
{0, 0, -(m[1][0]-m[0][1]) / denom}};
}
else
{
result[3] = {{1,0,0},{0,1,0},{0,0,1}};
result[3] *= 1.0/(8.0*sqrt(p[3]));
T denom = 32 * pow(p[3],1.5);
T offDiag = 1.0/(4*sqrt(p[3]));
T denom = 32 * pow(p[3],1.5);
T offDiag = 1.0/(4*sqrt(p[3]));
result[0] = { {-(m[2][1]-m[1][2]) / denom, 0, 0},
{0, -(m[2][1]-m[1][2]) / denom, -offDiag},
{0, offDiag, -(m[2][1]-m[1][2]) / denom}};
result[0] = { {-(m[2][1]-m[1][2]) / denom, 0, 0},
{0, -(m[2][1]-m[1][2]) / denom, -offDiag},
{0, offDiag, -(m[2][1]-m[1][2]) / denom}};
result[1] = { {-(m[0][2]-m[2][0]) / denom, 0, offDiag},
{0, -(m[0][2]-m[2][0]) / denom, 0},
{-offDiag, 0, -(m[0][2]-m[2][0]) / denom}};
result[1] = { {-(m[0][2]-m[2][0]) / denom, 0, offDiag},
{0, -(m[0][2]-m[2][0]) / denom, 0},
{-offDiag, 0, -(m[0][2]-m[2][0]) / denom}};
result[2] = { {-(m[1][0]-m[0][1]) / denom, -offDiag, 0},
{offDiag, -(m[1][0]-m[0][1]) / denom, 0},
{0, 0, -(m[1][0]-m[0][1]) / denom}};
result[2] = { {-(m[1][0]-m[0][1]) / denom, -offDiag, 0},
{offDiag, -(m[1][0]-m[0][1]) / denom, 0},
{0, 0, -(m[1][0]-m[0][1]) / denom}};
}
return result;
}
return result;
}
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result = v;
EmbeddedTangentVector data = *this;
result.axpy(-1*(data*result), data);
return result;
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
Dune::FieldVector<T,4> data = *this;
T sp = v*data;
EmbeddedTangentVector result = *this;
result *= sp;
return result;
}
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result = v;
EmbeddedTangentVector data = *this;
result.axpy(-1*(data*result), data);
return result;
}
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
Dune::FieldVector<T,4> data = *this;
T sp = v*data;
EmbeddedTangentVector result = *this;
result *= sp;
return result;
}
EmbeddedTangentVector result;
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
T sp = v*(*this);
EmbeddedTangentVector result;
for (int i=0; i<embeddedDim; i++)
result[i] = -sp * z[i];
T sp = v*(*this);
return result;
}
for (int i=0; i<embeddedDim; i++)
result[i] = -sp * z[i];
/** \brief Project a vector in R^4 onto the unit quaternions
*
* \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)!
*/
static Rotation<T,3> projectOnto(const CoordinateType& p)
{
Rotation<T,3> result(p);
result /= result.two_norm();
return result;
}
return result;
}
/** \brief Derivative of the projection of a vector in R^4 onto the unit quaternions
*
* \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)!
*/
static auto derivativeOfProjection(const Dune::FieldVector<T,4>& p)
{
Dune::FieldMatrix<T,4,4> result;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
result[i][j] = ( (i==j) - p[i]*p[j] / p.two_norm2() ) / p.two_norm();
return result;
}
/** \brief Project a vector in R^4 onto the unit quaternions
*
* \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)!
*/
static Rotation<T,3> projectOnto(const CoordinateType& p)
{
Rotation<T,3> result(p);
result /= result.two_norm();
return result;
}
/** \brief The global coordinates, if you really want them */
const CoordinateType& globalCoordinates() const {
return *this;
}
/** \brief Derivative of the projection of a vector in R^4 onto the unit quaternions
*
* \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)!
*/
static auto derivativeOfProjection(const Dune::FieldVector<T,4>& p)
{
Dune::FieldMatrix<T,4,4> result;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
result[i][j] = ( (i==j) - p[i]*p[j] / p.two_norm2() ) / p.two_norm();
return result;
}
/** \brief Compute an orthonormal basis of the tangent space of the unit quaternions
*
* Since the unit quaternions are an odd-dimensional sphere, there exists a globally
* continuous orthonormal frame field. I took the expression here from
* "Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in Rod Mechanics', page 83"
*/
Dune::FieldMatrix<T,3,4> orthonormalFrame() const {
return {{ (*this)[3], (*this)[2], -(*this)[1], -(*this)[0]},
{-(*this)[2], (*this)[3], (*this)[0], -(*this)[1]},
{ (*this)[1], -(*this)[0], (*this)[3], -(*this)[2]}};
/** \brief The global coordinates, if you really want them */
const CoordinateType& globalCoordinates() const {
return *this;
}
}
/** \brief Compute an orthonormal basis of the tangent space of the unit quaternions
*
* Since the unit quaternions are an odd-dimensional sphere, there exists a globally
* continuous orthonormal frame field. I took the expression here from
* "Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in Rod Mechanics', page 83"
*/
Dune::FieldMatrix<T,3,4> orthonormalFrame() const {
return {{ (*this)[3], (*this)[2], -(*this)[1], -(*this)[0]},
{-(*this)[2], (*this)[3], (*this)[0], -(*this)[1]},
{ (*this)[1], -(*this)[0], (*this)[3], -(*this)[2]}};
};
}
/** \brief Convert a unit quaternion to an orthogonal matrix
*
* This method returns a callable object which does the conversion.
* It can be used together with Functions::makeComposedGridFunction
* to turn a quaternion-valued function into a matrix-valued one.
*/
constexpr static auto quaternionToMatrix = []<typename RT>(Rotation<RT,3> quaternion) -> Dune::FieldMatrix<RT,3,3>
{
Dune::FieldMatrix<RT,3,3> rotationMatrix;
quaternion.matrix(rotationMatrix);
return rotationMatrix;
};
/** \brief Convert an orthogonal matrix to a unit quaternion
*
* This method returns a callable object which does the conversion.
* It can be used together with Functions::makeComposedGridFunction
* to turn a matrix-valued function into a quaternion-valued one.
*/
constexpr static auto matrixToQuaternion = []<typename RT>(const Dune::FieldMatrix<RT,3>& matrix) -> Dune::FieldVector<RT,4>
{
Rotation<RT,3> rotation;
rotation.set(matrix);
return rotation;
};
};
} // namespace Dune::GFE
#endif
......@@ -9,528 +9,534 @@
#include <dune/gfe/tensor3.hh>
#include <dune/gfe/symmetricmatrix.hh>
template <class T, int N>
class Rotation;
/** \brief A unit vector in R^N
\tparam N Dimension of the embedding space
\tparam T The type used for individual coordinates
*/
template <class T, int N>
class UnitVector
namespace Dune::GFE
{
// Rotation<T,3> is friend, because it needs the various derivatives of the arccos
friend class Rotation<T,3>;
/** \brief Computes sin(x) / x without getting unstable for small x */
static T sinc(const T& x) {
using std::sin;
return (x < 1e-2) ? 1.0-x*x/6.0+ Dune::power(x,4)/120.0-Dune::power(x,6)/5040.0+Dune::power(x,8)/362880.0 : sin(x)/x;
}
/** \brief Compute arccos^2 without using the (at 1) nondifferentiable function acos x close to 1 */
static T arcCosSquared(const T& x) {
using std::acos;
const T eps = 1e-2;
if (x > 1-eps) { // acos is not differentiable, use the series expansion instead,
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return -2 * (x-1) + 1.0/3 * (x-1)*(x-1) - 4.0/45 * (x-1)*(x-1)*(x-1);
return 11665028.0/4729725.0
-141088.0/45045.0*x
+ 413.0/429.0*x*x
- 5344.0/12285.0*Dune::power(x,3)
+ 245.0/1287.0*Dune::power(x,4)
- 1632.0/25025.0*Dune::power(x,5)
+ 56.0/3861.0*Dune::power(x,6)
- 32.0/21021.0*Dune::power(x,7);
} else {
return Dune::power(acos(x),2);
template <class T, int N>
class Rotation;
/** \brief A unit vector in R^N
\tparam N Dimension of the embedding space
\tparam T The type used for individual coordinates
*/
template <class T, int N>
class UnitVector
{
// Rotation<T,3> is friend, because it needs the various derivatives of the arccos
friend class Rotation<T,3>;
/** \brief Computes sin(x) / x without getting unstable for small x */
static T sinc(const T& x) {
using std::sin;
return (x < 1e-2) ? 1.0-x*x/6.0+ Dune::power(x,4)/120.0-Dune::power(x,6)/5040.0+Dune::power(x,8)/362880.0 : sin(x)/x;
}
}
/** \brief Compute the derivative of arccos^2 without getting unstable for x close to 1 */
static T derivativeOfArcCosSquared(const T& x) {
using std::acos;
using std::sqrt;
const T eps = 1e-2;
if (x > 1-eps) { // regular expression is unstable, use the series expansion instead
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return -2 + 2*(x-1)/3 - 4/15*(x-1)*(x-1);
return -47104.0/15015.0
+12614.0/6435.0*x
-63488.0/45045.0*x*x
+ 1204.0/1287.0*Dune::power(x,3)
- 2048.0/4095.0*Dune::power(x,4)
+ 112.0/585.0*Dune::power(x,5)
-2048.0/45045.0*Dune::power(x,6)
+ 32.0/6435.0*Dune::power(x,7);
} else if (x < -1+eps) { // The function is not differentiable
DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
} else
return -2*acos(x) / sqrt(1-x*x);
}
/** \brief Compute the second derivative of arccos^2 without getting unstable for x close to 1 */
static T secondDerivativeOfArcCosSquared(const T& x) {
using std::acos;
using std::pow;
const T eps = 1e-2;
if (x > 1-eps) { // regular expression is unstable, use the series expansion instead
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return 2.0/3 - 8*(x-1)/15;
return 1350030.0/676039.0+5632.0/2028117.0*Dune::power(x,10)
-1039056896.0/334639305.0*x
+150876.0/39767.0*x*x
-445186048.0/111546435.0*Dune::power(x,3)
+ 343728.0/96577.0*Dune::power(x,4)
- 57769984.0/22309287.0*Dune::power(x,5)
+ 710688.0/482885.0*Dune::power(x,6)
- 41615360.0/66927861.0*Dune::power(x,7)
+ 616704.0/3380195.0*Dune::power(x,8)
- 245760.0/7436429.0*Dune::power(x,9);
} else if (x < -1+eps) { // The function is not differentiable
DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
} else
return 2/(1-x*x) - 2*x*acos(x) / pow(1-x*x,1.5);
}
/** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */
static T thirdDerivativeOfArcCosSquared(const T& x) {
using std::acos;
using std::sqrt;
const T eps = 1e-2;
if (x > 1-eps) { // regular expression is unstable, use the series expansion instead
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return -8.0/15 + 24*(x-1)/35;
return -1039056896.0/334639305.0
+301752.0/39767.0*x
-445186048.0/37182145.0*x*x
+1374912.0/96577.0*Dune::power(x,3)
-288849920.0/22309287.0*Dune::power(x,4)
+4264128.0/482885.0*Dune::power(x,5)
-41615360.0/9561123.0*Dune::power(x,6)
+4933632.0/3380195.0*Dune::power(x,7)
-2211840.0/7436429.0*Dune::power(x,8)
+56320.0/2028117.0*Dune::power(x,9);
} else if (x < -1+eps) { // The function is not differentiable
DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
} else {
T d = 1-x*x;
return 6*x/(d*d) - 6*x*x*acos(x)/(d*d*sqrt(d)) - 2*acos(x)/(d*sqrt(d));
/** \brief Compute arccos^2 without using the (at 1) nondifferentiable function acos x close to 1 */
static T arcCosSquared(const T& x) {
using std::acos;
const T eps = 1e-2;
if (x > 1-eps) { // acos is not differentiable, use the series expansion instead,
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return -2 * (x-1) + 1.0/3 * (x-1)*(x-1) - 4.0/45 * (x-1)*(x-1)*(x-1);
return 11665028.0/4729725.0
-141088.0/45045.0*x
+ 413.0/429.0*x*x
- 5344.0/12285.0*Dune::power(x,3)
+ 245.0/1287.0*Dune::power(x,4)
- 1632.0/25025.0*Dune::power(x,5)
+ 56.0/3861.0*Dune::power(x,6)
- 32.0/21021.0*Dune::power(x,7);
} else {
return Dune::power(acos(x),2);
}
}
}
template <class T2, int N2>
friend class UnitVector;
/** \brief Compute the derivative of arccos^2 without getting unstable for x close to 1 */
static T derivativeOfArcCosSquared(const T& x) {
using std::acos;
using std::sqrt;
const T eps = 1e-2;
if (x > 1-eps) { // regular expression is unstable, use the series expansion instead
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return -2 + 2*(x-1)/3 - 4/15*(x-1)*(x-1);
return -47104.0/15015.0
+12614.0/6435.0*x
-63488.0/45045.0*x*x
+ 1204.0/1287.0*Dune::power(x,3)
- 2048.0/4095.0*Dune::power(x,4)
+ 112.0/585.0*Dune::power(x,5)
-2048.0/45045.0*Dune::power(x,6)
+ 32.0/6435.0*Dune::power(x,7);
} else if (x < -1+eps) { // The function is not differentiable
DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
} else
return -2*acos(x) / sqrt(1-x*x);
}
public:
/** \brief Compute the second derivative of arccos^2 without getting unstable for x close to 1 */
static T secondDerivativeOfArcCosSquared(const T& x) {
using std::acos;
using std::pow;
const T eps = 1e-2;
if (x > 1-eps) { // regular expression is unstable, use the series expansion instead
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return 2.0/3 - 8*(x-1)/15;
return 1350030.0/676039.0+5632.0/2028117.0*Dune::power(x,10)
-1039056896.0/334639305.0*x
+150876.0/39767.0*x*x
-445186048.0/111546435.0*Dune::power(x,3)
+ 343728.0/96577.0*Dune::power(x,4)
- 57769984.0/22309287.0*Dune::power(x,5)
+ 710688.0/482885.0*Dune::power(x,6)
- 41615360.0/66927861.0*Dune::power(x,7)
+ 616704.0/3380195.0*Dune::power(x,8)
- 245760.0/7436429.0*Dune::power(x,9);
} else if (x < -1+eps) { // The function is not differentiable
DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
} else
return 2/(1-x*x) - 2*x*acos(x) / pow(1-x*x,1.5);
}
/** \brief The type used for coordinates */
typedef T ctype;
typedef T field_type;
/** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */
static T thirdDerivativeOfArcCosSquared(const T& x) {
using std::acos;
using std::sqrt;
const T eps = 1e-2;
if (x > 1-eps) { // regular expression is unstable, use the series expansion instead
// we need here lots of terms to be sure that the numerical derivatives are also within maschine precision
//return -8.0/15 + 24*(x-1)/35;
return -1039056896.0/334639305.0
+301752.0/39767.0*x
-445186048.0/37182145.0*x*x
+1374912.0/96577.0*Dune::power(x,3)
-288849920.0/22309287.0*Dune::power(x,4)
+4264128.0/482885.0*Dune::power(x,5)
-41615360.0/9561123.0*Dune::power(x,6)
+4933632.0/3380195.0*Dune::power(x,7)
-2211840.0/7436429.0*Dune::power(x,8)
+56320.0/2028117.0*Dune::power(x,9);
} else if (x < -1+eps) { // The function is not differentiable
DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!");
} else {
T d = 1-x*x;
return 6*x/(d*d) - 6*x*x*acos(x)/(d*d*sqrt(d)) - 2*acos(x)/(d*sqrt(d));
}
}
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,N> CoordinateType;
template <class T2, int N2>
friend class UnitVector;
/** \brief Dimension of the manifold formed by unit vectors */
static const int dim = N-1;
public:
/** \brief Dimension of the Euclidean space the manifold is embedded in */
static const int embeddedDim = N;
/** \brief The type used for coordinates */
typedef T ctype;
typedef T field_type;
typedef Dune::FieldVector<T,N-1> TangentVector;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,N> CoordinateType;
typedef Dune::FieldVector<T,N> EmbeddedTangentVector;
/** \brief Dimension of the manifold formed by unit vectors */
static const int dim = N-1;
/** \brief The global convexity radius of the unit sphere */
static constexpr double convexityRadius = 0.5*M_PI;
/** \brief Dimension of the Euclidean space the manifold is embedded in */
static const int embeddedDim = N;
/** \brief The return type of the derivativeOfProjection method */
typedef Dune::FieldMatrix<T, N, N> DerivativeOfProjection;
typedef Dune::FieldVector<T,N-1> TangentVector;
/** \brief Default constructor */
UnitVector()
{}
typedef Dune::FieldVector<T,N> EmbeddedTangentVector;
/** \brief Constructor from a vector. The vector gets normalized! */
UnitVector(const Dune::FieldVector<T,N>& vector)
: data_(vector)
{
data_ /= data_.two_norm();
}
/** \brief The global convexity radius of the unit sphere */
static constexpr double convexityRadius = 0.5*M_PI;
/** \brief Constructor from an array. The array gets normalized! */
UnitVector(const std::array<T,N>& vector)
{
for (int i=0; i<N; i++)
data_[i] = vector[i];
data_ /= data_.two_norm();
}
/** \brief Assignment from UnitVector with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
UnitVector& operator <<= (const UnitVector<T2,N>& other) {
for (int i=0; i<N; i++)
data_[i] <<= other.data_[i];
return *this;
}
/** \brief Rebind the UnitVector to another coordinate type */
template<class U>
struct rebind
{
typedef UnitVector<U,N> other;
};
/** \brief The return type of the derivativeOfProjection method */
typedef Dune::FieldMatrix<T, N, N> DerivativeOfProjection;
/** \brief Default constructor */
UnitVector()
{}
/** \brief Constructor from a vector. The vector gets normalized! */
UnitVector(const Dune::FieldVector<T,N>& vector)
: data_(vector)
{
data_ /= data_.two_norm();
}
UnitVector<T,N>& operator=(const Dune::FieldVector<T,N>& vector)
{
data_ = vector;
data_ /= data_.two_norm();
return *this;
}
/** \brief Constructor from an array. The array gets normalized! */
UnitVector(const std::array<T,N>& vector)
{
for (int i=0; i<N; i++)
data_[i] = vector[i];
data_ /= data_.two_norm();
}
/** \brief The exponential map */
static UnitVector exp(const UnitVector& p, const TangentVector& v) {
/** \brief Assignment from UnitVector with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
UnitVector& operator <<= (const UnitVector<T2,N>& other) {
for (int i=0; i<N; i++)
data_[i] <<= other.data_[i];
return *this;
}
Dune::FieldMatrix<T,N-1,N> frame = p.orthonormalFrame();
/** \brief Rebind the UnitVector to another coordinate type */
template<class U>
struct rebind
{
typedef UnitVector<U,N> other;
};
EmbeddedTangentVector ev;
frame.mtv(v,ev);
return exp(p,ev);
}
/** \brief The exponential map */
static UnitVector exp(const UnitVector& p, const EmbeddedTangentVector& v) {
UnitVector<T,N>& operator=(const Dune::FieldVector<T,N>& vector)
{
data_ = vector;
data_ /= data_.two_norm();
return *this;
}
using std::abs;
using std::cos;
assert( abs(p.data_*v) < 1e-5 );
/** \brief The exponential map */
static UnitVector exp(const UnitVector& p, const TangentVector& v) {
const T norm = v.two_norm();
UnitVector result = p;
result.data_ *= cos(norm);
result.data_.axpy(sinc(norm), v);
return result;
}
Dune::FieldMatrix<T,N-1,N> frame = p.orthonormalFrame();
/** \brief The inverse of the exponential map
*
* \results A vector in the tangent space of p
*/
static EmbeddedTangentVector log(const UnitVector& p, const UnitVector& q)
{
EmbeddedTangentVector result = p.projectOntoTangentSpace(q.data_-p.data_);
if (result.two_norm() > 1e-10)
result *= distance(p,q) / result.two_norm();
return result;
}
EmbeddedTangentVector ev;
frame.mtv(v,ev);
/** \brief Length of the great arc connecting the two points */
static T distance(const UnitVector& a, const UnitVector& b) {
return exp(p,ev);
}
/** \brief The exponential map */
static UnitVector exp(const UnitVector& p, const EmbeddedTangentVector& v) {
using std::acos;
using std::min;
using std::abs;
using std::cos;
assert( abs(p.data_*v) < 1e-5 );
// Not nice: we are in a class for unit vectors, but the class is actually
// supposed to handle perturbations of unit vectors as well. Therefore
// we normalize here.
T x = a.data_ * b.data_/a.data_.two_norm()/b.data_.two_norm();
const T norm = v.two_norm();
UnitVector result = p;
result.data_ *= cos(norm);
result.data_.axpy(sinc(norm), v);
return result;
}
/** \brief The inverse of the exponential map
*
* \results A vector in the tangent space of p
*/
static EmbeddedTangentVector log(const UnitVector& p, const UnitVector& q)
{
EmbeddedTangentVector result = p.projectOntoTangentSpace(q.data_-p.data_);
if (result.two_norm() > 1e-10)
result *= distance(p,q) / result.two_norm();
return result;
}
// paranoia: if the argument is just eps larger than 1 acos returns NaN
x = min(x,1.0);
/** \brief Length of the great arc connecting the two points */
static T distance(const UnitVector& a, const UnitVector& b) {
return acos(x);
}
using std::acos;
using std::min;
// Not nice: we are in a class for unit vectors, but the class is actually
// supposed to handle perturbations of unit vectors as well. Therefore
// we normalize here.
T x = a.data_ * b.data_/a.data_.two_norm()/b.data_.two_norm();
// paranoia: if the argument is just eps larger than 1 acos returns NaN
x = min(x,1.0);
return acos(x);
}
#if ADOLC_ADOUBLE_H
/** \brief Squared length of the great arc connecting the two points */
static adouble distanceSquared(const UnitVector<double,N>& a, const UnitVector<adouble,N>& b)
{
// Not nice: we are in a class for unit vectors, but the class is actually
// supposed to handle perturbations of unit vectors as well. Therefore
// we normalize here.
adouble x = a.data_ * b.data_ / (a.data_.two_norm()*b.data_.two_norm());
// paranoia: if the argument is just eps larger than 1 acos returns NaN
using std::min;
x = min(x,1.0);
// Special implementation that remains AD-differentiable near x==1
return arcCosSquared(x);
}
/** \brief Squared length of the great arc connecting the two points */
static adouble distanceSquared(const UnitVector<double,N>& a, const UnitVector<adouble,N>& b)
{
// Not nice: we are in a class for unit vectors, but the class is actually
// supposed to handle perturbations of unit vectors as well. Therefore
// we normalize here.
adouble x = a.data_ * b.data_ / (a.data_.two_norm()*b.data_.two_norm());
// paranoia: if the argument is just eps larger than 1 acos returns NaN
using std::fmin;
x = fmin(x,1.0);
// Special implementation that remains AD-differentiable near x==1
return arcCosSquared(x);
}
#endif
/** \brief Compute the gradient of the squared distance function keeping the first argument fixed
/** \brief Compute the gradient of the squared distance function keeping the first argument fixed
Unlike the distance itself the squared distance is differentiable at zero
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& a, const UnitVector& b) {
T x = a.data_ * b.data_;
Unlike the distance itself the squared distance is differentiable at zero
*/
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& a, const UnitVector& b) {
T x = a.data_ * b.data_;
EmbeddedTangentVector result = a.data_;
EmbeddedTangentVector result = a.data_;
result *= derivativeOfArcCosSquared(x);
result *= derivativeOfArcCosSquared(x);
// Project gradient onto the tangent plane at b in order to obtain the surface gradient
result = b.projectOntoTangentSpace(result);
// Project gradient onto the tangent plane at b in order to obtain the surface gradient
result = b.projectOntoTangentSpace(result);
// Gradient must be a tangent vector at b, in other words, orthogonal to it
using std::abs;
assert(abs(b.data_ * result) < 1e-5);
// Gradient must be a tangent vector at b, in other words, orthogonal to it
using std::abs;
assert(abs(b.data_ * result) < 1e-5);
return result;
}
return result;
}
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) {
Unlike the distance itself the squared distance is differentiable at zero
*/
static SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) {
T sp = p.data_ * q.data_;
T sp = p.data_ * q.data_;
Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
Dune::SymmetricMatrix<T,N> A;
for (int i=0; i<N; i++)
for (int j=0; j<=i; j++)
A(i,j) = pProjected[i]*pProjected[j];
SymmetricMatrix<T,N> A;
for (int i=0; i<N; i++)
for (int j=0; j<=i; j++)
A(i,j) = pProjected[i]*pProjected[j];
A *= secondDerivativeOfArcCosSquared(sp);
A *= secondDerivativeOfArcCosSquared(sp);
// Compute matrix B (see notes)
Dune::SymmetricMatrix<T,N> Pq;
for (int i=0; i<N; i++)
for (int j=0; j<=i; j++)
Pq(i,j) = (i==j) - q.data_[i]*q.data_[j];
// Compute matrix B (see notes)
SymmetricMatrix<T,N> Pq;
for (int i=0; i<N; i++)
for (int j=0; j<=i; j++)
Pq(i,j) = (i==j) - q.data_[i]*q.data_[j];
// Bring it all together
A.axpy(-1*derivativeOfArcCosSquared(sp)*sp, Pq);
// Bring it all together
A.axpy(-1*derivativeOfArcCosSquared(sp)*sp, Pq);
return A;
}
return A;
}
/** \brief Compute the mixed second derivate \partial d^2 / \partial da db
/** \brief Compute the mixed second derivate \partial d^2 / \partial da db
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const UnitVector& a, const UnitVector& b) {
T sp = a.data_ * b.data_;
// Compute vector A (see notes)
Dune::FieldMatrix<T,1,N> row;
row[0] = b.projectOntoTangentSpace(a.globalCoordinates());
Dune::FieldVector<T,N> tmp = a.projectOntoTangentSpace(b.globalCoordinates());
Dune::FieldMatrix<T,N,1> column;
for (int i=0; i<N; i++) // turn row vector into column vector
column[i] = tmp[i];
Dune::FieldMatrix<T,N,N> A;
// A = row * column
Dune::FMatrixHelp::multMatrix(column,row,A);
A *= secondDerivativeOfArcCosSquared(sp);
// Compute matrix B (see notes)
Dune::FieldMatrix<T,N,N> Pp, Pq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++) {
Pp[i][j] = (i==j) - a.data_[i]*a.data_[j];
Pq[i][j] = (i==j) - b.data_[i]*b.data_[j];
}
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const UnitVector& a, const UnitVector& b) {
Dune::FieldMatrix<T,N,N> B;
Dune::FMatrixHelp::multMatrix(Pp,Pq,B);
T sp = a.data_ * b.data_;
// Bring it all together
A.axpy(derivativeOfArcCosSquared(sp), B);
// Compute vector A (see notes)
Dune::FieldMatrix<T,1,N> row;
row[0] = b.projectOntoTangentSpace(a.globalCoordinates());
return A;
}
Dune::FieldVector<T,N> tmp = a.projectOntoTangentSpace(b.globalCoordinates());
Dune::FieldMatrix<T,N,1> column;
for (int i=0; i<N; i++) // turn row vector into column vector
column[i] = tmp[i];
Dune::FieldMatrix<T,N,N> A;
// A = row * column
Dune::FMatrixHelp::multMatrix(column,row,A);
A *= secondDerivativeOfArcCosSquared(sp);
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
// Compute matrix B (see notes)
Dune::FieldMatrix<T,N,N> Pp, Pq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++) {
Pp[i][j] = (i==j) - a.data_[i]*a.data_[j];
Pq[i][j] = (i==j) - b.data_[i]*b.data_[j];
}
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) {
Dune::FieldMatrix<T,N,N> B;
Dune::FMatrixHelp::multMatrix(Pp,Pq,B);
Tensor3<T,N,N,N> result;
// Bring it all together
A.axpy(derivativeOfArcCosSquared(sp), B);
T sp = p.data_ * q.data_;
return A;
}
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,N,N> Pq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++) {
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) {
result[i][j][k] = thirdDerivativeOfArcCosSquared(sp) * pProjected[i] * pProjected[j] * pProjected[k]
- secondDerivativeOfArcCosSquared(sp) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k]
- secondDerivativeOfArcCosSquared(sp) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j]
- secondDerivativeOfArcCosSquared(sp) * pProjected[i] * Pq[j][k] * sp
+ derivativeOfArcCosSquared(sp) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp
- derivativeOfArcCosSquared(sp) * p.globalCoordinates()[i] * Pq[j][k];
}
Tensor3<T,N,N,N> result;
result = Pq * result;
T sp = p.data_ * q.data_;
return result;
}
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,N,N> Pq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const UnitVector& p, const UnitVector& q) {
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++) {
Tensor3<T,N,N,N> result;
result[i][j][k] = thirdDerivativeOfArcCosSquared(sp) * pProjected[i] * pProjected[j] * pProjected[k]
- secondDerivativeOfArcCosSquared(sp) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k]
- secondDerivativeOfArcCosSquared(sp) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j]
- secondDerivativeOfArcCosSquared(sp) * pProjected[i] * Pq[j][k] * sp
+ derivativeOfArcCosSquared(sp) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp
- derivativeOfArcCosSquared(sp) * p.globalCoordinates()[i] * Pq[j][k];
}
T sp = p.data_ * q.data_;
result = Pq * result;
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,N,N> Pp, Pq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++) {
Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j];
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
}
return result;
}
Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
Dune::FieldVector<T,N> qProjected = p.projectOntoTangentSpace(q.globalCoordinates());
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
Tensor3<T,N,N,N> derivativeOfPqOTimesPq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++) {
derivativeOfPqOTimesPq[i][j][k] = 0;
for (int l=0; l<N; l++)
derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]);
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const UnitVector& p, const UnitVector& q) {
Tensor3<T,N,N,N> result;
T sp = p.data_ * q.data_;
// The projection matrix onto the tangent space at p and q
Dune::FieldMatrix<T,N,N> Pp, Pq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++) {
Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j];
Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j];
}
result = thirdDerivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,pProjected,pProjected)
+ secondDerivativeOfArcCosSquared(sp) * derivativeOfPqOTimesPq
- secondDerivativeOfArcCosSquared(sp) * sp * Tensor3<T,N,N,N>::product(qProjected,Pq)
- derivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,Pq);
Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates());
Dune::FieldVector<T,N> qProjected = p.projectOntoTangentSpace(q.globalCoordinates());
return result;
}
Tensor3<T,N,N,N> derivativeOfPqOTimesPq;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++) {
derivativeOfPqOTimesPq[i][j][k] = 0;
for (int l=0; l<N; l++)
derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]);
}
result = thirdDerivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,pProjected,pProjected)
+ secondDerivativeOfArcCosSquared(sp) * derivativeOfPqOTimesPq
- secondDerivativeOfArcCosSquared(sp) * sp * Tensor3<T,N,N,N>::product(qProjected,Pq)
- derivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,Pq);
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result = v;
result.axpy(-1*(data_*result), data_);
return result;
}
return result;
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
return (v*data_) * data_;
}
/** \brief The Weingarten map
*
* The Weingarten map computes the derivative of a normal vector v with respect to a tangent vector z.
*/
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
return -(v*data_) * z;
}
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result = v;
result.axpy(-1*(data_*result), data_);
return result;
}
static UnitVector<T,N> projectOnto(const CoordinateType& p)
{
UnitVector<T,N> result(p);
result.data_ /= result.data_.two_norm();
return result;
}
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
return (v*data_) * data_;
}
static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p)
{
auto normSquared = p.two_norm2();
auto norm = std::sqrt(normSquared);
/** \brief The Weingarten map
*
* The Weingarten map computes the derivative of a normal vector v with respect to a tangent vector z.
*/
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
return -(v*data_) * z;
}
Dune::FieldMatrix<T,N,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = ( (i==j) - p[i]*p[j] / normSquared ) / norm;
return result;
}
static UnitVector<T,N> projectOnto(const CoordinateType& p)
{
UnitVector<T,N> result(p);
result.data_ /= result.data_.two_norm();
return result;
}
/** \brief The global coordinates, if you really want them */
const CoordinateType& globalCoordinates() const {
return data_;
}
static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p)
{
auto normSquared = p.two_norm2();
auto norm = std::sqrt(normSquared);
/** \brief Compute an orthonormal basis of the tangent space of S^n.
Dune::FieldMatrix<T,N,N> result;
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = ( (i==j) - p[i]*p[j] / normSquared ) / norm;
return result;
}
This basis is of course not globally continuous.
*/
Dune::FieldMatrix<T,N-1,N> orthonormalFrame() const {
/** \brief The global coordinates, if you really want them */
const CoordinateType& globalCoordinates() const {
return data_;
}
Dune::FieldMatrix<T,N-1,N> result;
/** \brief Compute an orthonormal basis of the tangent space of S^n.
// Coordinates of the stereographic projection
Dune::FieldVector<T,N-1> X;
This basis is of course not globally continuous.
*/
Dune::FieldMatrix<T,N-1,N> orthonormalFrame() const {
if (data_[N-1] <= 0) {
Dune::FieldMatrix<T,N-1,N> result;
// Stereographic projection from the north pole onto R^{N-1}
for (size_t i=0; i<N-1; i++)
X[i] = data_[i] / (1-data_[N-1]);
// Coordinates of the stereographic projection
Dune::FieldVector<T,N-1> X;
} else {
if (data_[N-1] <= 0) {
// Stereographic projection from the south pole onto R^{N-1}
for (size_t i=0; i<N-1; i++)
X[i] = data_[i] / (1+data_[N-1]);
// Stereographic projection from the north pole onto R^{N-1}
for (size_t i=0; i<N-1; i++)
X[i] = data_[i] / (1-data_[N-1]);
}
} else {
T RSquared = X.two_norm2();
// Stereographic projection from the south pole onto R^{N-1}
for (size_t i=0; i<N-1; i++)
X[i] = data_[i] / (1+data_[N-1]);
for (size_t i=0; i<N-1; i++)
for (size_t j=0; j<N-1; j++)
// Note: the matrix is the transpose of the one in the paper
result[j][i] = 2*(i==j)*(1+RSquared) - 4*X[i]*X[j];
}
for (size_t j=0; j<N-1; j++)
result[j][N-1] = 4*X[j];
T RSquared = X.two_norm2();
for (size_t i=0; i<N-1; i++)
for (size_t j=0; j<N-1; j++)
// Note: the matrix is the transpose of the one in the paper
result[j][i] = 2*(i==j)*(1+RSquared) - 4*X[i]*X[j];
// Upper hemisphere: adapt formulas so it is the stereographic projection from the south pole
if (data_[N-1] > 0)
for (size_t j=0; j<N-1; j++)
result[j][N-1] *= -1;
result[j][N-1] = 4*X[j];
// normalize the rows to make the orthogonal basis orthonormal
for (size_t i=0; i<N-1; i++)
result[i] /= result[i].two_norm();
// Upper hemisphere: adapt formulas so it is the stereographic projection from the south pole
if (data_[N-1] > 0)
for (size_t j=0; j<N-1; j++)
result[j][N-1] *= -1;
return result;
}
// normalize the rows to make the orthogonal basis orthonormal
for (size_t i=0; i<N-1; i++)
result[i] /= result[i].two_norm();
/** \brief Write unit vector object to output stream */
friend std::ostream& operator<< (std::ostream& s, const UnitVector& unitVector)
{
return s << unitVector.data_;
}
return result;
}
/** \brief Write unit vector object to output stream */
friend std::ostream& operator<< (std::ostream& s, const UnitVector& unitVector)
{
return s << unitVector.data_;
}
private:
private:
Dune::FieldVector<T,N> data_;
};
Dune::FieldVector<T,N> data_;
};
} // namespace Dune::GFE
#endif
......@@ -12,253 +12,258 @@
// int SIGN(const T& a, const T& b) {return 1;}
#define SIGN(a,b)((b)>=0.0 ? fabs(a) : -fabs(a))
/** Computes (a^2 + b^2 )1/2 without destructive underflow or overflow. */
template <class T>
T pythag(T a, T b)
namespace Dune::GFE
{
T absa=std::fabs(a);
T absb=std::fabs(b);
if (absa > absb)
return absa*std::sqrt(T(1.0)+(absb/absa)*(absb/absa));
else
return (absb == 0.0) ? T(0.0) : absb*sqrt(T(1.0)+(absa/absb)*(absa/absb));
}
/** Computes (a^2 + b^2 )1/2 without destructive underflow or overflow. */
template <class T>
T pythag(T a, T b)
{
T absa=std::fabs(a);
T absb=std::fabs(b);
if (absa > absb)
return absa*std::sqrt(T(1.0)+(absb/absa)*(absb/absa));
else
return (absb == 0.0) ? T(0.0) : absb*sqrt(T(1.0)+(absa/absb)*(absa/absb));
}
/**
Given a matrix a[1..m][1..n], this routine computes its singular value decomposition, A =
U W V^T . The matrix U replaces a on output. The diagonal matrix of singular values W is out-
put as a vector w[1..n]. The matrix V (not the transpose V T ) is output as v[1..n][1..n].
*/
template <class T, int m, int n>
void svdcmp(Dune::FieldMatrix<T,m,n>& a_, Dune::FieldVector<T,n>& w, Dune::FieldMatrix<T,n,n>& v_)
{
Dune::FieldMatrix<T,m+1,n+1> a(0);
Dune::FieldMatrix<T,n+1,n+1> v(0);
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
a[i+1][j+1] = a_[i][j];
/**
Given a matrix a[1..m][1..n], this routine computes its singular value decomposition, A =
U W V^T . The matrix U replaces a on output. The diagonal matrix of singular values W is out-
put as a vector w[1..n]. The matrix V (not the transpose V T ) is output as v[1..n][1..n].
*/
template <class T, int m, int n>
void svdcmp(Dune::FieldMatrix<T,m,n>& a_, Dune::FieldVector<T,n>& w, Dune::FieldMatrix<T,n,n>& v_)
{
Dune::FieldMatrix<T,m+1,n+1> a(0);
Dune::FieldMatrix<T,n+1,n+1> v(0);
int flag,i,its,j,jj,k,l,nm;
T anorm,c,f,g,h,s,scale,x,y,z;
T rv1[n+1]; // 1 too large to accommodate fortran numbering
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
a[i+1][j+1] = a_[i][j];
//Householder reduction to bidiagonal form.
g=scale=anorm=0.0;
int flag,i,its,j,jj,k,l,nm;
T anorm,c,f,g,h,s,scale,x,y,z;
T rv1[n+1]; // 1 too large to accommodate fortran numbering
for (i=1; i<=n; i++) {
//Householder reduction to bidiagonal form.
g=scale=anorm=0.0;
l=i+1;
rv1[i]=scale*g;
g=s=scale=0.0;
for (i=1; i<=n; i++) {
if (i <= m) {
for (k=i; k<=m; k++)
scale += std::abs(a[k][i]);
if (scale!=0) {
for (k=i; k<=m; k++) {
a[k][i] /= scale;
s += a[k][i]*a[k][i];
}
f=a[i][i];
g = -SIGN(std::sqrt(s),f);
h=f*g-s;
a[i][i]=f-g;
for (j=l; j<=n; j++) {
for (s=0.0,k=i; k<=m; k++)
s += a[k][i]*a[k][j];
f=s/h;
l=i+1;
rv1[i]=scale*g;
g=s=scale=0.0;
if (i <= m) {
for (k=i; k<=m; k++)
scale += std::abs(a[k][i]);
if (scale!=0) {
for (k=i; k<=m; k++) {
a[k][i] /= scale;
s += a[k][i]*a[k][i];
}
f=a[i][i];
g = -SIGN(std::sqrt(s),f);
h=f*g-s;
a[i][i]=f-g;
for (j=l; j<=n; j++) {
for (s=0.0,k=i; k<=m; k++)
s += a[k][i]*a[k][j];
f=s/h;
for (k=i; k<=m; k++)
a[k][j] += f*a[k][i];
}
for (k=i; k<=m; k++)
a[k][j] += f*a[k][i];
a[k][i] *= scale;
}
for (k=i; k<=m; k++)
a[k][i] *= scale;
}
}
w[i-1]=scale *g;
g=s=scale=0.0;
w[i-1]=scale *g;
g=s=scale=0.0;
if (i <= m && i != n) {
if (i <= m && i != n) {
for (k=l; k<=n; k++) scale += fabs(a[i][k]);
if (scale!=0) {
for (k=l; k<=n; k++) scale += fabs(a[i][k]);
if (scale!=0) {
for (k=l; k<=n; k++) {
a[i][k] /= scale;
s += a[i][k]*a[i][k];
}
f=a[i][l];
g = -SIGN(std::sqrt(s),f);
h=f*g-s;
a[i][l]=f-g;
for (k=l; k<=n; k++) rv1[k]=a[i][k]/h;
for (j=l; j<=m; j++) {
for (s=0.0,k=l; k<=n; k++)
s += a[j][k]*a[i][k];
for (k=l; k<=n; k++) {
a[i][k] /= scale;
s += a[i][k]*a[i][k];
}
f=a[i][l];
g = -SIGN(std::sqrt(s),f);
h=f*g-s;
a[i][l]=f-g;
for (k=l; k<=n; k++) rv1[k]=a[i][k]/h;
for (j=l; j<=m; j++) {
for (s=0.0,k=l; k<=n; k++)
s += a[j][k]*a[i][k];
for (k=l; k<=n; k++)
a[j][k] += s*rv1[k];
}
for (k=l; k<=n; k++)
a[j][k] += s*rv1[k];
a[i][k] *= scale;
}
for (k=l; k<=n; k++)
a[i][k] *= scale;
}
}
anorm = std::max(anorm,(std::abs(w[i-1])+std::abs(rv1[i])));
anorm = std::max(anorm,(std::abs(w[i-1])+std::abs(rv1[i])));
}
}
// Accumulation of right-hand transformations.
for (i=n; i>=1; i--) {
if (i < n) {
if (g!=0) {
// Double division to avoid possible underflow.
for (j=l; j<=n; j++)
v[j][i]=(a[i][j]/a[i][l])/g;
for (j=l; j<=n; j++) {
for (s=0.0,k=l; k<=n; k++) s += a[i][k]*v[k][j];
for (k=l; k<=n; k++) v[k][j] += s*v[k][i];
// Accumulation of right-hand transformations.
for (i=n; i>=1; i--) {
if (i < n) {
if (g!=0) {
// Double division to avoid possible underflow.
for (j=l; j<=n; j++)
v[j][i]=(a[i][j]/a[i][l])/g;
for (j=l; j<=n; j++) {
for (s=0.0,k=l; k<=n; k++) s += a[i][k]*v[k][j];
for (k=l; k<=n; k++) v[k][j] += s*v[k][i];
}
}
for (j=l; j<=n; j++) v[i][j]=v[j][i]=0.0;
}
for (j=l; j<=n; j++) v[i][j]=v[j][i]=0.0;
v[i][i]=1.0;
g=rv1[i];
l=i;
}
v[i][i]=1.0;
g=rv1[i];
l=i;
}
// Accumulation of left-hand transformations.
//for (i=IMIN(m,n);i>=1;i--) {
for (i=std::min(m,n); i>=1; i--) {
l=i+1;
g=w[i-1];
for (j=l; j<=n; j++) a[i][j]=0.0;
if (g!=0) {
g=1.0/g;
for (j=l; j<=n; j++) {
for (s=0.0,k=l; k<=m; k++) s += a[k][i]*a[k][j];
f=(s/a[i][i])*g;
for (k=i; k<=m; k++) a[k][j] += f*a[k][i];
}
for (j=i; j<=m; j++) a[j][i] *= g;
} else for (j=i; j<=m; j++) a[j][i]=0.0;
++a[i][i];
}
// Accumulation of left-hand transformations.
//for (i=IMIN(m,n);i>=1;i--) {
for (i=std::min(m,n); i>=1; i--) {
l=i+1;
g=w[i-1];
for (j=l; j<=n; j++) a[i][j]=0.0;
if (g!=0) {
g=1.0/g;
for (j=l; j<=n; j++) {
for (s=0.0,k=l; k<=m; k++) s += a[k][i]*a[k][j];
f=(s/a[i][i])*g;
for (k=i; k<=m; k++) a[k][j] += f*a[k][i];
}
for (j=i; j<=m; j++) a[j][i] *= g;
} else for (j=i; j<=m; j++) a[j][i]=0.0;
++a[i][i];
}
// Diagonalization of the bidiagonal form: Loop over
//singular values, and over allowed iterations.
for (k=n; k>=1; k--) {
// Diagonalization of the bidiagonal form: Loop over
//singular values, and over allowed iterations.
for (k=n; k>=1; k--) {
for (its=1; its<=30; its++) {
flag=1;
// Test for splitting.
for (l=k; l>=1; l--) {
// Note that rv1[1] is always zero.
nm=l-1;
if ((T)(fabs(rv1[l])+anorm) == anorm) {
flag=0;
break;
for (its=1; its<=30; its++) {
flag=1;
// Test for splitting.
for (l=k; l>=1; l--) {
// Note that rv1[1] is always zero.
nm=l-1;
if ((T)(fabs(rv1[l])+anorm) == anorm) {
flag=0;
break;
}
if ((T)(fabs(w[nm-1])+anorm) == anorm) break;
}
if ((T)(fabs(w[nm-1])+anorm) == anorm) break;
}
if (flag) {
// Cancellation of rv1[l], if l > 1.
c=0.0;
s=1.0;
for (i=l; i<=k; i++) {
if (flag) {
// Cancellation of rv1[l], if l > 1.
c=0.0;
s=1.0;
for (i=l; i<=k; i++) {
f=s*rv1[i];
rv1[i]=c*rv1[i];
if ((T)(fabs(f)+anorm) == anorm) break;
g=w[i-1];
h=pythag(f,g);
w[i-1]=h;
h=1.0/h;
c=g*h;
s = -f*h;
for (j=1; j<=m; j++) {
y=a[j][nm];
z=a[j][i];
a[j][nm]=y*c+z*s;
a[j][i]=z*c-y*s;
f=s*rv1[i];
rv1[i]=c*rv1[i];
if ((T)(fabs(f)+anorm) == anorm) break;
g=w[i-1];
h=pythag(f,g);
w[i-1]=h;
h=1.0/h;
c=g*h;
s = -f*h;
for (j=1; j<=m; j++) {
y=a[j][nm];
z=a[j][i];
a[j][nm]=y*c+z*s;
a[j][i]=z*c-y*s;
}
}
}
}
z=w[k-1];
//Convergence.
if (l == k) {
// Singular value is made nonnegative.
if (z < 0.0) {
w[k-1] = -z;
for (j=1; j<=n; j++) v[j][k] = -v[j][k];
}
break;
}
if (its == 30)
std::cerr << "no convergence in 30 svdcmp iterations" << std::endl;
// Shift from bottom 2-by-2 minor.
x=w[l-1];
nm=k-1;
y=w[nm-1];
g=rv1[nm];
h=rv1[k];
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
g=pythag(f,T(1.0));
f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
// Next QR transformation:
c=s=1.0;
for (j=l; j<=nm; j++) {
i=j+1;
g=rv1[i];
y=w[i-1];
h=s*g;
g=c*g;
z=pythag(f,h);
rv1[j]=z;
c=f/z;
s=h/z;
f=x*c+g*s;
g = g*c-x*s;
h=y*s;
y *= c;
for (jj=1; jj<=n; jj++) {
x=v[jj][j];
z=v[jj][i];
v[jj][j]=x*c+z*s;
v[jj][i]=z*c-x*s;
}
z=pythag(f,h);
// Rotation can be arbitrary if z = 0.
w[j-1]=z;
if (z!=0) {
z=1.0/z;
c=f*z;
s=h*z;
z=w[k-1];
//Convergence.
if (l == k) {
// Singular value is made nonnegative.
if (z < 0.0) {
w[k-1] = -z;
for (j=1; j<=n; j++) v[j][k] = -v[j][k];
}
break;
}
f=c*g+s*y;
x=c*y-s*g;
if (its == 30)
std::cerr << "no convergence in 30 svdcmp iterations" << std::endl;
// Shift from bottom 2-by-2 minor.
x=w[l-1];
nm=k-1;
y=w[nm-1];
g=rv1[nm];
h=rv1[k];
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
g=pythag(f,T(1.0));
f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
// Next QR transformation:
c=s=1.0;
for (j=l; j<=nm; j++) {
i=j+1;
g=rv1[i];
y=w[i-1];
h=s*g;
g=c*g;
z=pythag(f,h);
rv1[j]=z;
c=f/z;
s=h/z;
f=x*c+g*s;
g = g*c-x*s;
h=y*s;
y *= c;
for (jj=1; jj<=n; jj++) {
x=v[jj][j];
z=v[jj][i];
v[jj][j]=x*c+z*s;
v[jj][i]=z*c-x*s;
}
z=pythag(f,h);
// Rotation can be arbitrary if z = 0.
w[j-1]=z;
if (z!=0) {
z=1.0/z;
c=f*z;
s=h*z;
}
f=c*g+s*y;
x=c*y-s*g;
for (jj=1; jj<=m; jj++) {
y=a[jj][j];
z=a[jj][i];
a[jj][j]=y*c+z*s;
a[jj][i]=z*c-y*s;
for (jj=1; jj<=m; jj++) {
y=a[jj][j];
z=a[jj][i];
a[jj][j]=y*c+z*s;
a[jj][i]=z*c-y*s;
}
}
rv1[l]=0.0;
rv1[k]=f;
w[k-1]=x;
}
rv1[l]=0.0;
rv1[k]=f;
w[k-1]=x;
}
}
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
a_[i][j] = a[i+1][j+1];
for (int i=0; i<m; i++)
for (int j=0; j<n; j++)
a_[i][j] = a[i+1][j+1];
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
v_[i][j] = v[i+1][j+1];
}
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
v_[i][j] = v[i+1][j+1];
}
} // namespace Dune::GFE
#endif
......@@ -4,7 +4,8 @@
#include <dune/common/fvector.hh>
#include <dune/common/fmatrix.hh>
namespace Dune {
namespace Dune::GFE
{
/** \brief A class implementing a symmetric matrix with compile-time size
*
......@@ -93,5 +94,6 @@ namespace Dune {
Dune::FieldVector<T,N*(N+1)/2> data_;
};
}
} // namespace Dune::GFE
#endif
......@@ -9,7 +9,7 @@
#include <dune/gfe/gramschmidtsolver.hh>
template <class TargetSpace>
void TargetSpaceRiemannianTRSolver<TargetSpace>::
void Dune::GFE::TargetSpaceRiemannianTRSolver<TargetSpace>::
setup(const AverageDistanceAssembler<TargetSpace>* assembler,
const TargetSpace& x,
double tolerance,
......@@ -25,7 +25,7 @@ setup(const AverageDistanceAssembler<TargetSpace>* assembler,
template <class TargetSpace>
void TargetSpaceRiemannianTRSolver<TargetSpace>::solve()
void Dune::GFE::TargetSpaceRiemannianTRSolver<TargetSpace>::solve()
{
assert(minNumberOfIterations_ > 0);
......
......@@ -5,62 +5,68 @@
#include <dune/gfe/symmetricmatrix.hh>
/** \brief Riemannian trust-region solver for geodesic finite-element problems
\tparam TargetSpace The manifold that our functions take values in
*/
template <class TargetSpace>
class TargetSpaceRiemannianTRSolver
: public NumProc
namespace Dune::GFE
{
const static int blocksize = TargetSpace::TangentVector::dimension;
const static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension;
// Centralize the field type here
typedef typename TargetSpace::ctype field_type;
/** \brief Riemannian trust-region solver for geodesic finite-element problems
\tparam TargetSpace The manifold that our functions take values in
*/
template <class TargetSpace>
class TargetSpaceRiemannianTRSolver
: public NumProc
{
const static int blocksize = TargetSpace::TangentVector::dimension;
const static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension;
typedef Dune::SymmetricMatrix<field_type, embeddedBlocksize> MatrixType;
typedef Dune::FieldVector<field_type, embeddedBlocksize> CorrectionType;
// Centralize the field type here
typedef typename TargetSpace::ctype field_type;
public:
typedef SymmetricMatrix<field_type, embeddedBlocksize> MatrixType;
typedef Dune::FieldVector<field_type, embeddedBlocksize> CorrectionType;
/** \brief Set up the solver using a monotone multigrid method as the inner solver */
void setup(const AverageDistanceAssembler<TargetSpace>* assembler,
const TargetSpace& x,
double tolerance,
int maxNewtonSteps);
public:
void solve();
/** \brief Set up the solver using a monotone multigrid method as the inner solver */
void setup(const AverageDistanceAssembler<TargetSpace>* assembler,
const TargetSpace& x,
double tolerance,
int maxNewtonSteps);
void setInitialSolution(const TargetSpace& x) {
x_ = x;
}
void solve();
TargetSpace getSol() const {return x_;}
void setInitialSolution(const TargetSpace& x) {
x_ = x;
}
protected:
TargetSpace getSol() const {return x_;}
/** \brief The solution vector */
TargetSpace x_;
protected:
/** \brief Tolerance of the solver */
double tolerance_;
/** \brief The solution vector */
TargetSpace x_;
/** \brief Maximum number of Newton steps */
size_t maxNewtonSteps_;
/** \brief Tolerance of the solver */
double tolerance_;
/** \brief The assembler for the average-distance functional */
const AverageDistanceAssembler<TargetSpace>* assembler_;
/** \brief Maximum number of Newton steps */
size_t maxNewtonSteps_;
/** \brief Specify a minimal number of iterations the Newton solver has to do
*
* This is needed when working with automatic differentiation. While a very low
* number of iterations may be enough to precisely compute the value of a
* geodesic finite element function, a higher number may be needed to make an AD
* system compute a derivative with sufficient precision.
*/
size_t minNumberOfIterations_;
/** \brief The assembler for the average-distance functional */
const AverageDistanceAssembler<TargetSpace>* assembler_;
/** \brief Specify a minimal number of iterations the Newton solver has to do
*
* This is needed when working with automatic differentiation. While a very low
* number of iterations may be enough to precisely compute the value of a
* geodesic finite element function, a higher number may be needed to make an AD
* system compute a derivative with sufficient precision.
*/
size_t minNumberOfIterations_;
};
};
} // namespace Dune::GFE
#include "targetspacertrsolver.cc"
......
......@@ -8,189 +8,195 @@
#include <array>
#include <dune/common/fmatrix.hh>
/** \brief A third-rank tensor
*/
template <class T, int N1, int N2, int N3>
class Tensor3
: public std::array<Dune::FieldMatrix<T,N2,N3>,N1>
{
public:
/** \brief Default constructor */
Tensor3() {}
/** \brief Constructor from a scalar */
Tensor3(const T& c)
{
for (int i=0; i<N1; i++)
(*this)[i] = c;
}
T infinity_norm() const
{
static_assert(N1>0, "infinity_norm not implemented for empty tensors");
T norm = (*this)[0].infinity_norm();
for (int i=1; i<N1; i++)
norm = std::max(norm, (*this)[i].infinity_norm());
return norm;
}
T frobenius_norm() const
{
T norm = 0;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
norm += (*this)[i][j][k] * (*this)[i][j][k];
return std::sqrt(norm);
}
/** \brief The squared Frobenius norm, i.e., the sum of squared entries */
T frobenius_norm2() const
{
T norm = 0;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
norm += (*this)[i][j][k] * (*this)[i][j][k];
return norm;
}
Tensor3<T,N1,N2,N3>& axpy(const T& alpha, const Tensor3<T,N1,N2,N3>& other)
{
for (int i=0; i<N1; i++)
(*this)[i].axpy(alpha,other[i]);
return *this;
}
static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldVector<T,N2>& b, const Dune::FieldVector<T,N3>& c)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i]*b[j]*c[k];
return result;
}
static Tensor3<T,N1,N2,N3> product(const Dune::FieldMatrix<T,N1,N2>& ab, const Dune::FieldVector<T,N3>& c)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = ab[i][j]*c[k];
return result;
}
static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldMatrix<T,N2,N3>& bc)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i]*bc[j][k];
return result;
}
template <int N4>
friend Tensor3<T,N1,N2,N4> operator*(const Tensor3<T,N1,N2,N3>& a, const Dune::FieldMatrix<T,N3,N4>& b)
{
Tensor3<T,N1,N2,N4> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N4; k++) {
result[i][j][k] = 0;
for (int l=0; l<N3; l++)
result[i][j][k] += a[i][j][l]*b[l][k];
}
return result;
}
template <int N4>
friend Tensor3<T,N1,N3,N4> operator*(const Dune::FieldMatrix<T,N1,N2>& a, const Tensor3<T,N2,N3,N4>& b)
{
Tensor3<T,N1,N3,N4> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N3; j++)
for (int k=0; k<N4; k++) {
result[i][j][k] = 0;
for (int l=0; l<N2; l++)
result[i][j][k] += a[i][l]*b[l][j][k];
}
return result;
}
friend Tensor3<T,N1,N2,N3> operator+(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i][j][k] + b[i][j][k];
return result;
}
friend Tensor3<T,N1,N2,N3> operator-(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i][j][k] - b[i][j][k];
return result;
}
namespace Dune::GFE
{
friend Tensor3<T,N1,N2,N3> operator*(const T& scalar, const Tensor3<T,N1,N2,N3>& tensor)
/** \brief A third-rank tensor
*/
template <class T, int N1, int N2, int N3>
class Tensor3
: public std::array<Dune::FieldMatrix<T,N2,N3>,N1>
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = scalar * tensor[i][j][k];
return result;
}
Tensor3<T,N1,N2,N3>& operator*=(const T& scalar)
public:
/** \brief Default constructor */
Tensor3() {}
/** \brief Constructor from a scalar */
Tensor3(const T& c)
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
(*this)[i][j][k] = c;
}
T infinity_norm() const
{
static_assert(N1>0, "infinity_norm not implemented for empty tensors");
T norm = (*this)[0].infinity_norm();
for (int i=1; i<N1; i++)
norm = std::max(norm, (*this)[i].infinity_norm());
return norm;
}
T frobenius_norm() const
{
T norm = 0;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
norm += (*this)[i][j][k] * (*this)[i][j][k];
return std::sqrt(norm);
}
/** \brief The squared Frobenius norm, i.e., the sum of squared entries */
T frobenius_norm2() const
{
T norm = 0;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
norm += (*this)[i][j][k] * (*this)[i][j][k];
return norm;
}
Tensor3<T,N1,N2,N3>& axpy(const T& alpha, const Tensor3<T,N1,N2,N3>& other)
{
for (int i=0; i<N1; i++)
(*this)[i].axpy(alpha,other[i]);
return *this;
}
static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldVector<T,N2>& b, const Dune::FieldVector<T,N3>& c)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i]*b[j]*c[k];
return result;
}
static Tensor3<T,N1,N2,N3> product(const Dune::FieldMatrix<T,N1,N2>& ab, const Dune::FieldVector<T,N3>& c)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = ab[i][j]*c[k];
return result;
}
static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldMatrix<T,N2,N3>& bc)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i]*bc[j][k];
return result;
}
template <int N4>
friend Tensor3<T,N1,N2,N4> operator*(const Tensor3<T,N1,N2,N3>& a, const Dune::FieldMatrix<T,N3,N4>& b)
{
Tensor3<T,N1,N2,N4> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N4; k++) {
result[i][j][k] = 0;
for (int l=0; l<N3; l++)
result[i][j][k] += a[i][j][l]*b[l][k];
}
return result;
}
template <int N4>
friend Tensor3<T,N1,N3,N4> operator*(const Dune::FieldMatrix<T,N1,N2>& a, const Tensor3<T,N2,N3,N4>& b)
{
Tensor3<T,N1,N3,N4> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N3; j++)
for (int k=0; k<N4; k++) {
result[i][j][k] = 0;
for (int l=0; l<N2; l++)
result[i][j][k] += a[i][l]*b[l][j][k];
}
return result;
}
friend Tensor3<T,N1,N2,N3> operator+(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i][j][k] + b[i][j][k];
return result;
}
friend Tensor3<T,N1,N2,N3> operator-(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = a[i][j][k] - b[i][j][k];
return result;
}
friend Tensor3<T,N1,N2,N3> operator*(const T& scalar, const Tensor3<T,N1,N2,N3>& tensor)
{
Tensor3<T,N1,N2,N3> result;
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
result[i][j][k] = scalar * tensor[i][j][k];
return result;
}
Tensor3<T,N1,N2,N3>& operator*=(const T& scalar)
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
(*this)[i][j][k] *= scalar;
return *this;
}
};
//! Output operator for Tensor3
template <class T, int N1, int N2, int N3>
inline std::ostream& operator<< (std::ostream& s, const Tensor3<T,N1,N2,N3>& tensor)
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (int k=0; k<N3; k++)
(*this)[i][j][k] *= scalar;
return *this;
s << tensor[i];
return s;
}
};
//! Output operator for Tensor3
template <class T, int N1, int N2, int N3>
inline std::ostream& operator<< (std::ostream& s, const Tensor3<T,N1,N2,N3>& tensor)
{
for (int i=0; i<N1; i++)
s << tensor[i];
return s;
}
} // namespace Dune::GFE
#endif
......@@ -10,122 +10,127 @@
#include <dune/common/fmatrix.hh>
#include <dune/istl/matrix.hh>
/** \brief A third-rank tensor with two static (SS) and one dynamic (D) dimension
*
* \tparam T Type of the entries
* \tparam N1 Size of the first dimension
* \tparam N2 Size of the second dimension
*/
template <class T, int N1, int N2>
class TensorSSD
namespace Dune::GFE
{
public:
/** \brief Constructor with the third dimension */
explicit TensorSSD(size_t N3)
: N3_(N3)
/** \brief A third-rank tensor with two static (SS) and one dynamic (D) dimension
*
* \tparam T Type of the entries
* \tparam N1 Size of the first dimension
* \tparam N2 Size of the second dimension
*/
template <class T, int N1, int N2>
class TensorSSD
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
data_[i][j].resize(N3_);
}
public:
/** \brief Constructor with the third dimension */
explicit TensorSSD(size_t N3)
: N3_(N3)
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
data_[i][j].resize(N3_);
}
size_t dim(int index) const
{
switch (index) {
case 0 :
return N1;
case 1 :
return N2;
case 2 :
return N3_;
default :
assert(false);
size_t dim(int index) const
{
switch (index) {
case 0 :
return N1;
case 1 :
return N2;
case 2 :
return N3_;
default :
assert(false);
}
// Make compiler happy even if NDEBUG is set
return 0;
}
// Make compiler happy even if NDEBUG is set
return 0;
}
/** \brief Direct access to individual entries */
T& operator()(size_t i, size_t j, size_t k)
{
assert(i<N1 && j<N2 && k<N3_);
return data_[i][j][k];
}
/** \brief Direct access to individual entries */
T& operator()(size_t i, size_t j, size_t k)
{
assert(i<N1 && j<N2 && k<N3_);
return data_[i][j][k];
}
/** \brief Direct const access to individual entries */
const T& operator()(size_t i, size_t j, size_t k) const
{
assert(i<N1 && j<N2 && k<N3_);
return data_[i][j][k];
}
/** \brief Direct const access to individual entries */
const T& operator()(size_t i, size_t j, size_t k) const
{
assert(i<N1 && j<N2 && k<N3_);
return data_[i][j][k];
}
/** \brief Assignment from scalar */
TensorSSD<T,N1,N2>& operator=(const T& scalar)
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (size_t k=0; k<dim(2); k++)
data_[i][j][k] = scalar;
/** \brief Assignment from scalar */
TensorSSD<T,N1,N2>& operator=(const T& scalar)
{
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (size_t k=0; k<dim(2); k++)
data_[i][j][k] = scalar;
return *this;
}
return *this;
}
friend TensorSSD<T,N1,N2> operator*(const TensorSSD<T,N1,N2>& a, const Dune::Matrix<T>& b)
{
TensorSSD<T,N1,N2> result(b.M());
friend TensorSSD<T,N1,N2> operator*(const TensorSSD<T,N1,N2>& a, const Dune::Matrix<T>& b)
{
TensorSSD<T,N1,N2> result(b.M());
assert(a.dim(2)==b.N());
size_t N4 = a.dim(2); // third dimension of a
assert(a.dim(2)==b.N());
size_t N4 = a.dim(2); // third dimension of a
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (size_t k=0; k<b.M(); k++) {
result.data_[i][j][k] = 0;
for (size_t l=0; l<N4; l++)
result.data_[i][j][k] += a.data_[i][j][l]*b[l][k];
}
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (size_t k=0; k<b.M(); k++) {
result.data_[i][j][k] = 0;
for (size_t l=0; l<N4; l++)
result.data_[i][j][k] += a.data_[i][j][l]*b[l][k];
}
return result;
}
return result;
}
friend TensorSSD<T,N1,N2> operator+(const TensorSSD<T,N1,N2>& a, const TensorSSD<T,N1,N2>& b)
{
assert(a.dim(2)==b.dim(2));
size_t N3 = a.dim(2);
TensorSSD<T,N1,N2> result(N3);
friend TensorSSD<T,N1,N2> operator+(const TensorSSD<T,N1,N2>& a, const TensorSSD<T,N1,N2>& b)
{
assert(a.dim(2)==b.dim(2));
size_t N3 = a.dim(2);
TensorSSD<T,N1,N2> result(N3);
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (size_t k=0; k<N3; k++)
result.data_[i][j][k] = a.data_[i][j][k] + b.data_[i][j][k];
for (int i=0; i<N1; i++)
for (int j=0; j<N2; j++)
for (size_t k=0; k<N3; k++)
result.data_[i][j][k] = a.data_[i][j][k] + b.data_[i][j][k];
return result;
}
return result;
}
private:
private:
// having the dynamic data type on the inside is kind of a stupid data layout
std::array<std::array<std::vector<T>, N2>, N1> data_;
// having the dynamic data type on the inside is kind of a stupid data layout
std::array<std::array<std::vector<T>, N2>, N1> data_;
// size of the third dimension
size_t N3_;
};
// size of the third dimension
size_t N3_;
};
//! Output operator for TensorSSD
template <class T, int N1, int N2>
inline std::ostream& operator<< (std::ostream& s, const TensorSSD<T,N1,N2>& tensor)
{
for (int i=0; i<N1; i++) {
for (int j=0; j<N2; j++) {
for (size_t k=0; k<tensor.dim(2); k++)
s << tensor(i,j,k) << " ";
//! Output operator for TensorSSD
template <class T, int N1, int N2>
inline std::ostream& operator<< (std::ostream& s, const TensorSSD<T,N1,N2>& tensor)
{
for (int i=0; i<N1; i++) {
for (int j=0; j<N2; j++) {
for (size_t k=0; k<tensor.dim(2); k++)
s << tensor(i,j,k) << " ";
s << std::endl;
}
s << std::endl;
}
s << std::endl;
return s;
}
return s;
}
} // namespace Dune::GFE
#endif
......@@ -9,192 +9,196 @@
#include <dune/solvers/solvers/iterativesolver.hh>
#include <dune/solvers/solvers/quadraticipopt.hh>
/** \brief Base solver for a monotone multigrid solver when used as the inner solver in a trust region method
*
* Monotone multigrid methods solve constrained problems even on the coarsest level. Therefore, the choice
* of possible coarse grid solvers is limited. I have tried both Gauss-Seidel and IPOpt, and both of them
* have not satisfied my. Gauss-Seidel is slow when the coarse grid is not coarse enough. The results computed
* by IPOpt appear to be imprecise, and I didn't get good trust-region convergence. I don't really understand
* this -- it may be a bug in our IPOpt wrapper code.
* Ideally, one would use a direct solver, in particular close to the solution. However, a direct solver may
* not produce admissible corrections. Also, it may produce energy increase. Therefore, the TrustRegionMMGBaseSolver
* does a pragmatic approach: a solution is first computed using UMFPack, disregarding the obstacles.
* If the result is admissible and decreases the energy we keep it. Otherwise we fall back to IPOpt.
*/
template <class MatrixType, class VectorType>
class TrustRegionMMGBaseSolver
: public IterativeSolver<VectorType>,
public CanIgnore<Dune::BitSetVector<VectorType::block_type::dimension> >
namespace Dune::GFE
{
typedef typename VectorType::field_type field_type;
// For complex-valued data
typedef typename Dune::FieldTraits<field_type>::real_type real_type;
constexpr static int blocksize = VectorType::value_type::dimension;
typedef Dune::BitSetVector<blocksize> BitVectorType;
public:
/** \brief Constructor taking all relevant data */
TrustRegionMMGBaseSolver(Solver::VerbosityMode verbosity)
: IterativeSolver<VectorType, BitVectorType>(100, // maxIterations
1e-8, // tolerance
nullptr,
verbosity,
false) // false = use absolute error
{}
void setProblem(const MatrixType& matrix,
VectorType& x,
const VectorType& rhs) {
matrix_ = &matrix;
x_ = &x;
rhs_ = &rhs;
}
/** \brief Checks whether all relevant member variables are set
* \exception SolverError if the iteration step is not set up properly
/** \brief Base solver for a monotone multigrid solver when used as the inner solver in a trust region method
*
* Monotone multigrid methods solve constrained problems even on the coarsest level. Therefore, the choice
* of possible coarse grid solvers is limited. I have tried both Gauss-Seidel and IPOpt, and both of them
* have not satisfied my. Gauss-Seidel is slow when the coarse grid is not coarse enough. The results computed
* by IPOpt appear to be imprecise, and I didn't get good trust-region convergence. I don't really understand
* this -- it may be a bug in our IPOpt wrapper code.
* Ideally, one would use a direct solver, in particular close to the solution. However, a direct solver may
* not produce admissible corrections. Also, it may produce energy increase. Therefore, the TrustRegionMMGBaseSolver
* does a pragmatic approach: a solution is first computed using UMFPack, disregarding the obstacles.
* If the result is admissible and decreases the energy we keep it. Otherwise we fall back to IPOpt.
*/
virtual void check() const;
template <class MatrixType, class VectorType>
class TrustRegionMMGBaseSolver
: public IterativeSolver<VectorType>,
public CanIgnore<Dune::BitSetVector<VectorType::block_type::dimension> >
{
typedef typename VectorType::field_type field_type;
// For complex-valued data
typedef typename Dune::FieldTraits<field_type>::real_type real_type;
constexpr static int blocksize = VectorType::value_type::dimension;
typedef Dune::BitSetVector<blocksize> BitVectorType;
public:
/** \brief Constructor taking all relevant data */
TrustRegionMMGBaseSolver(Solver::VerbosityMode verbosity)
: IterativeSolver<VectorType, BitVectorType>(100, // maxIterations
1e-8, // tolerance
nullptr,
verbosity,
false) // false = use absolute error
{}
void setProblem(const MatrixType& matrix,
VectorType& x,
const VectorType& rhs) {
matrix_ = &matrix;
x_ = &x;
rhs_ = &rhs;
}
virtual void preprocess();
/** \brief Checks whether all relevant member variables are set
* \exception SolverError if the iteration step is not set up properly
*/
virtual void check() const;
/** \brief Loop, call the iteration procedure
* and monitor convergence
*/
virtual void solve();
virtual void preprocess();
//! The quadratic term in the quadratic energy, is assumed to be symmetric
const MatrixType* matrix_;
/** \brief Loop, call the iteration procedure
* and monitor convergence
*/
virtual void solve();
//! Vector to store the solution
VectorType* x_;
//! The quadratic term in the quadratic energy, is assumed to be symmetric
const MatrixType* matrix_;
//! The linear term in the quadratic energy
const VectorType* rhs_;
//! Vector to store the solution
VectorType* x_;
std::vector<BoxConstraint<field_type,blocksize> >* obstacles_;
//! The linear term in the quadratic energy
const VectorType* rhs_;
};
std::vector<BoxConstraint<field_type,blocksize> >* obstacles_;
};
template <class MatrixType, class VectorType>
void TrustRegionMMGBaseSolver<MatrixType, VectorType>::check() const
{
// check base class
IterativeSolver<VectorType,BitVectorType>::check();
}
template <class MatrixType, class VectorType>
void TrustRegionMMGBaseSolver<MatrixType, VectorType>::preprocess()
{
//this->iterationStep_->preprocess();
}
template <class MatrixType, class VectorType>
void TrustRegionMMGBaseSolver<MatrixType, VectorType>::check() const
{
// check base class
IterativeSolver<VectorType,BitVectorType>::check();
}
template <class MatrixType, class VectorType>
void TrustRegionMMGBaseSolver<MatrixType, VectorType>::solve()
{
MatrixType modifiedStiffness = *matrix_;
VectorType modifiedRhs = *rhs_;
template <class MatrixType, class VectorType>
void TrustRegionMMGBaseSolver<MatrixType, VectorType>::preprocess()
{
//this->iterationStep_->preprocess();
}
///////////////////////////////////////////
// Modify Dirichlet rows
///////////////////////////////////////////
template <class MatrixType, class VectorType>
void TrustRegionMMGBaseSolver<MatrixType, VectorType>::solve()
{
MatrixType modifiedStiffness = *matrix_;
VectorType modifiedRhs = *rhs_;
for (size_t j=0; j<modifiedStiffness.N(); j++)
for (int k=0; k<blocksize; k++)
modifiedStiffness[j][j][k][k] += 0.0;
///////////////////////////////////////////
// Modify Dirichlet rows
///////////////////////////////////////////
for (size_t j=0; j<modifiedStiffness.N(); j++)
{
auto cIt = modifiedStiffness[j].begin();
auto cEndIt = modifiedStiffness[j].end();
for (size_t j=0; j<modifiedStiffness.N(); j++)
for (int k=0; k<blocksize; k++)
modifiedStiffness[j][j][k][k] += 0.0;
for (; cIt!=cEndIt; ++cIt)
for (size_t j=0; j<modifiedStiffness.N(); j++)
{
auto cIt = modifiedStiffness[j].begin();
auto cEndIt = modifiedStiffness[j].end();
for (; cIt!=cEndIt; ++cIt)
{
for (int k=0; k<blocksize; k++)
if ((*this->ignoreNodes_)[j][k])
{
(*cIt)[k] = 0;
if (cIt.index()==j)
(*cIt)[k][k] = 1.0;
}
}
for (int k=0; k<blocksize; k++)
if ((*this->ignoreNodes_)[j][k])
{
(*cIt)[k] = 0;
if (cIt.index()==j)
(*cIt)[k][k] = 1.0;
}
modifiedRhs[j][k] = 0.0;
}
for (int k=0; k<blocksize; k++)
if ((*this->ignoreNodes_)[j][k])
modifiedRhs[j][k] = 0.0;
}
/////////////////////////////////////////////////////////////////
// Solve the system
/////////////////////////////////////////////////////////////////
Dune::InverseOperatorResult statistics;
Dune::UMFPack<MatrixType> solver(modifiedStiffness);
solver.setOption(UMFPACK_PRL, 0); // no output at all
solver.apply(*x_, modifiedRhs, statistics);
// Model increase? Then the matrix is not positive definite -- fall back to ipopt solver
// The model decrease is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs>
// Note that rhs = -g
VectorType tmp(x_->size());
tmp = 0;
matrix_->umv(*x_, tmp);
double modelDecrease = (modifiedRhs*(*x_)) - 0.5 * ((*x_)*tmp);
if (std::isnan(modelDecrease) or modelDecrease < 0 or true)
{
std::cout << "Model increase: " << -modelDecrease << ", falling back to slower solver" << std::endl;
/////////////////////////////////////////////////////////////////
// Solve the system
/////////////////////////////////////////////////////////////////
Dune::InverseOperatorResult statistics;
Dune::UMFPack<MatrixType> solver(modifiedStiffness);
solver.setOption(UMFPACK_PRL, 0); // no output at all
solver.apply(*x_, modifiedRhs, statistics);
// Model increase? Then the matrix is not positive definite -- fall back to ipopt solver
// The model decrease is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs>
// Note that rhs = -g
VectorType tmp(x_->size());
tmp = 0;
matrix_->umv(*x_, tmp);
double modelDecrease = (modifiedRhs*(*x_)) - 0.5 * ((*x_)*tmp);
if (std::isnan(modelDecrease) or modelDecrease < 0 or true)
{
std::cout << "Model increase: " << -modelDecrease << ", falling back to slower solver" << std::endl;
std::cout << "Total VARIABLES: " << x_->size() << " x " << blocksize << " = " << x_->size()*blocksize << std::endl;
std::cout << "Dirichlet DOFS: " << this->ignoreNodes_->count() << std::endl;
std::cout << "Total VARIABLES: " << x_->size() << " x " << blocksize << " = " << x_->size()*blocksize << std::endl;
std::cout << "Dirichlet DOFS: " << this->ignoreNodes_->count() << std::endl;
QuadraticIPOptSolver<MatrixType, VectorType> baseSolver;
baseSolver.verbosity_ = NumProc::REDUCED;
baseSolver.tolerance_ = 1e-8;
QuadraticIPOptSolver<MatrixType, VectorType> baseSolver;
baseSolver.verbosity_ = NumProc::REDUCED;
baseSolver.tolerance_ = 1e-8;
*x_ = 0;
baseSolver.setProblem(*matrix_, *x_, *rhs_);
baseSolver.obstacles_ = obstacles_;
baseSolver.ignoreNodes_ = this->ignoreNodes_;
*x_ = 0;
baseSolver.setProblem(*matrix_, *x_, *rhs_);
baseSolver.obstacles_ = obstacles_;
baseSolver.ignoreNodes_ = this->ignoreNodes_;
baseSolver.solve();
}
baseSolver.solve();
}
// Check whether the solution is admissible wrt the obstacles
bool admissible = true;
assert (obstacles_->size() == x_->size());
for (size_t i=0; i<obstacles_->size(); i++)
{
for (int j=0; j<blocksize; j++)
if ( (*x_)[i][j] > (*obstacles_)[i].upper(j) or (*x_)[i][j] < (*obstacles_)[i].lower(j) )
{
admissible = false;
// Check whether the solution is admissible wrt the obstacles
bool admissible = true;
assert (obstacles_->size() == x_->size());
for (size_t i=0; i<obstacles_->size(); i++)
{
for (int j=0; j<blocksize; j++)
if ( (*x_)[i][j] > (*obstacles_)[i].upper(j) or (*x_)[i][j] < (*obstacles_)[i].lower(j) )
{
admissible = false;
break;
}
if (not admissible)
break;
}
}
if (not admissible)
break;
}
{
std::cout << "Inadmissible step, falling back to slower solver" << std::endl;
if (not admissible)
{
std::cout << "Inadmissible step, falling back to slower solver" << std::endl;
QuadraticIPOptSolver<MatrixType, VectorType> baseSolver;
baseSolver.verbosity_ = NumProc::REDUCED;
baseSolver.tolerance_ = 1e-8;
QuadraticIPOptSolver<MatrixType, VectorType> baseSolver;
baseSolver.verbosity_ = NumProc::REDUCED;
baseSolver.tolerance_ = 1e-8;
*x_ = 0;
baseSolver.setProblem(*matrix_, *x_, *rhs_);
baseSolver.obstacles_ = obstacles_;
baseSolver.ignoreNodes_ = this->ignoreNodes_;
*x_ = 0;
baseSolver.setProblem(*matrix_, *x_, *rhs_);
baseSolver.obstacles_ = obstacles_;
baseSolver.ignoreNodes_ = this->ignoreNodes_;
baseSolver.solve();
}
baseSolver.solve();
}
}
} // namespace Dune::GFE
#endif
......@@ -17,358 +17,355 @@
// For parallel infrastructure stuff:
#include <dune/grid/io/file/vtk.hh>
namespace Dune {
namespace GFE {
/** \brief A class representing a VTK file, but independent from the Dune grid interface
*
* This file is supposed to represent an abstraction layer in between the pure XML used for VTK files,
* and the VTKWriter from dune-grid, which knows about grids. In theory, the dune-grid VTKWriter
* could use this class to simplify its own code. More importantly, the VTKFile class allows to
* write files containing second-order geometries, which is currently not possible with the dune-grid
* VTKWriter.
*/
class VTKFile
namespace Dune::GFE
{
/** \brief A class representing a VTK file, but independent from the Dune grid interface
*
* This file is supposed to represent an abstraction layer in between the pure XML used for VTK files,
* and the VTKWriter from dune-grid, which knows about grids. In theory, the dune-grid VTKWriter
* could use this class to simplify its own code. More importantly, the VTKFile class allows to
* write files containing second-order geometries, which is currently not possible with the dune-grid
* VTKWriter.
*/
class VTKFile
{
public:
/** \brief Write the file to disk */
void write(const std::string& filename) const
{
int argc = 0;
char** argv = nullptr;
Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc,argv);
std::string fullfilename = filename + ".vtu";
public:
// Prepend rank and communicator size to the filename, if there are more than one process
if (mpiHelper.size() > 1)
fullfilename = getParallelPieceName(filename, "", mpiHelper.rank(), mpiHelper.size());
/** \brief Write the file to disk */
void write(const std::string& filename) const
// Write the pvtu file that ties together the different parts
if (mpiHelper.size() > 1 && mpiHelper.rank()==0)
{
int argc = 0;
char** argv = nullptr;
Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc,argv);
std::ofstream pvtuOutFile(getParallelName(filename, "", mpiHelper.size()));
Dune::VTK::PVTUWriter writer(pvtuOutFile, Dune::VTK::unstructuredGrid);
std::string fullfilename = filename + ".vtu";
writer.beginMain();
// Prepend rank and communicator size to the filename, if there are more than one process
if (mpiHelper.size() > 1)
fullfilename = getParallelPieceName(filename, "", mpiHelper.rank(), mpiHelper.size());
writer.beginPointData();
writer.addArray("director0", 3, VTK::Precision::float32);
writer.addArray("director1", 3, VTK::Precision::float32);
writer.addArray("director2", 3, VTK::Precision::float32);
writer.addArray("zCoord", 1, VTK::Precision::float32);
writer.endPointData();
// Write the pvtu file that ties together the different parts
if (mpiHelper.size() > 1 && mpiHelper.rank()==0)
{
std::ofstream pvtuOutFile(getParallelName(filename, "", mpiHelper.size()));
Dune::VTK::PVTUWriter writer(pvtuOutFile, Dune::VTK::unstructuredGrid);
writer.beginCellData();
writer.addArray("mycelldata", 1, VTK::Precision::float32);
writer.endCellData();
writer.beginMain();
// dump point coordinates
writer.beginPoints();
writer.addArray("Coordinates", 3, VTK::Precision::float32);
writer.endPoints();
writer.beginPointData();
writer.addArray("director0", 3, VTK::Precision::float32);
writer.addArray("director1", 3, VTK::Precision::float32);
writer.addArray("director2", 3, VTK::Precision::float32);
writer.addArray("zCoord", 1, VTK::Precision::float32);
writer.endPointData();
for (int i=0; i<mpiHelper.size(); i++)
writer.addPiece(getParallelPieceName(filename, "", i, mpiHelper.size()));
writer.beginCellData();
writer.addArray("mycelldata", 1, VTK::Precision::float32);
writer.endCellData();
// finish main section
writer.endMain();
}
// dump point coordinates
writer.beginPoints();
writer.addArray("Coordinates", 3, VTK::Precision::float32);
writer.endPoints();
std::ofstream outFile(fullfilename);
// Write header
outFile << "<?xml version=\"1.0\"?>" << std::endl;
outFile << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl;
outFile << " <UnstructuredGrid>" << std::endl;
outFile << " <Piece NumberOfCells=\"" << cellOffsets_.size() << "\" NumberOfPoints=\"" << points_.size() << "\">" << std::endl;
// Write vertex coordinates
outFile << " <Points>" << std::endl;
{ // extra parenthesis to control destruction of the pointsWriter object
Dune::VTK::AsciiDataArrayWriter pointsWriter(outFile, "Coordinates", 3, Dune::Indent(4), VTK::Precision::float32);
for (size_t i=0; i<points_.size(); i++)
for (int j=0; j<3; j++)
pointsWriter.write(points_[i][j]);
} // destructor of pointsWriter objects writes trailing </DataArray> to file
outFile << " </Points>" << std::endl;
// Write elements
outFile << " <Cells>" << std::endl;
{ // extra parenthesis to control destruction of the cellConnectivityWriter object
Dune::VTK::AsciiDataArrayWriter cellConnectivityWriter(outFile, "connectivity", 1, Dune::Indent(4), VTK::Precision::int32);
for (size_t i=0; i<cellConnectivity_.size(); i++)
cellConnectivityWriter.write(cellConnectivity_[i]);
}
for (int i=0; i<mpiHelper.size(); i++)
writer.addPiece(getParallelPieceName(filename, "", i, mpiHelper.size()));
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter cellOffsetsWriter(outFile, "offsets", 1, Dune::Indent(4), VTK::Precision::int32);
for (size_t i=0; i<cellOffsets_.size(); i++)
cellOffsetsWriter.write(cellOffsets_[i]);
}
// finish main section
writer.endMain();
}
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter cellTypesWriter(outFile, "types", 1, Dune::Indent(4), VTK::Precision::uint32);
for (size_t i=0; i<cellTypes_.size(); i++)
cellTypesWriter.write(cellTypes_[i]);
}
std::ofstream outFile(fullfilename);
// Write header
outFile << "<?xml version=\"1.0\"?>" << std::endl;
outFile << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl;
outFile << " <UnstructuredGrid>" << std::endl;
outFile << " <Piece NumberOfCells=\"" << cellOffsets_.size() << "\" NumberOfPoints=\"" << points_.size() << "\">" << std::endl;
// Write vertex coordinates
outFile << " <Points>" << std::endl;
{ // extra parenthesis to control destruction of the pointsWriter object
Dune::VTK::AsciiDataArrayWriter pointsWriter(outFile, "Coordinates", 3, Dune::Indent(4), VTK::Precision::float32);
for (size_t i=0; i<points_.size(); i++)
for (int j=0; j<3; j++)
pointsWriter.write(points_[i][j]);
} // destructor of pointsWriter objects writes trailing </DataArray> to file
outFile << " </Points>" << std::endl;
// Write elements
outFile << " <Cells>" << std::endl;
{ // extra parenthesis to control destruction of the cellConnectivityWriter object
Dune::VTK::AsciiDataArrayWriter cellConnectivityWriter(outFile, "connectivity", 1, Dune::Indent(4), VTK::Precision::int32);
for (size_t i=0; i<cellConnectivity_.size(); i++)
cellConnectivityWriter.write(cellConnectivity_[i]);
}
outFile << " </Cells>" << std::endl;
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter cellOffsetsWriter(outFile, "offsets", 1, Dune::Indent(4), VTK::Precision::int32);
for (size_t i=0; i<cellOffsets_.size(); i++)
cellOffsetsWriter.write(cellOffsets_[i]);
}
//////////////////////////////////////////////////
// Point data
//////////////////////////////////////////////////
outFile << " <PointData Scalars=\"zCoord\" Vectors=\"director0\">" << std::endl;
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter cellTypesWriter(outFile, "types", 1, Dune::Indent(4), VTK::Precision::uint32);
for (size_t i=0; i<cellTypes_.size(); i++)
cellTypesWriter.write(cellTypes_[i]);
}
// Z coordinate for better visualization of wrinkles
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter zCoordWriter(outFile, "zCoord", 1, Dune::Indent(4), VTK::Precision::float32);
for (size_t i=0; i<zCoord_.size(); i++)
zCoordWriter.write(zCoord_[i]);
}
outFile << " </Cells>" << std::endl;
// The three director fields
for (size_t i=0; i<3; i++)
{
Dune::VTK::AsciiDataArrayWriter directorWriter(outFile, "director" + std::to_string(i), 3, Dune::Indent(4), VTK::Precision::float32);
for (size_t j=0; j<directors_[i].size(); j++)
for (int k=0; k<3; k++)
directorWriter.write(directors_[i][j][k]);
}
//////////////////////////////////////////////////
// Point data
//////////////////////////////////////////////////
outFile << " <PointData Scalars=\"zCoord\" Vectors=\"director0\">" << std::endl;
outFile << " </PointData>" << std::endl;
// Z coordinate for better visualization of wrinkles
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter zCoordWriter(outFile, "zCoord", 1, Dune::Indent(4), VTK::Precision::float32);
for (size_t i=0; i<zCoord_.size(); i++)
zCoordWriter.write(zCoord_[i]);
}
//////////////////////////////////////////////////
// Cell data
//////////////////////////////////////////////////
// The three director fields
for (size_t i=0; i<3; i++)
{
Dune::VTK::AsciiDataArrayWriter directorWriter(outFile, "director" + std::to_string(i), 3, Dune::Indent(4), VTK::Precision::float32);
for (size_t j=0; j<directors_[i].size(); j++)
for (int k=0; k<3; k++)
directorWriter.write(directors_[i][j][k]);
if (cellData_.size() > 0)
{
outFile << " <CellData>" << std::endl;
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter cellDataWriter(outFile, "mycelldata", 1, Dune::Indent(4), VTK::Precision::float32);
for (size_t i=0; i<cellData_.size(); i++)
cellDataWriter.write(cellData_[i]);
}
outFile << " </CellData>" << std::endl;
}
outFile << " </PointData>" << std::endl;
//////////////////////////////////////////////////
// Write footer
//////////////////////////////////////////////////
outFile << " </Piece>" << std::endl;
outFile << " </UnstructuredGrid>" << std::endl;
outFile << "</VTKFile>" << std::endl;
//////////////////////////////////////////////////
// Cell data
//////////////////////////////////////////////////
if (cellData_.size() > 0)
{
outFile << " <CellData>" << std::endl;
{ // extra parenthesis to control destruction of the writer object
Dune::VTK::AsciiDataArrayWriter cellDataWriter(outFile, "mycelldata", 1, Dune::Indent(4), VTK::Precision::float32);
for (size_t i=0; i<cellData_.size(); i++)
cellDataWriter.write(cellData_[i]);
}
outFile << " </CellData>" << std::endl;
}
}
/** \brief Read a VTKFile object from a file
*
* This method uses TinyXML2. If you do not have TinyXML2 installed the code will simply throw a NotImplemented exception.
*/
void read(const std::string& filename)
{
int argc = 0;
char** argv = nullptr;
Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc,argv);
//////////////////////////////////////////////////
// Write footer
//////////////////////////////////////////////////
outFile << " </Piece>" << std::endl;
outFile << " </UnstructuredGrid>" << std::endl;
outFile << "</VTKFile>" << std::endl;
std::string fullfilename = filename; // + ".vtu";
// Prepend rank and communicator size to the filename, if there are more than one process
if (mpiHelper.size() > 1)
fullfilename = getParallelPieceName(filename, "", mpiHelper.rank(), mpiHelper.size());
#if ! HAVE_TINYXML2
DUNE_THROW(Dune::NotImplemented, "You need TinyXML2 for vtk file reading!");
#else
tinyxml2::XMLDocument doc;
if (doc.LoadFile(fullfilename.c_str()) != tinyxml2::XML_SUCCESS)
{
DUNE_THROW(Dune::IOError, "Couldn't open the file '" << fullfilename << "'");
}
/** \brief Read a VTKFile object from a file
*
* This method uses TinyXML2. If you do not have TinyXML2 installed the code will simply throw a NotImplemented exception.
*/
void read(const std::string& filename)
// Get number of cells and number of points
int numberOfCells;
int numberOfPoints;
// First try whether this is a vtu file
tinyxml2::XMLElement* pieceElement = doc.FirstChildElement( "VTKFile" )->FirstChildElement( "UnstructuredGrid" )->FirstChildElement( "Piece" );
if (pieceElement)
{
pieceElement->QueryIntAttribute( "NumberOfCells", &numberOfCells );
}
else // No vtu file? So let's try vtp
if ((pieceElement = doc.FirstChildElement( "VTKFile" )->FirstChildElement( "PolyData" )->FirstChildElement( "Piece" )) )
{
int argc = 0;
char** argv = nullptr;
Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc,argv);
pieceElement->QueryIntAttribute( "NumberOfPolys", &numberOfCells );
}
else
DUNE_THROW(Dune::IOError, "Unsupported file type found");
std::string fullfilename = filename;// + ".vtu";
pieceElement->QueryIntAttribute( "NumberOfPoints", &numberOfPoints );
// Prepend rank and communicator size to the filename, if there are more than one process
if (mpiHelper.size() > 1)
fullfilename = getParallelPieceName(filename, "", mpiHelper.rank(), mpiHelper.size());
//////////////////////////////////
// Read point coordinates
//////////////////////////////////
points_.resize(numberOfPoints);
tinyxml2::XMLElement* pointsElement = pieceElement->FirstChildElement( "Points" )->FirstChildElement( "DataArray" );
#if ! HAVE_TINYXML2
DUNE_THROW(Dune::NotImplemented, "You need TinyXML2 for vtk file reading!");
#else
tinyxml2::XMLDocument doc;
if (doc.LoadFile(fullfilename.c_str()) != tinyxml2::XML_SUCCESS)
std::stringstream coordsStream(pointsElement->GetText());
double inDouble;
size_t i=0;
while (coordsStream >> inDouble)
{
points_[i/3][i%3] = inDouble;
i++;
}
///////////////////////////////////
// Read cells
///////////////////////////////////
tinyxml2::XMLElement* cellsElement = pieceElement->FirstChildElement( "Cells" )->FirstChildElement( "DataArray" );
if (not cellsElement)
cellsElement = pieceElement->FirstChildElement( "Polys" )->FirstChildElement( "DataArray" );
while (cellsElement != nullptr)
{
int inInt;
if (cellsElement->Attribute("Name", "connectivity"))
{
DUNE_THROW(Dune::IOError, "Couldn't open the file '" << fullfilename << "'");
std::stringstream connectivityStream(cellsElement->GetText());
while (connectivityStream >> inInt)
cellConnectivity_.push_back(inInt);
}
// Get number of cells and number of points
int numberOfCells;
int numberOfPoints;
// First try whether this is a vtu file
tinyxml2::XMLElement* pieceElement = doc.FirstChildElement( "VTKFile" )->FirstChildElement( "UnstructuredGrid" )->FirstChildElement( "Piece" );
if (pieceElement)
if (cellsElement->Attribute("Name", "offsets"))
{
pieceElement->QueryIntAttribute( "NumberOfCells", &numberOfCells );
std::stringstream offsetsStream(cellsElement->GetText());
while (offsetsStream >> inInt)
cellOffsets_.push_back(inInt);
}
else // No vtu file? So let's try vtp
if ((pieceElement = doc.FirstChildElement( "VTKFile" )->FirstChildElement( "PolyData" )->FirstChildElement( "Piece" )) )
if (cellsElement->Attribute("Name", "types"))
{
pieceElement->QueryIntAttribute( "NumberOfPolys", &numberOfCells );
std::stringstream typesStream(cellsElement->GetText());
while (typesStream >> inInt)
cellTypes_.push_back(inInt);
}
else
DUNE_THROW(Dune::IOError, "Unsupported file type found");
pieceElement->QueryIntAttribute( "NumberOfPoints", &numberOfPoints );
//////////////////////////////////
// Read point coordinates
//////////////////////////////////
points_.resize(numberOfPoints);
tinyxml2::XMLElement* pointsElement = pieceElement->FirstChildElement( "Points" )->FirstChildElement( "DataArray" );
std::stringstream coordsStream(pointsElement->GetText());
cellsElement = cellsElement->NextSiblingElement();
}
double inDouble;
size_t i=0;
while (coordsStream >> inDouble)
{
points_[i/3][i%3] = inDouble;
i++;
}
/////////////////////////////////////
// Read point data
/////////////////////////////////////
///////////////////////////////////
// Read cells
///////////////////////////////////
tinyxml2::XMLElement* cellsElement = pieceElement->FirstChildElement( "Cells" )->FirstChildElement( "DataArray" );
if (not cellsElement)
cellsElement = pieceElement->FirstChildElement( "Polys" )->FirstChildElement( "DataArray" );
tinyxml2::XMLElement* pointDataElement = pieceElement->FirstChildElement( "PointData" )->FirstChildElement( "DataArray" );
while (cellsElement != nullptr)
while (pointDataElement)
{
size_t j=0;
std::stringstream directorStream(pointDataElement->GetText());
if (pointDataElement->Attribute("Name", "director0"))
{
int inInt;
if (cellsElement->Attribute("Name", "connectivity"))
{
std::stringstream connectivityStream(cellsElement->GetText());
while (connectivityStream >> inInt)
cellConnectivity_.push_back(inInt);
}
if (cellsElement->Attribute("Name", "offsets"))
{
std::stringstream offsetsStream(cellsElement->GetText());
while (offsetsStream >> inInt)
cellOffsets_.push_back(inInt);
}
if (cellsElement->Attribute("Name", "types"))
directors_[0].resize(numberOfPoints);
while (directorStream >> inDouble)
{
std::stringstream typesStream(cellsElement->GetText());
while (typesStream >> inInt)
cellTypes_.push_back(inInt);
directors_[0][j/3][j%3] = inDouble;
j++;
}
cellsElement = cellsElement->NextSiblingElement();
}
/////////////////////////////////////
// Read point data
/////////////////////////////////////
tinyxml2::XMLElement* pointDataElement = pieceElement->FirstChildElement( "PointData" )->FirstChildElement( "DataArray" );
while (pointDataElement)
if (pointDataElement->Attribute("Name", "director1"))
{
size_t j=0;
std::stringstream directorStream(pointDataElement->GetText());
if (pointDataElement->Attribute("Name", "director0"))
directors_[1].resize(numberOfPoints);
while (directorStream >> inDouble)
{
directors_[0].resize(numberOfPoints);
while (directorStream >> inDouble)
{
directors_[0][j/3][j%3] = inDouble;
j++;
}
directors_[1][j/3][j%3] = inDouble;
j++;
}
if (pointDataElement->Attribute("Name", "director1"))
{
directors_[1].resize(numberOfPoints);
while (directorStream >> inDouble)
{
directors_[1][j/3][j%3] = inDouble;
j++;
}
}
if (pointDataElement->Attribute("Name", "director2"))
}
if (pointDataElement->Attribute("Name", "director2"))
{
directors_[2].resize(numberOfPoints);
while (directorStream >> inDouble)
{
directors_[2].resize(numberOfPoints);
while (directorStream >> inDouble)
{
directors_[2][j/3][j%3] = inDouble;
j++;
}
directors_[2][j/3][j%3] = inDouble;
j++;
}
pointDataElement = pointDataElement->NextSiblingElement();
}
#endif
pointDataElement = pointDataElement->NextSiblingElement();
}
#endif
}
std::vector<Dune::FieldVector<double,3> > points_;
std::vector<Dune::FieldVector<double,3> > points_;
std::vector<unsigned int> cellConnectivity_;
std::vector<unsigned int> cellConnectivity_;
std::vector<int> cellOffsets_;
std::vector<int> cellOffsets_;
std::vector<int> cellTypes_;
std::vector<int> cellTypes_;
std::vector<double> zCoord_;
std::vector<double> zCoord_;
std::vector<double> cellData_;
std::vector<double> cellData_;
std::array<std::vector<Dune::FieldVector<double,3> >, 3 > directors_;
std::array<std::vector<Dune::FieldVector<double,3> >, 3 > directors_;
private:
private:
/** \brief Extend filename to contain communicator rank and size
*
* Copied from dune-grid vtkwriter.hh
*/
static std::string getParallelPieceName(const std::string& name,
const std::string& path,
int commRank, int commSize)
{
std::ostringstream s;
if(path.size() > 0) {
s << path;
if(path[path.size()-1] != '/')
s << '/';
}
s << 's' << std::setw(4) << std::setfill('0') << commSize << '-';
s << 'p' << std::setw(4) << std::setfill('0') << commRank << '-';
s << name;
if (true) //(GridType::dimension > 1)
s << ".vtu";
else
s << ".vtp";
return s.str();
/** \brief Extend filename to contain communicator rank and size
*
* Copied from dune-grid vtkwriter.hh
*/
static std::string getParallelPieceName(const std::string& name,
const std::string& path,
int commRank, int commSize)
{
std::ostringstream s;
if(path.size() > 0) {
s << path;
if(path[path.size()-1] != '/')
s << '/';
}
/** \brief Extend filename to contain communicator rank and size
*
* Copied from dune-grid vtkwriter.hh
*/
static std::string getParallelName(const std::string& name,
const std::string& path,
int commSize)
{
std::ostringstream s;
if(path.size() > 0) {
s << path;
if(path[path.size()-1] != '/')
s << '/';
}
s << 's' << std::setw(4) << std::setfill('0') << commSize << '-';
s << name;
if (true) //(GridType::dimension > 1)
s << ".pvtu";
else
s << ".pvtp";
return s.str();
s << 's' << std::setw(4) << std::setfill('0') << commSize << '-';
s << 'p' << std::setw(4) << std::setfill('0') << commRank << '-';
s << name;
if (true) //(GridType::dimension > 1)
s << ".vtu";
else
s << ".vtp";
return s.str();
}
/** \brief Extend filename to contain communicator rank and size
*
* Copied from dune-grid vtkwriter.hh
*/
static std::string getParallelName(const std::string& name,
const std::string& path,
int commSize)
{
std::ostringstream s;
if(path.size() > 0) {
s << path;
if(path[path.size()-1] != '/')
s << '/';
}
};
}
}
s << 's' << std::setw(4) << std::setfill('0') << commSize << '-';
s << name;
if (true) //(GridType::dimension > 1)
s << ".pvtu";
else
s << ".pvtp";
return s.str();
}
};
} // namespace Dune::GFE
#endif
import math
class ParameterSet(dict):
def __init__(self, *args, **kwargs):
super(ParameterSet, self).__init__(*args, **kwargs)
self.__dict__ = self
parameterSet = ParameterSet()
#############################################
# Paths
#############################################
parameterSet.resultPath = '/home/klaus/Desktop/Dune-Hermite/dune-gfe/outputs_L-clamped-Plate'
parameterSet.instrumentedPath = '/home/klaus/Desktop/Dune-Hermite/dune-gfe/outputs_L-clamped-Plate/instrumented'
parameterSet.executablePath = '/home/klaus/Desktop/Dune-Hermite/dune-gfe/build-cmake/src'
parameterSet.baseName= 'L-clamped-Plate'
#############################################
# Grid parameters
#############################################
nX=1
nY=1
parameterSet.structuredGrid = 'simplex'
parameterSet.lower = '0 0'
parameterSet.upper = '4 4'
parameterSet.elements = str(nX)+' '+ str(nY)
parameterSet.macroGridLevel = 3
#############################################
# Solver parameters
#############################################
# Choose solver: "RNHM" (Default:Riemannian Newton with Hessian modification), "RiemannianTR" (Riemannain Trust-region method)
parameterSet.Solver = "RNHM"
# Tolerance of the multigrid solver
parameterSet.tolerance = 1e-12
# Maximum number of multigrid iterations
parameterSet.maxProximalNewtonSteps = 100
# Initial regularization
parameterSet.initialRegularization = 1
# Measure convergence
parameterSet.instrumented = 0
############################
# Problem specifications
############################
# Dimension of the domain (only used for numerical-test python files)
parameterSet.dim = 2
# Dimension of the target space
parameterSet.targetDim = 3
parameterSet.targetSpace = 'BendingIsometry'
#############################################
# Options
#############################################
# Write discrete solution as .vtk-File
parameterSet.writeVTK = 1
# Write Dof-Vector to .txt-file
parameterSet.writeDOFvector = 0
#############################################
# Dirichlet boundary indicator
#############################################
def dirichlet_indicator(x) :
if( (x[0] <= 0.001) or (x[1]<=0.001)):
return True
else:
return False
#############################################
# Initial iterate function
#############################################
def f(x):
return [x[0], x[1], 0]
def df(x):
return ((1,0),
(0,1),
(0,0))
fdf = (f, df)
#############################################
# Force
############################################
def force(x):
return [0, 0, 0.025]