From d984ed41f20f99737f7ec46894586d9ef6ce0648 Mon Sep 17 00:00:00 2001 From: Oliver Sander <oliver.sander@tu-dresden.de> Date: Wed, 18 Sep 2024 07:25:58 +0200 Subject: [PATCH] Move external loads out of NonplanarCosseratShellEnergy That is one step towards splitting off the energy density into a separate class. Also, it helps to unify the code paths for the different grid dimensions in cosserat-continuum.cc --- .../nonplanarcosseratshellenergy.hh | 115 +----------------- src/cosserat-continuum.cc | 98 ++++++++------- test/nonplanarcosseratenergytest.cc | 6 +- 3 files changed, 56 insertions(+), 163 deletions(-) diff --git a/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh b/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh index 9e5a8765..6fd7055a 100644 --- a/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh +++ b/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh @@ -95,14 +95,8 @@ public: * \param stressFreeStateGridFunction Pointer to a parametrization representing the Cosserat shell in a stress-free state */ NonplanarCosseratShellEnergy(const Dune::ParameterTree& parameters, - const StressFreeStateGridFunction* stressFreeStateGridFunction, - const BoundaryPatch<GridView>* neumannBoundary, - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction, - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad) - : stressFreeStateGridFunction_(stressFreeStateGridFunction), - neumannBoundary_(neumannBoundary), - neumannFunction_(neumannFunction), - volumeLoad_(volumeLoad) + const StressFreeStateGridFunction* stressFreeStateGridFunction) + : stressFreeStateGridFunction_(stressFreeStateGridFunction) { // The shell thickness thickness_ = parameters.template get<double>("thickness"); @@ -211,15 +205,6 @@ public: /** \brief The geometry of the reference deformation used for assembling */ const StressFreeStateGridFunction* stressFreeStateGridFunction_; - - /** \brief The Neumann boundary */ - const BoundaryPatch<GridView>* neumannBoundary_; - - /** \brief The function implementing the Neumann data */ - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction_; - - /** \brief The function implementing a volume load */ - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad_; }; template <class Basis, int dim, class field_type, class StressFreeStateGridFunction> @@ -416,54 +401,6 @@ energy(const typename Basis::LocalView& localView, // Add energy density energy += quad[pt].weight() * integrationElement * energyDensity; - - /////////////////////////////////////////////////////////// - // Volume load contribution - /////////////////////////////////////////////////////////// - - if (not volumeLoad_) - continue; - - // Value of the volume load density at the current position - Dune::FieldVector<double,3> volumeLoadDensity = volumeLoad_(geometry.global(quad[pt].position())); - - // Only translational dofs are affected by the volume load - for (size_t i=0; i<volumeLoadDensity.size(); i++) - energy += (volumeLoadDensity[i] * value[_0].globalCoordinates()[i]) * quad[pt].weight() * integrationElement; - } - - - ////////////////////////////////////////////////////////////////////////////// - // Assemble boundary contributions - ////////////////////////////////////////////////////////////////////////////// - - if (not neumannFunction_) - return energy; - - for (auto&& it : intersections(neumannBoundary_->gridView(),element) ) - { - if (not neumannBoundary_ or not neumannBoundary_->contains(it)) - continue; - - const auto& quad = Dune::QuadratureRules<DT, gridDim-1>::rule(it.type(), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) - { - // Local position of the quadrature point - const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position()); - - const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); - - // The value of the local function - Dune::GFE::ProductManifold<RealTuple<field_type,dim>,Rotation<field_type,dim> > value = localGeodesicFEFunction.evaluate(quadPos); - - // Value of the Neumann data at the current position - Dune::FieldVector<double,3> neumannValue = neumannFunction_(it.geometry().global(quad[pt].position())); - - // Only translational dofs are affected by the Neumann force - for (size_t i=0; i<neumannValue.size(); i++) - energy += (neumannValue[i] * value[_0].globalCoordinates()[i]) * quad[pt].weight() * integrationElement; - } } return energy; @@ -659,54 +596,6 @@ energy(const typename Basis::LocalView& localView, // Add energy density energy += quad[pt].weight() * integrationElement * energyDensity; - - /////////////////////////////////////////////////////////// - // Volume load contribution - /////////////////////////////////////////////////////////// - - if (not volumeLoad_) - continue; - - // Value of the volume load density at the current position - Dune::FieldVector<double,3> volumeLoadDensity = volumeLoad_(geometry.global(quad[pt].position())); - - // Only translational dofs are affected by the volume load - for (size_t i=0; i<volumeLoadDensity.size(); i++) - energy += (volumeLoadDensity[i] * deformationValue[i]) * quad[pt].weight() * integrationElement; - } - - - ////////////////////////////////////////////////////////////////////////////// - // Assemble boundary contributions - ////////////////////////////////////////////////////////////////////////////// - - if (not neumannFunction_) - return energy; - - for (auto&& it : intersections(neumannBoundary_->gridView(),element) ) - { - if (not neumannBoundary_ or not neumannBoundary_->contains(it)) - continue; - - const auto& quad = Dune::QuadratureRules<DT, gridDim-1>::rule(it.type(), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) - { - // Local position of the quadrature point - const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position()); - - const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); - - // The value of the local function - RealTuple<field_type,dim> deformationValue = localDeformationGFEFunction.evaluate(quadPos); - - // Value of the Neumann data at the current position - Dune::FieldVector<double,3> neumannValue = neumannFunction_(it.geometry().global(quad[pt].position())); - - // Only translational dofs are affected by the Neumann force - for (size_t i=0; i<neumannValue.size(); i++) - energy += (neumannValue[i] * deformationValue[i]) * quad[pt].weight() * integrationElement; - } } return energy; diff --git a/src/cosserat-continuum.cc b/src/cosserat-continuum.cc index 5c1c451d..f4250ceb 100644 --- a/src/cosserat-continuum.cc +++ b/src/cosserat-continuum.cc @@ -546,48 +546,69 @@ int main (int argc, char *argv[]) try if (orientationDirichletDofs[i][0]) x[_1][i].set(dOV[i]); - if (dim==dimworld) { - auto sumEnergy = std::make_shared<GFE::SumEnergy<CompositeBasis, RealTuple<adouble,3>,Rotation<adouble,3> > >(); + //////////////////////////////////////////////////////////////// + // Build the assembler for the tangent problems + //////////////////////////////////////////////////////////////// + + // Construct the interpolation rule, i.e., the geometric finite element + using ScalarDeformationLocalFiniteElement = decltype(compositeBasis.localView().tree().child(_0,0).finiteElement()); + using ScalarRotationLocalFiniteElement = decltype(compositeBasis.localView().tree().child(_1,0).finiteElement()); - // The Cosserat shell energy - using ScalarDeformationLocalFiniteElement = decltype(compositeBasis.localView().tree().child(_0,0).finiteElement()); - using ScalarRotationLocalFiniteElement = decltype(compositeBasis.localView().tree().child(_1,0).finiteElement()); + using AInterpolationRule = std::tuple<LocalGeodesicFEFunction<dim, double, ScalarDeformationLocalFiniteElement, RealTuple<adouble,3> >, + LocalGeodesicFEFunction<dim, double, ScalarRotationLocalFiniteElement, Rotation<adouble,3> > >; - using AInterpolationRule = std::tuple<LocalGeodesicFEFunction<dim, double, ScalarDeformationLocalFiniteElement, RealTuple<adouble,3> >, - LocalGeodesicFEFunction<dim, double, ScalarRotationLocalFiniteElement, Rotation<adouble,3> > >; + using ATargetSpace = typename TargetSpace::rebind<adouble>::other; - using ATargetSpace = typename TargetSpace::rebind<adouble>::other; + using LocalCoordinate = typename GridType::Codim<0>::Entity::Geometry::LocalCoordinate; - using LocalCoordinate = typename GridType::Codim<0>::Entity::Geometry::LocalCoordinate; + // The energy on one element + auto sumEnergy = std::make_shared<GFE::SumEnergy<CompositeBasis, RealTuple<adouble,3>,Rotation<adouble,3> > >(); + + if (dim==dimworld) { auto cosseratDensity = createDensity<LocalCoordinate>(materialParameters); auto localCosseratEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis,AInterpolationRule,ATargetSpace> >(cosseratDensity); sumEnergy->addLocalEnergy(localCosseratEnergy); + } + else + { + static_assert((dim==dimworld) || (dim==2 && dimworld==3), + "For dim!=dimworld, only the case dim==2 and dimworld==3 is supported!"); + + auto localCosseratEnergy + = std::make_shared<NonplanarCosseratShellEnergy<CompositeBasis, 3, adouble, decltype(creator)> >(materialParameters, + &creator); - // The Neumann surface load term - auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<adouble,3>, Rotation<adouble,3> > >(neumannBoundary,neumannFunction); - sumEnergy->addLocalEnergy(neumannEnergy); - - // The volume load term - // TODO: There is a bug here: The volumeLoad function is currently defined in global coordinates, - // but the density expects it to be in local coordinates with respect to the element being - // integrated over. I have to think about where the binding should happen. - // Practically, the bug does not really show, because all our volume loads - // are constant in space anyway. - auto volumeLoadDensity = std::make_shared<GFE::CosseratVolumeLoadDensity<LocalCoordinate,adouble> >(volumeLoad); - auto volumeLoadEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, AInterpolationRule, ATargetSpace> >(volumeLoadDensity); - sumEnergy->addLocalEnergy(volumeLoadEnergy); - - // The local assembler - LocalGeodesicFEADOLCStiffness<CompositeBasis,TargetSpace> localGFEADOLCStiffness(sumEnergy, - adolcScalarMode); - - MixedGFEAssembler<CompositeBasis,TargetSpace> mixedAssembler(compositeBasis, localGFEADOLCStiffness); - - //////////////////////////////////////////// - // Set up the solver - //////////////////////////////////////////// + sumEnergy->addLocalEnergy(localCosseratEnergy); + } + + // The Neumann surface load term + auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<adouble,3>, Rotation<adouble,3> > >(neumannBoundary,neumannFunction); + sumEnergy->addLocalEnergy(neumannEnergy); + + // The volume load term + // TODO: There is a bug here: The volumeLoad function is currently defined in global coordinates, + // but the density expects it to be in local coordinates with respect to the element being + // integrated over. I have to think about where the binding should happen. + // Practically, the bug does not really show, because all our volume loads + // are constant in space anyway. + auto volumeLoadDensity = std::make_shared<GFE::CosseratVolumeLoadDensity<LocalCoordinate,adouble> >(volumeLoad); + auto volumeLoadEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, AInterpolationRule, ATargetSpace> >(volumeLoadDensity); + sumEnergy->addLocalEnergy(volumeLoadEnergy); + + // The local assembler + LocalGeodesicFEADOLCStiffness<CompositeBasis,TargetSpace> localGFEADOLCStiffness(sumEnergy, + adolcScalarMode); + + MixedGFEAssembler<CompositeBasis,TargetSpace> mixedAssembler(compositeBasis, localGFEADOLCStiffness); + + //////////////////////////////////////////// + // Set up the solver + //////////////////////////////////////////// + // TODO: There is no need why the solver setup code should depend on the grid dimension + if (dim==dimworld) + { #if MIXED_SPACE if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { @@ -707,19 +728,6 @@ int main (int argc, char *argv[]) try } #endif } else { //dim != dimworld - using StiffnessType = LocalGeodesicFEADOLCStiffness<CompositeBasis, TargetSpace>; - std::shared_ptr<StiffnessType> localGFEStiffness; - -#if HAVE_DUNE_CURVEDGEOMETRY && WORLD_DIM == 3 && GRID_DIM == 2 - auto localCosseratEnergy = std::make_shared<NonplanarCosseratShellEnergy<CompositeBasis, 3, adouble, decltype(creator)> >(materialParameters, - &creator, - neumannBoundary.get(), - neumannFunction, - volumeLoad); - - localGFEStiffness = std::make_shared<StiffnessType>(localCosseratEnergy, adolcScalarMode); -#endif - MixedGFEAssembler<CompositeBasis,TargetSpace> mixedAssembler(compositeBasis, localGFEStiffness); #if MIXED_SPACE MixedRiemannianTrustRegionSolver<GridType, CompositeBasis, diff --git a/test/nonplanarcosseratenergytest.cc b/test/nonplanarcosseratenergytest.cc index a23461d4..44a5d254 100644 --- a/test/nonplanarcosseratenergytest.cc +++ b/test/nonplanarcosseratenergytest.cc @@ -124,11 +124,7 @@ double calculateEnergy(const FlatGridView& flatGridView, double, GridGeometry>; - ShellEnergy nonplanarCosseratShellEnergy(materialParameters, - &curvedGridGeometry, - nullptr, - nullptr, - nullptr); + ShellEnergy nonplanarCosseratShellEnergy(materialParameters, &curvedGridGeometry); /////////////////////////////////////////////////// // Compute the energy -- GitLab