/* $Id$ */
/* */
-/* Copyright (C) 2008, 2009, 2010 by the deal.II authors */
+/* Copyright (C) 2008, 2009, 2010, 2011 by the deal.II authors */
/* */
/* This file is subject to QPL and may not be distributed */
/* without copyright and license information. Please refer */
#include <sstream>
-using namespace dealii;
-
+namespace Step42
+{
+ using namespace dealii;
-template <int dim>
-struct InnerPreconditioner;
+ template <int dim>
+ struct InnerPreconditioner;
-template <>
-struct InnerPreconditioner<2>
-{
- typedef SparseDirectUMFPACK type;
-};
+ template <>
+ struct InnerPreconditioner<2>
+ {
+ typedef SparseDirectUMFPACK type;
+ };
-template <>
-struct InnerPreconditioner<3>
-{
- typedef SparseILU<double> type;
-};
-template <typename MATRIX>
-void copy(const MATRIX &matrix,
- FullMatrix<double> &full_matrix)
-{
- const unsigned int m = matrix.m();
- const unsigned int n = matrix.n();
- full_matrix.reinit(n,m);
+ template <>
+ struct InnerPreconditioner<3>
+ {
+ typedef SparseILU<double> type;
+ };
- Vector<double> unit (n);
- Vector<double> result (m);
- for(unsigned int i=0; i<n; ++i)
+ template <typename MATRIX>
+ void copy(const MATRIX &matrix,
+ FullMatrix<double> &full_matrix)
{
- unit(i) = 1;
- for(unsigned int j=0; j<m; ++j)
- {
- matrix.vmult(result,unit);
- full_matrix(i,j) = result(j);
- }
- unit(i) = 0;
+ const unsigned int m = matrix.m();
+ const unsigned int n = matrix.n();
+ full_matrix.reinit(n,m);
+
+ Vector<double> unit (n);
+ Vector<double> result (m);
+ for(unsigned int i=0; i<n; ++i)
+ {
+ unit(i) = 1;
+ for(unsigned int j=0; j<m; ++j)
+ {
+ matrix.vmult(result,unit);
+ full_matrix(i,j) = result(j);
+ }
+ unit(i) = 0;
+ }
}
-}
-template <int dim>
-class StokesProblem
-{
- public:
- StokesProblem (const unsigned int degree);
- void run ();
+ template <int dim>
+ class StokesProblem
+ {
+ public:
+ StokesProblem (const unsigned int degree);
+ void run ();
- private:
- void setup_dofs ();
- void assemble_system ();
- void assemble_multigrid ();
- void solve ();
- void solve_block ();
+ private:
+ void setup_dofs ();
+ void assemble_system ();
+ void assemble_multigrid ();
+ void solve ();
+ void solve_block ();
- void find_dofs_on_lower_level (std::vector<std::vector<bool> > &lower_dofs,
- std::vector<std::vector<bool> > &boundary_dofs);
+ void find_dofs_on_lower_level (std::vector<std::vector<bool> > &lower_dofs,
+ std::vector<std::vector<bool> > &boundary_dofs);
- void output_results (const unsigned int refinement_cycle) const;
- void refine_mesh ();
+ void output_results (const unsigned int refinement_cycle) const;
+ void refine_mesh ();
- const unsigned int degree;
+ const unsigned int degree;
- Triangulation<dim> triangulation;
- FESystem<dim> fe;
- MGDoFHandler<dim> dof_handler;
+ Triangulation<dim> triangulation;
+ FESystem<dim> fe;
+ MGDoFHandler<dim> dof_handler;
- ConstraintMatrix constraints;
+ ConstraintMatrix constraints;
- BlockSparsityPattern sparsity_pattern;
- BlockSparseMatrix<double> system_matrix;
+ BlockSparsityPattern sparsity_pattern;
+ BlockSparseMatrix<double> system_matrix;
- BlockVector<double> solution;
- BlockVector<double> system_rhs;
+ BlockVector<double> solution;
+ BlockVector<double> system_rhs;
- MGLevelObject<ConstraintMatrix> mg_constraints;
- MGLevelObject<BlockSparsityPattern> mg_sparsity;
- MGLevelObject<BlockSparseMatrix<double> > mg_matrices;
+ MGLevelObject<ConstraintMatrix> mg_constraints;
+ MGLevelObject<BlockSparsityPattern> mg_sparsity;
+ MGLevelObject<BlockSparseMatrix<double> > mg_matrices;
- MGLevelObject<BlockSparseMatrix<double> > mg_interface_matrices;
- MGConstrainedDoFs mg_constrained_dofs;
- std::vector<std::vector<unsigned int> > mg_dofs_per_component;
+ MGLevelObject<BlockSparseMatrix<double> > mg_interface_matrices;
+ MGConstrainedDoFs mg_constrained_dofs;
+ std::vector<std::vector<unsigned int> > mg_dofs_per_component;
- std::vector<std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type> > mg_A_preconditioner;
- std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type> A_preconditioner;
-};
+ std::vector<std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type> > mg_A_preconditioner;
+ std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type> A_preconditioner;
+ };
-template <int dim>
-class BoundaryValues : public Function<dim>
-{
- public:
- BoundaryValues () : Function<dim>(dim+1) {}
+ template <int dim>
+ class BoundaryValues : public Function<dim>
+ {
+ public:
+ BoundaryValues () : Function<dim>(dim+1) {}
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
- virtual void vector_value (const Point<dim> &p,
- Vector<double> &value) const;
-};
+ virtual void vector_value (const Point<dim> &p,
+ Vector<double> &value) const;
+ };
-template <int dim>
-double
-BoundaryValues<dim>::value (const Point<dim> &p,
- const unsigned int component) const
-{
- Assert (component < this->n_components,
- ExcIndexRange (component, 0, this->n_components));
+ template <int dim>
+ double
+ BoundaryValues<dim>::value (const Point<dim> &p,
+ const unsigned int component) const
+ {
+ Assert (component < this->n_components,
+ ExcIndexRange (component, 0, this->n_components));
- if (component == 0 && p[0] == 0)
- return (dim == 2 ? - p[1]*(p[1]-1.) : p[1]*(p[1]-1.) * p[2]*(p[2]-1.));
- return 0;
-}
+ if (component == 0 && p[0] == 0)
+ return (dim == 2 ? - p[1]*(p[1]-1.) : p[1]*(p[1]-1.) * p[2]*(p[2]-1.));
+ return 0;
+ }
-template <int dim>
-void
-BoundaryValues<dim>::vector_value (const Point<dim> &p,
- Vector<double> &values) const
-{
- for (unsigned int c=0; c<this->n_components; ++c)
- values(c) = BoundaryValues<dim>::value (p, c);
-}
+ template <int dim>
+ void
+ BoundaryValues<dim>::vector_value (const Point<dim> &p,
+ Vector<double> &values) const
+ {
+ for (unsigned int c=0; c<this->n_components; ++c)
+ values(c) = BoundaryValues<dim>::value (p, c);
+ }
-template <int dim>
-class RightHandSide : public Function<dim>
-{
- public:
- RightHandSide () : Function<dim>(dim+1) {}
+ template <int dim>
+ class RightHandSide : public Function<dim>
+ {
+ public:
+ RightHandSide () : Function<dim>(dim+1) {}
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
- virtual void vector_value (const Point<dim> &p,
- Vector<double> &value) const;
+ virtual void vector_value (const Point<dim> &p,
+ Vector<double> &value) const;
-};
+ };
-template <int dim>
-double
-RightHandSide<dim>::value (const Point<dim> &/*p*/,
- const unsigned int component) const
-{
- return (component == 1 ? 1 : 0);
-}
+ template <int dim>
+ double
+ RightHandSide<dim>::value (const Point<dim> &/*p*/,
+ const unsigned int component) const
+ {
+ return (component == 1 ? 1 : 0);
+ }
-template <int dim>
-void
-RightHandSide<dim>::vector_value (const Point<dim> &p,
- Vector<double> &values) const
-{
- for (unsigned int c=0; c<this->n_components; ++c)
- values(c) = RightHandSide<dim>::value (p, c);
-}
+ template <int dim>
+ void
+ RightHandSide<dim>::vector_value (const Point<dim> &p,
+ Vector<double> &values) const
+ {
+ for (unsigned int c=0; c<this->n_components; ++c)
+ values(c) = RightHandSide<dim>::value (p, c);
+ }
-template <class Matrix, class Preconditioner>
-class InverseMatrix : public Subscriptor
-{
- public:
- InverseMatrix (const Matrix &m,
- const Preconditioner &preconditioner);
+ template <class Matrix, class Preconditioner>
+ class InverseMatrix : public Subscriptor
+ {
+ public:
+ InverseMatrix (const Matrix &m,
+ const Preconditioner &preconditioner);
- void vmult (Vector<double> &dst,
- const Vector<double> &src) const;
+ void vmult (Vector<double> &dst,
+ const Vector<double> &src) const;
- mutable std::string name;
- private:
- const SmartPointer<const Matrix> matrix;
- const SmartPointer<const Preconditioner> preconditioner;
-};
+ mutable std::string name;
+ private:
+ const SmartPointer<const Matrix> matrix;
+ const SmartPointer<const Preconditioner> preconditioner;
+ };
-template <class Matrix, class Preconditioner>
-InverseMatrix<Matrix,Preconditioner>::InverseMatrix (const Matrix &m,
- const Preconditioner &preconditioner)
- :
- matrix (&m),
- preconditioner (&preconditioner)
-{}
+ template <class Matrix, class Preconditioner>
+ InverseMatrix<Matrix,Preconditioner>::InverseMatrix (const Matrix &m,
+ const Preconditioner &preconditioner)
+ :
+ matrix (&m),
+ preconditioner (&preconditioner)
+ {}
-template <class Matrix, class Preconditioner>
-void InverseMatrix<Matrix,Preconditioner>::vmult (Vector<double> &dst,
- const Vector<double> &src) const
-{
- SolverControl solver_control (src.size(), 1.0e-12*src.l2_norm());
- SolverCG<> cg (solver_control);
+ template <class Matrix, class Preconditioner>
+ void InverseMatrix<Matrix,Preconditioner>::vmult (Vector<double> &dst,
+ const Vector<double> &src) const
+ {
+ SolverControl solver_control (src.size(), 1.0e-12*src.l2_norm());
+ SolverCG<> cg (solver_control);
- dst = 0;
+ dst = 0;
- try
- {
- cg.solve (*matrix, dst, src, *preconditioner);
- }
- catch (...)
- {
- std::cout << "Failure in " << __PRETTY_FUNCTION__ << std::endl;
- abort ();
- }
+ try
+ {
+ cg.solve (*matrix, dst, src, *preconditioner);
+ }
+ catch (...)
+ {
+ std::cout << "Failure in " << __PRETTY_FUNCTION__ << std::endl;
+ abort ();
+ }
#ifdef STEP_42_TEST
- if (name == "in schur")
- std::cout << " " << solver_control.last_step()
- << " inner CG steps inside the Schur complement ";
- else if (name == "top left")
- std::cout << " " << solver_control.last_step()
- << " CG steps on the top left block ";
- else if (name == "rhs")
- std::cout << " " << solver_control.last_step()
- << " CG steps for computing the r.h.s. ";
- else
- abort ();
-
- std::cout << solver_control.initial_value() << "->" << solver_control.last_value()
- << std::endl;
+ if (name == "in schur")
+ std::cout << " " << solver_control.last_step()
+ << " inner CG steps inside the Schur complement ";
+ else if (name == "top left")
+ std::cout << " " << solver_control.last_step()
+ << " CG steps on the top left block ";
+ else if (name == "rhs")
+ std::cout << " " << solver_control.last_step()
+ << " CG steps for computing the r.h.s. ";
+ else
+ abort ();
+
+ std::cout << solver_control.initial_value() << "->" << solver_control.last_value()
+ << std::endl;
#endif
-}
+ }
-template <class PreconditionerA, class PreconditionerMp>
-class BlockSchurPreconditioner : public Subscriptor
-{
- public:
- BlockSchurPreconditioner (const BlockSparseMatrix<double> &S,
- const InverseMatrix<SparseMatrix<double>,PreconditionerMp> &Mpinv,
- const PreconditionerA &Apreconditioner);
-
- void vmult (BlockVector<double> &dst,
- const BlockVector<double> &src) const;
-
- private:
- const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
- const SmartPointer<const InverseMatrix<SparseMatrix<double>,
- PreconditionerMp > > m_inverse;
- const PreconditionerA &a_preconditioner;
-
- mutable Vector<double> tmp;
-
-};
-
-template <class PreconditionerA, class PreconditionerMp>
-BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::BlockSchurPreconditioner(
- const BlockSparseMatrix<double> &S,
- const InverseMatrix<SparseMatrix<double>,PreconditionerMp> &Mpinv,
- const PreconditionerA &Apreconditioner
- )
- :
- system_matrix (&S),
- m_inverse (&Mpinv),
- a_preconditioner (Apreconditioner),
- tmp (S.block(1,1).m())
-{}
-
- // Now the interesting function, the multiplication of
- // the preconditioner with a BlockVector.
-template <class PreconditionerA, class PreconditionerMp>
-void BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::vmult (
- BlockVector<double> &dst,
- const BlockVector<double> &src) const
-{
- // Form u_new = A^{-1} u
- a_preconditioner.vmult (dst.block(0), src.block(0));
- // Form tmp = - B u_new + p
- // (<code>SparseMatrix::residual</code>
- // does precisely this)
- system_matrix->block(1,0).residual(tmp, dst.block(0), src.block(1));
- // Change sign in tmp
- tmp *= -1;
- // Multiply by approximate Schur complement
- // (i.e. a pressure mass matrix)
- m_inverse->vmult (dst.block(1), tmp);
-}
+ template <class PreconditionerA, class PreconditionerMp>
+ class BlockSchurPreconditioner : public Subscriptor
+ {
+ public:
+ BlockSchurPreconditioner (const BlockSparseMatrix<double> &S,
+ const InverseMatrix<SparseMatrix<double>,PreconditionerMp> &Mpinv,
+ const PreconditionerA &Apreconditioner);
+
+ void vmult (BlockVector<double> &dst,
+ const BlockVector<double> &src) const;
+
+ private:
+ const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
+ const SmartPointer<const InverseMatrix<SparseMatrix<double>,
+ PreconditionerMp > > m_inverse;
+ const PreconditionerA &a_preconditioner;
+
+ mutable Vector<double> tmp;
+
+ };
+
+ template <class PreconditionerA, class PreconditionerMp>
+ BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::BlockSchurPreconditioner(
+ const BlockSparseMatrix<double> &S,
+ const InverseMatrix<SparseMatrix<double>,PreconditionerMp> &Mpinv,
+ const PreconditionerA &Apreconditioner
+ )
+ :
+ system_matrix (&S),
+ m_inverse (&Mpinv),
+ a_preconditioner (Apreconditioner),
+ tmp (S.block(1,1).m())
+ {}
+
+ // Now the interesting function, the multiplication of
+ // the preconditioner with a BlockVector.
+ template <class PreconditionerA, class PreconditionerMp>
+ void BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::vmult (
+ BlockVector<double> &dst,
+ const BlockVector<double> &src) const
+ {
+ // Form u_new = A^{-1} u
+ a_preconditioner.vmult (dst.block(0), src.block(0));
+ // Form tmp = - B u_new + p
+ // (<code>SparseMatrix::residual</code>
+ // does precisely this)
+ system_matrix->block(1,0).residual(tmp, dst.block(0), src.block(1));
+ // Change sign in tmp
+ tmp *= -1;
+ // Multiply by approximate Schur complement
+ // (i.e. a pressure mass matrix)
+ m_inverse->vmult (dst.block(1), tmp);
+ }
-template <class Preconditioner>
-class SchurComplement : public Subscriptor
-{
- public:
- SchurComplement (const BlockSparseMatrix<double> &system_matrix,
- const InverseMatrix<SparseMatrix<double>, Preconditioner> &A_inverse);
+ template <class Preconditioner>
+ class SchurComplement : public Subscriptor
+ {
+ public:
+ SchurComplement (const BlockSparseMatrix<double> &system_matrix,
+ const InverseMatrix<SparseMatrix<double>, Preconditioner> &A_inverse);
- void vmult (Vector<double> &dst,
- const Vector<double> &src) const;
+ void vmult (Vector<double> &dst,
+ const Vector<double> &src) const;
- unsigned int m() const
- {
- return system_matrix->block(1,1).m();
- }
+ unsigned int m() const
+ {
+ return system_matrix->block(1,1).m();
+ }
- unsigned int n() const
- {
- return system_matrix->block(1,1).n();
- }
+ unsigned int n() const
+ {
+ return system_matrix->block(1,1).n();
+ }
- private:
- const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
- const SmartPointer<const InverseMatrix<SparseMatrix<double>, Preconditioner> > A_inverse;
+ private:
+ const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
+ const SmartPointer<const InverseMatrix<SparseMatrix<double>, Preconditioner> > A_inverse;
- mutable Vector<double> tmp1, tmp2;
-};
+ mutable Vector<double> tmp1, tmp2;
+ };
-template <class Preconditioner>
-SchurComplement<Preconditioner>::
-SchurComplement (const BlockSparseMatrix<double> &system_matrix,
- const InverseMatrix<SparseMatrix<double>,Preconditioner> &A_inverse)
- :
- system_matrix (&system_matrix),
- A_inverse (&A_inverse),
- tmp1 (system_matrix.block(0,0).m()),
- tmp2 (system_matrix.block(0,0).m())
-{}
+ template <class Preconditioner>
+ SchurComplement<Preconditioner>::
+ SchurComplement (const BlockSparseMatrix<double> &system_matrix,
+ const InverseMatrix<SparseMatrix<double>,Preconditioner> &A_inverse)
+ :
+ system_matrix (&system_matrix),
+ A_inverse (&A_inverse),
+ tmp1 (system_matrix.block(0,0).m()),
+ tmp2 (system_matrix.block(0,0).m())
+ {}
-template <class Preconditioner>
-void SchurComplement<Preconditioner>::vmult (Vector<double> &dst,
- const Vector<double> &src) const
-{
- system_matrix->block(0,1).vmult (tmp1, src);
- A_inverse->name = "in schur";
- A_inverse->vmult (tmp2, tmp1);
- system_matrix->block(1,0).vmult (dst, tmp2);
- dst *= -1;
- system_matrix->block(1,1).vmult_add (dst, src);
- dst *= -1;
-}
+ template <class Preconditioner>
+ void SchurComplement<Preconditioner>::vmult (Vector<double> &dst,
+ const Vector<double> &src) const
+ {
+ system_matrix->block(0,1).vmult (tmp1, src);
+ A_inverse->name = "in schur";
+ A_inverse->vmult (tmp2, tmp1);
+ system_matrix->block(1,0).vmult (dst, tmp2);
+ dst *= -1;
+ system_matrix->block(1,1).vmult_add (dst, src);
+ dst *= -1;
+ }
-template <int dim>
-StokesProblem<dim>::StokesProblem (const unsigned int degree)
- :
- degree (degree),
- triangulation (Triangulation<dim>::limit_level_difference_at_vertices),
- fe (FE_Q<dim>(degree+1), dim,
- FE_Q<dim>(degree), 1),
- dof_handler (triangulation)
-{}
+ template <int dim>
+ StokesProblem<dim>::StokesProblem (const unsigned int degree)
+ :
+ degree (degree),
+ triangulation (Triangulation<dim>::limit_level_difference_at_vertices),
+ fe (FE_Q<dim>(degree+1), dim,
+ FE_Q<dim>(degree), 1),
+ dof_handler (triangulation)
+ {}
-template <int dim>
-void StokesProblem<dim>::setup_dofs ()
-{
- A_preconditioner.reset ();
- mg_A_preconditioner.resize (0);
- system_matrix.clear ();
+ template <int dim>
+ void StokesProblem<dim>::setup_dofs ()
+ {
+ A_preconditioner.reset ();
+ mg_A_preconditioner.resize (0);
+ system_matrix.clear ();
- dof_handler.distribute_dofs (fe);
+ dof_handler.distribute_dofs (fe);
// DoFRenumbering::Cuthill_McKee (dof_handler);
- std::vector<unsigned int> block_component (dim+1,0);
- block_component[dim] = 1;
- DoFRenumbering::component_wise (dof_handler, block_component);
+ std::vector<unsigned int> block_component (dim+1,0);
+ block_component[dim] = 1;
+ DoFRenumbering::component_wise (dof_handler, block_component);
- {
- constraints.clear ();
- typename FunctionMap<dim>::type dirichlet_boundary;
- ZeroFunction<dim> homogeneous_dirichlet_bc (dim+1); //TODO: go back to BoundaryValues
-
- dirichlet_boundary[0] = &homogeneous_dirichlet_bc;
- MappingQ1<dim> mapping;
-
- std::vector<bool> component_mask (dim+1, true);
- component_mask[dim] = false;
- VectorTools::interpolate_boundary_values (mapping,
- dof_handler,
- dirichlet_boundary,
- constraints,
- component_mask);
-
- DoFTools::make_hanging_node_constraints (dof_handler,
- constraints);
-
- mg_constrained_dofs.clear();
- mg_constrained_dofs.initialize(dof_handler, dirichlet_boundary);
- }
+ {
+ constraints.clear ();
+ typename FunctionMap<dim>::type dirichlet_boundary;
+ ZeroFunction<dim> homogeneous_dirichlet_bc (dim+1); //TODO: go back to BoundaryValues
+
+ dirichlet_boundary[0] = &homogeneous_dirichlet_bc;
+ MappingQ1<dim> mapping;
+
+ std::vector<bool> component_mask (dim+1, true);
+ component_mask[dim] = false;
+ VectorTools::interpolate_boundary_values (mapping,
+ dof_handler,
+ dirichlet_boundary,
+ constraints,
+ component_mask);
+
+ DoFTools::make_hanging_node_constraints (dof_handler,
+ constraints);
+
+ mg_constrained_dofs.clear();
+ mg_constrained_dofs.initialize(dof_handler, dirichlet_boundary);
+ }
- constraints.close ();
+ constraints.close ();
- std::vector<unsigned int> dofs_per_block (2);
- DoFTools::count_dofs_per_block (dof_handler, dofs_per_block,
- block_component);
- const unsigned int n_u = dofs_per_block[0],
- n_p = dofs_per_block[1];
+ std::vector<unsigned int> dofs_per_block (2);
+ DoFTools::count_dofs_per_block (dof_handler, dofs_per_block,
+ block_component);
+ const unsigned int n_u = dofs_per_block[0],
+ n_p = dofs_per_block[1];
- std::cout << " Number of active cells: "
- << triangulation.n_active_cells()
- << std::endl
- << " Number of degrees of freedom: "
- << dof_handler.n_dofs()
- << " (" << n_u << '+' << n_p << ')'
- << std::endl;
+ std::cout << " Number of active cells: "
+ << triangulation.n_active_cells()
+ << std::endl
+ << " Number of degrees of freedom: "
+ << dof_handler.n_dofs()
+ << " (" << n_u << '+' << n_p << ')'
+ << std::endl;
- {
- BlockCompressedSimpleSparsityPattern csp (2,2);
+ {
+ BlockCompressedSimpleSparsityPattern csp (2,2);
- csp.block(0,0).reinit (n_u, n_u);
- csp.block(1,0).reinit (n_p, n_u);
- csp.block(0,1).reinit (n_u, n_p);
- csp.block(1,1).reinit (n_p, n_p);
+ csp.block(0,0).reinit (n_u, n_u);
+ csp.block(1,0).reinit (n_p, n_u);
+ csp.block(0,1).reinit (n_u, n_p);
+ csp.block(1,1).reinit (n_p, n_p);
- csp.collect_sizes();
+ csp.collect_sizes();
- DoFTools::make_sparsity_pattern (
+ DoFTools::make_sparsity_pattern (
static_cast<const DoFHandler<dim>&>(dof_handler),
csp, constraints, false);
- sparsity_pattern.copy_from (csp);
- }
+ sparsity_pattern.copy_from (csp);
+ }
- system_matrix.reinit (sparsity_pattern);
+ system_matrix.reinit (sparsity_pattern);
- solution.reinit (2);
- solution.block(0).reinit (n_u);
- solution.block(1).reinit (n_p);
- solution.collect_sizes ();
+ solution.reinit (2);
+ solution.block(0).reinit (n_u);
+ solution.block(1).reinit (n_p);
+ solution.collect_sizes ();
- system_rhs.reinit (2);
- system_rhs.block(0).reinit (n_u);
- system_rhs.block(1).reinit (n_p);
- system_rhs.collect_sizes ();
+ system_rhs.reinit (2);
+ system_rhs.block(0).reinit (n_u);
+ system_rhs.block(1).reinit (n_p);
+ system_rhs.collect_sizes ();
- //now setup stuff for mg
- const unsigned int nlevels = triangulation.n_levels();
+ //now setup stuff for mg
+ const unsigned int nlevels = triangulation.n_levels();
- mg_matrices.resize(0, nlevels-1);
- mg_matrices.clear ();
- mg_interface_matrices.resize(0, nlevels-1);
- mg_interface_matrices.clear ();
- mg_sparsity.resize(0, nlevels-1);
+ mg_matrices.resize(0, nlevels-1);
+ mg_matrices.clear ();
+ mg_interface_matrices.resize(0, nlevels-1);
+ mg_interface_matrices.clear ();
+ mg_sparsity.resize(0, nlevels-1);
- mg_dofs_per_component.resize (nlevels);
- for (unsigned int level=0; level<nlevels; ++level)
- mg_dofs_per_component[level].resize (2);
+ mg_dofs_per_component.resize (nlevels);
+ for (unsigned int level=0; level<nlevels; ++level)
+ mg_dofs_per_component[level].resize (2);
- MGTools::count_dofs_per_block (dof_handler, mg_dofs_per_component,
- block_component);
- for (unsigned int level=0; level<nlevels; ++level)
- std::cout << " Level " << level << ": "
- << dof_handler.n_dofs (level) << " ("
- << mg_dofs_per_component[level][0] << '+'
- << mg_dofs_per_component[level][1] << ')'
- << std::endl;
+ MGTools::count_dofs_per_block (dof_handler, mg_dofs_per_component,
+ block_component);
+ for (unsigned int level=0; level<nlevels; ++level)
+ std::cout << " Level " << level << ": "
+ << dof_handler.n_dofs (level) << " ("
+ << mg_dofs_per_component[level][0] << '+'
+ << mg_dofs_per_component[level][1] << ')'
+ << std::endl;
- for (unsigned int level=0; level<nlevels; ++level)
- {
- DoFRenumbering::component_wise (dof_handler, level, block_component);
-
- BlockCompressedSparsityPattern bcsp (mg_dofs_per_component[level],
- mg_dofs_per_component[level]);
- MGTools::make_sparsity_pattern(dof_handler, bcsp, level);
- mg_sparsity[level].copy_from (bcsp);
- mg_matrices[level].reinit (mg_sparsity[level]);
- mg_interface_matrices[level].reinit (mg_sparsity[level]);
+ for (unsigned int level=0; level<nlevels; ++level)
+ {
+ DoFRenumbering::component_wise (dof_handler, level, block_component);
+
+ BlockCompressedSparsityPattern bcsp (mg_dofs_per_component[level],
+ mg_dofs_per_component[level]);
+ MGTools::make_sparsity_pattern(dof_handler, bcsp, level);
+ mg_sparsity[level].copy_from (bcsp);
+ mg_matrices[level].reinit (mg_sparsity[level]);
+ mg_interface_matrices[level].reinit (mg_sparsity[level]);
+ }
}
-}
-
-template <int dim>
-void StokesProblem<dim>::assemble_system ()
-{
- system_matrix=0;
- system_rhs=0;
-
- QGauss<dim> quadrature_formula(degree+2);
-
- FEValues<dim> fe_values (fe, quadrature_formula,
- update_values |
- update_quadrature_points |
- update_JxW_values |
- update_gradients);
-
- const unsigned int dofs_per_cell = fe.dofs_per_cell;
- const unsigned int n_q_points = quadrature_formula.size();
-
- FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
- Vector<double> local_rhs (dofs_per_cell);
-
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+ template <int dim>
+ void StokesProblem<dim>::assemble_system ()
+ {
+ system_matrix=0;
+ system_rhs=0;
- const RightHandSide<dim> right_hand_side;
- std::vector<Vector<double> > rhs_values (n_q_points,
- Vector<double>(dim+1));
+ QGauss<dim> quadrature_formula(degree+2);
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_values |
+ update_quadrature_points |
+ update_JxW_values |
+ update_gradients);
- const FEValuesExtractors::Vector velocities (0);
- const FEValuesExtractors::Scalar pressure (dim);
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+ Vector<double> local_rhs (dofs_per_cell);
- std::vector<Tensor<2,dim> > phi_grads_u (dofs_per_cell);
- std::vector<double> div_phi_u (dofs_per_cell);
- std::vector<double> phi_p (dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- typename MGDoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- fe_values.reinit (cell);
- local_matrix = 0;
- local_rhs = 0;
+ const RightHandSide<dim> right_hand_side;
+ std::vector<Vector<double> > rhs_values (n_q_points,
+ Vector<double>(dim+1));
- right_hand_side.vector_value_list(fe_values.get_quadrature_points(),
- rhs_values);
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- for (unsigned int k=0; k<dofs_per_cell; ++k)
- {
- phi_grads_u[k] = fe_values[velocities].gradient (k, q);
- div_phi_u[k] = fe_values[velocities].divergence (k, q);
- phi_p[k] = fe_values[pressure].value (k, q);
- }
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- {
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- {
- local_matrix(i,j) += (scalar_product(phi_grads_u[i], phi_grads_u[j])
- - div_phi_u[i] * phi_p[j]
- - phi_p[i] * div_phi_u[j]
- - phi_p[i] * phi_p[j]
- )
- * fe_values.JxW(q);
- }
-
- const unsigned int component_i =
- fe.system_to_component_index(i).first;
- local_rhs(i) += fe_values.shape_value(i,q) *
- rhs_values[q](component_i) *
- fe_values.JxW(q);
- }
- }
+ const FEValuesExtractors::Vector velocities (0);
+ const FEValuesExtractors::Scalar pressure (dim);
+ std::vector<Tensor<2,dim> > phi_grads_u (dofs_per_cell);
+ std::vector<double> div_phi_u (dofs_per_cell);
+ std::vector<double> phi_p (dofs_per_cell);
- cell->get_dof_indices (local_dof_indices);
- constraints.distribute_local_to_global (local_matrix, local_rhs,
- local_dof_indices,
- system_matrix, system_rhs);
- }
-}
+ typename MGDoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ fe_values.reinit (cell);
+ local_matrix = 0;
+ local_rhs = 0;
+
+ right_hand_side.vector_value_list(fe_values.get_quadrature_points(),
+ rhs_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ phi_grads_u[k] = fe_values[velocities].gradient (k, q);
+ div_phi_u[k] = fe_values[velocities].divergence (k, q);
+ phi_p[k] = fe_values[pressure].value (k, q);
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ {
+ local_matrix(i,j) += (scalar_product(phi_grads_u[i], phi_grads_u[j])
+ - div_phi_u[i] * phi_p[j]
+ - phi_p[i] * div_phi_u[j]
+ - phi_p[i] * phi_p[j]
+ )
+ * fe_values.JxW(q);
+ }
+
+ const unsigned int component_i =
+ fe.system_to_component_index(i).first;
+ local_rhs(i) += fe_values.shape_value(i,q) *
+ rhs_values[q](component_i) *
+ fe_values.JxW(q);
+ }
+ }
+
+
+
+
+ cell->get_dof_indices (local_dof_indices);
+ constraints.distribute_local_to_global (local_matrix, local_rhs,
+ local_dof_indices,
+ system_matrix, system_rhs);
+ }
+ }
-template <int dim>
-void StokesProblem<dim>::assemble_multigrid ()
-{
- QGauss<dim> quadrature_formula(degree+2);
- FEValues<dim> fe_values (fe, quadrature_formula,
- update_values |
- update_quadrature_points |
- update_JxW_values |
- update_gradients);
+ template <int dim>
+ void StokesProblem<dim>::assemble_multigrid ()
+ {
+ QGauss<dim> quadrature_formula(degree+2);
+ FEValues<dim> fe_values (fe, quadrature_formula,
+ update_values |
+ update_quadrature_points |
+ update_JxW_values |
+ update_gradients);
- const unsigned int dofs_per_cell = fe.dofs_per_cell;
- const unsigned int n_q_points = quadrature_formula.size();
+ const unsigned int dofs_per_cell = fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
- FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- const FEValuesExtractors::Vector velocities (0);
- const FEValuesExtractors::Scalar pressure (dim);
+ const FEValuesExtractors::Vector velocities (0);
+ const FEValuesExtractors::Scalar pressure (dim);
- std::vector<Tensor<2,dim> > phi_grads_u (dofs_per_cell);
- std::vector<double> div_phi_u (dofs_per_cell);
- std::vector<double> phi_p (dofs_per_cell);
+ std::vector<Tensor<2,dim> > phi_grads_u (dofs_per_cell);
+ std::vector<double> div_phi_u (dofs_per_cell);
+ std::vector<double> phi_p (dofs_per_cell);
- std::vector<std::vector<bool> > interface_dofs
- = mg_constrained_dofs.get_refinement_edge_indices ();
- std::vector<std::vector<bool> > boundary_interface_dofs
- = mg_constrained_dofs.get_refinement_edge_boundary_indices ();
+ std::vector<std::vector<bool> > interface_dofs
+ = mg_constrained_dofs.get_refinement_edge_indices ();
+ std::vector<std::vector<bool> > boundary_interface_dofs
+ = mg_constrained_dofs.get_refinement_edge_boundary_indices ();
- std::vector<ConstraintMatrix> boundary_constraints (triangulation.n_levels());
- std::vector<ConstraintMatrix> boundary_interface_constraints (triangulation.n_levels());
- for (unsigned int level=0; level<triangulation.n_levels(); ++level)
- {
- boundary_constraints[level].add_lines (interface_dofs[level]);
- boundary_constraints[level].add_lines (mg_constrained_dofs.get_boundary_indices()[level]);
- boundary_constraints[level].close ();
+ std::vector<ConstraintMatrix> boundary_constraints (triangulation.n_levels());
+ std::vector<ConstraintMatrix> boundary_interface_constraints (triangulation.n_levels());
+ for (unsigned int level=0; level<triangulation.n_levels(); ++level)
+ {
+ boundary_constraints[level].add_lines (interface_dofs[level]);
+ boundary_constraints[level].add_lines (mg_constrained_dofs.get_boundary_indices()[level]);
+ boundary_constraints[level].close ();
- boundary_interface_constraints[level]
- .add_lines (boundary_interface_dofs[level]);
- boundary_interface_constraints[level].close ();
- }
+ boundary_interface_constraints[level]
+ .add_lines (boundary_interface_dofs[level]);
+ boundary_interface_constraints[level].close ();
+ }
- typename MGDoFHandler<dim>::cell_iterator
- cell = dof_handler.begin(),
- endc = dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- // Remember the level of the
- // current cell.
- const unsigned int level = cell->level();
- // Compute the values specified
- // by update flags above.
- fe_values.reinit (cell);
- local_matrix = 0;
-
- for (unsigned int q=0; q<n_q_points; ++q)
+ typename MGDoFHandler<dim>::cell_iterator
+ cell = dof_handler.begin(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
{
- for (unsigned int k=0; k<dofs_per_cell; ++k)
- {
- phi_grads_u[k] = fe_values[velocities].gradient (k, q);
- div_phi_u[k] = fe_values[velocities].divergence (k, q);
- phi_p[k] = fe_values[pressure].value (k, q);
- }
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
+ // Remember the level of the
+ // current cell.
+ const unsigned int level = cell->level();
+ // Compute the values specified
+ // by update flags above.
+ fe_values.reinit (cell);
+ local_matrix = 0;
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ phi_grads_u[k] = fe_values[velocities].gradient (k, q);
+ div_phi_u[k] = fe_values[velocities].divergence (k, q);
+ phi_p[k] = fe_values[pressure].value (k, q);
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
- local_matrix(i,j) += (
- scalar_product(phi_grads_u[i], phi_grads_u[j])
- - div_phi_u[i] * phi_p[j]
- - phi_p[i] * div_phi_u[j]
+ local_matrix(i,j) += (
+ scalar_product(phi_grads_u[i], phi_grads_u[j])
+ - div_phi_u[i] * phi_p[j]
+ - phi_p[i] * div_phi_u[j]
// - phi_p[i] * phi_p[j]
- )
- * fe_values.JxW(q);
+ )
+ * fe_values.JxW(q);
+ }
+
+ cell->get_mg_dof_indices (local_dof_indices);
+ boundary_constraints[level]
+ .distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ mg_matrices[level]);
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ if( !(interface_dofs[level][local_dof_indices[i]]==true &&
+ interface_dofs[level][local_dof_indices[j]]==false))
+ local_matrix(i,j) = 0;
+
+ boundary_interface_constraints[level]
+ .distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ mg_interface_matrices[level]);
}
- cell->get_mg_dof_indices (local_dof_indices);
- boundary_constraints[level]
- .distribute_local_to_global (local_matrix,
- local_dof_indices,
- mg_matrices[level]);
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- if( !(interface_dofs[level][local_dof_indices[i]]==true &&
- interface_dofs[level][local_dof_indices[j]]==false))
- local_matrix(i,j) = 0;
-
- boundary_interface_constraints[level]
- .distribute_local_to_global (local_matrix,
- local_dof_indices,
- mg_interface_matrices[level]);
- }
-
- mg_A_preconditioner.resize (triangulation.n_levels());
- for (unsigned int level=0; level<triangulation.n_levels(); ++level)
- {
- mg_A_preconditioner[level]
- = std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type>(new typename InnerPreconditioner<dim>::type());
- mg_A_preconditioner[level]
- ->initialize (mg_matrices[level].block(0,0),
- typename InnerPreconditioner<dim>::type::AdditionalData());
- }
-}
+ mg_A_preconditioner.resize (triangulation.n_levels());
+ for (unsigned int level=0; level<triangulation.n_levels(); ++level)
+ {
+ mg_A_preconditioner[level]
+ = std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type>(new typename InnerPreconditioner<dim>::type());
+ mg_A_preconditioner[level]
+ ->initialize (mg_matrices[level].block(0,0),
+ typename InnerPreconditioner<dim>::type::AdditionalData());
+ }
+ }
-template <typename InnerPreconditioner>
-class SchurComplementSmoother
-{
- public:
- struct AdditionalData
- {
- const InnerPreconditioner *A_preconditioner;
- };
+ template <typename InnerPreconditioner>
+ class SchurComplementSmoother
+ {
+ public:
+ struct AdditionalData
+ {
+ const InnerPreconditioner *A_preconditioner;
+ };
- void initialize (const BlockSparseMatrix<double> &system_matrix,
- const AdditionalData &data);
+ void initialize (const BlockSparseMatrix<double> &system_matrix,
+ const AdditionalData &data);
- void vmult (BlockVector<double> &dst,
- const BlockVector<double> &src) const;
+ void vmult (BlockVector<double> &dst,
+ const BlockVector<double> &src) const;
- void Tvmult (BlockVector<double> &dst,
- const BlockVector<double> &src) const;
+ void Tvmult (BlockVector<double> &dst,
+ const BlockVector<double> &src) const;
- void clear ();
+ void clear ();
- private:
- SmartPointer<const BlockSparseMatrix<double> > system_matrix;
- SmartPointer<const InnerPreconditioner> A_preconditioner;
-};
+ private:
+ SmartPointer<const BlockSparseMatrix<double> > system_matrix;
+ SmartPointer<const InnerPreconditioner> A_preconditioner;
+ };
-template <typename InnerPreconditioner>
-void
-SchurComplementSmoother<InnerPreconditioner>::
-initialize (const BlockSparseMatrix<double> &system_matrix,
- const AdditionalData &data)
-{
- this->system_matrix = &system_matrix;
- this->A_preconditioner = data.A_preconditioner;
-}
+ template <typename InnerPreconditioner>
+ void
+ SchurComplementSmoother<InnerPreconditioner>::
+ initialize (const BlockSparseMatrix<double> &system_matrix,
+ const AdditionalData &data)
+ {
+ this->system_matrix = &system_matrix;
+ this->A_preconditioner = data.A_preconditioner;
+ }
-template <typename InnerPreconditioner>
-void
-SchurComplementSmoother<InnerPreconditioner>::
-vmult (BlockVector<double> &dst,
- const BlockVector<double> &src) const
-{
+ template <typename InnerPreconditioner>
+ void
+ SchurComplementSmoother<InnerPreconditioner>::
+ vmult (BlockVector<double> &dst,
+ const BlockVector<double> &src) const
+ {
#ifdef STEP_42_TEST
- std::cout << "Entering smoother with " << dst.size() << " unknowns" << std::endl;
+ std::cout << "Entering smoother with " << dst.size() << " unknowns" << std::endl;
#endif
- SparseDirectUMFPACK direct_solver;
- direct_solver.initialize(*system_matrix);
- Vector<double> solution, rhs;
- solution = dst;
- rhs = src;
- direct_solver.vmult(solution, rhs);
- dst = solution;
+ SparseDirectUMFPACK direct_solver;
+ direct_solver.initialize(*system_matrix);
+ Vector<double> solution, rhs;
+ solution = dst;
+ rhs = src;
+ direct_solver.vmult(solution, rhs);
+ dst = solution;
/*
const InverseMatrix<SparseMatrix<double>,InnerPreconditioner>
- A_inverse (system_matrix->block(0,0), *A_preconditioner);
+ A_inverse (system_matrix->block(0,0), *A_preconditioner);
Vector<double> tmp (dst.block(0).size());
{
- Vector<double> schur_rhs (dst.block(1).size());
- A_inverse.name = "rhs";
- A_inverse.vmult (tmp, src.block(0));
+ Vector<double> schur_rhs (dst.block(1).size());
+ A_inverse.name = "rhs";
+ A_inverse.vmult (tmp, src.block(0));
// std::cout << " TMP " << tmp.l2_norm() << std::endl;
- system_matrix->block(1,0).vmult (schur_rhs, tmp);
- schur_rhs -= src.block(1);
+system_matrix->block(1,0).vmult (schur_rhs, tmp);
+schur_rhs -= src.block(1);
// std::cout << " BLOCK 1 " << src.block(1).l2_norm() << std::endl;
// std::cout << " SCHUR RHS " << schur_rhs.l2_norm() << std::endl;
- SchurComplement<InnerPreconditioner>
- schur_complement (*system_matrix, A_inverse);
-
- // The usual control structures for
- // the solver call are created...
- SolverControl solver_control (dst.block(1).size(),
- 1e-1*schur_rhs.l2_norm());
- SolverGMRES<> cg (solver_control);
-
-#ifdef STEP_42_TEST
- std::cout << " Starting Schur complement solver -- "
- << schur_complement.m() << " unknowns"
- << std::endl;
-#endif
- try
- {
- cg.solve (schur_complement, dst.block(1), schur_rhs,
- PreconditionIdentity());
- }
- catch (...)
- {
- std::cout << "Failure in " << __PRETTY_FUNCTION__ << std::endl;
- std::cout << schur_rhs.l2_norm () << std::endl;
- abort ();
- }
+SchurComplement<InnerPreconditioner>
+schur_complement (*system_matrix, A_inverse);
+
+ // The usual control structures for
+ // the solver call are created...
+ SolverControl solver_control (dst.block(1).size(),
+ 1e-1*schur_rhs.l2_norm());
+ SolverGMRES<> cg (solver_control);
+
+ #ifdef STEP_42_TEST
+ std::cout << " Starting Schur complement solver -- "
+ << schur_complement.m() << " unknowns"
+ << std::endl;
+ #endif
+ try
+ {
+ cg.solve (schur_complement, dst.block(1), schur_rhs,
+ PreconditionIdentity());
+ }
+ catch (...)
+ {
+ std::cout << "Failure in " << __PRETTY_FUNCTION__ << std::endl;
+ std::cout << schur_rhs.l2_norm () << std::endl;
+ abort ();
+ }
// no constraints to be taken care of here
#ifdef STEP_42_TEST
- std::cout << " "
- << solver_control.last_step()
- << " CG Schur complement iterations in smoother "
- << solver_control.initial_value() << "->" << solver_control.last_value()
- << std::endl;
+std::cout << " "
+<< solver_control.last_step()
+<< " CG Schur complement iterations in smoother "
+<< solver_control.initial_value() << "->" << solver_control.last_value()
+<< std::endl;
#endif
- }
+}
- {
- system_matrix->block(0,1).vmult (tmp, dst.block(1));
- tmp *= -1;
- tmp += src.block(0);
+{
+system_matrix->block(0,1).vmult (tmp, dst.block(1));
+tmp *= -1;
+tmp += src.block(0);
- A_inverse.name = "top left";
- A_inverse.vmult (dst.block(0), tmp);
+A_inverse.name = "top left";
+A_inverse.vmult (dst.block(0), tmp);
// no constraints here either
- }
+}
#ifdef STEP_42_TEST
- std::cout << "Exiting smoother with " << dst.size() << " unknowns" << std::endl;
+std::cout << "Exiting smoother with " << dst.size() << " unknowns" << std::endl;
#endif
*/
-}
+ }
-template <typename InnerPreconditioner>
-void
-SchurComplementSmoother<InnerPreconditioner>::clear ()
-{}
+ template <typename InnerPreconditioner>
+ void
+ SchurComplementSmoother<InnerPreconditioner>::clear ()
+ {}
-template <typename InnerPreconditioner>
-void
-SchurComplementSmoother<InnerPreconditioner>::
-Tvmult (BlockVector<double> &,
- const BlockVector<double> &) const
-{
- Assert (false, ExcNotImplemented());
-}
+ template <typename InnerPreconditioner>
+ void
+ SchurComplementSmoother<InnerPreconditioner>::
+ Tvmult (BlockVector<double> &,
+ const BlockVector<double> &) const
+ {
+ Assert (false, ExcNotImplemented());
+ }
-template <int dim>
-void StokesProblem<dim>::solve ()
-{
- system_matrix.block(1,1) = 0;
- assemble_multigrid ();
- typedef PreconditionMG<dim, BlockVector<double>, MGTransferPrebuilt<BlockVector<double> > >
- MGPREC;
-
- GrowingVectorMemory<BlockVector<double> > mg_vector_memory;
-
- MGTransferPrebuilt<BlockVector<double> > mg_transfer(constraints, mg_constrained_dofs);
- std::vector<unsigned int> block_component (dim+1,0);
- block_component[dim] = 1;
- mg_transfer.set_component_to_block_map (block_component);
- mg_transfer.build_matrices(dof_handler);
-
- FullMatrix<float> mg_coarse_matrix;
- mg_coarse_matrix.copy_from (mg_matrices[0]);
- MGCoarseGridHouseholder<float, BlockVector<double> > mg_coarse;
- mg_coarse.initialize(mg_coarse_matrix);
-
- MGMatrix<BlockSparseMatrix<double>, BlockVector<double> >
- mg_matrix(&mg_matrices);
- MGMatrix<BlockSparseMatrix<double>, BlockVector<double> >
- mg_interface_up(&mg_interface_matrices);
- MGMatrix<BlockSparseMatrix<double>, BlockVector<double> >
- mg_interface_down(&mg_interface_matrices);
-
- typedef
- SchurComplementSmoother<typename InnerPreconditioner<dim>::type>
- Smoother;
-
- MGSmootherPrecondition<BlockSparseMatrix<double>,
- Smoother,
- BlockVector<double> >
- mg_smoother(mg_vector_memory);
-
- MGLevelObject<typename Smoother::AdditionalData>
- smoother_data (0, triangulation.n_levels()-1);
-
- for (unsigned int level=0; level<triangulation.n_levels(); ++level)
- smoother_data[level].A_preconditioner = mg_A_preconditioner[level].get();
-
- mg_smoother.initialize(mg_matrices, smoother_data);
- mg_smoother.set_steps(2);
-
- Multigrid<BlockVector<double> > mg(dof_handler,
- mg_matrix,
- mg_coarse,
- mg_transfer,
- mg_smoother,
- mg_smoother);
- mg.set_debug(3);
- mg.set_edge_matrices(mg_interface_down, mg_interface_up);
-
- MGPREC preconditioner(dof_handler, mg, mg_transfer);
-
- SolverControl solver_control (system_matrix.m(),
- 1e-6*system_rhs.l2_norm());
- GrowingVectorMemory<BlockVector<double> > vector_memory;
- SolverGMRES<BlockVector<double> >::AdditionalData gmres_data;
- gmres_data.max_n_tmp_vectors = 100;
-
- SolverGMRES<BlockVector<double> > gmres(solver_control, vector_memory,
- gmres_data);
+ template <int dim>
+ void StokesProblem<dim>::solve ()
+ {
+ system_matrix.block(1,1) = 0;
+ assemble_multigrid ();
+ typedef PreconditionMG<dim, BlockVector<double>, MGTransferPrebuilt<BlockVector<double> > >
+ MGPREC;
+
+ GrowingVectorMemory<BlockVector<double> > mg_vector_memory;
+
+ MGTransferPrebuilt<BlockVector<double> > mg_transfer(constraints, mg_constrained_dofs);
+ std::vector<unsigned int> block_component (dim+1,0);
+ block_component[dim] = 1;
+ mg_transfer.set_component_to_block_map (block_component);
+ mg_transfer.build_matrices(dof_handler);
+
+ FullMatrix<float> mg_coarse_matrix;
+ mg_coarse_matrix.copy_from (mg_matrices[0]);
+ MGCoarseGridHouseholder<float, BlockVector<double> > mg_coarse;
+ mg_coarse.initialize(mg_coarse_matrix);
+
+ MGMatrix<BlockSparseMatrix<double>, BlockVector<double> >
+ mg_matrix(&mg_matrices);
+ MGMatrix<BlockSparseMatrix<double>, BlockVector<double> >
+ mg_interface_up(&mg_interface_matrices);
+ MGMatrix<BlockSparseMatrix<double>, BlockVector<double> >
+ mg_interface_down(&mg_interface_matrices);
+
+ typedef
+ SchurComplementSmoother<typename InnerPreconditioner<dim>::type>
+ Smoother;
+
+ MGSmootherPrecondition<BlockSparseMatrix<double>,
+ Smoother,
+ BlockVector<double> >
+ mg_smoother(mg_vector_memory);
+
+ MGLevelObject<typename Smoother::AdditionalData>
+ smoother_data (0, triangulation.n_levels()-1);
+
+ for (unsigned int level=0; level<triangulation.n_levels(); ++level)
+ smoother_data[level].A_preconditioner = mg_A_preconditioner[level].get();
+
+ mg_smoother.initialize(mg_matrices, smoother_data);
+ mg_smoother.set_steps(2);
+
+ Multigrid<BlockVector<double> > mg(dof_handler,
+ mg_matrix,
+ mg_coarse,
+ mg_transfer,
+ mg_smoother,
+ mg_smoother);
+ mg.set_debug(3);
+ mg.set_edge_matrices(mg_interface_down, mg_interface_up);
+
+ MGPREC preconditioner(dof_handler, mg, mg_transfer);
+
+ SolverControl solver_control (system_matrix.m(),
+ 1e-6*system_rhs.l2_norm());
+ GrowingVectorMemory<BlockVector<double> > vector_memory;
+ SolverGMRES<BlockVector<double> >::AdditionalData gmres_data;
+ gmres_data.max_n_tmp_vectors = 100;
+
+ SolverGMRES<BlockVector<double> > gmres(solver_control, vector_memory,
+ gmres_data);
// PreconditionIdentity precondition_identity;
#ifdef STEP_42_TEST
- std::cout << "Starting outer GMRES complement solver" << std::endl;
+ std::cout << "Starting outer GMRES complement solver" << std::endl;
#endif
- try
- {
- gmres.solve(system_matrix, solution, system_rhs,
- preconditioner);
- }
- catch (...)
- {
- std::cout << "Failure in " << __PRETTY_FUNCTION__ << std::endl;
- abort ();
- }
+ try
+ {
+ gmres.solve(system_matrix, solution, system_rhs,
+ preconditioner);
+ }
+ catch (...)
+ {
+ std::cout << "Failure in " << __PRETTY_FUNCTION__ << std::endl;
+ abort ();
+ }
- constraints.distribute (solution);
+ constraints.distribute (solution);
- std::cout << solver_control.last_step()
- << " outer GMRES iterations ";
-}
+ std::cout << solver_control.last_step()
+ << " outer GMRES iterations ";
+ }
template <int dim>
-void StokesProblem<dim>::solve_block ()
-{
- std::cout << " Computing preconditioner..." << std::endl << std::flush;
-
- A_preconditioner
- = std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type>(new typename InnerPreconditioner<dim>::type());
- A_preconditioner->initialize (system_matrix.block(0,0),
- typename InnerPreconditioner<dim>::type::AdditionalData());
-
- SparseMatrix<double> pressure_mass_matrix;
- pressure_mass_matrix.reinit(sparsity_pattern.block(1,1));
- pressure_mass_matrix.copy_from(system_matrix.block(1,1));
- system_matrix.block(1,1) = 0;
-
- SparseILU<double> pmass_preconditioner;
- pmass_preconditioner.initialize (pressure_mass_matrix,
- SparseILU<double>::AdditionalData());
-
- InverseMatrix<SparseMatrix<double>,SparseILU<double> >
- m_inverse (pressure_mass_matrix, pmass_preconditioner);
-
- BlockSchurPreconditioner<typename InnerPreconditioner<dim>::type,
- SparseILU<double> >
+ void StokesProblem<dim>::solve_block ()
+ {
+ std::cout << " Computing preconditioner..." << std::endl << std::flush;
+
+ A_preconditioner
+ = std_cxx1x::shared_ptr<typename InnerPreconditioner<dim>::type>(new typename InnerPreconditioner<dim>::type());
+ A_preconditioner->initialize (system_matrix.block(0,0),
+ typename InnerPreconditioner<dim>::type::AdditionalData());
+
+ SparseMatrix<double> pressure_mass_matrix;
+ pressure_mass_matrix.reinit(sparsity_pattern.block(1,1));
+ pressure_mass_matrix.copy_from(system_matrix.block(1,1));
+ system_matrix.block(1,1) = 0;
+
+ SparseILU<double> pmass_preconditioner;
+ pmass_preconditioner.initialize (pressure_mass_matrix,
+ SparseILU<double>::AdditionalData());
+
+ InverseMatrix<SparseMatrix<double>,SparseILU<double> >
+ m_inverse (pressure_mass_matrix, pmass_preconditioner);
+
+ BlockSchurPreconditioner<typename InnerPreconditioner<dim>::type,
+ SparseILU<double> >
preconditioner (system_matrix, m_inverse, *A_preconditioner);
- SolverControl solver_control (system_matrix.m(),
- 1e-6*system_rhs.l2_norm());
- GrowingVectorMemory<BlockVector<double> > vector_memory;
- SolverGMRES<BlockVector<double> >::AdditionalData gmres_data;
- gmres_data.max_n_tmp_vectors = 100;
+ SolverControl solver_control (system_matrix.m(),
+ 1e-6*system_rhs.l2_norm());
+ GrowingVectorMemory<BlockVector<double> > vector_memory;
+ SolverGMRES<BlockVector<double> >::AdditionalData gmres_data;
+ gmres_data.max_n_tmp_vectors = 100;
- SolverGMRES<BlockVector<double> > gmres(solver_control, vector_memory,
- gmres_data);
+ SolverGMRES<BlockVector<double> > gmres(solver_control, vector_memory,
+ gmres_data);
- gmres.solve(system_matrix, solution, system_rhs,
- preconditioner);
+ gmres.solve(system_matrix, solution, system_rhs,
+ preconditioner);
- constraints.distribute (solution);
+ constraints.distribute (solution);
- std::cout << " "
- << solver_control.last_step()
- << " block GMRES iterations ";
-}
+ std::cout << " "
+ << solver_control.last_step()
+ << " block GMRES iterations ";
+ }
-template <int dim>
-void
-StokesProblem<dim>::output_results (const unsigned int refinement_cycle) const
-{
- std::vector<std::string> solution_names (dim, "velocity");
- solution_names.push_back ("pressure");
+ template <int dim>
+ void
+ StokesProblem<dim>::output_results (const unsigned int refinement_cycle) const
+ {
+ std::vector<std::string> solution_names (dim, "velocity");
+ solution_names.push_back ("pressure");
- std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ data_component_interpretation
+ (dim, DataComponentInterpretation::component_is_part_of_vector);
data_component_interpretation
- (dim, DataComponentInterpretation::component_is_part_of_vector);
- data_component_interpretation
- .push_back (DataComponentInterpretation::component_is_scalar);
-
- DataOut<dim> data_out;
- data_out.attach_dof_handler (dof_handler);
- data_out.add_data_vector (solution, solution_names,
- DataOut<dim>::type_dof_data,
- data_component_interpretation);
- data_out.build_patches ();
-
- std::ostringstream filename;
- filename << "solution-"
- << Utilities::int_to_string (refinement_cycle, 2)
- << ".vtk";
-
- std::ofstream output (filename.str().c_str());
- data_out.write_vtk (output);
-}
+ .push_back (DataComponentInterpretation::component_is_scalar);
+
+ DataOut<dim> data_out;
+ data_out.attach_dof_handler (dof_handler);
+ data_out.add_data_vector (solution, solution_names,
+ DataOut<dim>::type_dof_data,
+ data_component_interpretation);
+ data_out.build_patches ();
+
+ std::ostringstream filename;
+ filename << "solution-"
+ << Utilities::int_to_string (refinement_cycle, 2)
+ << ".vtk";
+
+ std::ofstream output (filename.str().c_str());
+ data_out.write_vtk (output);
+ }
-template <int dim>
-void
-StokesProblem<dim>::refine_mesh ()
-{
- Vector<float> estimated_error_per_cell (triangulation.n_active_cells());
-
- std::vector<bool> component_mask (dim+1, false);
- component_mask[dim] = true;
- KellyErrorEstimator<dim>::estimate (static_cast<const DoFHandler<dim>&>(dof_handler),
- QGauss<dim-1>(degree+1),
- typename FunctionMap<dim>::type(),
- solution,
- estimated_error_per_cell,
- component_mask);
-
- GridRefinement::refine_and_coarsen_fixed_number (triangulation,
- estimated_error_per_cell,
- 0.3, 0.0);
- triangulation.execute_coarsening_and_refinement ();
-}
+ template <int dim>
+ void
+ StokesProblem<dim>::refine_mesh ()
+ {
+ Vector<float> estimated_error_per_cell (triangulation.n_active_cells());
+
+ std::vector<bool> component_mask (dim+1, false);
+ component_mask[dim] = true;
+ KellyErrorEstimator<dim>::estimate (static_cast<const DoFHandler<dim>&>(dof_handler),
+ QGauss<dim-1>(degree+1),
+ typename FunctionMap<dim>::type(),
+ solution,
+ estimated_error_per_cell,
+ component_mask);
+
+ GridRefinement::refine_and_coarsen_fixed_number (triangulation,
+ estimated_error_per_cell,
+ 0.3, 0.0);
+ triangulation.execute_coarsening_and_refinement ();
+ }
-template <int dim>
-void StokesProblem<dim>::run ()
-{
+ template <int dim>
+ void StokesProblem<dim>::run ()
{
- std::vector<unsigned int> subdivisions (dim, 1);
- subdivisions[0] = 1;
-
- const Point<dim> bottom_left = (dim == 2 ?
- Point<dim>(0,0) :
- Point<dim>(0,0,0));
- const Point<dim> top_right = (dim == 2 ?
- Point<dim>(1,1) :
- Point<dim>(1,1,1));
-
- GridGenerator::subdivided_hyper_rectangle (triangulation,
- subdivisions,
- bottom_left,
- top_right);
- }
+ {
+ std::vector<unsigned int> subdivisions (dim, 1);
+ subdivisions[0] = 1;
+
+ const Point<dim> bottom_left = (dim == 2 ?
+ Point<dim>(0,0) :
+ Point<dim>(0,0,0));
+ const Point<dim> top_right = (dim == 2 ?
+ Point<dim>(1,1) :
+ Point<dim>(1,1,1));
+
+ GridGenerator::subdivided_hyper_rectangle (triangulation,
+ subdivisions,
+ bottom_left,
+ top_right);
+ }
- for (typename Triangulation<dim>::active_cell_iterator
- cell = triangulation.begin_active();
- cell != triangulation.end(); ++cell)
- for (unsigned int f=0; f<GeometryInfo<dim>::faces_per_cell; ++f)
- if (cell->face(f)->center()[0] == 1)
- cell->face(f)->set_all_boundary_indicators(1);
+ for (typename Triangulation<dim>::active_cell_iterator
+ cell = triangulation.begin_active();
+ cell != triangulation.end(); ++cell)
+ for (unsigned int f=0; f<GeometryInfo<dim>::faces_per_cell; ++f)
+ if (cell->face(f)->center()[0] == 1)
+ cell->face(f)->set_all_boundary_indicators(1);
- triangulation.refine_global (1);
+ triangulation.refine_global (1);
- for (unsigned int refinement_cycle = 0; refinement_cycle<10;
- ++refinement_cycle)
- {
- std::cout << "Refinement cycle " << refinement_cycle << std::endl;
+ for (unsigned int refinement_cycle = 0; refinement_cycle<10;
+ ++refinement_cycle)
+ {
+ std::cout << "Refinement cycle " << refinement_cycle << std::endl;
- if (refinement_cycle > 0)
- refine_mesh ();
+ if (refinement_cycle > 0)
+ refine_mesh ();
- std::ostringstream out_filename;
- out_filename << "gitter"
- << refinement_cycle
- << ".eps";
+ std::ostringstream out_filename;
+ out_filename << "gitter"
+ << refinement_cycle
+ << ".eps";
- std::ofstream grid_output (out_filename.str().c_str());
- GridOut grid_out;
- grid_out.write_eps (triangulation, grid_output);
+ std::ofstream grid_output (out_filename.str().c_str());
+ GridOut grid_out;
+ grid_out.write_eps (triangulation, grid_output);
- setup_dofs ();
+ setup_dofs ();
- std::cout << " Assembling..." << std::endl << std::flush;
- assemble_system ();
+ std::cout << " Assembling..." << std::endl << std::flush;
+ assemble_system ();
- std::cout << " Solving..." << std::flush;
+ std::cout << " Solving..." << std::flush;
- solve_block ();
- output_results (refinement_cycle);
- system ("mv solution-* block");
+ solve_block ();
+ output_results (refinement_cycle);
+ system ("mv solution-* block");
- solution = 0;
+ solution = 0;
- solve ();
- output_results (refinement_cycle);
- system ("mv solution-* mg");
+ solve ();
+ output_results (refinement_cycle);
+ system ("mv solution-* mg");
- std::cout << std::endl;
- }
+ std::cout << std::endl;
+ }
+ }
}
{
try
{
+ using namespace dealii;
+ using namespace Step42;
+
deallog.depth_console (0);
StokesProblem<2> flow_problem(1);
/* $Id$ */
/* */
-/* Copyright (C) 2010 by Chih-Che Chueh and the deal.II authors */
+/* Copyright (C) 2010, 2011 by Chih-Che Chueh and the deal.II authors */
/* */
/* This file is subject to QPL and may not be distributed */
/* without copyright and license information. Please refer */
// the functionality of these well-known
// deal.II library files and some C++ header
// files.
- //
+ //
// In this program, we use a tensor-valued
// coefficient. Since it may have a spatial
// dependence, we consider it a tensor-valued
// function. The following include file
// provides the TensorFunction class that
// offers such functionality:
- //
+ //
// Then we need to include some header files
// that provide vector, matrix, and
// preconditioner classes that implement
// interfaces to the matrix and vector
// classes based on Trilinos as well as
// Trilinos preconditioners:
- //
+ //
// At the end of this top-matter, we import
// all deal.II names into the global
// namespace:
#include <fstream>
#include <sstream>
-using namespace dealii;
+namespace Step43
+{
+ using namespace dealii;
- // @sect3{The InverseMatrix class template}
+ // @sect3{The InverseMatrix class template}
- // This part is exactly the same as that used in step-31.
+ // This part is exactly the same as that used in step-31.
- // @sect3{Schur complement preconditioner}
+ // @sect3{Schur complement preconditioner}
- // This part for the Schur complement
- // preconditioner is almost the same as that
- // used in step-31. The only difference is
- // that the original variable name
- // stokes_matrix is replaced by another name
- // darcy_matrix to satisfy our problem.
-namespace LinearSolvers
-{
- template <class Matrix, class Preconditioner>
- class InverseMatrix : public Subscriptor
+ // This part for the Schur complement
+ // preconditioner is almost the same as that
+ // used in step-31. The only difference is
+ // that the original variable name
+ // stokes_matrix is replaced by another name
+ // darcy_matrix to satisfy our problem.
+ namespace LinearSolvers
{
- public:
- InverseMatrix (const Matrix &m,
- const Preconditioner &preconditioner);
+ template <class Matrix, class Preconditioner>
+ class InverseMatrix : public Subscriptor
+ {
+ public:
+ InverseMatrix (const Matrix &m,
+ const Preconditioner &preconditioner);
- template <typename VectorType>
- void vmult (VectorType &dst,
- const VectorType &src) const;
+ template <typename VectorType>
+ void vmult (VectorType &dst,
+ const VectorType &src) const;
- private:
- const SmartPointer<const Matrix> matrix;
- const Preconditioner &preconditioner;
- };
+ private:
+ const SmartPointer<const Matrix> matrix;
+ const Preconditioner &preconditioner;
+ };
- template <class Matrix, class Preconditioner>
- InverseMatrix<Matrix,Preconditioner>::
- InverseMatrix (const Matrix &m,
- const Preconditioner &preconditioner)
- :
- matrix (&m),
- preconditioner (preconditioner)
- {}
+ template <class Matrix, class Preconditioner>
+ InverseMatrix<Matrix,Preconditioner>::
+ InverseMatrix (const Matrix &m,
+ const Preconditioner &preconditioner)
+ :
+ matrix (&m),
+ preconditioner (preconditioner)
+ {}
- template <class Matrix, class Preconditioner>
- template <typename VectorType>
- void
- InverseMatrix<Matrix,Preconditioner>::
- vmult (VectorType &dst,
- const VectorType &src) const
- {
- SolverControl solver_control (src.size(), 1e-7*src.l2_norm());
- SolverCG<VectorType> cg (solver_control);
+ template <class Matrix, class Preconditioner>
+ template <typename VectorType>
+ void
+ InverseMatrix<Matrix,Preconditioner>::
+ vmult (VectorType &dst,
+ const VectorType &src) const
+ {
+ SolverControl solver_control (src.size(), 1e-7*src.l2_norm());
+ SolverCG<VectorType> cg (solver_control);
- dst = 0;
+ dst = 0;
- try
- {
- cg.solve (*matrix, dst, src, preconditioner);
- }
- catch (std::exception &e)
- {
- Assert (false, ExcMessage(e.what()));
- }
+ try
+ {
+ cg.solve (*matrix, dst, src, preconditioner);
+ }
+ catch (std::exception &e)
+ {
+ Assert (false, ExcMessage(e.what()));
+ }
+ }
+
+ template <class PreconditionerA, class PreconditionerMp>
+ class BlockSchurPreconditioner : public Subscriptor
+ {
+ public:
+ BlockSchurPreconditioner (
+ const TrilinosWrappers::BlockSparseMatrix &S,
+ const InverseMatrix<TrilinosWrappers::SparseMatrix,
+ PreconditionerMp> &Mpinv,
+ const PreconditionerA &Apreconditioner);
+
+ void vmult (TrilinosWrappers::BlockVector &dst,
+ const TrilinosWrappers::BlockVector &src) const;
+
+ private:
+ const SmartPointer<const TrilinosWrappers::BlockSparseMatrix> darcy_matrix;
+ const SmartPointer<const InverseMatrix<TrilinosWrappers::SparseMatrix,
+ PreconditionerMp > > m_inverse;
+ const PreconditionerA &a_preconditioner;
+
+ mutable TrilinosWrappers::Vector tmp;
+ };
+
+
+
+ template <class PreconditionerA, class PreconditionerMp>
+ BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::
+ BlockSchurPreconditioner(const TrilinosWrappers::BlockSparseMatrix &S,
+ const InverseMatrix<TrilinosWrappers::SparseMatrix,
+ PreconditionerMp> &Mpinv,
+ const PreconditionerA &Apreconditioner)
+ :
+ darcy_matrix (&S),
+ m_inverse (&Mpinv),
+ a_preconditioner (Apreconditioner),
+ tmp (darcy_matrix->block(1,1).m())
+ {}
+
+
+ template <class PreconditionerA, class PreconditionerMp>
+ void BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::vmult (
+ TrilinosWrappers::BlockVector &dst,
+ const TrilinosWrappers::BlockVector &src) const
+ {
+ a_preconditioner.vmult (dst.block(0), src.block(0));
+ darcy_matrix->block(1,0).residual(tmp, dst.block(0), src.block(1));
+ tmp *= -1;
+ m_inverse->vmult (dst.block(1), tmp);
+ }
}
- template <class PreconditionerA, class PreconditionerMp>
- class BlockSchurPreconditioner : public Subscriptor
+
+ // @sect3{The TwoPhaseFlowProblem class}
+
+ // The definition of the class that defines
+ // the top-level logic of solving the
+ // time-dependent advection-dominated
+ // two-phase flow problem (or
+ // Buckley-Leverett problem
+ // [Buckley 1942]) is mainly based on
+ // three tutorial programs (step-21, step-31,
+ // step-33). The main difference is that,
+ // since adaptive operator splitting is
+ // considered, we need a bool-type variable
+ // solve_pressure_velocity_part to tell us
+ // when we need to solve the pressure and
+ // velocity part, need another bool-type
+ // variable
+ // previous_solve_pressure_velocity_part to
+ // determine if we have to cumulate
+ // micro-time steps that we need them to do
+ // extrapolation for the total velocity, and
+ // some solution vectors
+ // (e.g. nth_darcy_solution_after_solving_pressure_part
+ // and
+ // n_minus_oneth_darcy_solution_after_solving_pressure_part)
+ // to store some solutions in previous time
+ // steps after the solution of the pressure
+ // and velocity part.
+ //
+ // The member functions within this class
+ // have been named so properly so that
+ // readers can easily understand what they
+ // are doing.
+ //
+ // Like step-31, this tutorial uses two
+ // DoFHandler objects for the darcy system
+ // (presure and velocity) and
+ // saturation. This is because we want it to
+ // run faster, which reasons have been
+ // described in step-31.
+ //
+ // There is yet another important thing:
+ // unlike step-31. this step uses one more
+ // ConstraintMatrix object called
+ // darcy_preconditioner_constraints. This
+ // constraint object only for assembling the
+ // matrix for darcy preconditioner includes
+ // hanging node constrants as well as
+ // Dirichlet boundary value
+ // constraints. Without this constraint
+ // object for the preconditioner, we cannot
+ // get the convergence results when we solve
+ // darcy linear system.
+ //
+ // The last one variable indicates whether
+ // the matrix needs to be rebuilt the next
+ // time the corresponding build functions are
+ // called. This allows us to move the
+ // corresponding if into the function and
+ // thereby keeping our main run() function
+ // clean and easy to read.
+ template <int dim>
+ class TwoPhaseFlowProblem
{
public:
- BlockSchurPreconditioner (
- const TrilinosWrappers::BlockSparseMatrix &S,
- const InverseMatrix<TrilinosWrappers::SparseMatrix,
- PreconditionerMp> &Mpinv,
- const PreconditionerA &Apreconditioner);
-
- void vmult (TrilinosWrappers::BlockVector &dst,
- const TrilinosWrappers::BlockVector &src) const;
+ TwoPhaseFlowProblem (const unsigned int degree);
+ void run ();
private:
- const SmartPointer<const TrilinosWrappers::BlockSparseMatrix> darcy_matrix;
- const SmartPointer<const InverseMatrix<TrilinosWrappers::SparseMatrix,
- PreconditionerMp > > m_inverse;
- const PreconditionerA &a_preconditioner;
-
- mutable TrilinosWrappers::Vector tmp;
+ void setup_dofs ();
+ void assemble_darcy_preconditioner ();
+ void build_darcy_preconditioner ();
+ void assemble_darcy_system ();
+ void assemble_saturation_system ();
+ void assemble_saturation_matrix ();
+ void assemble_saturation_rhs ();
+ void assemble_saturation_rhs_cell_term (const FEValues<dim> &saturation_fe_values,
+ const FEValues<dim> &darcy_fe_values,
+ const std::vector<unsigned int> &local_dof_indices,
+ const double global_u_infty,
+ const double global_S_variation,
+ const double global_Omega_diameter);
+ void assemble_saturation_rhs_boundary_term (const FEFaceValues<dim> &saturation_fe_face_values,
+ const FEFaceValues<dim> &darcy_fe_face_values,
+ const std::vector<unsigned int> &local_dof_indices);
+ double get_maximal_velocity () const;
+ std::pair<double,double> get_extrapolated_saturation_range () const;
+ void solve ();
+ bool determine_whether_to_solve_pressure_velocity_part () const;
+ void compute_refinement_indicators (Vector<double> &indicator) const;
+ void refine_grid (const Vector<double> &indicator);
+ void project_back_saturation ();
+ void output_results () const;
+
+ static
+ double
+ compute_viscosity(const std::vector<double> &old_saturation,
+ const std::vector<double> &old_old_saturation,
+ const std::vector<Tensor<1,dim> > &old_saturation_grads,
+ const std::vector<Tensor<1,dim> > &old_old_saturation_grads,
+ const std::vector<Vector<double> > &present_darcy_values,
+ const double global_u_infty,
+ const double global_S_variation,
+ const double global_Omega_diameter,
+ const double cell_diameter,
+ const double old_time_step,
+ const double viscosity);
+
+
+ const unsigned int degree;
+
+ Triangulation<dim> triangulation;
+
+ const unsigned int darcy_degree;
+ FESystem<dim> darcy_fe;
+ DoFHandler<dim> darcy_dof_handler;
+ ConstraintMatrix darcy_constraints;
+
+ ConstraintMatrix darcy_preconditioner_constraints;
+
+ TrilinosWrappers::BlockSparseMatrix darcy_matrix;
+ TrilinosWrappers::BlockSparseMatrix darcy_preconditioner_matrix;
+
+ TrilinosWrappers::BlockVector darcy_solution;
+ TrilinosWrappers::BlockVector darcy_rhs;
+
+ TrilinosWrappers::BlockVector nth_darcy_solution_after_solving_pressure_part;
+ TrilinosWrappers::BlockVector n_minus_oneth_darcy_solution_after_solving_pressure_part;
+
+ const unsigned int saturation_degree;
+ FE_Q<dim> saturation_fe;
+ DoFHandler<dim> saturation_dof_handler;
+ ConstraintMatrix saturation_constraints;
+
+ TrilinosWrappers::SparseMatrix saturation_matrix;
+
+ TrilinosWrappers::Vector predictor_saturation_solution;
+ TrilinosWrappers::Vector saturation_solution;
+ TrilinosWrappers::Vector old_saturation_solution;
+ TrilinosWrappers::Vector old_old_saturation_solution;
+ TrilinosWrappers::Vector saturation_rhs;
+
+ TrilinosWrappers::Vector nth_saturation_solution_after_solving_pressure_part;
+
+ const unsigned int n_refinement_steps;
+ bool solve_pressure_velocity_part;
+ bool previous_solve_pressure_velocity_part;
+
+ const double saturation_level;
+ const double saturation_value;
+
+ double n_minus_oneth_time_step;
+ double cumulative_nth_time_step;
+
+ double time_step;
+ double old_time_step;
+ unsigned int timestep_number;
+ double viscosity;
+
+ std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC> Amg_preconditioner;
+ std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC> Mp_preconditioner;
+
+ bool rebuild_saturation_matrix;
};
+ // @sect3{Pressure right hand side, Pressure boundary values and saturation initial value classes}
- template <class PreconditionerA, class PreconditionerMp>
- BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::
- BlockSchurPreconditioner(const TrilinosWrappers::BlockSparseMatrix &S,
- const InverseMatrix<TrilinosWrappers::SparseMatrix,
- PreconditionerMp> &Mpinv,
- const PreconditionerA &Apreconditioner)
- :
- darcy_matrix (&S),
- m_inverse (&Mpinv),
- a_preconditioner (Apreconditioner),
- tmp (darcy_matrix->block(1,1).m())
- {}
-
-
- template <class PreconditionerA, class PreconditionerMp>
- void BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::vmult (
- TrilinosWrappers::BlockVector &dst,
- const TrilinosWrappers::BlockVector &src) const
+ // This part is directly taken from step-21
+ // so there is no need to repeat the same
+ // descriptions.
+ template <int dim>
+ class PressureRightHandSide : public Function<dim>
{
- a_preconditioner.vmult (dst.block(0), src.block(0));
- darcy_matrix->block(1,0).residual(tmp, dst.block(0), src.block(1));
- tmp *= -1;
- m_inverse->vmult (dst.block(1), tmp);
- }
-}
-
-
- // @sect3{The TwoPhaseFlowProblem class}
-
- // The definition of the class that defines
- // the top-level logic of solving the
- // time-dependent advection-dominated
- // two-phase flow problem (or
- // Buckley-Leverett problem
- // [Buckley 1942]) is mainly based on
- // three tutorial programs (step-21, step-31,
- // step-33). The main difference is that,
- // since adaptive operator splitting is
- // considered, we need a bool-type variable
- // solve_pressure_velocity_part to tell us
- // when we need to solve the pressure and
- // velocity part, need another bool-type
- // variable
- // previous_solve_pressure_velocity_part to
- // determine if we have to cumulate
- // micro-time steps that we need them to do
- // extrapolation for the total velocity, and
- // some solution vectors
- // (e.g. nth_darcy_solution_after_solving_pressure_part
- // and
- // n_minus_oneth_darcy_solution_after_solving_pressure_part)
- // to store some solutions in previous time
- // steps after the solution of the pressure
- // and velocity part.
- //
- // The member functions within this class
- // have been named so properly so that
- // readers can easily understand what they
- // are doing.
- //
- // Like step-31, this tutorial uses two
- // DoFHandler objects for the darcy system
- // (presure and velocity) and
- // saturation. This is because we want it to
- // run faster, which reasons have been
- // described in step-31.
- //
- // There is yet another important thing:
- // unlike step-31. this step uses one more
- // ConstraintMatrix object called
- // darcy_preconditioner_constraints. This
- // constraint object only for assembling the
- // matrix for darcy preconditioner includes
- // hanging node constrants as well as
- // Dirichlet boundary value
- // constraints. Without this constraint
- // object for the preconditioner, we cannot
- // get the convergence results when we solve
- // darcy linear system.
- //
- // The last one variable indicates whether
- // the matrix needs to be rebuilt the next
- // time the corresponding build functions are
- // called. This allows us to move the
- // corresponding if into the function and
- // thereby keeping our main run() function
- // clean and easy to read.
-template <int dim>
-class TwoPhaseFlowProblem
-{
- public:
- TwoPhaseFlowProblem (const unsigned int degree);
- void run ();
-
- private:
- void setup_dofs ();
- void assemble_darcy_preconditioner ();
- void build_darcy_preconditioner ();
- void assemble_darcy_system ();
- void assemble_saturation_system ();
- void assemble_saturation_matrix ();
- void assemble_saturation_rhs ();
- void assemble_saturation_rhs_cell_term (const FEValues<dim> &saturation_fe_values,
- const FEValues<dim> &darcy_fe_values,
- const std::vector<unsigned int> &local_dof_indices,
- const double global_u_infty,
- const double global_S_variation,
- const double global_Omega_diameter);
- void assemble_saturation_rhs_boundary_term (const FEFaceValues<dim> &saturation_fe_face_values,
- const FEFaceValues<dim> &darcy_fe_face_values,
- const std::vector<unsigned int> &local_dof_indices);
- double get_maximal_velocity () const;
- std::pair<double,double> get_extrapolated_saturation_range () const;
- void solve ();
- bool determine_whether_to_solve_pressure_velocity_part () const;
- void compute_refinement_indicators (Vector<double> &indicator) const;
- void refine_grid (const Vector<double> &indicator);
- void project_back_saturation ();
- void output_results () const;
-
- static
- double
- compute_viscosity(const std::vector<double> &old_saturation,
- const std::vector<double> &old_old_saturation,
- const std::vector<Tensor<1,dim> > &old_saturation_grads,
- const std::vector<Tensor<1,dim> > &old_old_saturation_grads,
- const std::vector<Vector<double> > &present_darcy_values,
- const double global_u_infty,
- const double global_S_variation,
- const double global_Omega_diameter,
- const double cell_diameter,
- const double old_time_step,
- const double viscosity);
-
-
- const unsigned int degree;
-
- Triangulation<dim> triangulation;
-
- const unsigned int darcy_degree;
- FESystem<dim> darcy_fe;
- DoFHandler<dim> darcy_dof_handler;
- ConstraintMatrix darcy_constraints;
-
- ConstraintMatrix darcy_preconditioner_constraints;
-
- TrilinosWrappers::BlockSparseMatrix darcy_matrix;
- TrilinosWrappers::BlockSparseMatrix darcy_preconditioner_matrix;
-
- TrilinosWrappers::BlockVector darcy_solution;
- TrilinosWrappers::BlockVector darcy_rhs;
-
- TrilinosWrappers::BlockVector nth_darcy_solution_after_solving_pressure_part;
- TrilinosWrappers::BlockVector n_minus_oneth_darcy_solution_after_solving_pressure_part;
-
- const unsigned int saturation_degree;
- FE_Q<dim> saturation_fe;
- DoFHandler<dim> saturation_dof_handler;
- ConstraintMatrix saturation_constraints;
-
- TrilinosWrappers::SparseMatrix saturation_matrix;
-
- TrilinosWrappers::Vector predictor_saturation_solution;
- TrilinosWrappers::Vector saturation_solution;
- TrilinosWrappers::Vector old_saturation_solution;
- TrilinosWrappers::Vector old_old_saturation_solution;
- TrilinosWrappers::Vector saturation_rhs;
-
- TrilinosWrappers::Vector nth_saturation_solution_after_solving_pressure_part;
-
- const unsigned int n_refinement_steps;
- bool solve_pressure_velocity_part;
- bool previous_solve_pressure_velocity_part;
-
- const double saturation_level;
- const double saturation_value;
-
- double n_minus_oneth_time_step;
- double cumulative_nth_time_step;
-
- double time_step;
- double old_time_step;
- unsigned int timestep_number;
- double viscosity;
-
- std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC> Amg_preconditioner;
- std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC> Mp_preconditioner;
-
- bool rebuild_saturation_matrix;
-};
-
-
- // @sect3{Pressure right hand side, Pressure boundary values and saturation initial value classes}
-
- // This part is directly taken from step-21
- // so there is no need to repeat the same
- // descriptions.
-template <int dim>
-class PressureRightHandSide : public Function<dim>
-{
- public:
- PressureRightHandSide () : Function<dim>(1) {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
-};
-
-
-
-template <int dim>
-double
-PressureRightHandSide<dim>::value (const Point<dim> &/*p*/,
- const unsigned int /*component*/) const
-{
- return 0;
-}
-
-
-template <int dim>
-class PressureBoundaryValues : public Function<dim>
-{
- public:
- PressureBoundaryValues () : Function<dim>(1) {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
-};
-
-
-template <int dim>
-double
-PressureBoundaryValues<dim>::value (const Point<dim> &p,
- const unsigned int /*component*/) const
-{
- return 1-p[0];
-}
-
-
-template <int dim>
-class SaturationBoundaryValues : public Function<dim>
-{
- public:
- SaturationBoundaryValues () : Function<dim>(1) {}
+ public:
+ PressureRightHandSide () : Function<dim>(1) {}
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
-};
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
+ };
-template <int dim>
-double
-SaturationBoundaryValues<dim>::value (const Point<dim> &p,
- const unsigned int /*component*/) const
-{
- if (p[0] == 0)
- return 1;
- else
+ template <int dim>
+ double
+ PressureRightHandSide<dim>::value (const Point<dim> &/*p*/,
+ const unsigned int /*component*/) const
+ {
return 0;
-}
-
-
-template <int dim>
-class SaturationInitialValues : public Function<dim>
-{
- public:
- SaturationInitialValues () : Function<dim>(1) {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component = 0) const;
-
- virtual void vector_value (const Point<dim> &p,
- Vector<double> &value) const;
-
-};
+ }
-template <int dim>
-double
-SaturationInitialValues<dim>::value (const Point<dim> &/*p*/,
- const unsigned int /*component*/) const
-{
- return 0;
-}
+ template <int dim>
+ class PressureBoundaryValues : public Function<dim>
+ {
+ public:
+ PressureBoundaryValues () : Function<dim>(1) {}
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
+ };
-template <int dim>
-void
-SaturationInitialValues<dim>::vector_value (const Point<dim> &p,
- Vector<double> &values) const
-{
- for (unsigned int c=0; c<this->n_components; ++c)
- values(c) = SaturationInitialValues<dim>::value (p,c);
-}
+ template <int dim>
+ double
+ PressureBoundaryValues<dim>::value (const Point<dim> &p,
+ const unsigned int /*component*/) const
+ {
+ return 1-p[0];
+ }
- // @sect3{Permeability models}
- // In this tutorial, we still use two
- // permeability models previous used in
- // step-21 so we refrain from excessive
- // comments about them. But we want to note
- // that if ones use the Random Medium model,
- // they can change one parameter called the
- // number of high-permeability regions/points
- // to increase the amount of permeability in
- // the computational domain.
-namespace SingleCurvingCrack
-{
template <int dim>
- class KInverse : public TensorFunction<2,dim>
+ class SaturationBoundaryValues : public Function<dim>
{
public:
- KInverse ()
- :
- TensorFunction<2,dim> ()
- {}
+ SaturationBoundaryValues () : Function<dim>(1) {}
- virtual void value_list (const std::vector<Point<dim> > &points,
- std::vector<Tensor<2,dim> > &values) const;
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
};
+
template <int dim>
- void
- KInverse<dim>::value_list (const std::vector<Point<dim> > &points,
- std::vector<Tensor<2,dim> > &values) const
+ double
+ SaturationBoundaryValues<dim>::value (const Point<dim> &p,
+ const unsigned int /*component*/) const
{
- Assert (points.size() == values.size(),
- ExcDimensionMismatch (points.size(), values.size()));
-
- for (unsigned int p=0; p<points.size(); ++p)
- {
- values[p].clear ();
-
- const double distance_to_flowline
- = std::fabs(points[p][1]-0.5-0.1*std::sin(10*points[p][0]));
-
- const double permeability = std::max(std::exp(-(distance_to_flowline*
- distance_to_flowline)
- / (0.1 * 0.1)),
- 0.01);
-
- for (unsigned int d=0; d<dim; ++d)
- values[p][d][d] = 1./permeability;
- }
+ if (p[0] == 0)
+ return 1;
+ else
+ return 0;
}
-}
-namespace RandomMedium
-{
template <int dim>
- class KInverse : public TensorFunction<2,dim>
+ class SaturationInitialValues : public Function<dim>
{
public:
- KInverse ()
- :
- TensorFunction<2,dim> ()
- {}
+ SaturationInitialValues () : Function<dim>(1) {}
- virtual void value_list (const std::vector<Point<dim> > &points,
- std::vector<Tensor<2,dim> > &values) const;
+ virtual double value (const Point<dim> &p,
+ const unsigned int component = 0) const;
- private:
- static std::vector<Point<dim> > centers;
+ virtual void vector_value (const Point<dim> &p,
+ Vector<double> &value) const;
- static std::vector<Point<dim> > get_centers ();
};
-
template <int dim>
- std::vector<Point<dim> >
- KInverse<dim>::centers = KInverse<dim>::get_centers();
+ double
+ SaturationInitialValues<dim>::value (const Point<dim> &/*p*/,
+ const unsigned int /*component*/) const
+ {
+ return 0;
+ }
template <int dim>
- std::vector<Point<dim> >
- KInverse<dim>::get_centers ()
+ void
+ SaturationInitialValues<dim>::vector_value (const Point<dim> &p,
+ Vector<double> &values) const
{
- const unsigned int N = (dim == 2 ?
- 40 :
- (dim == 3 ?
- 100 :
- throw ExcNotImplemented()));
-
- std::vector<Point<dim> > centers_list (N);
- for (unsigned int i=0; i<N; ++i)
- for (unsigned int d=0; d<dim; ++d)
- centers_list[i][d] = static_cast<double>(rand())/RAND_MAX;
-
- return centers_list;
+ for (unsigned int c=0; c<this->n_components; ++c)
+ values(c) = SaturationInitialValues<dim>::value (p,c);
}
+ // @sect3{Permeability models}
- template <int dim>
- void
- KInverse<dim>::value_list (const std::vector<Point<dim> > &points,
- std::vector<Tensor<2,dim> > &values) const
+ // In this tutorial, we still use two
+ // permeability models previous used in
+ // step-21 so we refrain from excessive
+ // comments about them. But we want to note
+ // that if ones use the Random Medium model,
+ // they can change one parameter called the
+ // number of high-permeability regions/points
+ // to increase the amount of permeability in
+ // the computational domain.
+ namespace SingleCurvingCrack
{
- Assert (points.size() == values.size(),
- ExcDimensionMismatch (points.size(), values.size()));
+ template <int dim>
+ class KInverse : public TensorFunction<2,dim>
+ {
+ public:
+ KInverse ()
+ :
+ TensorFunction<2,dim> ()
+ {}
- for (unsigned int p=0; p<points.size(); ++p)
- {
- values[p].clear ();
+ virtual void value_list (const std::vector<Point<dim> > &points,
+ std::vector<Tensor<2,dim> > &values) const;
+ };
- double permeability = 0;
- for (unsigned int i=0; i<centers.size(); ++i)
- permeability += std::exp(-(points[p]-centers[i]).square()
- / (0.05 * 0.05));
- const double normalized_permeability
- = std::min (std::max(permeability, 0.01), 4.);
+ template <int dim>
+ void
+ KInverse<dim>::value_list (const std::vector<Point<dim> > &points,
+ std::vector<Tensor<2,dim> > &values) const
+ {
+ Assert (points.size() == values.size(),
+ ExcDimensionMismatch (points.size(), values.size()));
- for (unsigned int d=0; d<dim; ++d)
- values[p][d][d] = 1./normalized_permeability;
- }
+ for (unsigned int p=0; p<points.size(); ++p)
+ {
+ values[p].clear ();
+
+ const double distance_to_flowline
+ = std::fabs(points[p][1]-0.5-0.1*std::sin(10*points[p][0]));
+
+ const double permeability = std::max(std::exp(-(distance_to_flowline*
+ distance_to_flowline)
+ / (0.1 * 0.1)),
+ 0.01);
+
+ for (unsigned int d=0; d<dim; ++d)
+ values[p][d][d] = 1./permeability;
+ }
+ }
}
-}
- // @sect3{Physical quantities}
+ namespace RandomMedium
+ {
+ template <int dim>
+ class KInverse : public TensorFunction<2,dim>
+ {
+ public:
+ KInverse ()
+ :
+ TensorFunction<2,dim> ()
+ {}
- // The implementations of all the physical
- // quantities such as total mobility
- // $\lambda_t$ and fractional flow of water
- // $F$ are taken from step-21 so again we
- // don't have do any comment about them.
-double mobility_inverse (const double S,
- const double viscosity)
-{
- return 1.0 /(1.0/viscosity * S * S + (1-S) * (1-S));
-}
+ virtual void value_list (const std::vector<Point<dim> > &points,
+ std::vector<Tensor<2,dim> > &values) const;
-double f_saturation (const double S,
- const double viscosity)
-{
- return S*S /( S * S +viscosity * (1-S) * (1-S));
-}
+ private:
+ static std::vector<Point<dim> > centers;
-double get_fractional_flow_derivative (const double S,
- const double viscosity)
-{
- const double temp = ( S * S + viscosity * (1-S) * (1-S) );
+ static std::vector<Point<dim> > get_centers ();
+ };
- const double numerator = 2.0 * S * temp
- -
- S * S *
- ( 2.0 * S - 2.0 * viscosity * (1-S) );
- const double denomerator = std::pow(temp, 2.0 );
- return numerator / denomerator;
-}
+ template <int dim>
+ std::vector<Point<dim> >
+ KInverse<dim>::centers = KInverse<dim>::get_centers();
+
+
+ template <int dim>
+ std::vector<Point<dim> >
+ KInverse<dim>::get_centers ()
+ {
+ const unsigned int N = (dim == 2 ?
+ 40 :
+ (dim == 3 ?
+ 100 :
+ throw ExcNotImplemented()));
+
+ std::vector<Point<dim> > centers_list (N);
+ for (unsigned int i=0; i<N; ++i)
+ for (unsigned int d=0; d<dim; ++d)
+ centers_list[i][d] = static_cast<double>(rand())/RAND_MAX;
+
+ return centers_list;
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::TwoPhaseFlowProblem}
-
- // The constructor of this class is an
- // extension of the constructor in step-21
- // and step-31. We need to add the various
- // variables that concern the saturation. As
- // discussed in the introduction, we are
- // going to use $Q_2 \times Q_1$
- // (Taylor-Hood) elements again for the darcy
- // system, which element combination fulfills
- // the Ladyzhenskaya-Babuska-Brezzi (LBB)
- // conditions
- // [Brezzi and Fortin 1991, Chen 2005], and $Q_1$
- // elements for the saturation. However, by
- // using variables that store the polynomial
- // degree of the darcy and temperature finite
- // elements, it is easy to consistently
- // modify the degree of the elements as well
- // as all quadrature formulas used on them
- // downstream. Moreover, we initialize the
- // time stepping, variables related to
- // operator splitting as well as the option
- // for matrix assembly and preconditioning:
-template <int dim>
-TwoPhaseFlowProblem<dim>::TwoPhaseFlowProblem (const unsigned int degree)
- :
- degree (degree),
- darcy_degree (degree),
- darcy_fe (FE_Q<dim>(darcy_degree+1), dim,
- FE_Q<dim>(darcy_degree), 1),
- darcy_dof_handler (triangulation),
-
- saturation_degree (degree),
- saturation_fe (saturation_degree),
- saturation_dof_handler (triangulation),
-
- n_refinement_steps (4),
- solve_pressure_velocity_part (false),
- previous_solve_pressure_velocity_part (false),
-
- saturation_level (2),
- saturation_value (0.5),
-
- time_step (0),
- old_time_step (0),
- viscosity (0.2),
-
- rebuild_saturation_matrix (true)
-{}
-
-
- // @sect3{TwoPhaseFlowProblem<dim>::setup_dofs}
-
- // This is the function that sets up the
- // DoFHandler objects we have here (one for
- // the darcy part and one for the saturation
- // part) as well as set to the right sizes
- // the various objects required for the
- // linear algebra in this program. Its basic
- // operations are similar to what authors in
- // step-31 did.
- //
- // The body of the function first enumerates
- // all degrees of freedom for the darcy and
- // saturation systems. For the darcy part,
- // degrees of freedom are then sorted to
- // ensure that velocities precede pressure
- // DoFs so that we can partition the darcy
- // matrix into a $2 \times 2$ matrix. Like
- // step-31, the present step does not perform
- // any additional DoF renumbering.
- //
- // Then, we need to incorporate hanging node
- // constraints and Dirichlet boundary value
- // constraints into
- // darcy_preconditioner_constraints. However,
- // this constraints are only set to the
- // pressure component since the Schur
- // complement preconditioner that corresponds
- // to the porous media flow operator in
- // non-mixed form, $-\nabla \cdot [\mathbf K
- // \lambda_t(S)]\nabla$. Therefore, we use a
- // component_mask that filters out the
- // velocity component, so that the
- // condensation is performed on pressure
- // degrees of freedom only.
- //
- // After having done so, we count the number
- // of degrees of freedom in the various
- // blocks:
- //
- // The next step is to create the sparsity
- // pattern for the darcy and saturation
- // system matrices as well as the
- // preconditioner matrix from which we build
- // the darcy preconditioner. As in step-31,
- // we choose to create the pattern not as in
- // the first few tutorial programs, but by
- // using the blocked version of
- // CompressedSimpleSparsityPattern. The
- // reason for doing this is mainly memory,
- // that is, the SparsityPattern class would
- // consume too much memory when used in three
- // spatial dimensions as we intend to do for
- // this program. So, for this, we follow the
- // same way as step-31 did and we don't have
- // to repeat descriptions again for the rest
- // of the member function.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::setup_dofs ()
-{
- std::vector<unsigned int> darcy_block_component (dim+1,0);
- darcy_block_component[dim] = 1;
- {
- darcy_dof_handler.distribute_dofs (darcy_fe);
- DoFRenumbering::Cuthill_McKee (darcy_dof_handler);
- DoFRenumbering::component_wise (darcy_dof_handler, darcy_block_component);
- darcy_constraints.clear ();
- DoFTools::make_hanging_node_constraints (darcy_dof_handler, darcy_constraints);
- darcy_constraints.close ();
+ template <int dim>
+ void
+ KInverse<dim>::value_list (const std::vector<Point<dim> > &points,
+ std::vector<Tensor<2,dim> > &values) const
+ {
+ Assert (points.size() == values.size(),
+ ExcDimensionMismatch (points.size(), values.size()));
+
+ for (unsigned int p=0; p<points.size(); ++p)
+ {
+ values[p].clear ();
+
+ double permeability = 0;
+ for (unsigned int i=0; i<centers.size(); ++i)
+ permeability += std::exp(-(points[p]-centers[i]).square()
+ / (0.05 * 0.05));
+
+ const double normalized_permeability
+ = std::min (std::max(permeability, 0.01), 4.);
+
+ for (unsigned int d=0; d<dim; ++d)
+ values[p][d][d] = 1./normalized_permeability;
+ }
+ }
}
- {
- saturation_dof_handler.distribute_dofs (saturation_fe);
- saturation_constraints.clear ();
- DoFTools::make_hanging_node_constraints (saturation_dof_handler, saturation_constraints);
- saturation_constraints.close ();
+
+ // @sect3{Physical quantities}
+
+ // The implementations of all the physical
+ // quantities such as total mobility
+ // $\lambda_t$ and fractional flow of water
+ // $F$ are taken from step-21 so again we
+ // don't have do any comment about them.
+ double mobility_inverse (const double S,
+ const double viscosity)
+ {
+ return 1.0 /(1.0/viscosity * S * S + (1-S) * (1-S));
}
+
+ double f_saturation (const double S,
+ const double viscosity)
{
- darcy_preconditioner_constraints.clear ();
+ return S*S /( S * S +viscosity * (1-S) * (1-S));
+ }
- std::vector<bool> component_mask (dim+1, false);
- component_mask[dim] = true;
+ double get_fractional_flow_derivative (const double S,
+ const double viscosity)
+ {
+ const double temp = ( S * S + viscosity * (1-S) * (1-S) );
+ const double numerator = 2.0 * S * temp
+ -
+ S * S *
+ ( 2.0 * S - 2.0 * viscosity * (1-S) );
- DoFTools::make_hanging_node_constraints (darcy_dof_handler, darcy_preconditioner_constraints);
- DoFTools::make_zero_boundary_constraints (darcy_dof_handler, darcy_preconditioner_constraints, component_mask);
+ const double denomerator = std::pow(temp, 2.0 );
- darcy_preconditioner_constraints.close ();
+ return numerator / denomerator;
}
- std::vector<unsigned int> darcy_dofs_per_block (2);
- DoFTools::count_dofs_per_block (darcy_dof_handler, darcy_dofs_per_block, darcy_block_component);
- const unsigned int n_u = darcy_dofs_per_block[0],
- n_p = darcy_dofs_per_block[1],
- n_s = saturation_dof_handler.n_dofs();
-
- std::cout << "Number of active cells: "
- << triangulation.n_active_cells()
- << " (on "
- << triangulation.n_levels()
- << " levels)"
- << std::endl
- << "Number of degrees of freedom: "
- << n_u + n_p + n_s
- << " (" << n_u << '+' << n_p << '+'<< n_s <<')'
- << std::endl
- << std::endl;
+ // @sect3{TwoPhaseFlowProblem<dim>::TwoPhaseFlowProblem}
+
+ // The constructor of this class is an
+ // extension of the constructor in step-21
+ // and step-31. We need to add the various
+ // variables that concern the saturation. As
+ // discussed in the introduction, we are
+ // going to use $Q_2 \times Q_1$
+ // (Taylor-Hood) elements again for the darcy
+ // system, which element combination fulfills
+ // the Ladyzhenskaya-Babuska-Brezzi (LBB)
+ // conditions
+ // [Brezzi and Fortin 1991, Chen 2005], and $Q_1$
+ // elements for the saturation. However, by
+ // using variables that store the polynomial
+ // degree of the darcy and temperature finite
+ // elements, it is easy to consistently
+ // modify the degree of the elements as well
+ // as all quadrature formulas used on them
+ // downstream. Moreover, we initialize the
+ // time stepping, variables related to
+ // operator splitting as well as the option
+ // for matrix assembly and preconditioning:
+ template <int dim>
+ TwoPhaseFlowProblem<dim>::TwoPhaseFlowProblem (const unsigned int degree)
+ :
+ degree (degree),
+ darcy_degree (degree),
+ darcy_fe (FE_Q<dim>(darcy_degree+1), dim,
+ FE_Q<dim>(darcy_degree), 1),
+ darcy_dof_handler (triangulation),
- {
- darcy_matrix.clear ();
+ saturation_degree (degree),
+ saturation_fe (saturation_degree),
+ saturation_dof_handler (triangulation),
- BlockCompressedSimpleSparsityPattern csp (2,2);
+ n_refinement_steps (4),
+ solve_pressure_velocity_part (false),
+ previous_solve_pressure_velocity_part (false),
- csp.block(0,0).reinit (n_u, n_u);
- csp.block(0,1).reinit (n_u, n_p);
- csp.block(1,0).reinit (n_p, n_u);
- csp.block(1,1).reinit (n_p, n_p);
+ saturation_level (2),
+ saturation_value (0.5),
- csp.collect_sizes ();
+ time_step (0),
+ old_time_step (0),
+ viscosity (0.2),
- Table<2,DoFTools::Coupling> coupling (dim+1, dim+1);
+ rebuild_saturation_matrix (true)
+ {}
- for (unsigned int c=0; c<dim+1; ++c)
- for (unsigned int d=0; d<dim+1; ++d)
- if (! ((c==dim) && (d==dim)))
- coupling[c][d] = DoFTools::always;
- else
- coupling[c][d] = DoFTools::none;
+ // @sect3{TwoPhaseFlowProblem<dim>::setup_dofs}
+
+ // This is the function that sets up the
+ // DoFHandler objects we have here (one for
+ // the darcy part and one for the saturation
+ // part) as well as set to the right sizes
+ // the various objects required for the
+ // linear algebra in this program. Its basic
+ // operations are similar to what authors in
+ // step-31 did.
+ //
+ // The body of the function first enumerates
+ // all degrees of freedom for the darcy and
+ // saturation systems. For the darcy part,
+ // degrees of freedom are then sorted to
+ // ensure that velocities precede pressure
+ // DoFs so that we can partition the darcy
+ // matrix into a $2 \times 2$ matrix. Like
+ // step-31, the present step does not perform
+ // any additional DoF renumbering.
+ //
+ // Then, we need to incorporate hanging node
+ // constraints and Dirichlet boundary value
+ // constraints into
+ // darcy_preconditioner_constraints. However,
+ // this constraints are only set to the
+ // pressure component since the Schur
+ // complement preconditioner that corresponds
+ // to the porous media flow operator in
+ // non-mixed form, $-\nabla \cdot [\mathbf K
+ // \lambda_t(S)]\nabla$. Therefore, we use a
+ // component_mask that filters out the
+ // velocity component, so that the
+ // condensation is performed on pressure
+ // degrees of freedom only.
+ //
+ // After having done so, we count the number
+ // of degrees of freedom in the various
+ // blocks:
+ //
+ // The next step is to create the sparsity
+ // pattern for the darcy and saturation
+ // system matrices as well as the
+ // preconditioner matrix from which we build
+ // the darcy preconditioner. As in step-31,
+ // we choose to create the pattern not as in
+ // the first few tutorial programs, but by
+ // using the blocked version of
+ // CompressedSimpleSparsityPattern. The
+ // reason for doing this is mainly memory,
+ // that is, the SparsityPattern class would
+ // consume too much memory when used in three
+ // spatial dimensions as we intend to do for
+ // this program. So, for this, we follow the
+ // same way as step-31 did and we don't have
+ // to repeat descriptions again for the rest
+ // of the member function.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::setup_dofs ()
+ {
+ std::vector<unsigned int> darcy_block_component (dim+1,0);
+ darcy_block_component[dim] = 1;
+ {
+ darcy_dof_handler.distribute_dofs (darcy_fe);
+ DoFRenumbering::Cuthill_McKee (darcy_dof_handler);
+ DoFRenumbering::component_wise (darcy_dof_handler, darcy_block_component);
- DoFTools::make_sparsity_pattern (darcy_dof_handler, coupling, csp,
- darcy_constraints, false);
+ darcy_constraints.clear ();
+ DoFTools::make_hanging_node_constraints (darcy_dof_handler, darcy_constraints);
+ darcy_constraints.close ();
+ }
+ {
+ saturation_dof_handler.distribute_dofs (saturation_fe);
- darcy_matrix.reinit (csp);
- }
+ saturation_constraints.clear ();
+ DoFTools::make_hanging_node_constraints (saturation_dof_handler, saturation_constraints);
+ saturation_constraints.close ();
+ }
+ {
+ darcy_preconditioner_constraints.clear ();
- {
- Amg_preconditioner.reset ();
- Mp_preconditioner.reset ();
- darcy_preconditioner_matrix.clear ();
+ std::vector<bool> component_mask (dim+1, false);
+ component_mask[dim] = true;
- BlockCompressedSimpleSparsityPattern csp (2,2);
- csp.block(0,0).reinit (n_u, n_u);
- csp.block(0,1).reinit (n_u, n_p);
- csp.block(1,0).reinit (n_p, n_u);
- csp.block(1,1).reinit (n_p, n_p);
+ DoFTools::make_hanging_node_constraints (darcy_dof_handler, darcy_preconditioner_constraints);
+ DoFTools::make_zero_boundary_constraints (darcy_dof_handler, darcy_preconditioner_constraints, component_mask);
- csp.collect_sizes ();
+ darcy_preconditioner_constraints.close ();
+ }
- Table<2,DoFTools::Coupling> coupling (dim+1, dim+1);
- for (unsigned int c=0; c<dim+1; ++c)
- for (unsigned int d=0; d<dim+1; ++d)
- if (c == d)
- coupling[c][d] = DoFTools::always;
- else
- coupling[c][d] = DoFTools::none;
- DoFTools::make_sparsity_pattern (darcy_dof_handler, coupling, csp,
- darcy_constraints, false);
+ std::vector<unsigned int> darcy_dofs_per_block (2);
+ DoFTools::count_dofs_per_block (darcy_dof_handler, darcy_dofs_per_block, darcy_block_component);
+ const unsigned int n_u = darcy_dofs_per_block[0],
+ n_p = darcy_dofs_per_block[1],
+ n_s = saturation_dof_handler.n_dofs();
+
+ std::cout << "Number of active cells: "
+ << triangulation.n_active_cells()
+ << " (on "
+ << triangulation.n_levels()
+ << " levels)"
+ << std::endl
+ << "Number of degrees of freedom: "
+ << n_u + n_p + n_s
+ << " (" << n_u << '+' << n_p << '+'<< n_s <<')'
+ << std::endl
+ << std::endl;
- darcy_preconditioner_matrix.reinit (csp);
- }
+ {
+ darcy_matrix.clear ();
+ BlockCompressedSimpleSparsityPattern csp (2,2);
- {
- saturation_matrix.clear ();
+ csp.block(0,0).reinit (n_u, n_u);
+ csp.block(0,1).reinit (n_u, n_p);
+ csp.block(1,0).reinit (n_p, n_u);
+ csp.block(1,1).reinit (n_p, n_p);
- CompressedSimpleSparsityPattern csp (n_s, n_s);
+ csp.collect_sizes ();
- DoFTools::make_sparsity_pattern (saturation_dof_handler, csp,
- saturation_constraints, false);
+ Table<2,DoFTools::Coupling> coupling (dim+1, dim+1);
+ for (unsigned int c=0; c<dim+1; ++c)
+ for (unsigned int d=0; d<dim+1; ++d)
+ if (! ((c==dim) && (d==dim)))
+ coupling[c][d] = DoFTools::always;
+ else
+ coupling[c][d] = DoFTools::none;
- saturation_matrix.reinit (csp);
- }
- darcy_solution.reinit (2);
- darcy_solution.block(0).reinit (n_u);
- darcy_solution.block(1).reinit (n_p);
- darcy_solution.collect_sizes ();
+ DoFTools::make_sparsity_pattern (darcy_dof_handler, coupling, csp,
+ darcy_constraints, false);
- nth_darcy_solution_after_solving_pressure_part.reinit (2);
- nth_darcy_solution_after_solving_pressure_part.block(0).reinit (n_u);
- nth_darcy_solution_after_solving_pressure_part.block(1).reinit (n_p);
- nth_darcy_solution_after_solving_pressure_part.collect_sizes ();
+ darcy_matrix.reinit (csp);
+ }
- n_minus_oneth_darcy_solution_after_solving_pressure_part.reinit (2);
- n_minus_oneth_darcy_solution_after_solving_pressure_part.block(0).reinit (n_u);
- n_minus_oneth_darcy_solution_after_solving_pressure_part.block(1).reinit (n_p);
- n_minus_oneth_darcy_solution_after_solving_pressure_part.collect_sizes ();
+ {
+ Amg_preconditioner.reset ();
+ Mp_preconditioner.reset ();
+ darcy_preconditioner_matrix.clear ();
- darcy_rhs.reinit (2);
- darcy_rhs.block(0).reinit (n_u);
- darcy_rhs.block(1).reinit (n_p);
- darcy_rhs.collect_sizes ();
+ BlockCompressedSimpleSparsityPattern csp (2,2);
- predictor_saturation_solution.reinit (n_s);
- saturation_solution.reinit (n_s);
- old_saturation_solution.reinit (n_s);
- old_old_saturation_solution.reinit (n_s);
+ csp.block(0,0).reinit (n_u, n_u);
+ csp.block(0,1).reinit (n_u, n_p);
+ csp.block(1,0).reinit (n_p, n_u);
+ csp.block(1,1).reinit (n_p, n_p);
- nth_saturation_solution_after_solving_pressure_part.reinit (n_s);
+ csp.collect_sizes ();
- saturation_rhs.reinit (n_s);
-}
+ Table<2,DoFTools::Coupling> coupling (dim+1, dim+1);
+ for (unsigned int c=0; c<dim+1; ++c)
+ for (unsigned int d=0; d<dim+1; ++d)
+ if (c == d)
+ coupling[c][d] = DoFTools::always;
+ else
+ coupling[c][d] = DoFTools::none;
+ DoFTools::make_sparsity_pattern (darcy_dof_handler, coupling, csp,
+ darcy_constraints, false);
+
+ darcy_preconditioner_matrix.reinit (csp);
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_darcy_preconditioner}
-
- // This function assembles the matrix we use
- // for preconditioning the darcy system. What
- // we need are a vector matrix weighted by
- // $\left(\mathbf{K} \lambda_t\right)^{-1}$
- // on the velocity components and a mass
- // matrix weighted by $\left(\mathbf{K}
- // \lambda_t\right)$ on the pressure
- // component. We start by generating a
- // quadrature object of appropriate order,
- // the FEValues object that can give values
- // and gradients at the quadrature points
- // (together with quadrature weights). Next
- // we create data structures for the cell
- // matrix and the relation between local and
- // global DoFs. The vectors phi_u and
- // grad_phi_p are going to hold the values of
- // the basis functions in order to faster
- // build up the local matrices, as was
- // already done in step-22. Before we start
- // the loop over all active cells, we have to
- // specify which components are pressure and
- // which are velocity.
- //
- // The creation of the local matrix is rather
- // simple. There are only a term weighted by
- // $\left(\mathbf{K} \lambda_t\right)^{-1}$
- // (on the velocity) and a mass matrix
- // weighted by $\left(\mathbf{K}
- // \lambda_t\right)$ to be generated, so the
- // creation of the local matrix is done in
- // two lines. Once the local matrix is ready
- // (loop over rows and columns in the local
- // matrix on each quadrature point), we get
- // the local DoF indices and write the local
- // information into the global matrix. We do
- // this by directly applying the constraints
- // (i.e. darcy_preconditioner_constraints)
- // from hanging nodes locally and Dirichlet
- // boundary conditions with zero values. By
- // doing so, we don't have to do that
- // afterwards, and we don't also write into
- // entries of the matrix that will actually
- // be set to zero again later when
- // eliminating constraints.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::assemble_darcy_preconditioner ()
-{
- std::cout << " Rebuilding darcy preconditioner..." << std::endl;
-
- darcy_preconditioner_matrix = 0;
-
- const QGauss<dim> quadrature_formula(darcy_degree+2);
- FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
- update_JxW_values |
- update_values |
- update_gradients |
- update_quadrature_points);
- FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
- update_values);
-
- const unsigned int dofs_per_cell = darcy_fe.dofs_per_cell;
- const unsigned int n_q_points = quadrature_formula.size();
-
- const RandomMedium::KInverse<dim> k_inverse;
-// const SingleCurvingCrack::KInverse<dim> k_inverse;
- std::vector<Tensor<2,dim> > k_inverse_values (n_q_points);
- Tensor<2,dim> k_value;
-
- std::vector<double> old_saturation_values (n_q_points);
-
- FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
-
- std::vector<Tensor<1,dim> > phi_u (dofs_per_cell);
- std::vector<Tensor<1,dim> > grad_phi_p (dofs_per_cell);
-
- const FEValuesExtractors::Vector velocities (0);
- const FEValuesExtractors::Scalar pressure (dim);
-
- typename DoFHandler<dim>::active_cell_iterator
- cell = darcy_dof_handler.begin_active(),
- endc = darcy_dof_handler.end();
- typename DoFHandler<dim>::active_cell_iterator
- saturation_cell = saturation_dof_handler.begin_active();
-
- for (; cell!=endc; ++cell, ++saturation_cell)
{
- darcy_fe_values.reinit (cell);
- saturation_fe_values.reinit (saturation_cell);
-
- local_matrix = 0;
-
- saturation_fe_values.get_function_values (old_saturation_solution, old_saturation_values);
-
- k_inverse.value_list (darcy_fe_values.get_quadrature_points(),
- k_inverse_values);
-
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- const double old_s = old_saturation_values[q];
- const double mobility = 1.0 / mobility_inverse(old_s,viscosity);
-
- k_value.clear ();
- for (unsigned int d=0; d<dim; d++)
- k_value[d][d] = 1.0 / k_inverse_values[q][d][d];
-
- for (unsigned int k=0; k<dofs_per_cell; ++k)
- {
- phi_u[k] = darcy_fe_values[velocities].value (k,q);
- grad_phi_p[k] = darcy_fe_values[pressure].gradient (k,q);
- }
-
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- {
- local_matrix(i,j) += (k_inverse_values[q] * mobility_inverse(old_s,viscosity) *
- phi_u[i] * phi_u[j]
- +
- k_value * mobility *
- grad_phi_p[i] * grad_phi_p[j])
- * darcy_fe_values.JxW(q);
- }
- }
-
- cell->get_dof_indices (local_dof_indices);
- darcy_preconditioner_constraints.distribute_local_to_global (local_matrix,
- local_dof_indices,
- darcy_preconditioner_matrix);
- }
-}
+ saturation_matrix.clear ();
+ CompressedSimpleSparsityPattern csp (n_s, n_s);
- // @sect3{TwoPhaseFlowProblem<dim>::build_darcy_preconditioner}
-
- // This function generates the inner
- // preconditioners that are going to be used
- // for the Schur complement block
- // preconditioner. The preconditioners need
- // to be regenerated at every saturation time
- // step since they contain the independent
- // variables saturation $S$ with time.
- //
- // Next, we set up the preconditioner for the
- // velocity-velocity matrix
- // $\mathbf{M}^{\mathbf{u}}$ and the Schur
- // complement $\mathbf{S}$. As explained in
- // the introduction, we are going to use an
- // IC preconditioner based on a vector matrix
- // (which is spectrally close to the darcy
- // matrix $\mathbf{M}^{\mathbf{u}}$) and
- // another based on a Laplace vector matrix
- // (which is spectrally close to the
- // non-mixed pressure matrix
- // $\mathbf{S}$). Usually, the
- // TrilinosWrappers::PreconditionIC class can
- // be seen as a good black-box preconditioner
- // which does not need any special knowledge.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::build_darcy_preconditioner ()
-{
- assemble_darcy_preconditioner ();
+ DoFTools::make_sparsity_pattern (saturation_dof_handler, csp,
+ saturation_constraints, false);
- Amg_preconditioner = std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC>
- (new TrilinosWrappers::PreconditionIC());
- Amg_preconditioner->initialize(darcy_preconditioner_matrix.block(0,0));
- Mp_preconditioner = std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC>
- (new TrilinosWrappers::PreconditionIC());
- Mp_preconditioner->initialize(darcy_preconditioner_matrix.block(1,1));
+ saturation_matrix.reinit (csp);
+ }
-}
+ darcy_solution.reinit (2);
+ darcy_solution.block(0).reinit (n_u);
+ darcy_solution.block(1).reinit (n_p);
+ darcy_solution.collect_sizes ();
+ nth_darcy_solution_after_solving_pressure_part.reinit (2);
+ nth_darcy_solution_after_solving_pressure_part.block(0).reinit (n_u);
+ nth_darcy_solution_after_solving_pressure_part.block(1).reinit (n_p);
+ nth_darcy_solution_after_solving_pressure_part.collect_sizes ();
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_darcy_system}
-
- // This is the function that assembles the
- // linear system for the darcy system.
- //
- // Regarding the technical details of
- // implementation, the procedures are similar
- // to those in step-22 and step-31 we reset
- // matrix and vector, create a quadrature
- // formula on the cells, and then create the
- // respective FEValues object. For the update
- // flags, we require basis function
- // derivatives only in case of a full
- // assembly, since they are not needed for
- // the right hand side; as always, choosing
- // the minimal set of flags depending on what
- // is currently needed makes the call to
- // FEValues::reinit further down in the
- // program more efficient.
- //
- // There is one thing that needs to be
- // commented ¡V since we have a separate
- // finite element and DoFHandler for the
- // saturation, we need to generate a second
- // FEValues object for the proper evaluation
- // of the saturation solution. This isn't too
- // complicated to realize here: just use the
- // saturation structures and set an update
- // flag for the basis function values which
- // we need for evaluation of the saturation
- // solution. The only important part to
- // remember here is that the same quadrature
- // formula is used for both FEValues objects
- // to ensure that we get matching information
- // when we loop over the quadrature points of
- // the two objects.
- //
- // The declarations proceed with some
- // shortcuts for array sizes, the creation of
- // the local matrix, right hand side as well
- // as the vector for the indices of the local
- // dofs compared to the global system.
- //
- // Note that in its present form, the
- // function uses the permeability implemented
- // in the RandomMedium::KInverse
- // class. Switching to the single curved
- // crack permeability function is as simple
- // as just changing the namespace name.
- //
- // Here's the an important step: we have to
- // get the values of the saturation function
- // of the previous time step at the
- // quadrature points. To this end, we can use
- // the FEValues::get_function_values
- // (previously already used in step-9,
- // step-14 and step-15), a function that
- // takes a solution vector and returns a list
- // of function values at the quadrature
- // points of the present cell. In fact, it
- // returns the complete vector-valued
- // solution at each quadrature point,
- // i.e. not only the saturation but also the
- // velocities and pressure:
- //
- // Next we need a vector that will contain
- // the values of the saturation solution at
- // the previous time level at the quadrature
- // points to assemble the source term in the
- // right hand side of the momentum
- // equation. Let's call this vector
- // old_saturation_values.
- //
- // The set of vectors we create next hold the
- // evaluations of the basis functions as well
- // as their gradients and symmetrized
- // gradients that will be used for creating
- // the matrices. Putting these into their own
- // arrays rather than asking the FEValues
- // object for this information each time it
- // is needed is an optimization to accelerate
- // the assembly process, see step-22 for
- // details.
- //
- // The last two declarations are used to
- // extract the individual blocks (velocity,
- // pressure, saturation) from the total FE
- // system.
- //
- // Now start the loop over all cells in the
- // problem. We are working on two different
- // DoFHandlers for this assembly routine, so
- // we must have two different cell iterators
- // for the two objects in use. This might
- // seem a bit peculiar, since both the darcy
- // system and the saturation system use the
- // same grid, but that's the only way to keep
- // degrees of freedom in sync. The first
- // statements within the loop are again all
- // very familiar, doing the update of the
- // finite element data as specified by the
- // update flags, zeroing out the local arrays
- // and getting the values of the old solution
- // at the quadrature points. Then we are
- // ready to loop over the quadrature points
- // on the cell.
- //
- // Once this is done, we start the loop over
- // the rows and columns of the local matrix
- // and feed the matrix with the relevant
- // products.
- //
- // The last step in the loop over all cells
- // is to enter the local contributions into
- // the global matrix and vector structures to
- // the positions specified in
- // local_dof_indices. Again, we let the
- // ConstraintMatrix class do the insertion of
- // the cell matrix elements to the global
- // matrix, which already condenses the
- // hanging node constraints.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::assemble_darcy_system ()
-{
- darcy_matrix = 0;
- darcy_rhs = 0;
+ n_minus_oneth_darcy_solution_after_solving_pressure_part.reinit (2);
+ n_minus_oneth_darcy_solution_after_solving_pressure_part.block(0).reinit (n_u);
+ n_minus_oneth_darcy_solution_after_solving_pressure_part.block(1).reinit (n_p);
+ n_minus_oneth_darcy_solution_after_solving_pressure_part.collect_sizes ();
- QGauss<dim> quadrature_formula(darcy_degree+2);
- QGauss<dim-1> face_quadrature_formula(darcy_degree+2);
+ darcy_rhs.reinit (2);
+ darcy_rhs.block(0).reinit (n_u);
+ darcy_rhs.block(1).reinit (n_p);
+ darcy_rhs.collect_sizes ();
- FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
- update_values | update_gradients |
- update_quadrature_points | update_JxW_values);
+ predictor_saturation_solution.reinit (n_s);
+ saturation_solution.reinit (n_s);
+ old_saturation_solution.reinit (n_s);
+ old_old_saturation_solution.reinit (n_s);
- FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
- update_values);
+ nth_saturation_solution_after_solving_pressure_part.reinit (n_s);
+
+ saturation_rhs.reinit (n_s);
+ }
- FEFaceValues<dim> darcy_fe_face_values (darcy_fe, face_quadrature_formula,
- update_values | update_normal_vectors |
- update_quadrature_points | update_JxW_values);
- const unsigned int dofs_per_cell = darcy_fe.dofs_per_cell;
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_darcy_preconditioner}
+
+ // This function assembles the matrix we use
+ // for preconditioning the darcy system. What
+ // we need are a vector matrix weighted by
+ // $\left(\mathbf{K} \lambda_t\right)^{-1}$
+ // on the velocity components and a mass
+ // matrix weighted by $\left(\mathbf{K}
+ // \lambda_t\right)$ on the pressure
+ // component. We start by generating a
+ // quadrature object of appropriate order,
+ // the FEValues object that can give values
+ // and gradients at the quadrature points
+ // (together with quadrature weights). Next
+ // we create data structures for the cell
+ // matrix and the relation between local and
+ // global DoFs. The vectors phi_u and
+ // grad_phi_p are going to hold the values of
+ // the basis functions in order to faster
+ // build up the local matrices, as was
+ // already done in step-22. Before we start
+ // the loop over all active cells, we have to
+ // specify which components are pressure and
+ // which are velocity.
+ //
+ // The creation of the local matrix is rather
+ // simple. There are only a term weighted by
+ // $\left(\mathbf{K} \lambda_t\right)^{-1}$
+ // (on the velocity) and a mass matrix
+ // weighted by $\left(\mathbf{K}
+ // \lambda_t\right)$ to be generated, so the
+ // creation of the local matrix is done in
+ // two lines. Once the local matrix is ready
+ // (loop over rows and columns in the local
+ // matrix on each quadrature point), we get
+ // the local DoF indices and write the local
+ // information into the global matrix. We do
+ // this by directly applying the constraints
+ // (i.e. darcy_preconditioner_constraints)
+ // from hanging nodes locally and Dirichlet
+ // boundary conditions with zero values. By
+ // doing so, we don't have to do that
+ // afterwards, and we don't also write into
+ // entries of the matrix that will actually
+ // be set to zero again later when
+ // eliminating constraints.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::assemble_darcy_preconditioner ()
+ {
+ std::cout << " Rebuilding darcy preconditioner..." << std::endl;
- const unsigned int n_q_points = quadrature_formula.size();
- const unsigned int n_face_q_points = face_quadrature_formula.size();
+ darcy_preconditioner_matrix = 0;
- FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
- Vector<double> local_rhs (dofs_per_cell);
+ const QGauss<dim> quadrature_formula(darcy_degree+2);
+ FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
+ update_JxW_values |
+ update_values |
+ update_gradients |
+ update_quadrature_points);
+ FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
+ update_values);
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+ const unsigned int dofs_per_cell = darcy_fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
- const PressureRightHandSide<dim> pressure_right_hand_side;
- const PressureBoundaryValues<dim> pressure_boundary_values;
- const RandomMedium::KInverse<dim> k_inverse;
+ const RandomMedium::KInverse<dim> k_inverse;
// const SingleCurvingCrack::KInverse<dim> k_inverse;
- std::vector<double> pressure_rhs_values (n_q_points);
- std::vector<double> boundary_values (n_face_q_points);
- std::vector<Tensor<2,dim> > k_inverse_values (n_q_points);
+ std::vector<Tensor<2,dim> > k_inverse_values (n_q_points);
+ Tensor<2,dim> k_value;
- std::vector<double> old_saturation_values (n_q_points);
+ std::vector<double> old_saturation_values (n_q_points);
- std::vector<Tensor<1,dim> > phi_u (dofs_per_cell);
- std::vector<double> div_phi_u (dofs_per_cell);
- std::vector<double> phi_p (dofs_per_cell);
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- const FEValuesExtractors::Vector velocities (0);
- const FEValuesExtractors::Scalar pressure (dim);
+ std::vector<Tensor<1,dim> > phi_u (dofs_per_cell);
+ std::vector<Tensor<1,dim> > grad_phi_p (dofs_per_cell);
- typename DoFHandler<dim>::active_cell_iterator
- cell = darcy_dof_handler.begin_active(),
- endc = darcy_dof_handler.end();
- typename DoFHandler<dim>::active_cell_iterator
- saturation_cell = saturation_dof_handler.begin_active();
+ const FEValuesExtractors::Vector velocities (0);
+ const FEValuesExtractors::Scalar pressure (dim);
- for (; cell!=endc; ++cell, ++saturation_cell)
- {
- darcy_fe_values.reinit (cell);
- saturation_fe_values.reinit (saturation_cell);
-
- local_matrix = 0;
- local_rhs = 0;
-
- saturation_fe_values.get_function_values (old_saturation_solution, old_saturation_values);
-
- pressure_right_hand_side.value_list (darcy_fe_values.get_quadrature_points(),
- pressure_rhs_values);
- k_inverse.value_list (darcy_fe_values.get_quadrature_points(),
- k_inverse_values);
-
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- for (unsigned int k=0; k<dofs_per_cell; ++k)
- {
- phi_u[k] = darcy_fe_values[velocities].value (k,q);
- div_phi_u[k] = darcy_fe_values[velocities].divergence (k,q);
- phi_p[k] = darcy_fe_values[pressure].value (k,q);
- }
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- {
- const double old_s = old_saturation_values[q];
- for (unsigned int j=0; j<=i; ++j)
- {
- local_matrix(i,j) += (phi_u[i] * k_inverse_values[q] *
- mobility_inverse(old_s,viscosity) * phi_u[j]
- - div_phi_u[i] * phi_p[j]
- - phi_p[i] * div_phi_u[j])
- * darcy_fe_values.JxW(q);
- }
-
- local_rhs(i) += (-phi_p[i] * pressure_rhs_values[q])*
- darcy_fe_values.JxW(q);
- }
- }
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = darcy_dof_handler.begin_active(),
+ endc = darcy_dof_handler.end();
+ typename DoFHandler<dim>::active_cell_iterator
+ saturation_cell = saturation_dof_handler.begin_active();
- for (unsigned int face_no=0;
- face_no<GeometryInfo<dim>::faces_per_cell;
- ++face_no)
- if (cell->at_boundary(face_no))
- {
- darcy_fe_face_values.reinit (cell, face_no);
-
- pressure_boundary_values
- .value_list (darcy_fe_face_values.get_quadrature_points(),
- boundary_values);
-
- for (unsigned int q=0; q<n_face_q_points; ++q)
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- {
- const Tensor<1,dim>
- phi_i_u = darcy_fe_face_values[velocities].value (i, q);
-
- local_rhs(i) += -(phi_i_u *
- darcy_fe_face_values.normal_vector(q) *
- boundary_values[q] *
- darcy_fe_face_values.JxW(q));
- }
- }
+ for (; cell!=endc; ++cell, ++saturation_cell)
+ {
+ darcy_fe_values.reinit (cell);
+ saturation_fe_values.reinit (saturation_cell);
+
+ local_matrix = 0;
+
+ saturation_fe_values.get_function_values (old_saturation_solution, old_saturation_values);
+
+ k_inverse.value_list (darcy_fe_values.get_quadrature_points(),
+ k_inverse_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ const double old_s = old_saturation_values[q];
+ const double mobility = 1.0 / mobility_inverse(old_s,viscosity);
+
+ k_value.clear ();
+ for (unsigned int d=0; d<dim; d++)
+ k_value[d][d] = 1.0 / k_inverse_values[q][d][d];
+
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ phi_u[k] = darcy_fe_values[velocities].value (k,q);
+ grad_phi_p[k] = darcy_fe_values[pressure].gradient (k,q);
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ {
+ local_matrix(i,j) += (k_inverse_values[q] * mobility_inverse(old_s,viscosity) *
+ phi_u[i] * phi_u[j]
+ +
+ k_value * mobility *
+ grad_phi_p[i] * grad_phi_p[j])
+ * darcy_fe_values.JxW(q);
+ }
+ }
+
+ cell->get_dof_indices (local_dof_indices);
+ darcy_preconditioner_constraints.distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ darcy_preconditioner_matrix);
+ }
+ }
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=i+1; j<dofs_per_cell; ++j)
- local_matrix(i,j) = local_matrix(j,i);
- cell->get_dof_indices (local_dof_indices);
+ // @sect3{TwoPhaseFlowProblem<dim>::build_darcy_preconditioner}
+
+ // This function generates the inner
+ // preconditioners that are going to be used
+ // for the Schur complement block
+ // preconditioner. The preconditioners need
+ // to be regenerated at every saturation time
+ // step since they contain the independent
+ // variables saturation $S$ with time.
+ //
+ // Next, we set up the preconditioner for the
+ // velocity-velocity matrix
+ // $\mathbf{M}^{\mathbf{u}}$ and the Schur
+ // complement $\mathbf{S}$. As explained in
+ // the introduction, we are going to use an
+ // IC preconditioner based on a vector matrix
+ // (which is spectrally close to the darcy
+ // matrix $\mathbf{M}^{\mathbf{u}}$) and
+ // another based on a Laplace vector matrix
+ // (which is spectrally close to the
+ // non-mixed pressure matrix
+ // $\mathbf{S}$). Usually, the
+ // TrilinosWrappers::PreconditionIC class can
+ // be seen as a good black-box preconditioner
+ // which does not need any special knowledge.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::build_darcy_preconditioner ()
+ {
+ assemble_darcy_preconditioner ();
- darcy_constraints.distribute_local_to_global (local_matrix,
- local_rhs,
- local_dof_indices,
- darcy_matrix,
- darcy_rhs);
+ Amg_preconditioner = std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC>
+ (new TrilinosWrappers::PreconditionIC());
+ Amg_preconditioner->initialize(darcy_preconditioner_matrix.block(0,0));
- }
-}
+ Mp_preconditioner = std_cxx1x::shared_ptr<TrilinosWrappers::PreconditionIC>
+ (new TrilinosWrappers::PreconditionIC());
+ Mp_preconditioner->initialize(darcy_preconditioner_matrix.block(1,1));
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_system}
-
- // This function is to assemble the linear
- // system for the saturation transport
- // equation. It includes two member
- // functions: assemble_saturation_matrix ()
- // and assemble_saturation_rhs (). The former
- // function that assembles the saturation
- // left hand side needs to be changed only
- // when grids have been changed since the
- // matrix is filled only with basis
- // functions. However, the latter that
- // assembles the right hand side must be
- // changed at every saturation time step
- // since it depends on an unknown variable
- // saturation.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::assemble_saturation_system ()
-{
- if ( rebuild_saturation_matrix == true )
- {
- saturation_matrix = 0;
- assemble_saturation_matrix ();
- }
- saturation_rhs = 0;
- assemble_saturation_rhs ();
-}
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_darcy_system}
+
+ // This is the function that assembles the
+ // linear system for the darcy system.
+ //
+ // Regarding the technical details of
+ // implementation, the procedures are similar
+ // to those in step-22 and step-31 we reset
+ // matrix and vector, create a quadrature
+ // formula on the cells, and then create the
+ // respective FEValues object. For the update
+ // flags, we require basis function
+ // derivatives only in case of a full
+ // assembly, since they are not needed for
+ // the right hand side; as always, choosing
+ // the minimal set of flags depending on what
+ // is currently needed makes the call to
+ // FEValues::reinit further down in the
+ // program more efficient.
+ //
+ // There is one thing that needs to be
+ // commented ¡V since we have a separate
+ // finite element and DoFHandler for the
+ // saturation, we need to generate a second
+ // FEValues object for the proper evaluation
+ // of the saturation solution. This isn't too
+ // complicated to realize here: just use the
+ // saturation structures and set an update
+ // flag for the basis function values which
+ // we need for evaluation of the saturation
+ // solution. The only important part to
+ // remember here is that the same quadrature
+ // formula is used for both FEValues objects
+ // to ensure that we get matching information
+ // when we loop over the quadrature points of
+ // the two objects.
+ //
+ // The declarations proceed with some
+ // shortcuts for array sizes, the creation of
+ // the local matrix, right hand side as well
+ // as the vector for the indices of the local
+ // dofs compared to the global system.
+ //
+ // Note that in its present form, the
+ // function uses the permeability implemented
+ // in the RandomMedium::KInverse
+ // class. Switching to the single curved
+ // crack permeability function is as simple
+ // as just changing the namespace name.
+ //
+ // Here's the an important step: we have to
+ // get the values of the saturation function
+ // of the previous time step at the
+ // quadrature points. To this end, we can use
+ // the FEValues::get_function_values
+ // (previously already used in step-9,
+ // step-14 and step-15), a function that
+ // takes a solution vector and returns a list
+ // of function values at the quadrature
+ // points of the present cell. In fact, it
+ // returns the complete vector-valued
+ // solution at each quadrature point,
+ // i.e. not only the saturation but also the
+ // velocities and pressure:
+ //
+ // Next we need a vector that will contain
+ // the values of the saturation solution at
+ // the previous time level at the quadrature
+ // points to assemble the source term in the
+ // right hand side of the momentum
+ // equation. Let's call this vector
+ // old_saturation_values.
+ //
+ // The set of vectors we create next hold the
+ // evaluations of the basis functions as well
+ // as their gradients and symmetrized
+ // gradients that will be used for creating
+ // the matrices. Putting these into their own
+ // arrays rather than asking the FEValues
+ // object for this information each time it
+ // is needed is an optimization to accelerate
+ // the assembly process, see step-22 for
+ // details.
+ //
+ // The last two declarations are used to
+ // extract the individual blocks (velocity,
+ // pressure, saturation) from the total FE
+ // system.
+ //
+ // Now start the loop over all cells in the
+ // problem. We are working on two different
+ // DoFHandlers for this assembly routine, so
+ // we must have two different cell iterators
+ // for the two objects in use. This might
+ // seem a bit peculiar, since both the darcy
+ // system and the saturation system use the
+ // same grid, but that's the only way to keep
+ // degrees of freedom in sync. The first
+ // statements within the loop are again all
+ // very familiar, doing the update of the
+ // finite element data as specified by the
+ // update flags, zeroing out the local arrays
+ // and getting the values of the old solution
+ // at the quadrature points. Then we are
+ // ready to loop over the quadrature points
+ // on the cell.
+ //
+ // Once this is done, we start the loop over
+ // the rows and columns of the local matrix
+ // and feed the matrix with the relevant
+ // products.
+ //
+ // The last step in the loop over all cells
+ // is to enter the local contributions into
+ // the global matrix and vector structures to
+ // the positions specified in
+ // local_dof_indices. Again, we let the
+ // ConstraintMatrix class do the insertion of
+ // the cell matrix elements to the global
+ // matrix, which already condenses the
+ // hanging node constraints.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::assemble_darcy_system ()
+ {
+ darcy_matrix = 0;
+ darcy_rhs = 0;
+ QGauss<dim> quadrature_formula(darcy_degree+2);
+ QGauss<dim-1> face_quadrature_formula(darcy_degree+2);
+ FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
+ update_values | update_gradients |
+ update_quadrature_points | update_JxW_values);
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_matrix}
+ FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
+ update_values);
- // This function is easily understood since
- // it only forms a simple mass matrix for the
- // left hand side of the saturation linear
- // system by basis functions phi_i_s and
- // phi_j_s only. Finally, as usual, we enter
- // the local contribution into the global
- // matrix by specifying the position in
- // local_dof_indices. This is done by letting
- // the ConstraintMatrix class do the
- // insertion of the cell matrix elements to
- // the global matrix, which already condenses
- // the hanging node constraints.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::assemble_saturation_matrix ()
-{
- QGauss<dim> quadrature_formula(saturation_degree+2);
+ FEFaceValues<dim> darcy_fe_face_values (darcy_fe, face_quadrature_formula,
+ update_values | update_normal_vectors |
+ update_quadrature_points | update_JxW_values);
- FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
- update_values | update_JxW_values);
+ const unsigned int dofs_per_cell = darcy_fe.dofs_per_cell;
- const unsigned int dofs_per_cell = saturation_fe.dofs_per_cell;
+ const unsigned int n_q_points = quadrature_formula.size();
+ const unsigned int n_face_q_points = face_quadrature_formula.size();
- const unsigned int n_q_points = quadrature_formula.size();
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+ Vector<double> local_rhs (dofs_per_cell);
- FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
- Vector<double> local_rhs (dofs_per_cell);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+ const PressureRightHandSide<dim> pressure_right_hand_side;
+ const PressureBoundaryValues<dim> pressure_boundary_values;
+ const RandomMedium::KInverse<dim> k_inverse;
+// const SingleCurvingCrack::KInverse<dim> k_inverse;
- typename DoFHandler<dim>::active_cell_iterator
- cell = saturation_dof_handler.begin_active(),
- endc = saturation_dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- saturation_fe_values.reinit (cell);
- local_matrix = 0;
- local_rhs = 0;
-
- for (unsigned int q=0; q<n_q_points; ++q)
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- {
- const double phi_i_s = saturation_fe_values.shape_value (i,q);
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- {
- const double phi_j_s = saturation_fe_values.shape_value (j,q);
- local_matrix(i,j) += phi_i_s * phi_j_s * saturation_fe_values.JxW(q);
- }
- }
- cell->get_dof_indices (local_dof_indices);
-
- saturation_constraints.distribute_local_to_global (local_matrix,
- local_dof_indices,
- saturation_matrix);
+ std::vector<double> pressure_rhs_values (n_q_points);
+ std::vector<double> boundary_values (n_face_q_points);
+ std::vector<Tensor<2,dim> > k_inverse_values (n_q_points);
- }
-}
+ std::vector<double> old_saturation_values (n_q_points);
+ std::vector<Tensor<1,dim> > phi_u (dofs_per_cell);
+ std::vector<double> div_phi_u (dofs_per_cell);
+ std::vector<double> phi_p (dofs_per_cell);
+ const FEValuesExtractors::Vector velocities (0);
+ const FEValuesExtractors::Scalar pressure (dim);
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_rhs}
-
- // This function is to assemble the right
- // hand side of the saturation transport
- // equation. Before assembling it, we have to
- // call two FEValues objects for the darcy
- // and saturation systems respectively and,
- // even more, two FEFaceValues objects for
- // the both systems because we have a
- // boundary integral term in the weak form of
- // saturation equation. For the FEFaceValues
- // object of the saturation system, we also
- // enter the normal vectors with an update
- // flag update_normal_vectors.
- //
- // Next, before looping over all the cells,
- // we have to compute some parameters
- // (e.g. global_u_infty, global_S_variasion,
- // and global_Omega_diameter) that the
- // artificial viscosity $\nu$ needs, which
- // desriptions have been appearing in
- // step-31.
- //
- // Next, we start to loop over all the
- // saturation and darcy cells to put the
- // local contributions into the global
- // vector. In this loop, in order to simplify
- // the implementation in this function, we
- // generate two more functions: one is
- // assemble_saturation_rhs_cell_term and the
- // other is
- // assemble_saturation_rhs_boundary_term,
- // which is contained in an inner boudary
- // loop. The former is to assemble the
- // integral cell term with neccessary
- // arguments and the latter is to assemble
- // the integral global boundary $\Omega$
- // terms. It should be noted that we achieve
- // the insertion of the cell or boundary
- // vector elements to the global vector in
- // the two functions rather than in this
- // present function by giving these two
- // functions with a common argument
- // local_dof_indices, and two arguments
- // saturation_fe_values darcy_fe_values for
- // assemble_saturation_rhs_cell_term and
- // another two arguments
- // saturation_fe_face_values
- // darcy_fe_face_values for
- // assemble_saturation_rhs_boundary_term.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::assemble_saturation_rhs ()
-{
- QGauss<dim> quadrature_formula(saturation_degree+2);
- QGauss<dim-1> face_quadrature_formula(saturation_degree+2);
-
- FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
- update_values | update_gradients |
- update_quadrature_points | update_JxW_values);
- FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
- update_values);
- FEFaceValues<dim> saturation_fe_face_values (saturation_fe, face_quadrature_formula,
- update_values | update_normal_vectors |
- update_quadrature_points | update_JxW_values);
- FEFaceValues<dim> darcy_fe_face_values (darcy_fe, face_quadrature_formula,
- update_values);
- FEFaceValues<dim> saturation_fe_face_values_neighbor (saturation_fe, face_quadrature_formula,
- update_values);
-
- const unsigned int dofs_per_cell = saturation_dof_handler.get_fe().dofs_per_cell;
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
-
- const double global_u_infty = get_maximal_velocity ();
- const std::pair<double,double>
- global_S_range = get_extrapolated_saturation_range ();
- const double global_S_variasion = global_S_range.second - global_S_range.first;
- const double global_Omega_diameter = GridTools::diameter (triangulation);
-
- typename DoFHandler<dim>::active_cell_iterator
- cell = saturation_dof_handler.begin_active(),
- endc = saturation_dof_handler.end();
- typename DoFHandler<dim>::active_cell_iterator
- darcy_cell = darcy_dof_handler.begin_active();
- for (; cell!=endc; ++cell, ++darcy_cell)
- {
- saturation_fe_values.reinit (cell);
- darcy_fe_values.reinit (darcy_cell);
-
- cell->get_dof_indices (local_dof_indices);
-
- assemble_saturation_rhs_cell_term(saturation_fe_values,
- darcy_fe_values,
- local_dof_indices,
- global_u_infty,
- global_S_variasion,
- global_Omega_diameter);
-
- for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell;
- ++face_no)
- {
-
- if (cell->at_boundary(face_no))
- {
- darcy_fe_face_values.reinit (darcy_cell, face_no);
- saturation_fe_face_values.reinit (cell, face_no);
- assemble_saturation_rhs_boundary_term (saturation_fe_face_values,
- darcy_fe_face_values,
- local_dof_indices);
- }
- }
- }
-}
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = darcy_dof_handler.begin_active(),
+ endc = darcy_dof_handler.end();
+ typename DoFHandler<dim>::active_cell_iterator
+ saturation_cell = saturation_dof_handler.begin_active();
+ for (; cell!=endc; ++cell, ++saturation_cell)
+ {
+ darcy_fe_values.reinit (cell);
+ saturation_fe_values.reinit (saturation_cell);
+
+ local_matrix = 0;
+ local_rhs = 0;
+
+ saturation_fe_values.get_function_values (old_saturation_solution, old_saturation_values);
+
+ pressure_right_hand_side.value_list (darcy_fe_values.get_quadrature_points(),
+ pressure_rhs_values);
+ k_inverse.value_list (darcy_fe_values.get_quadrature_points(),
+ k_inverse_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ for (unsigned int k=0; k<dofs_per_cell; ++k)
+ {
+ phi_u[k] = darcy_fe_values[velocities].value (k,q);
+ div_phi_u[k] = darcy_fe_values[velocities].divergence (k,q);
+ phi_p[k] = darcy_fe_values[pressure].value (k,q);
+ }
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ const double old_s = old_saturation_values[q];
+ for (unsigned int j=0; j<=i; ++j)
+ {
+ local_matrix(i,j) += (phi_u[i] * k_inverse_values[q] *
+ mobility_inverse(old_s,viscosity) * phi_u[j]
+ - div_phi_u[i] * phi_p[j]
+ - phi_p[i] * div_phi_u[j])
+ * darcy_fe_values.JxW(q);
+ }
+
+ local_rhs(i) += (-phi_p[i] * pressure_rhs_values[q])*
+ darcy_fe_values.JxW(q);
+ }
+ }
+
+ for (unsigned int face_no=0;
+ face_no<GeometryInfo<dim>::faces_per_cell;
+ ++face_no)
+ if (cell->at_boundary(face_no))
+ {
+ darcy_fe_face_values.reinit (cell, face_no);
+
+ pressure_boundary_values
+ .value_list (darcy_fe_face_values.get_quadrature_points(),
+ boundary_values);
+
+ for (unsigned int q=0; q<n_face_q_points; ++q)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ const Tensor<1,dim>
+ phi_i_u = darcy_fe_face_values[velocities].value (i, q);
+
+ local_rhs(i) += -(phi_i_u *
+ darcy_fe_face_values.normal_vector(q) *
+ boundary_values[q] *
+ darcy_fe_face_values.JxW(q));
+ }
+ }
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ for (unsigned int j=i+1; j<dofs_per_cell; ++j)
+ local_matrix(i,j) = local_matrix(j,i);
+
+ cell->get_dof_indices (local_dof_indices);
+
+ darcy_constraints.distribute_local_to_global (local_matrix,
+ local_rhs,
+ local_dof_indices,
+ darcy_matrix,
+ darcy_rhs);
+ }
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_rhs_cell_term}
-
- // In this function, we actually compute
- // every artificial viscosity for every
- // element. Then, with the artificial value,
- // we can finish assembling the saturation
- // right hand side cell integral
- // terms. Finally, we can pass the local
- // contributions on to the global vector with
- // the position specified in
- // local_dof_indices.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::
-assemble_saturation_rhs_cell_term (const FEValues<dim> &saturation_fe_values,
- const FEValues<dim> &darcy_fe_values,
- const std::vector<unsigned int> &local_dof_indices,
- const double global_u_infty,
- const double global_S_variation,
- const double global_Omega_diameter)
-{
- const unsigned int dofs_per_cell = saturation_fe_values.dofs_per_cell;
- const unsigned int n_q_points = saturation_fe_values.n_quadrature_points;
-
- Vector<double> local_rhs (dofs_per_cell);
-
- std::vector<double> old_saturation_solution_values(n_q_points);
- std::vector<double> old_old_saturation_solution_values(n_q_points);
- std::vector<Tensor<1,dim> > old_grad_saturation_solution_values(n_q_points);
- std::vector<Tensor<1,dim> > old_old_grad_saturation_solution_values(n_q_points);
- std::vector<Vector<double> > present_darcy_solution_values(n_q_points, Vector<double>(dim+1));
-
- saturation_fe_values.get_function_values (old_saturation_solution, old_saturation_solution_values);
- saturation_fe_values.get_function_values (old_old_saturation_solution, old_old_saturation_solution_values);
- saturation_fe_values.get_function_grads (old_saturation_solution, old_grad_saturation_solution_values);
- saturation_fe_values.get_function_grads (old_old_saturation_solution, old_old_grad_saturation_solution_values);
- darcy_fe_values.get_function_values (darcy_solution, present_darcy_solution_values);
-
- const double nu
- = compute_viscosity (old_saturation_solution_values,
- old_old_saturation_solution_values,
- old_grad_saturation_solution_values,
- old_old_grad_saturation_solution_values,
- present_darcy_solution_values,
- global_u_infty,
- global_S_variation,
- global_Omega_diameter,
- saturation_fe_values.get_cell()->diameter(),
- old_time_step,
- viscosity);
-
- for (unsigned int q=0; q<n_q_points; ++q)
- for (unsigned int i=0; i<dofs_per_cell; ++i)
+
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_system}
+
+ // This function is to assemble the linear
+ // system for the saturation transport
+ // equation. It includes two member
+ // functions: assemble_saturation_matrix ()
+ // and assemble_saturation_rhs (). The former
+ // function that assembles the saturation
+ // left hand side needs to be changed only
+ // when grids have been changed since the
+ // matrix is filled only with basis
+ // functions. However, the latter that
+ // assembles the right hand side must be
+ // changed at every saturation time step
+ // since it depends on an unknown variable
+ // saturation.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::assemble_saturation_system ()
+ {
+ if ( rebuild_saturation_matrix == true )
{
- const double old_s = old_saturation_solution_values[q];
- Tensor<1,dim> present_u;
- for (unsigned int d=0; d<dim; ++d)
- present_u[d] = present_darcy_solution_values[q](d);
-
- const double phi_i_s = saturation_fe_values.shape_value (i, q);
- const Tensor<1,dim> grad_phi_i_s = saturation_fe_values.shape_grad (i, q);
-
- local_rhs(i) += (time_step *
- f_saturation(old_s,viscosity) *
- present_u *
- grad_phi_i_s
- -
- time_step *
- nu *
- old_grad_saturation_solution_values[q] * grad_phi_i_s
- +
- old_s * phi_i_s)
- *
- saturation_fe_values.JxW(q);
+ saturation_matrix = 0;
+ assemble_saturation_matrix ();
}
- saturation_constraints.distribute_local_to_global (local_rhs,
- local_dof_indices,
- saturation_rhs);
-}
+ saturation_rhs = 0;
+ assemble_saturation_rhs ();
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_rhs_boundary_term}
-
- // In this function, we have to give
- // upwinding in the global boundary faces,
- // i.e. we impose the Dirichlet boundary
- // conditions only on inflow parts of global
- // boundary, which has been described in
- // step-21 so we refrain from giving more
- // descriptions about that.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::
-assemble_saturation_rhs_boundary_term (const FEFaceValues<dim> &saturation_fe_face_values,
- const FEFaceValues<dim> &darcy_fe_face_values,
- const std::vector<unsigned int> &local_dof_indices)
-{
- const unsigned int dofs_per_cell = saturation_fe_face_values.dofs_per_cell;
- const unsigned int n_face_q_points = saturation_fe_face_values.n_quadrature_points;
- Vector<double> local_rhs (dofs_per_cell);
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_matrix}
- std::vector<double> old_saturation_solution_values_face(n_face_q_points);
- std::vector<Vector<double> > present_darcy_solution_values_face(n_face_q_points, Vector<double>(dim+1));
- std::vector<double> neighbor_saturation (n_face_q_points);
+ // This function is easily understood since
+ // it only forms a simple mass matrix for the
+ // left hand side of the saturation linear
+ // system by basis functions phi_i_s and
+ // phi_j_s only. Finally, as usual, we enter
+ // the local contribution into the global
+ // matrix by specifying the position in
+ // local_dof_indices. This is done by letting
+ // the ConstraintMatrix class do the
+ // insertion of the cell matrix elements to
+ // the global matrix, which already condenses
+ // the hanging node constraints.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::assemble_saturation_matrix ()
+ {
+ QGauss<dim> quadrature_formula(saturation_degree+2);
- saturation_fe_face_values.get_function_values (old_saturation_solution, old_saturation_solution_values_face);
- darcy_fe_face_values.get_function_values (darcy_solution, present_darcy_solution_values_face);
+ FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
+ update_values | update_JxW_values);
- SaturationBoundaryValues<dim> saturation_boundary_values;
- saturation_boundary_values
- .value_list (saturation_fe_face_values.get_quadrature_points(),
- neighbor_saturation);
+ const unsigned int dofs_per_cell = saturation_fe.dofs_per_cell;
- for (unsigned int q=0; q<n_face_q_points; ++q)
- {
- Tensor<1,dim> present_u_face;
- for (unsigned int d=0; d<dim; ++d)
- present_u_face[d] = present_darcy_solution_values_face[q](d);
+ const unsigned int n_q_points = quadrature_formula.size();
- const double normal_flux = present_u_face *
- saturation_fe_face_values.normal_vector(q);
+ FullMatrix<double> local_matrix (dofs_per_cell, dofs_per_cell);
+ Vector<double> local_rhs (dofs_per_cell);
- const bool is_outflow_q_point = (normal_flux >= 0);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- local_rhs(i) -= time_step *
- normal_flux *
- f_saturation((is_outflow_q_point == true
- ?
- old_saturation_solution_values_face[q]
- :
- neighbor_saturation[q]),
- viscosity) *
- saturation_fe_face_values.shape_value (i,q) *
- saturation_fe_face_values.JxW(q);
- }
- saturation_constraints.distribute_local_to_global (local_rhs,
- local_dof_indices,
- saturation_rhs);
-}
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = saturation_dof_handler.begin_active(),
+ endc = saturation_dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ saturation_fe_values.reinit (cell);
+ local_matrix = 0;
+ local_rhs = 0;
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ const double phi_i_s = saturation_fe_values.shape_value (i,q);
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ {
+ const double phi_j_s = saturation_fe_values.shape_value (j,q);
+ local_matrix(i,j) += phi_i_s * phi_j_s * saturation_fe_values.JxW(q);
+ }
+ }
+ cell->get_dof_indices (local_dof_indices);
+
+ saturation_constraints.distribute_local_to_global (local_matrix,
+ local_dof_indices,
+ saturation_matrix);
+ }
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::solve}
-
- // This function is to implement the operator
- // splitting algorithm. At the beginning of
- // the implementation, we decide whther to
- // solve the pressure-velocity part by
- // running an a posteriori criterion, which
- // will be described in the following
- // function. If we get the bool variable true
- // from that function, we will solve the
- // pressure-velocity part for updated
- // velocity. Then, we use GMRES with the
- // Schur complement preconditioner to solve
- // this linear system, as is described in the
- // Introduction. After solving the velocity
- // and pressure, we need to keep the
- // solutions for linear extrapolations in the
- // future. It is noted that we always solve
- // the pressure-velocity part in the first
- // three micro time steps to ensure accuracy
- // at the beginning of computation, and to
- // provide starting data to linearly
- // extrapolate previously computed velocities
- // to the current time step.
- //
- // On the other hand, if we get a false
- // variable from the criterion, we will
- // directly use linear extrapolation to
- // compute the updated velocity for the
- // solution of saturation later.
- //
- // Next, like step-21, this program need to
- // compute the present time step.
- //
- // Next, we need to use two bool variables
- // solve_pressure_velocity_part and
- // previous_solve_pressure_velocity_part to
- // decide whether we stop or continue
- // cumulating the micro time steps for linear
- // extropolations in the next iteration. With
- // the reason, we need one variable
- // cumulative_nth_time_step for keeping the
- // present aggregated micro time steps and
- // anther one n_minus_oneth_time_step for
- // retaining the previous micro time steps.
- //
- // Finally, we start to calculate the
- // saturation part with the use of the
- // incomplete Cholesky decomposition for
- // preconditioning.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::solve ()
-{
- solve_pressure_velocity_part = determine_whether_to_solve_pressure_velocity_part ();
- if ( timestep_number <= 3 || solve_pressure_velocity_part == true )
- {
- std::cout << " Solving darcy system (pressure-velocity part)..." << std::endl;
- assemble_darcy_system ();
- build_darcy_preconditioner ();
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_rhs}
+
+ // This function is to assemble the right
+ // hand side of the saturation transport
+ // equation. Before assembling it, we have to
+ // call two FEValues objects for the darcy
+ // and saturation systems respectively and,
+ // even more, two FEFaceValues objects for
+ // the both systems because we have a
+ // boundary integral term in the weak form of
+ // saturation equation. For the FEFaceValues
+ // object of the saturation system, we also
+ // enter the normal vectors with an update
+ // flag update_normal_vectors.
+ //
+ // Next, before looping over all the cells,
+ // we have to compute some parameters
+ // (e.g. global_u_infty, global_S_variasion,
+ // and global_Omega_diameter) that the
+ // artificial viscosity $\nu$ needs, which
+ // desriptions have been appearing in
+ // step-31.
+ //
+ // Next, we start to loop over all the
+ // saturation and darcy cells to put the
+ // local contributions into the global
+ // vector. In this loop, in order to simplify
+ // the implementation in this function, we
+ // generate two more functions: one is
+ // assemble_saturation_rhs_cell_term and the
+ // other is
+ // assemble_saturation_rhs_boundary_term,
+ // which is contained in an inner boudary
+ // loop. The former is to assemble the
+ // integral cell term with neccessary
+ // arguments and the latter is to assemble
+ // the integral global boundary $\Omega$
+ // terms. It should be noted that we achieve
+ // the insertion of the cell or boundary
+ // vector elements to the global vector in
+ // the two functions rather than in this
+ // present function by giving these two
+ // functions with a common argument
+ // local_dof_indices, and two arguments
+ // saturation_fe_values darcy_fe_values for
+ // assemble_saturation_rhs_cell_term and
+ // another two arguments
+ // saturation_fe_face_values
+ // darcy_fe_face_values for
+ // assemble_saturation_rhs_boundary_term.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::assemble_saturation_rhs ()
+ {
+ QGauss<dim> quadrature_formula(saturation_degree+2);
+ QGauss<dim-1> face_quadrature_formula(saturation_degree+2);
+
+ FEValues<dim> saturation_fe_values (saturation_fe, quadrature_formula,
+ update_values | update_gradients |
+ update_quadrature_points | update_JxW_values);
+ FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
+ update_values);
+ FEFaceValues<dim> saturation_fe_face_values (saturation_fe, face_quadrature_formula,
+ update_values | update_normal_vectors |
+ update_quadrature_points | update_JxW_values);
+ FEFaceValues<dim> darcy_fe_face_values (darcy_fe, face_quadrature_formula,
+ update_values);
+ FEFaceValues<dim> saturation_fe_face_values_neighbor (saturation_fe, face_quadrature_formula,
+ update_values);
+
+ const unsigned int dofs_per_cell = saturation_dof_handler.get_fe().dofs_per_cell;
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ const double global_u_infty = get_maximal_velocity ();
+ const std::pair<double,double>
+ global_S_range = get_extrapolated_saturation_range ();
+ const double global_S_variasion = global_S_range.second - global_S_range.first;
+ const double global_Omega_diameter = GridTools::diameter (triangulation);
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = saturation_dof_handler.begin_active(),
+ endc = saturation_dof_handler.end();
+ typename DoFHandler<dim>::active_cell_iterator
+ darcy_cell = darcy_dof_handler.begin_active();
+ for (; cell!=endc; ++cell, ++darcy_cell)
{
- const LinearSolvers::InverseMatrix<TrilinosWrappers::SparseMatrix,
- TrilinosWrappers::PreconditionIC>
- mp_inverse (darcy_preconditioner_matrix.block(1,1), *Mp_preconditioner);
+ saturation_fe_values.reinit (cell);
+ darcy_fe_values.reinit (darcy_cell);
+
+ cell->get_dof_indices (local_dof_indices);
+
+ assemble_saturation_rhs_cell_term(saturation_fe_values,
+ darcy_fe_values,
+ local_dof_indices,
+ global_u_infty,
+ global_S_variasion,
+ global_Omega_diameter);
+
+ for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell;
+ ++face_no)
+ {
+
+ if (cell->at_boundary(face_no))
+ {
+ darcy_fe_face_values.reinit (darcy_cell, face_no);
+ saturation_fe_face_values.reinit (cell, face_no);
+ assemble_saturation_rhs_boundary_term (saturation_fe_face_values,
+ darcy_fe_face_values,
+ local_dof_indices);
+ }
+ }
+ }
+ }
- const LinearSolvers::BlockSchurPreconditioner<TrilinosWrappers::PreconditionIC,
- TrilinosWrappers::PreconditionIC>
- preconditioner (darcy_matrix, mp_inverse, *Amg_preconditioner);
- SolverControl solver_control (darcy_matrix.m(),
- 1e-6*darcy_rhs.l2_norm());
- SolverGMRES<TrilinosWrappers::BlockVector>
- gmres (solver_control,
- SolverGMRES<TrilinosWrappers::BlockVector >::AdditionalData(100));
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_rhs_cell_term}
- for (unsigned int i=0; i<darcy_solution.size(); ++i)
- if (darcy_constraints.is_constrained(i))
- darcy_solution(i) = 0;
+ // In this function, we actually compute
+ // every artificial viscosity for every
+ // element. Then, with the artificial value,
+ // we can finish assembling the saturation
+ // right hand side cell integral
+ // terms. Finally, we can pass the local
+ // contributions on to the global vector with
+ // the position specified in
+ // local_dof_indices.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::
+ assemble_saturation_rhs_cell_term (const FEValues<dim> &saturation_fe_values,
+ const FEValues<dim> &darcy_fe_values,
+ const std::vector<unsigned int> &local_dof_indices,
+ const double global_u_infty,
+ const double global_S_variation,
+ const double global_Omega_diameter)
+ {
+ const unsigned int dofs_per_cell = saturation_fe_values.dofs_per_cell;
+ const unsigned int n_q_points = saturation_fe_values.n_quadrature_points;
+
+ Vector<double> local_rhs (dofs_per_cell);
+
+ std::vector<double> old_saturation_solution_values(n_q_points);
+ std::vector<double> old_old_saturation_solution_values(n_q_points);
+ std::vector<Tensor<1,dim> > old_grad_saturation_solution_values(n_q_points);
+ std::vector<Tensor<1,dim> > old_old_grad_saturation_solution_values(n_q_points);
+ std::vector<Vector<double> > present_darcy_solution_values(n_q_points, Vector<double>(dim+1));
+
+ saturation_fe_values.get_function_values (old_saturation_solution, old_saturation_solution_values);
+ saturation_fe_values.get_function_values (old_old_saturation_solution, old_old_saturation_solution_values);
+ saturation_fe_values.get_function_grads (old_saturation_solution, old_grad_saturation_solution_values);
+ saturation_fe_values.get_function_grads (old_old_saturation_solution, old_old_grad_saturation_solution_values);
+ darcy_fe_values.get_function_values (darcy_solution, present_darcy_solution_values);
+
+ const double nu
+ = compute_viscosity (old_saturation_solution_values,
+ old_old_saturation_solution_values,
+ old_grad_saturation_solution_values,
+ old_old_grad_saturation_solution_values,
+ present_darcy_solution_values,
+ global_u_infty,
+ global_S_variation,
+ global_Omega_diameter,
+ saturation_fe_values.get_cell()->diameter(),
+ old_time_step,
+ viscosity);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ {
+ const double old_s = old_saturation_solution_values[q];
+ Tensor<1,dim> present_u;
+ for (unsigned int d=0; d<dim; ++d)
+ present_u[d] = present_darcy_solution_values[q](d);
+
+ const double phi_i_s = saturation_fe_values.shape_value (i, q);
+ const Tensor<1,dim> grad_phi_i_s = saturation_fe_values.shape_grad (i, q);
+
+ local_rhs(i) += (time_step *
+ f_saturation(old_s,viscosity) *
+ present_u *
+ grad_phi_i_s
+ -
+ time_step *
+ nu *
+ old_grad_saturation_solution_values[q] * grad_phi_i_s
+ +
+ old_s * phi_i_s)
+ *
+ saturation_fe_values.JxW(q);
+ }
- gmres.solve(darcy_matrix, darcy_solution, darcy_rhs, preconditioner);
+ saturation_constraints.distribute_local_to_global (local_rhs,
+ local_dof_indices,
+ saturation_rhs);
+ }
- darcy_constraints.distribute (darcy_solution);
- std::cout << " "
- << solver_control.last_step()
- << " GMRES iterations for darcy system (pressure-velocity part)."
- << std::endl;
+ // @sect3{TwoPhaseFlowProblem<dim>::assemble_saturation_rhs_boundary_term}
- }
+ // In this function, we have to give
+ // upwinding in the global boundary faces,
+ // i.e. we impose the Dirichlet boundary
+ // conditions only on inflow parts of global
+ // boundary, which has been described in
+ // step-21 so we refrain from giving more
+ // descriptions about that.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::
+ assemble_saturation_rhs_boundary_term (const FEFaceValues<dim> &saturation_fe_face_values,
+ const FEFaceValues<dim> &darcy_fe_face_values,
+ const std::vector<unsigned int> &local_dof_indices)
+ {
+ const unsigned int dofs_per_cell = saturation_fe_face_values.dofs_per_cell;
+ const unsigned int n_face_q_points = saturation_fe_face_values.n_quadrature_points;
- {
- n_minus_oneth_darcy_solution_after_solving_pressure_part = nth_darcy_solution_after_solving_pressure_part;
- nth_darcy_solution_after_solving_pressure_part = darcy_solution;
+ Vector<double> local_rhs (dofs_per_cell);
- nth_saturation_solution_after_solving_pressure_part = saturation_solution;
- }
- }
- else
- {
- darcy_solution.block(0) = nth_darcy_solution_after_solving_pressure_part.block(0);
- darcy_solution.block(0).sadd (2.0, -1.0, n_minus_oneth_darcy_solution_after_solving_pressure_part.block(0) );
+ std::vector<double> old_saturation_solution_values_face(n_face_q_points);
+ std::vector<Vector<double> > present_darcy_solution_values_face(n_face_q_points, Vector<double>(dim+1));
+ std::vector<double> neighbor_saturation (n_face_q_points);
- double extrapolated_time_step = GridTools::minimal_cell_diameter(triangulation) /
- get_maximal_velocity() / 8.0;
+ saturation_fe_face_values.get_function_values (old_saturation_solution, old_saturation_solution_values_face);
+ darcy_fe_face_values.get_function_values (darcy_solution, present_darcy_solution_values_face);
- double local_cumulative_time_step = cumulative_nth_time_step + extrapolated_time_step;
- double coef_1 = local_cumulative_time_step / n_minus_oneth_time_step;
- double coef_2 = ( 1.0 + coef_1 );
+ SaturationBoundaryValues<dim> saturation_boundary_values;
+ saturation_boundary_values
+ .value_list (saturation_fe_face_values.get_quadrature_points(),
+ neighbor_saturation);
- TrilinosWrappers::Vector tmp (darcy_solution.block(0).size());
- tmp = nth_darcy_solution_after_solving_pressure_part.block(0);
+ for (unsigned int q=0; q<n_face_q_points; ++q)
+ {
+ Tensor<1,dim> present_u_face;
+ for (unsigned int d=0; d<dim; ++d)
+ present_u_face[d] = present_darcy_solution_values_face[q](d);
+
+ const double normal_flux = present_u_face *
+ saturation_fe_face_values.normal_vector(q);
+
+ const bool is_outflow_q_point = (normal_flux >= 0);
+
+ for (unsigned int i=0; i<dofs_per_cell; ++i)
+ local_rhs(i) -= time_step *
+ normal_flux *
+ f_saturation((is_outflow_q_point == true
+ ?
+ old_saturation_solution_values_face[q]
+ :
+ neighbor_saturation[q]),
+ viscosity) *
+ saturation_fe_face_values.shape_value (i,q) *
+ saturation_fe_face_values.JxW(q);
+ }
+ saturation_constraints.distribute_local_to_global (local_rhs,
+ local_dof_indices,
+ saturation_rhs);
+ }
- tmp.sadd (coef_2, -coef_1, n_minus_oneth_darcy_solution_after_solving_pressure_part.block(0) );
- darcy_solution.block(0).sadd (0.5, 0.5, tmp);
- }
+ // @sect3{TwoPhaseFlowProblem<dim>::solve}
+
+ // This function is to implement the operator
+ // splitting algorithm. At the beginning of
+ // the implementation, we decide whther to
+ // solve the pressure-velocity part by
+ // running an a posteriori criterion, which
+ // will be described in the following
+ // function. If we get the bool variable true
+ // from that function, we will solve the
+ // pressure-velocity part for updated
+ // velocity. Then, we use GMRES with the
+ // Schur complement preconditioner to solve
+ // this linear system, as is described in the
+ // Introduction. After solving the velocity
+ // and pressure, we need to keep the
+ // solutions for linear extrapolations in the
+ // future. It is noted that we always solve
+ // the pressure-velocity part in the first
+ // three micro time steps to ensure accuracy
+ // at the beginning of computation, and to
+ // provide starting data to linearly
+ // extrapolate previously computed velocities
+ // to the current time step.
+ //
+ // On the other hand, if we get a false
+ // variable from the criterion, we will
+ // directly use linear extrapolation to
+ // compute the updated velocity for the
+ // solution of saturation later.
+ //
+ // Next, like step-21, this program need to
+ // compute the present time step.
+ //
+ // Next, we need to use two bool variables
+ // solve_pressure_velocity_part and
+ // previous_solve_pressure_velocity_part to
+ // decide whether we stop or continue
+ // cumulating the micro time steps for linear
+ // extropolations in the next iteration. With
+ // the reason, we need one variable
+ // cumulative_nth_time_step for keeping the
+ // present aggregated micro time steps and
+ // anther one n_minus_oneth_time_step for
+ // retaining the previous micro time steps.
+ //
+ // Finally, we start to calculate the
+ // saturation part with the use of the
+ // incomplete Cholesky decomposition for
+ // preconditioning.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::solve ()
+ {
+ solve_pressure_velocity_part = determine_whether_to_solve_pressure_velocity_part ();
+ if ( timestep_number <= 3 || solve_pressure_velocity_part == true )
+ {
+ std::cout << " Solving darcy system (pressure-velocity part)..." << std::endl;
- old_time_step = time_step;
- time_step = GridTools::minimal_cell_diameter(triangulation) /
- get_maximal_velocity() / 8.0;
+ assemble_darcy_system ();
+ build_darcy_preconditioner ();
- if ( timestep_number <= 3 || ( solve_pressure_velocity_part == true && previous_solve_pressure_velocity_part == true ) )
- {
- n_minus_oneth_time_step = time_step;
- cumulative_nth_time_step = 0.0;
- }
- else if ( solve_pressure_velocity_part == true && previous_solve_pressure_velocity_part == false )
- {
- n_minus_oneth_time_step = cumulative_nth_time_step;
- cumulative_nth_time_step = 0.0;
- }
- else
- {
- cumulative_nth_time_step += time_step;
- }
+ {
+ const LinearSolvers::InverseMatrix<TrilinosWrappers::SparseMatrix,
+ TrilinosWrappers::PreconditionIC>
+ mp_inverse (darcy_preconditioner_matrix.block(1,1), *Mp_preconditioner);
- previous_solve_pressure_velocity_part = solve_pressure_velocity_part;
+ const LinearSolvers::BlockSchurPreconditioner<TrilinosWrappers::PreconditionIC,
+ TrilinosWrappers::PreconditionIC>
+ preconditioner (darcy_matrix, mp_inverse, *Amg_preconditioner);
- std::cout << " Solving saturation transport equation..." << std::endl;
+ SolverControl solver_control (darcy_matrix.m(),
+ 1e-6*darcy_rhs.l2_norm());
- assemble_saturation_system ();
+ SolverGMRES<TrilinosWrappers::BlockVector>
+ gmres (solver_control,
+ SolverGMRES<TrilinosWrappers::BlockVector >::AdditionalData(100));
- {
- SolverControl solver_control (saturation_matrix.m(),
- 1e-8*saturation_rhs.l2_norm());
- SolverCG<TrilinosWrappers::Vector> cg (solver_control);
+ for (unsigned int i=0; i<darcy_solution.size(); ++i)
+ if (darcy_constraints.is_constrained(i))
+ darcy_solution(i) = 0;
- TrilinosWrappers::PreconditionIC preconditioner;
- preconditioner.initialize (saturation_matrix);
+ gmres.solve(darcy_matrix, darcy_solution, darcy_rhs, preconditioner);
- cg.solve (saturation_matrix, saturation_solution,
- saturation_rhs, preconditioner);
+ darcy_constraints.distribute (darcy_solution);
+ std::cout << " "
+ << solver_control.last_step()
+ << " GMRES iterations for darcy system (pressure-velocity part)."
+ << std::endl;
- saturation_constraints.distribute (saturation_solution);
+ }
- project_back_saturation ();
+ {
+ n_minus_oneth_darcy_solution_after_solving_pressure_part = nth_darcy_solution_after_solving_pressure_part;
+ nth_darcy_solution_after_solving_pressure_part = darcy_solution;
- std::cout << " "
- << solver_control.last_step()
- << " CG iterations for saturation."
- << std::endl;
+ nth_saturation_solution_after_solving_pressure_part = saturation_solution;
+ }
+ }
+ else
+ {
+ darcy_solution.block(0) = nth_darcy_solution_after_solving_pressure_part.block(0);
+ darcy_solution.block(0).sadd (2.0, -1.0, n_minus_oneth_darcy_solution_after_solving_pressure_part.block(0) );
- }
+ double extrapolated_time_step = GridTools::minimal_cell_diameter(triangulation) /
+ get_maximal_velocity() / 8.0;
-}
+ double local_cumulative_time_step = cumulative_nth_time_step + extrapolated_time_step;
+ double coef_1 = local_cumulative_time_step / n_minus_oneth_time_step;
+ double coef_2 = ( 1.0 + coef_1 );
+ TrilinosWrappers::Vector tmp (darcy_solution.block(0).size());
+ tmp = nth_darcy_solution_after_solving_pressure_part.block(0);
+ tmp.sadd (coef_2, -coef_1, n_minus_oneth_darcy_solution_after_solving_pressure_part.block(0) );
- // @sect3{TwoPhaseFlowProblem<dim>::determine_whether_to_solve_pressure_velocity_part}
-
- // This function is to implement the a
- // posteriori criterion for
- // adaptive operator splitting. As mentioned
- // in step-31, we use two FEValues objects
- // initialized with two cell iterators that
- // we walk in parallel through the two
- // DoFHandler objects associated with the
- // same Triangulation object; for these two
- // FEValues objects, we use of course the
- // same quadrature objects so that we can
- // iterate over the same set of quadrature
- // points, but each FEValues object will get
- // update flags only according to what it
- // actually needs to compute.
- //
- // In addition to this, if someone doesn't
- // want to perform their simulation with
- // operator splitting, they can lower the
- // criterion value (default value is $5.0$)
- // down to zero ad therefore numerical
- // algorithm becomes the original IMPES
- // method.
-template <int dim>
-bool
-TwoPhaseFlowProblem<dim>::determine_whether_to_solve_pressure_velocity_part () const
-{
- if (timestep_number <= 3)
- return true;
+ darcy_solution.block(0).sadd (0.5, 0.5, tmp);
+ }
- const QGauss<dim> quadrature_formula(saturation_degree+2);
- const unsigned int n_q_points = quadrature_formula.size();
- FEValues<dim> fe_values (saturation_fe, quadrature_formula,
- update_values | update_quadrature_points);
+ old_time_step = time_step;
+ time_step = GridTools::minimal_cell_diameter(triangulation) /
+ get_maximal_velocity() / 8.0;
- std::vector<double> old_saturation_after_solving_pressure (n_q_points);
- std::vector<double> present_saturation (n_q_points);
+ if ( timestep_number <= 3 || ( solve_pressure_velocity_part == true && previous_solve_pressure_velocity_part == true ) )
+ {
+ n_minus_oneth_time_step = time_step;
+ cumulative_nth_time_step = 0.0;
+ }
+ else if ( solve_pressure_velocity_part == true && previous_solve_pressure_velocity_part == false )
+ {
+ n_minus_oneth_time_step = cumulative_nth_time_step;
+ cumulative_nth_time_step = 0.0;
+ }
+ else
+ {
+ cumulative_nth_time_step += time_step;
+ }
- const RandomMedium::KInverse<dim> k_inverse;
-// const SingleCurvingCrack::KInverse<dim> k_inverse;
+ previous_solve_pressure_velocity_part = solve_pressure_velocity_part;
- std::vector<Tensor<2,dim> > k_inverse_values (n_q_points);
+ std::cout << " Solving saturation transport equation..." << std::endl;
- double max_global_aop_indicator = 0.0;
+ assemble_saturation_system ();
- typename DoFHandler<dim>::active_cell_iterator
- cell = saturation_dof_handler.begin_active(),
- endc = saturation_dof_handler.end();
- for (; cell!=endc; ++cell)
{
- double max_local_mobility_reciprocal_difference = 0.0;
- double max_local_permeability_inverse_l1_norm = 0.0;
+ SolverControl solver_control (saturation_matrix.m(),
+ 1e-8*saturation_rhs.l2_norm());
+ SolverCG<TrilinosWrappers::Vector> cg (solver_control);
- fe_values.reinit(cell);
- fe_values.get_function_values (nth_saturation_solution_after_solving_pressure_part,
- old_saturation_after_solving_pressure);
- fe_values.get_function_values (saturation_solution,
- present_saturation);
+ TrilinosWrappers::PreconditionIC preconditioner;
+ preconditioner.initialize (saturation_matrix);
- k_inverse.value_list (fe_values.get_quadrature_points(),
- k_inverse_values);
+ cg.solve (saturation_matrix, saturation_solution,
+ saturation_rhs, preconditioner);
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- double mobility_reciprocal_difference = std::fabs( mobility_inverse(present_saturation[q],viscosity)
- -
- mobility_inverse(old_saturation_after_solving_pressure[q],viscosity) );
- max_local_mobility_reciprocal_difference = std::max(max_local_mobility_reciprocal_difference,
- mobility_reciprocal_difference);
+ saturation_constraints.distribute (saturation_solution);
- max_local_permeability_inverse_l1_norm = std::max(max_local_permeability_inverse_l1_norm,
- k_inverse_values[q][0][0]);
- }
+ project_back_saturation ();
- max_global_aop_indicator = std::max(max_global_aop_indicator,
- (max_local_mobility_reciprocal_difference*max_local_permeability_inverse_l1_norm));
- }
+ std::cout << " "
+ << solver_control.last_step()
+ << " CG iterations for saturation."
+ << std::endl;
- if ( max_global_aop_indicator > 5.0 )
- {
- return true;
}
- else
- {
- std::cout << " Activating adaptive operating splitting" << std::endl;
- return false;
- }
-}
-
+ }
- // @sect3{TwoPhaseFlowProblem<dim>::compute_refinement_indicators}
-
- // This function is to to compute the
- // refinement indicator discussed in the
- // introduction for each cell and its
- // implementation is similar to that
- // contained in step-33. There is no need to
- // repeat descriptions about it.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::
-compute_refinement_indicators (Vector<double> &refinement_indicators) const
-{
- const QMidpoint<dim> quadrature_formula;
- FEValues<dim> fe_values (saturation_fe, quadrature_formula, update_gradients);
- std::vector<Tensor<1,dim> > grad_saturation (1);
- double max_refinement_indicator = 0.0;
+ // @sect3{TwoPhaseFlowProblem<dim>::determine_whether_to_solve_pressure_velocity_part}
+
+ // This function is to implement the a
+ // posteriori criterion for
+ // adaptive operator splitting. As mentioned
+ // in step-31, we use two FEValues objects
+ // initialized with two cell iterators that
+ // we walk in parallel through the two
+ // DoFHandler objects associated with the
+ // same Triangulation object; for these two
+ // FEValues objects, we use of course the
+ // same quadrature objects so that we can
+ // iterate over the same set of quadrature
+ // points, but each FEValues object will get
+ // update flags only according to what it
+ // actually needs to compute.
+ //
+ // In addition to this, if someone doesn't
+ // want to perform their simulation with
+ // operator splitting, they can lower the
+ // criterion value (default value is $5.0$)
+ // down to zero ad therefore numerical
+ // algorithm becomes the original IMPES
+ // method.
+ template <int dim>
+ bool
+ TwoPhaseFlowProblem<dim>::determine_whether_to_solve_pressure_velocity_part () const
+ {
+ if (timestep_number <= 3)
+ return true;
- typename DoFHandler<dim>::active_cell_iterator
- cell = saturation_dof_handler.begin_active(),
- endc = saturation_dof_handler.end();
- for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
- {
- fe_values.reinit(cell);
- fe_values.get_function_grads (predictor_saturation_solution,
- grad_saturation);
-
- refinement_indicators(cell_no)
- = std::log( 1.0 + std::sqrt( grad_saturation[0] *
- grad_saturation[0] ) );
- max_refinement_indicator = std::max(max_refinement_indicator,
- refinement_indicators(cell_no));
- }
+ const QGauss<dim> quadrature_formula(saturation_degree+2);
+ const unsigned int n_q_points = quadrature_formula.size();
-// std::cout << "max_refinement_indicator =" << max_refinement_indicator << std::endl;
-}
+ FEValues<dim> fe_values (saturation_fe, quadrature_formula,
+ update_values | update_quadrature_points);
+ std::vector<double> old_saturation_after_solving_pressure (n_q_points);
+ std::vector<double> present_saturation (n_q_points);
+ const RandomMedium::KInverse<dim> k_inverse;
+// const SingleCurvingCrack::KInverse<dim> k_inverse;
- // @sect3{TwoPhaseFlowProblem<dim>::refine_grid}
+ std::vector<Tensor<2,dim> > k_inverse_values (n_q_points);
- // This function is to decide if every cell
- // is refined or coarsened with computed
- // refinement indicators in the previous
- // function and do the interpolations of the
- // solution vectors. The main difference from
- // the previous time-dependent tutorials is
- // that there is no need to do the solution
- // interpolations if we don't have any cell
- // that is refined or coarsend, saving some
- // additional computing time.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::
-refine_grid (const Vector<double> &refinement_indicators)
-{
- const double current_saturation_level = saturation_level +
- n_refinement_steps;
+ double max_global_aop_indicator = 0.0;
- {
typename DoFHandler<dim>::active_cell_iterator
cell = saturation_dof_handler.begin_active(),
endc = saturation_dof_handler.end();
-
- for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
+ for (; cell!=endc; ++cell)
{
- cell->clear_coarsen_flag();
- cell->clear_refine_flag();
+ double max_local_mobility_reciprocal_difference = 0.0;
+ double max_local_permeability_inverse_l1_norm = 0.0;
- if ((cell->level() < current_saturation_level) &&
- (std::fabs(refinement_indicators(cell_no)) > saturation_value))
- cell->set_refine_flag();
- else
- if ((cell->level() > double(n_refinement_steps)) &&
- (std::fabs(refinement_indicators(cell_no)) < 0.75 * saturation_value))
- cell->set_coarsen_flag();
+ fe_values.reinit(cell);
+ fe_values.get_function_values (nth_saturation_solution_after_solving_pressure_part,
+ old_saturation_after_solving_pressure);
+ fe_values.get_function_values (saturation_solution,
+ present_saturation);
+
+ k_inverse.value_list (fe_values.get_quadrature_points(),
+ k_inverse_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ double mobility_reciprocal_difference = std::fabs( mobility_inverse(present_saturation[q],viscosity)
+ -
+ mobility_inverse(old_saturation_after_solving_pressure[q],viscosity) );
+
+ max_local_mobility_reciprocal_difference = std::max(max_local_mobility_reciprocal_difference,
+ mobility_reciprocal_difference);
+
+ max_local_permeability_inverse_l1_norm = std::max(max_local_permeability_inverse_l1_norm,
+ k_inverse_values[q][0][0]);
+ }
+
+ max_global_aop_indicator = std::max(max_global_aop_indicator,
+ (max_local_mobility_reciprocal_difference*max_local_permeability_inverse_l1_norm));
+ }
+
+ if ( max_global_aop_indicator > 5.0 )
+ {
+ return true;
+ }
+ else
+ {
+ std::cout << " Activating adaptive operating splitting" << std::endl;
+ return false;
}
}
- triangulation.prepare_coarsening_and_refinement ();
- unsigned int number_of_cells_refine = 0;
- unsigned int number_of_cells_coarsen = 0;
+ // @sect3{TwoPhaseFlowProblem<dim>::compute_refinement_indicators}
+
+ // This function is to to compute the
+ // refinement indicator discussed in the
+ // introduction for each cell and its
+ // implementation is similar to that
+ // contained in step-33. There is no need to
+ // repeat descriptions about it.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::
+ compute_refinement_indicators (Vector<double> &refinement_indicators) const
{
+
+ const QMidpoint<dim> quadrature_formula;
+ FEValues<dim> fe_values (saturation_fe, quadrature_formula, update_gradients);
+ std::vector<Tensor<1,dim> > grad_saturation (1);
+
+ double max_refinement_indicator = 0.0;
+
typename DoFHandler<dim>::active_cell_iterator
cell = saturation_dof_handler.begin_active(),
endc = saturation_dof_handler.end();
+ for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
+ {
+ fe_values.reinit(cell);
+ fe_values.get_function_grads (predictor_saturation_solution,
+ grad_saturation);
+
+ refinement_indicators(cell_no)
+ = std::log( 1.0 + std::sqrt( grad_saturation[0] *
+ grad_saturation[0] ) );
+ max_refinement_indicator = std::max(max_refinement_indicator,
+ refinement_indicators(cell_no));
+ }
- for (; cell!=endc; ++cell)
- if (cell->refine_flag_set())
- ++number_of_cells_refine;
- else
- if (cell->coarsen_flag_set())
- ++number_of_cells_coarsen;
+// std::cout << "max_refinement_indicator =" << max_refinement_indicator << std::endl;
}
- std::cout << " "
- << number_of_cells_refine
- << " cell(s) are going to be refined."
- << std::endl;
- std::cout << " "
- << number_of_cells_coarsen
- << " cell(s) are going to be coarsened."
- << std::endl;
- std::cout << std::endl;
- if ( number_of_cells_refine > 0 || number_of_cells_coarsen > 0 )
- {
- std::vector<TrilinosWrappers::Vector> x_saturation (3);
- x_saturation[0] = saturation_solution;
- x_saturation[1] = old_saturation_solution;
- x_saturation[2] = nth_saturation_solution_after_solving_pressure_part;
-
- std::vector<TrilinosWrappers::BlockVector> x_darcy (2);
- x_darcy[0] = nth_darcy_solution_after_solving_pressure_part;
- x_darcy[1] = n_minus_oneth_darcy_solution_after_solving_pressure_part;
+ // @sect3{TwoPhaseFlowProblem<dim>::refine_grid}
- SolutionTransfer<dim,TrilinosWrappers::Vector> saturation_soltrans(saturation_dof_handler);
+ // This function is to decide if every cell
+ // is refined or coarsened with computed
+ // refinement indicators in the previous
+ // function and do the interpolations of the
+ // solution vectors. The main difference from
+ // the previous time-dependent tutorials is
+ // that there is no need to do the solution
+ // interpolations if we don't have any cell
+ // that is refined or coarsend, saving some
+ // additional computing time.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::
+ refine_grid (const Vector<double> &refinement_indicators)
+ {
+ const double current_saturation_level = saturation_level +
+ n_refinement_steps;
- SolutionTransfer<dim,TrilinosWrappers::BlockVector> darcy_soltrans(darcy_dof_handler);
+ {
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = saturation_dof_handler.begin_active(),
+ endc = saturation_dof_handler.end();
+
+ for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
+ {
+ cell->clear_coarsen_flag();
+ cell->clear_refine_flag();
+
+ if ((cell->level() < current_saturation_level) &&
+ (std::fabs(refinement_indicators(cell_no)) > saturation_value))
+ cell->set_refine_flag();
+ else
+ if ((cell->level() > double(n_refinement_steps)) &&
+ (std::fabs(refinement_indicators(cell_no)) < 0.75 * saturation_value))
+ cell->set_coarsen_flag();
+ }
+ }
+ triangulation.prepare_coarsening_and_refinement ();
- triangulation.prepare_coarsening_and_refinement();
- saturation_soltrans.prepare_for_coarsening_and_refinement(x_saturation);
+ unsigned int number_of_cells_refine = 0;
+ unsigned int number_of_cells_coarsen = 0;
- darcy_soltrans.prepare_for_coarsening_and_refinement(x_darcy);
+ {
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = saturation_dof_handler.begin_active(),
+ endc = saturation_dof_handler.end();
- triangulation.execute_coarsening_and_refinement ();
- setup_dofs ();
+ for (; cell!=endc; ++cell)
+ if (cell->refine_flag_set())
+ ++number_of_cells_refine;
+ else
+ if (cell->coarsen_flag_set())
+ ++number_of_cells_coarsen;
+ }
- std::vector<TrilinosWrappers::Vector> tmp_saturation (3);
- tmp_saturation[0].reinit (saturation_solution);
- tmp_saturation[1].reinit (saturation_solution);
- tmp_saturation[2].reinit (saturation_solution);
- saturation_soltrans.interpolate(x_saturation, tmp_saturation);
+ std::cout << " "
+ << number_of_cells_refine
+ << " cell(s) are going to be refined."
+ << std::endl;
+ std::cout << " "
+ << number_of_cells_coarsen
+ << " cell(s) are going to be coarsened."
+ << std::endl;
- saturation_solution = tmp_saturation[0];
- old_saturation_solution = tmp_saturation[1];
- nth_saturation_solution_after_solving_pressure_part = tmp_saturation[2];
+ std::cout << std::endl;
- std::vector<TrilinosWrappers::BlockVector> tmp_darcy (2);
- tmp_darcy[0].reinit (darcy_solution);
- tmp_darcy[1].reinit (darcy_solution);
- darcy_soltrans.interpolate(x_darcy, tmp_darcy);
+ if ( number_of_cells_refine > 0 || number_of_cells_coarsen > 0 )
+ {
+ std::vector<TrilinosWrappers::Vector> x_saturation (3);
+ x_saturation[0] = saturation_solution;
+ x_saturation[1] = old_saturation_solution;
+ x_saturation[2] = nth_saturation_solution_after_solving_pressure_part;
- nth_darcy_solution_after_solving_pressure_part = tmp_darcy[0];
- n_minus_oneth_darcy_solution_after_solving_pressure_part = tmp_darcy[1];
+ std::vector<TrilinosWrappers::BlockVector> x_darcy (2);
+ x_darcy[0] = nth_darcy_solution_after_solving_pressure_part;
+ x_darcy[1] = n_minus_oneth_darcy_solution_after_solving_pressure_part;
- rebuild_saturation_matrix = true;
- }
- else
- {
- rebuild_saturation_matrix = false;
-
- std::vector<unsigned int> darcy_block_component (dim+1,0);
- darcy_block_component[dim] = 1;
-
- std::vector<unsigned int> darcy_dofs_per_block (2);
- DoFTools::count_dofs_per_block (darcy_dof_handler, darcy_dofs_per_block, darcy_block_component);
- const unsigned int n_u = darcy_dofs_per_block[0],
- n_p = darcy_dofs_per_block[1],
- n_s = saturation_dof_handler.n_dofs();
-
- std::cout << "Number of active cells: "
- << triangulation.n_active_cells()
- << " (on "
- << triangulation.n_levels()
- << " levels)"
- << std::endl
- << "Number of degrees of freedom: "
- << n_u + n_p + n_s
- << " (" << n_u << '+' << n_p << '+'<< n_s <<')'
- << std::endl
- << std::endl;
- }
+ SolutionTransfer<dim,TrilinosWrappers::Vector> saturation_soltrans(saturation_dof_handler);
-}
+ SolutionTransfer<dim,TrilinosWrappers::BlockVector> darcy_soltrans(darcy_dof_handler);
+ triangulation.prepare_coarsening_and_refinement();
+ saturation_soltrans.prepare_for_coarsening_and_refinement(x_saturation);
- // @sect3{TwoPhaseFlowProblem<dim>::output_results}
+ darcy_soltrans.prepare_for_coarsening_and_refinement(x_darcy);
- // This function to process the output
- // data. We only store the results when we
- // actually solve the pressure and velocity
- // part at the present time step. The rest of
- // the implementation is similar to that
- // output function in step-31, which
- // implementations has been explained in that
- // tutorial.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::output_results () const
-{
- if ( solve_pressure_velocity_part == false )
- return;
+ triangulation.execute_coarsening_and_refinement ();
+ setup_dofs ();
- const FESystem<dim> joint_fe (darcy_fe, 1,
- saturation_fe, 1);
- DoFHandler<dim> joint_dof_handler (triangulation);
- joint_dof_handler.distribute_dofs (joint_fe);
- Assert (joint_dof_handler.n_dofs() ==
- darcy_dof_handler.n_dofs() + saturation_dof_handler.n_dofs(),
- ExcInternalError());
+ std::vector<TrilinosWrappers::Vector> tmp_saturation (3);
+ tmp_saturation[0].reinit (saturation_solution);
+ tmp_saturation[1].reinit (saturation_solution);
+ tmp_saturation[2].reinit (saturation_solution);
+ saturation_soltrans.interpolate(x_saturation, tmp_saturation);
- Vector<double> joint_solution (joint_dof_handler.n_dofs());
+ saturation_solution = tmp_saturation[0];
+ old_saturation_solution = tmp_saturation[1];
+ nth_saturation_solution_after_solving_pressure_part = tmp_saturation[2];
- {
- std::vector<unsigned int> local_joint_dof_indices (joint_fe.dofs_per_cell);
- std::vector<unsigned int> local_darcy_dof_indices (darcy_fe.dofs_per_cell);
- std::vector<unsigned int> local_saturation_dof_indices (saturation_fe.dofs_per_cell);
+ std::vector<TrilinosWrappers::BlockVector> tmp_darcy (2);
+ tmp_darcy[0].reinit (darcy_solution);
+ tmp_darcy[1].reinit (darcy_solution);
+ darcy_soltrans.interpolate(x_darcy, tmp_darcy);
- typename DoFHandler<dim>::active_cell_iterator
- joint_cell = joint_dof_handler.begin_active(),
- joint_endc = joint_dof_handler.end(),
- darcy_cell = darcy_dof_handler.begin_active(),
- saturation_cell = saturation_dof_handler.begin_active();
+ nth_darcy_solution_after_solving_pressure_part = tmp_darcy[0];
+ n_minus_oneth_darcy_solution_after_solving_pressure_part = tmp_darcy[1];
- for (; joint_cell!=joint_endc; ++joint_cell, ++darcy_cell, ++saturation_cell)
+ rebuild_saturation_matrix = true;
+ }
+ else
{
- joint_cell->get_dof_indices (local_joint_dof_indices);
- darcy_cell->get_dof_indices (local_darcy_dof_indices);
- saturation_cell->get_dof_indices (local_saturation_dof_indices);
-
- for (unsigned int i=0; i<joint_fe.dofs_per_cell; ++i)
- if (joint_fe.system_to_base_index(i).first.first == 0)
- {
- Assert (joint_fe.system_to_base_index(i).second
- <
- local_darcy_dof_indices.size(),
- ExcInternalError());
- joint_solution(local_joint_dof_indices[i])
- = darcy_solution(local_darcy_dof_indices[joint_fe.system_to_base_index(i).second]);
- }
- else
- {
- Assert (joint_fe.system_to_base_index(i).first.first == 1,
- ExcInternalError());
- Assert (joint_fe.system_to_base_index(i).second
- <
- local_darcy_dof_indices.size(),
- ExcInternalError());
- joint_solution(local_joint_dof_indices[i])
- = saturation_solution(local_saturation_dof_indices[joint_fe.system_to_base_index(i).second]);
- }
-
+ rebuild_saturation_matrix = false;
+
+ std::vector<unsigned int> darcy_block_component (dim+1,0);
+ darcy_block_component[dim] = 1;
+
+ std::vector<unsigned int> darcy_dofs_per_block (2);
+ DoFTools::count_dofs_per_block (darcy_dof_handler, darcy_dofs_per_block, darcy_block_component);
+ const unsigned int n_u = darcy_dofs_per_block[0],
+ n_p = darcy_dofs_per_block[1],
+ n_s = saturation_dof_handler.n_dofs();
+
+ std::cout << "Number of active cells: "
+ << triangulation.n_active_cells()
+ << " (on "
+ << triangulation.n_levels()
+ << " levels)"
+ << std::endl
+ << "Number of degrees of freedom: "
+ << n_u + n_p + n_s
+ << " (" << n_u << '+' << n_p << '+'<< n_s <<')'
+ << std::endl
+ << std::endl;
}
+
}
- std::vector<std::string> joint_solution_names;
- switch (dim)
- {
- case 2:
- joint_solution_names.push_back ("u");
- joint_solution_names.push_back ("v");
- break;
-
- case 3:
- joint_solution_names.push_back ("u");
- joint_solution_names.push_back ("v");
- joint_solution_names.push_back ("w");
- break;
-
- default:
- Assert (false, ExcNotImplemented());
- }
- joint_solution_names.push_back ("pressure");
- joint_solution_names.push_back ("saturation");
- std::vector<DataComponentInterpretation::DataComponentInterpretation>
- data_component_interpretation
- (dim, DataComponentInterpretation::component_is_part_of_vector);
- data_component_interpretation
- .push_back (DataComponentInterpretation::component_is_scalar);
- data_component_interpretation
- .push_back (DataComponentInterpretation::component_is_scalar);
- DataOut<dim> data_out;
- data_out.attach_dof_handler (joint_dof_handler);
- data_out.add_data_vector (joint_solution, joint_solution_names,
- DataOut<dim>::type_dof_data,
- data_component_interpretation);
+ // @sect3{TwoPhaseFlowProblem<dim>::output_results}
- data_out.build_patches ();
+ // This function to process the output
+ // data. We only store the results when we
+ // actually solve the pressure and velocity
+ // part at the present time step. The rest of
+ // the implementation is similar to that
+ // output function in step-31, which
+ // implementations has been explained in that
+ // tutorial.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::output_results () const
+ {
+ if ( solve_pressure_velocity_part == false )
+ return;
- std::string filename = "solution-" +
- Utilities::int_to_string (timestep_number, 5) + ".tec";
- std::ofstream output (filename.c_str());
- data_out.write_tecplot (output);
-}
+ const FESystem<dim> joint_fe (darcy_fe, 1,
+ saturation_fe, 1);
+ DoFHandler<dim> joint_dof_handler (triangulation);
+ joint_dof_handler.distribute_dofs (joint_fe);
+ Assert (joint_dof_handler.n_dofs() ==
+ darcy_dof_handler.n_dofs() + saturation_dof_handler.n_dofs(),
+ ExcInternalError());
+ Vector<double> joint_solution (joint_dof_handler.n_dofs());
+ {
+ std::vector<unsigned int> local_joint_dof_indices (joint_fe.dofs_per_cell);
+ std::vector<unsigned int> local_darcy_dof_indices (darcy_fe.dofs_per_cell);
+ std::vector<unsigned int> local_saturation_dof_indices (saturation_fe.dofs_per_cell);
- // @sect3{TwoPhaseFlowProblem<dim>::THE_REMAINING_FUNCTIONS}
+ typename DoFHandler<dim>::active_cell_iterator
+ joint_cell = joint_dof_handler.begin_active(),
+ joint_endc = joint_dof_handler.end(),
+ darcy_cell = darcy_dof_handler.begin_active(),
+ saturation_cell = saturation_dof_handler.begin_active();
+
+ for (; joint_cell!=joint_endc; ++joint_cell, ++darcy_cell, ++saturation_cell)
+ {
+ joint_cell->get_dof_indices (local_joint_dof_indices);
+ darcy_cell->get_dof_indices (local_darcy_dof_indices);
+ saturation_cell->get_dof_indices (local_saturation_dof_indices);
+
+ for (unsigned int i=0; i<joint_fe.dofs_per_cell; ++i)
+ if (joint_fe.system_to_base_index(i).first.first == 0)
+ {
+ Assert (joint_fe.system_to_base_index(i).second
+ <
+ local_darcy_dof_indices.size(),
+ ExcInternalError());
+ joint_solution(local_joint_dof_indices[i])
+ = darcy_solution(local_darcy_dof_indices[joint_fe.system_to_base_index(i).second]);
+ }
+ else
+ {
+ Assert (joint_fe.system_to_base_index(i).first.first == 1,
+ ExcInternalError());
+ Assert (joint_fe.system_to_base_index(i).second
+ <
+ local_darcy_dof_indices.size(),
+ ExcInternalError());
+ joint_solution(local_joint_dof_indices[i])
+ = saturation_solution(local_saturation_dof_indices[joint_fe.system_to_base_index(i).second]);
+ }
- // The remaining functions that have been
- // used in step-31 so we don't have to
- // describe their implementations.
-template <int dim>
-void
-TwoPhaseFlowProblem<dim>::project_back_saturation ()
-{
- for (unsigned int i=0; i<saturation_solution.size(); ++i)
- if (saturation_solution(i) < 0)
- saturation_solution(i) = 0;
- else
- if (saturation_solution(i) > 1)
- saturation_solution(i) = 1;
-}
+ }
+ }
+ std::vector<std::string> joint_solution_names;
+ switch (dim)
+ {
+ case 2:
+ joint_solution_names.push_back ("u");
+ joint_solution_names.push_back ("v");
+ break;
+
+ case 3:
+ joint_solution_names.push_back ("u");
+ joint_solution_names.push_back ("v");
+ joint_solution_names.push_back ("w");
+ break;
+
+ default:
+ Assert (false, ExcNotImplemented());
+ }
+ joint_solution_names.push_back ("pressure");
+ joint_solution_names.push_back ("saturation");
+ std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ data_component_interpretation
+ (dim, DataComponentInterpretation::component_is_part_of_vector);
+ data_component_interpretation
+ .push_back (DataComponentInterpretation::component_is_scalar);
+ data_component_interpretation
+ .push_back (DataComponentInterpretation::component_is_scalar);
-template <int dim>
-double
-TwoPhaseFlowProblem<dim>::get_maximal_velocity () const
-{
- QGauss<dim> quadrature_formula(darcy_degree+2);
- const unsigned int n_q_points
- = quadrature_formula.size();
-
- FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
- update_values);
- std::vector<Vector<double> > darcy_solution_values(n_q_points,
- Vector<double>(dim+1));
- double max_velocity = 0;
-
- typename DoFHandler<dim>::active_cell_iterator
- cell = darcy_dof_handler.begin_active(),
- endc = darcy_dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- darcy_fe_values.reinit (cell);
- darcy_fe_values.get_function_values (darcy_solution, darcy_solution_values);
-
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- Tensor<1,dim> velocity;
- for (unsigned int i=0; i<dim; ++i)
- velocity[i] = darcy_solution_values[q](i);
-
- max_velocity = std::max (max_velocity,
- velocity.norm());
- }
- }
+ DataOut<dim> data_out;
- return max_velocity;
-}
+ data_out.attach_dof_handler (joint_dof_handler);
+ data_out.add_data_vector (joint_solution, joint_solution_names,
+ DataOut<dim>::type_dof_data,
+ data_component_interpretation);
+ data_out.build_patches ();
-template <int dim>
-std::pair<double,double>
-TwoPhaseFlowProblem<dim>::get_extrapolated_saturation_range () const
-{
- const QGauss<dim> quadrature_formula(saturation_degree+2);
- const unsigned int n_q_points = quadrature_formula.size();
+ std::string filename = "solution-" +
+ Utilities::int_to_string (timestep_number, 5) + ".tec";
+ std::ofstream output (filename.c_str());
+ data_out.write_tecplot (output);
+ }
- FEValues<dim> fe_values (saturation_fe, quadrature_formula,
- update_values);
- std::vector<double> old_saturation_values(n_q_points);
- std::vector<double> old_old_saturation_values(n_q_points);
- if (timestep_number != 0)
- {
- double min_saturation = (1. + time_step/old_time_step) *
- old_saturation_solution.linfty_norm()
- +
- time_step/old_time_step *
- old_old_saturation_solution.linfty_norm(),
- max_saturation = -min_saturation;
- typename DoFHandler<dim>::active_cell_iterator
- cell = saturation_dof_handler.begin_active(),
- endc = saturation_dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- fe_values.reinit (cell);
- fe_values.get_function_values (old_saturation_solution,
- old_saturation_values);
- fe_values.get_function_values (old_old_saturation_solution,
- old_old_saturation_values);
-
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- const double saturation =
- (1. + time_step/old_time_step) * old_saturation_values[q]-
- time_step/old_time_step * old_old_saturation_values[q];
-
- min_saturation = std::min (min_saturation, saturation);
- max_saturation = std::max (max_saturation, saturation);
- }
- }
-
- return std::make_pair(min_saturation, max_saturation);
- }
- else
- {
- double min_saturation = old_saturation_solution.linfty_norm(),
- max_saturation = -min_saturation;
+ // @sect3{TwoPhaseFlowProblem<dim>::THE_REMAINING_FUNCTIONS}
- typename DoFHandler<dim>::active_cell_iterator
- cell = saturation_dof_handler.begin_active(),
- endc = saturation_dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- fe_values.reinit (cell);
- fe_values.get_function_values (old_saturation_solution,
- old_saturation_values);
+ // The remaining functions that have been
+ // used in step-31 so we don't have to
+ // describe their implementations.
+ template <int dim>
+ void
+ TwoPhaseFlowProblem<dim>::project_back_saturation ()
+ {
+ for (unsigned int i=0; i<saturation_solution.size(); ++i)
+ if (saturation_solution(i) < 0)
+ saturation_solution(i) = 0;
+ else
+ if (saturation_solution(i) > 1)
+ saturation_solution(i) = 1;
+ }
- for (unsigned int q=0; q<n_q_points; ++q)
- {
- const double saturation = old_saturation_values[q];
- min_saturation = std::min (min_saturation, saturation);
- max_saturation = std::max (max_saturation, saturation);
- }
- }
+ template <int dim>
+ double
+ TwoPhaseFlowProblem<dim>::get_maximal_velocity () const
+ {
+ QGauss<dim> quadrature_formula(darcy_degree+2);
+ const unsigned int n_q_points
+ = quadrature_formula.size();
- return std::make_pair(min_saturation, max_saturation);
- }
-}
+ FEValues<dim> darcy_fe_values (darcy_fe, quadrature_formula,
+ update_values);
+ std::vector<Vector<double> > darcy_solution_values(n_q_points,
+ Vector<double>(dim+1));
+ double max_velocity = 0;
-template <int dim>
-double
-TwoPhaseFlowProblem<dim>::
-compute_viscosity (const std::vector<double> &old_saturation,
- const std::vector<double> &old_old_saturation,
- const std::vector<Tensor<1,dim> > &old_saturation_grads,
- const std::vector<Tensor<1,dim> > &old_old_saturation_grads,
- const std::vector<Vector<double> > &present_darcy_values,
- const double global_u_infty,
- const double global_S_variation,
- const double global_Omega_diameter,
- const double cell_diameter,
- const double old_time_step,
- const double viscosity)
-{
- const double beta = 0.08 * dim;
- const double alpha = 1;
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = darcy_dof_handler.begin_active(),
+ endc = darcy_dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ darcy_fe_values.reinit (cell);
+ darcy_fe_values.get_function_values (darcy_solution, darcy_solution_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ Tensor<1,dim> velocity;
+ for (unsigned int i=0; i<dim; ++i)
+ velocity[i] = darcy_solution_values[q](i);
+
+ max_velocity = std::max (max_velocity,
+ velocity.norm());
+ }
+ }
+
+ return max_velocity;
+ }
- if (global_u_infty == 0)
- return 5e-3 * cell_diameter;
- const unsigned int n_q_points = old_saturation.size();
+ template <int dim>
+ std::pair<double,double>
+ TwoPhaseFlowProblem<dim>::get_extrapolated_saturation_range () const
+ {
+ const QGauss<dim> quadrature_formula(saturation_degree+2);
+ const unsigned int n_q_points = quadrature_formula.size();
- double max_residual = 0;
- double max_velocity = 0;
+ FEValues<dim> fe_values (saturation_fe, quadrature_formula,
+ update_values);
+ std::vector<double> old_saturation_values(n_q_points);
+ std::vector<double> old_old_saturation_values(n_q_points);
- for (unsigned int q=0; q < n_q_points; ++q)
- {
- Tensor<1,dim> u;
- for (unsigned int d=0; d<dim; ++d)
- u[d] = present_darcy_values[q](d);
+ if (timestep_number != 0)
+ {
+ double min_saturation = (1. + time_step/old_time_step) *
+ old_saturation_solution.linfty_norm()
+ +
+ time_step/old_time_step *
+ old_old_saturation_solution.linfty_norm(),
+ max_saturation = -min_saturation;
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = saturation_dof_handler.begin_active(),
+ endc = saturation_dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ fe_values.reinit (cell);
+ fe_values.get_function_values (old_saturation_solution,
+ old_saturation_values);
+ fe_values.get_function_values (old_old_saturation_solution,
+ old_old_saturation_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ const double saturation =
+ (1. + time_step/old_time_step) * old_saturation_values[q]-
+ time_step/old_time_step * old_old_saturation_values[q];
+
+ min_saturation = std::min (min_saturation, saturation);
+ max_saturation = std::max (max_saturation, saturation);
+ }
+ }
+
+ return std::make_pair(min_saturation, max_saturation);
+ }
+ else
+ {
+ double min_saturation = old_saturation_solution.linfty_norm(),
+ max_saturation = -min_saturation;
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = saturation_dof_handler.begin_active(),
+ endc = saturation_dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ fe_values.reinit (cell);
+ fe_values.get_function_values (old_saturation_solution,
+ old_saturation_values);
+
+ for (unsigned int q=0; q<n_q_points; ++q)
+ {
+ const double saturation = old_saturation_values[q];
+
+ min_saturation = std::min (min_saturation, saturation);
+ max_saturation = std::max (max_saturation, saturation);
+ }
+ }
+
+ return std::make_pair(min_saturation, max_saturation);
+ }
+ }
- const double dS_dt = (old_saturation[q] - old_old_saturation[q])
- / old_time_step;
+ template <int dim>
+ double
+ TwoPhaseFlowProblem<dim>::
+ compute_viscosity (const std::vector<double> &old_saturation,
+ const std::vector<double> &old_old_saturation,
+ const std::vector<Tensor<1,dim> > &old_saturation_grads,
+ const std::vector<Tensor<1,dim> > &old_old_saturation_grads,
+ const std::vector<Vector<double> > &present_darcy_values,
+ const double global_u_infty,
+ const double global_S_variation,
+ const double global_Omega_diameter,
+ const double cell_diameter,
+ const double old_time_step,
+ const double viscosity)
+ {
+ const double beta = 0.08 * dim;
+ const double alpha = 1;
- const double dF_dS = get_fractional_flow_derivative ((old_saturation[q] + old_old_saturation[q]) / 2.0,
- viscosity);
+ if (global_u_infty == 0)
+ return 5e-3 * cell_diameter;
- const double u_grad_S = u * dF_dS *
- (old_saturation_grads[q] + old_old_saturation_grads[q]) / 2.0;
+ const unsigned int n_q_points = old_saturation.size();
- const double residual
- = std::abs((dS_dt + u_grad_S) *
- std::pow((old_saturation[q]+old_old_saturation[q]) / 2,
- alpha-1.));
+ double max_residual = 0;
+ double max_velocity = 0;
- max_residual = std::max (residual, max_residual);
- max_velocity = std::max (std::sqrt (u*u), max_velocity);
- }
+ for (unsigned int q=0; q < n_q_points; ++q)
+ {
+ Tensor<1,dim> u;
+ for (unsigned int d=0; d<dim; ++d)
+ u[d] = present_darcy_values[q](d);
- const double global_scaling = global_u_infty * global_S_variation /
- std::pow(global_Omega_diameter, alpha - 2.);
+ const double dS_dt = (old_saturation[q] - old_old_saturation[q])
+ / old_time_step;
- return (beta *
- max_velocity *
- std::min (cell_diameter,
- std::pow(cell_diameter,alpha) *
- max_residual / global_scaling));
-}
+ const double dF_dS = get_fractional_flow_derivative ((old_saturation[q] + old_old_saturation[q]) / 2.0,
+ viscosity);
+ const double u_grad_S = u * dF_dS *
+ (old_saturation_grads[q] + old_old_saturation_grads[q]) / 2.0;
- // @sect3{TwoPhaseFlowProblem<dim>::run}
-
- // In this function, we follow the structure
- // of the same function partly in step-21 and
- // partly in step-31 so again there is no
- // need to repeat it. However, since we
- // consider the simulation with grid
- // adaptivity, we need to compute a
- // saturation predictor, which implementation
- // was first used in step-33, for the
- // function that computes the refinement
- // indicators.
-template <int dim>
-void TwoPhaseFlowProblem<dim>::run ()
-{
- unsigned int pre_refinement_step = 0;
+ const double residual
+ = std::abs((dS_dt + u_grad_S) *
+ std::pow((old_saturation[q]+old_old_saturation[q]) / 2,
+ alpha-1.));
- GridGenerator::hyper_cube (triangulation, 0, 1);
- triangulation.refine_global (n_refinement_steps);
+ max_residual = std::max (residual, max_residual);
+ max_velocity = std::max (std::sqrt (u*u), max_velocity);
+ }
- setup_dofs ();
+ const double global_scaling = global_u_infty * global_S_variation /
+ std::pow(global_Omega_diameter, alpha - 2.);
- start_time_iteration:
+ return (beta *
+ max_velocity *
+ std::min (cell_diameter,
+ std::pow(cell_diameter,alpha) *
+ max_residual / global_scaling));
+ }
- VectorTools::project (saturation_dof_handler,
- saturation_constraints,
- QGauss<dim>(saturation_degree+2),
- SaturationInitialValues<dim>(),
- old_saturation_solution);
- timestep_number = 0;
- double time = 0;
+ // @sect3{TwoPhaseFlowProblem<dim>::run}
- do
- {
- std::cout << "Timestep " << timestep_number
- << ": t=" << time
- << ", dt=" << time_step
- << std::endl;
+ // In this function, we follow the structure
+ // of the same function partly in step-21 and
+ // partly in step-31 so again there is no
+ // need to repeat it. However, since we
+ // consider the simulation with grid
+ // adaptivity, we need to compute a
+ // saturation predictor, which implementation
+ // was first used in step-33, for the
+ // function that computes the refinement
+ // indicators.
+ template <int dim>
+ void TwoPhaseFlowProblem<dim>::run ()
+ {
+ unsigned int pre_refinement_step = 0;
- solve ();
+ GridGenerator::hyper_cube (triangulation, 0, 1);
+ triangulation.refine_global (n_refinement_steps);
- output_results ();
+ setup_dofs ();
- solve_pressure_velocity_part = false;
+ start_time_iteration:
- if ((timestep_number == 0) &&
- (pre_refinement_step < saturation_level))
- {
- predictor_saturation_solution = saturation_solution;
- predictor_saturation_solution.sadd (2.0, -1.0, old_saturation_solution);
- Vector<double> refinement_indicators (triangulation.n_active_cells());
- compute_refinement_indicators(refinement_indicators);
- refine_grid(refinement_indicators);
- ++pre_refinement_step;
- goto start_time_iteration;
- }
- else
- {
- predictor_saturation_solution = saturation_solution;
- predictor_saturation_solution.sadd (2.0, -1.0, old_saturation_solution);
- Vector<double> refinement_indicators (triangulation.n_active_cells());
- compute_refinement_indicators(refinement_indicators);
- refine_grid(refinement_indicators);
- }
+ VectorTools::project (saturation_dof_handler,
+ saturation_constraints,
+ QGauss<dim>(saturation_degree+2),
+ SaturationInitialValues<dim>(),
+ old_saturation_solution);
- time += time_step;
- ++timestep_number;
+ timestep_number = 0;
+ double time = 0;
- old_old_saturation_solution = old_saturation_solution;
- old_saturation_solution = saturation_solution;
+ do
+ {
+ std::cout << "Timestep " << timestep_number
+ << ": t=" << time
+ << ", dt=" << time_step
+ << std::endl;
+
+ solve ();
+
+ output_results ();
+
+ solve_pressure_velocity_part = false;
+
+ if ((timestep_number == 0) &&
+ (pre_refinement_step < saturation_level))
+ {
+ predictor_saturation_solution = saturation_solution;
+ predictor_saturation_solution.sadd (2.0, -1.0, old_saturation_solution);
+ Vector<double> refinement_indicators (triangulation.n_active_cells());
+ compute_refinement_indicators(refinement_indicators);
+ refine_grid(refinement_indicators);
+ ++pre_refinement_step;
+ goto start_time_iteration;
+ }
+ else
+ {
+ predictor_saturation_solution = saturation_solution;
+ predictor_saturation_solution.sadd (2.0, -1.0, old_saturation_solution);
+ Vector<double> refinement_indicators (triangulation.n_active_cells());
+ compute_refinement_indicators(refinement_indicators);
+ refine_grid(refinement_indicators);
+ }
- }
- while (time <= 250);
+ time += time_step;
+ ++timestep_number;
+
+ old_old_saturation_solution = old_saturation_solution;
+ old_saturation_solution = saturation_solution;
+
+ }
+ while (time <= 250);
+ }
}
+
int main ()
{
try
{
+ using namespace dealii;
+ using namespace Step43;
+
deallog.depth_console (0);
TwoPhaseFlowProblem<3> two_phase_flow_problem(1);
#include <fstream>
#include <sstream>
-using namespace dealii;
-// @sect3{Run-time parameters}
-namespace Parameters
+namespace Step44
{
+ using namespace dealii;
+
+// @sect3{Run-time parameters}
+ namespace Parameters
+ {
// Finite Element system
-struct FESystem
-{
- int poly_degree;
- int quad_order;
+ struct FESystem
+ {
+ int poly_degree;
+ int quad_order;
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-void FESystem::declare_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Finite element system");
+ void FESystem::declare_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Finite element system");
+ {
prm.declare_entry("Polynomial degree",
"1",
Patterns::Integer(),
"2",
Patterns::Integer(),
"Gauss quadrature order");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
-void FESystem::parse_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Finite element system");
+ void FESystem::parse_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Finite element system");
+ {
poly_degree = prm.get_integer("Polynomial degree");
quad_order = prm.get_integer("Quadrature order");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
// Geometry
-struct Geometry
-{
- int global_refinement;
- double scale;
- double p_p0;
+ struct Geometry
+ {
+ int global_refinement;
+ double scale;
+ double p_p0;
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-void Geometry::declare_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Geometry");
+ void Geometry::declare_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Geometry");
+ {
prm.declare_entry("Global refinement",
"2",
Patterns::Integer(),
"40",
Patterns::Selection("20|40|60|80|100"),
"Ratio of applied pressure to reference pressure");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
-void Geometry::parse_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Geometry");
+ void Geometry::parse_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Geometry");
+ {
global_refinement = prm.get_integer("Global refinement");
scale = prm.get_double("Grid scale");
p_p0= prm.get_double("Pressure ratio p/p0");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
// Materials
-struct Materials
-{
- double nu;
- double mu;
+ struct Materials
+ {
+ double nu;
+ double mu;
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-void Materials::declare_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Material properties");
+ void Materials::declare_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Material properties");
+ {
prm.declare_entry("Poisson's ratio",
"0.49",
Patterns::Double(),
"1.0e6",
Patterns::Double(),
"Shear modulus");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
-void Materials::parse_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Material properties");
+ void Materials::parse_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Material properties");
+ {
nu = prm.get_double("Poisson's ratio");
mu = prm.get_double("Shear modulus");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
// Linear solver
-struct LinearSolver
-{
- std::string type_lin;
- double tol_lin;
- double max_iterations_lin;
- double ssor_relaxation;
+ struct LinearSolver
+ {
+ std::string type_lin;
+ double tol_lin;
+ double max_iterations_lin;
+ double ssor_relaxation;
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-void LinearSolver::declare_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Linear solver");
+ void LinearSolver::declare_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Linear solver");
+ {
prm.declare_entry("Solver type",
"CG",
Patterns::Selection("CG|Direct"),
"0.6",
Patterns::Double(),
"SSOR preconditioner relaxation value");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
-void LinearSolver::parse_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Linear solver");
+ void LinearSolver::parse_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Linear solver");
+ {
type_lin = prm.get("Solver type");
tol_lin = prm.get_double("Residual");
max_iterations_lin = prm.get_double("Max iteration multiplier");
ssor_relaxation = prm.get_double("SSOR Relaxation");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
// Nonlinear solver
-struct NonlinearSolver
-{
- unsigned int max_iterations_NR;
- double tol_f;
- double tol_u;
+ struct NonlinearSolver
+ {
+ unsigned int max_iterations_NR;
+ double tol_f;
+ double tol_u;
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-void NonlinearSolver::declare_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Nonlinear solver");
+ void NonlinearSolver::declare_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Nonlinear solver");
+ {
prm.declare_entry("Max iterations Newton-Raphson",
"10",
Patterns::Integer(),
"1.0e-3",
Patterns::Double(),
"Displacement error tolerance");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
-void NonlinearSolver::parse_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Nonlinear solver");
+ void NonlinearSolver::parse_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Nonlinear solver");
+ {
max_iterations_NR = prm.get_integer("Max iterations Newton-Raphson");
tol_f = prm.get_double("Tolerance force");
tol_u = prm.get_double("Tolerance displacement");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
// Time
-struct Time
-{
- double end_time;
- double delta_t;
+ struct Time
+ {
+ double end_time;
+ double delta_t;
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-void Time::declare_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Time");
+ void Time::declare_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Time");
+ {
prm.declare_entry("End time",
"1",
Patterns::Double(),
"0.1",
Patterns::Double(),
"Time step size");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
-void Time::parse_parameters (ParameterHandler &prm)
-{
- prm.enter_subsection("Time");
+ void Time::parse_parameters (ParameterHandler &prm)
{
+ prm.enter_subsection("Time");
+ {
end_time = prm.get_double("End time");
delta_t = prm.get_double("Time step size");
+ }
+ prm.leave_subsection();
}
- prm.leave_subsection();
-}
// All parameters
-struct AllParameters
- :
+ struct AllParameters
+ :
public FESystem,
public Geometry,
public Materials,
public NonlinearSolver,
public Time
-{
- AllParameters (const std::string & input_file);
+ {
+ AllParameters (const std::string & input_file);
- static void declare_parameters (ParameterHandler &prm);
- void parse_parameters (ParameterHandler &prm);
-};
+ static void declare_parameters (ParameterHandler &prm);
+ void parse_parameters (ParameterHandler &prm);
+ };
-AllParameters::AllParameters (const std::string & input_file)
-{
- ParameterHandler prm;
- declare_parameters(prm);
- prm.read_input (input_file);
- parse_parameters(prm);
-}
+ AllParameters::AllParameters (const std::string & input_file)
+ {
+ ParameterHandler prm;
+ declare_parameters(prm);
+ prm.read_input (input_file);
+ parse_parameters(prm);
+ }
-void AllParameters::declare_parameters (ParameterHandler &prm)
-{
- FESystem::declare_parameters(prm);
- Geometry::declare_parameters(prm);
- Materials::declare_parameters(prm);
- LinearSolver::declare_parameters(prm);
- NonlinearSolver::declare_parameters(prm);
- Time::declare_parameters(prm);
-}
+ void AllParameters::declare_parameters (ParameterHandler &prm)
+ {
+ FESystem::declare_parameters(prm);
+ Geometry::declare_parameters(prm);
+ Materials::declare_parameters(prm);
+ LinearSolver::declare_parameters(prm);
+ NonlinearSolver::declare_parameters(prm);
+ Time::declare_parameters(prm);
+ }
-void AllParameters::parse_parameters (ParameterHandler &prm)
-{
- FESystem::parse_parameters(prm);
- Geometry::parse_parameters(prm);
- Materials::parse_parameters(prm);
- LinearSolver::parse_parameters(prm);
- NonlinearSolver::parse_parameters(prm);
- Time::parse_parameters(prm);
-}
-}
+ void AllParameters::parse_parameters (ParameterHandler &prm)
+ {
+ FESystem::parse_parameters(prm);
+ Geometry::parse_parameters(prm);
+ Materials::parse_parameters(prm);
+ LinearSolver::parse_parameters(prm);
+ NonlinearSolver::parse_parameters(prm);
+ Time::parse_parameters(prm);
+ }
+ }
// @sect3{General tools}
-namespace AdditionalTools
-{
-template <typename MatrixType>
-void extract_submatrix(const std::vector< unsigned int > &row_index_set,
- const std::vector< unsigned int > &column_index_set,
- const MatrixType &matrix,
- FullMatrix< double > &sub_matrix )
-{
+ namespace AdditionalTools
+ {
+ template <typename MatrixType>
+ void extract_submatrix(const std::vector< unsigned int > &row_index_set,
+ const std::vector< unsigned int > &column_index_set,
+ const MatrixType &matrix,
+ FullMatrix< double > &sub_matrix )
+ {
- const unsigned int n_rows_submatrix = row_index_set.size();
- const unsigned int n_cols_submatrix = column_index_set.size();
+ const unsigned int n_rows_submatrix = row_index_set.size();
+ const unsigned int n_cols_submatrix = column_index_set.size();
- sub_matrix.reinit(n_rows_submatrix, n_cols_submatrix);
+ sub_matrix.reinit(n_rows_submatrix, n_cols_submatrix);
- for (unsigned int sub_row = 0; sub_row < n_rows_submatrix; ++sub_row) {
+ for (unsigned int sub_row = 0; sub_row < n_rows_submatrix; ++sub_row) {
const unsigned int row = row_index_set[sub_row];
Assert (row<=matrix.m(), ExcIndexRange(row, 0, matrix.m()));
for (unsigned int sub_col = 0; sub_col < n_cols_submatrix; ++sub_col) {
- const unsigned int col = column_index_set[sub_col];
- Assert (col<=matrix.n(), ExcIndexRange(col, 0, matrix.n()));
+ const unsigned int col = column_index_set[sub_col];
+ Assert (col<=matrix.n(), ExcIndexRange(col, 0, matrix.n()));
- sub_matrix(sub_row,sub_col) = matrix(row, col);
+ sub_matrix(sub_row,sub_col) = matrix(row, col);
}
+ }
}
-}
-template <typename MatrixType>
-void replace_submatrix(const std::vector< unsigned int > &row_index_set,
- const std::vector< unsigned int > &column_index_set,
- const MatrixType &sub_matrix,
- FullMatrix< double > &matrix)
-{
- const unsigned int n_rows_submatrix = row_index_set.size();
- Assert (n_rows_submatrix<=sub_matrix.m(), ExcIndexRange(n_rows_submatrix, 0, sub_matrix.m()));
- const unsigned int n_cols_submatrix = column_index_set.size();
- Assert (n_cols_submatrix<=sub_matrix.n(), ExcIndexRange(n_cols_submatrix, 0, sub_matrix.n()));
+ template <typename MatrixType>
+ void replace_submatrix(const std::vector< unsigned int > &row_index_set,
+ const std::vector< unsigned int > &column_index_set,
+ const MatrixType &sub_matrix,
+ FullMatrix< double > &matrix)
+ {
+ const unsigned int n_rows_submatrix = row_index_set.size();
+ Assert (n_rows_submatrix<=sub_matrix.m(), ExcIndexRange(n_rows_submatrix, 0, sub_matrix.m()));
+ const unsigned int n_cols_submatrix = column_index_set.size();
+ Assert (n_cols_submatrix<=sub_matrix.n(), ExcIndexRange(n_cols_submatrix, 0, sub_matrix.n()));
- for (unsigned int sub_row = 0; sub_row < n_rows_submatrix; ++sub_row) {
+ for (unsigned int sub_row = 0; sub_row < n_rows_submatrix; ++sub_row) {
const unsigned int row = row_index_set[sub_row];
Assert (row<=matrix.m(), ExcIndexRange(row, 0, matrix.m()));
for (unsigned int sub_col = 0; sub_col < n_cols_submatrix; ++sub_col) {
- const unsigned int col = column_index_set[sub_col];
- Assert (col<=matrix.n(), ExcIndexRange(col, 0, matrix.n()));
+ const unsigned int col = column_index_set[sub_col];
+ Assert (col<=matrix.n(), ExcIndexRange(col, 0, matrix.n()));
- matrix(row, col) = sub_matrix(sub_row, sub_col);
+ matrix(row, col) = sub_matrix(sub_row, sub_col);
}
+ }
}
-}
-}
+ }
// @sect3{Time class}
-class Time {
-public:
- Time (const double & time_end,
- const double & delta_t)
- :
- timestep (0),
- time_current (0.0),
- time_end (time_end),
- delta_t (delta_t)
- {}
- virtual ~Time (void) {}
-
- const double & current (void) const {return time_current;}
- const double & end (void) const {return time_end;}
- const double & get_delta_t (void) const {return delta_t;}
- const unsigned int & get_timestep (void) const {return timestep;}
- void increment (void) {time_current += delta_t; ++timestep;}
-
-private:
- unsigned int timestep;
- double time_current;
- const double time_end;
- const double delta_t;
-};
+ class Time {
+ public:
+ Time (const double & time_end,
+ const double & delta_t)
+ :
+ timestep (0),
+ time_current (0.0),
+ time_end (time_end),
+ delta_t (delta_t)
+ {}
+ virtual ~Time (void) {}
+
+ const double & current (void) const {return time_current;}
+ const double & end (void) const {return time_end;}
+ const double & get_delta_t (void) const {return delta_t;}
+ const unsigned int & get_timestep (void) const {return timestep;}
+ void increment (void) {time_current += delta_t; ++timestep;}
+
+ private:
+ unsigned int timestep;
+ double time_current;
+ const double time_end;
+ const double delta_t;
+ };
// @sect3{Neo-Hookean material}
-template <int dim>
-class Material_NH
-{
-public:
- /// \brief Class constructor
- Material_NH (const double & lambda,
- const double & mu)
- :
- lambda_0 (lambda),
- mu_0 (mu),
- kappa_0 (lambda + 2.0/3.0*mu)
- { }
- virtual ~Material_NH (void) {};
-
- // Stress and constitutive tensors
- virtual SymmetricTensor<2, dim> get_T (const double & J,
- const SymmetricTensor <2, dim> & B)
- {
- const double dW_dJ = get_dU_dtheta (J);
- return mu_0*B + dW_dJ*J*I;
- }
+ template <int dim>
+ class Material_NH
+ {
+ public:
+ /// \brief Class constructor
+ Material_NH (const double & lambda,
+ const double & mu)
+ :
+ lambda_0 (lambda),
+ mu_0 (mu),
+ kappa_0 (lambda + 2.0/3.0*mu)
+ { }
+ virtual ~Material_NH (void) {};
- virtual SymmetricTensor<4, dim> get_JC (const double & J,
- const SymmetricTensor <2, dim> & B)
- {
- const double dW_dJ = get_dU_dtheta (J);
- const double d2W_dJ2 = get_d2U_dtheta2 (J);
- return J*( (dW_dJ + J*d2W_dJ2)*IxI - (2.0*dW_dJ)*II );
- }
+ // Stress and constitutive tensors
+ virtual SymmetricTensor<2, dim> get_T (const double & J,
+ const SymmetricTensor <2, dim> & B)
+ {
+ const double dW_dJ = get_dU_dtheta (J);
+ return mu_0*B + dW_dJ*J*I;
+ }
+
+ virtual SymmetricTensor<4, dim> get_JC (const double & J,
+ const SymmetricTensor <2, dim> & B)
+ {
+ const double dW_dJ = get_dU_dtheta (J);
+ const double d2W_dJ2 = get_d2U_dtheta2 (J);
+ return J*( (dW_dJ + J*d2W_dJ2)*IxI - (2.0*dW_dJ)*II );
+ }
- // Volumetric quantities methods
- double get_dU_dtheta (const double & d) {return kappa_0*(d - 1.0/d);}
- double get_d2U_dtheta2 (const double & d) {return kappa_0*(1.0 + 1.0/(d*d));}
+ // Volumetric quantities methods
+ double get_dU_dtheta (const double & d) {return kappa_0*(d - 1.0/d);}
+ double get_d2U_dtheta2 (const double & d) {return kappa_0*(1.0 + 1.0/(d*d));}
-protected:
- // Material properties
- const double lambda_0; // Lame modulus
- const double mu_0; // Shear modulus
- const double kappa_0; // Bulk modulus
+ protected:
+ // Material properties
+ const double lambda_0; // Lame modulus
+ const double mu_0; // Shear modulus
+ const double kappa_0; // Bulk modulus
- static SymmetricTensor<2, dim> const I;
- static SymmetricTensor<4, dim> const IxI;
- static SymmetricTensor<4, dim> const II;
-};
+ static SymmetricTensor<2, dim> const I;
+ static SymmetricTensor<4, dim> const IxI;
+ static SymmetricTensor<4, dim> const II;
+ };
-template <int dim> SymmetricTensor<2, dim> const Material_NH<dim>::I = SymmetricTensor<2, dim> (unit_symmetric_tensor <dim> ());
-template <int dim> SymmetricTensor<4, dim> const Material_NH<dim>::IxI = SymmetricTensor<4, dim> (outer_product (I, I));
-template <int dim> SymmetricTensor<4, dim> const Material_NH<dim>::II = SymmetricTensor<4, dim> (identity_tensor <dim> ());
+ template <int dim> SymmetricTensor<2, dim> const Material_NH<dim>::I = SymmetricTensor<2, dim> (unit_symmetric_tensor <dim> ());
+ template <int dim> SymmetricTensor<4, dim> const Material_NH<dim>::IxI = SymmetricTensor<4, dim> (outer_product (I, I));
+ template <int dim> SymmetricTensor<4, dim> const Material_NH<dim>::II = SymmetricTensor<4, dim> (identity_tensor <dim> ());
// @sect3{Quadrature point history}
-template <int dim>
-class PointHistory
-{
-public:
- PointHistory (void)
- :
- material (NULL),
- dilatation_n (1.0),
- pressure_n (0.0)
- { }
- virtual ~PointHistory (void) {delete material;}
-
- void setup_lqp ( Parameters::AllParameters & parameters )
- {
- const double lambda = 2.0*parameters.mu*parameters.nu / (1.0-2.0*parameters.nu);
- material = new Material_NH<dim> (lambda,
- parameters.mu);
+ template <int dim>
+ class PointHistory
+ {
+ public:
+ PointHistory (void)
+ :
+ material (NULL),
+ dilatation_n (1.0),
+ pressure_n (0.0)
+ { }
+ virtual ~PointHistory (void) {delete material;}
- // Initialise all tensors correctly
- update_values (Tensor <2,dim> (), 0.0, 1.0);
- }
+ void setup_lqp ( Parameters::AllParameters & parameters )
+ {
+ const double lambda = 2.0*parameters.mu*parameters.nu / (1.0-2.0*parameters.nu);
+ material = new Material_NH<dim> (lambda,
+ parameters.mu);
- // Total Variables
- void update_values (const Tensor<2, dim> & grad_u_n,
- const double & pressure,
- const double & dilatation)
- {
- // Calculated variables from displacement, displacement gradients
- const Tensor <2,dim> F = static_cast <Tensor < 2, dim> > (unit_symmetric_tensor <dim> ()) + grad_u_n;
- J = determinant(F);
- F_inv = invert(F);
- B_bar = std::pow(get_J(), -2.0/3.0) * symmetrize ( F* transpose (F) );
-
- // Precalculated pressure, dilatation
- pressure_n = pressure;
- dilatation_n = dilatation;
-
- // Now that all the necessary variables are set, we can update the stress tensors
- // Stress update can only update the stresses once the
- // dilatation has been set as p = p(d)
- T_bar = material->get_T (get_J(), get_B_bar());
- T_iso = dev_P*get_T_bar(); // Note: T_iso depends on T_bar
- T_vol = get_pressure()*get_J()*I;
- }
+ // Initialise all tensors correctly
+ update_values (Tensor <2,dim> (), 0.0, 1.0);
+ }
- // Displacement and strain
- const double & get_dilatation(void) const {return dilatation_n;}
- const double & get_J (void) const {return J;}
- const Tensor <2,dim> & get_F_inv (void) const {return F_inv;}
- const SymmetricTensor <2,dim> & get_B_bar (void) const {return B_bar;}
+ // Total Variables
+ void update_values (const Tensor<2, dim> & grad_u_n,
+ const double & pressure,
+ const double & dilatation)
+ {
+ // Calculated variables from displacement, displacement gradients
+ const Tensor <2,dim> F = static_cast <Tensor < 2, dim> > (unit_symmetric_tensor <dim> ()) + grad_u_n;
+ J = determinant(F);
+ F_inv = invert(F);
+ B_bar = std::pow(get_J(), -2.0/3.0) * symmetrize ( F* transpose (F) );
+
+ // Precalculated pressure, dilatation
+ pressure_n = pressure;
+ dilatation_n = dilatation;
+
+ // Now that all the necessary variables are set, we can update the stress tensors
+ // Stress update can only update the stresses once the
+ // dilatation has been set as p = p(d)
+ T_bar = material->get_T (get_J(), get_B_bar());
+ T_iso = dev_P*get_T_bar(); // Note: T_iso depends on T_bar
+ T_vol = get_pressure()*get_J()*I;
+ }
+
+ // Displacement and strain
+ const double & get_dilatation(void) const {return dilatation_n;}
+ const double & get_J (void) const {return J;}
+ const Tensor <2,dim> & get_F_inv (void) const {return F_inv;}
+ const SymmetricTensor <2,dim> & get_B_bar (void) const {return B_bar;}
- // Volumetric terms
- double get_dU_dtheta (void) {
+ // Volumetric terms
+ double get_dU_dtheta (void) {
return material->get_dU_dtheta(get_dilatation());
- }
+ }
- double get_d2U_dtheta2 (void) {
+ double get_d2U_dtheta2 (void) {
return material->get_d2U_dtheta2(get_dilatation());
- }
+ }
- // Stress
- double get_pressure(void) {return pressure_n;}
- const SymmetricTensor<2, dim> & get_T_iso (void) const {return T_iso;}
- const SymmetricTensor<2, dim> & get_T_vol (void) const {return T_vol;};
+ // Stress
+ double get_pressure(void) {return pressure_n;}
+ const SymmetricTensor<2, dim> & get_T_iso (void) const {return T_iso;}
+ const SymmetricTensor<2, dim> & get_T_vol (void) const {return T_vol;};
- // Tangent matrices
- SymmetricTensor <4,dim> get_C_iso(void)
- {
- const double & J = get_J();
- const SymmetricTensor<2, dim> & B_bar = get_B_bar();
- const SymmetricTensor<2, dim> & T_iso = get_T_iso();
+ // Tangent matrices
+ SymmetricTensor <4,dim> get_C_iso(void)
+ {
+ const double & J = get_J();
+ const SymmetricTensor<2, dim> & B_bar = get_B_bar();
+ const SymmetricTensor<2, dim> & T_iso = get_T_iso();
- const SymmetricTensor <4,dim> T_iso_x_I = outer_product(T_iso, I);
- const SymmetricTensor <4,dim> I_x_T_iso = outer_product(I, T_iso);
- const SymmetricTensor <4,dim> CC_bar = material->get_JC (J, B_bar);
+ const SymmetricTensor <4,dim> T_iso_x_I = outer_product(T_iso, I);
+ const SymmetricTensor <4,dim> I_x_T_iso = outer_product(I, T_iso);
+ const SymmetricTensor <4,dim> CC_bar = material->get_JC (J, B_bar);
- return 2.0/3.0*trace(get_T_bar())*dev_P
- - 2.0/3.0*(T_iso_x_I + I_x_T_iso)
- + dev_P*CC_bar*dev_P;
- }
+ return 2.0/3.0*trace(get_T_bar())*dev_P
+ - 2.0/3.0*(T_iso_x_I + I_x_T_iso)
+ + dev_P*CC_bar*dev_P;
+ }
- SymmetricTensor <4,dim> get_C_vol(void)
- {
- const double & p = get_pressure();
- const double & J = get_J();
- return p*J*(IxI - 2.0*II);
- }
+ SymmetricTensor <4,dim> get_C_vol(void)
+ {
+ const double & p = get_pressure();
+ const double & J = get_J();
+ return p*J*(IxI - 2.0*II);
+ }
-private:
- // === MATERIAL ===
- Material_NH <dim>* material;
-
- // ==== VOLUME, DISPLACEMENT AND STRAIN VARIABLES ====
- double dilatation_n; // Current dilatation
- double J;
- Tensor <2,dim> F_inv;
- SymmetricTensor <2,dim> B_bar;
- SymmetricTensor <2,dim> E;
-
- // ==== STRESS VARIABLES ====
- double pressure_n; // Current pressure
- SymmetricTensor<2, dim> T_bar;
- SymmetricTensor<2, dim> T_iso;
- SymmetricTensor<2, dim> T_vol;
- const SymmetricTensor<2, dim> & get_T_bar (void) const {return T_bar;}
-
- // Basis tensors
- static SymmetricTensor<2, dim> const I;
- static SymmetricTensor<4, dim> const IxI;
- static SymmetricTensor<4, dim> const II;
- static SymmetricTensor<4, dim> const dev_P;
-};
-
-template <int dim> SymmetricTensor<2,dim> const PointHistory<dim>::I
-= SymmetricTensor<2,dim> (unit_symmetric_tensor <dim> ());
-template <int dim> SymmetricTensor<4,dim> const PointHistory<dim>::IxI
-= SymmetricTensor<4,dim> (outer_product (I, I));
-template <int dim> SymmetricTensor<4,dim> const PointHistory<dim>::II
-= SymmetricTensor<4,dim> (identity_tensor <dim> ());
-template <int dim> SymmetricTensor<4,dim> const PointHistory<dim>::dev_P
-= SymmetricTensor<4,dim> (II - 1.0/3.0*IxI);
+ private:
+ // === MATERIAL ===
+ Material_NH <dim>* material;
+
+ // ==== VOLUME, DISPLACEMENT AND STRAIN VARIABLES ====
+ double dilatation_n; // Current dilatation
+ double J;
+ Tensor <2,dim> F_inv;
+ SymmetricTensor <2,dim> B_bar;
+ SymmetricTensor <2,dim> E;
+
+ // ==== STRESS VARIABLES ====
+ double pressure_n; // Current pressure
+ SymmetricTensor<2, dim> T_bar;
+ SymmetricTensor<2, dim> T_iso;
+ SymmetricTensor<2, dim> T_vol;
+ const SymmetricTensor<2, dim> & get_T_bar (void) const {return T_bar;}
+
+ // Basis tensors
+ static SymmetricTensor<2, dim> const I;
+ static SymmetricTensor<4, dim> const IxI;
+ static SymmetricTensor<4, dim> const II;
+ static SymmetricTensor<4, dim> const dev_P;
+ };
+
+ template <int dim> SymmetricTensor<2,dim> const PointHistory<dim>::I
+ = SymmetricTensor<2,dim> (unit_symmetric_tensor <dim> ());
+ template <int dim> SymmetricTensor<4,dim> const PointHistory<dim>::IxI
+ = SymmetricTensor<4,dim> (outer_product (I, I));
+ template <int dim> SymmetricTensor<4,dim> const PointHistory<dim>::II
+ = SymmetricTensor<4,dim> (identity_tensor <dim> ());
+ template <int dim> SymmetricTensor<4,dim> const PointHistory<dim>::dev_P
+ = SymmetricTensor<4,dim> (II - 1.0/3.0*IxI);
// @sect3{Quasi-static quasi-incompressible finite-strain solid}
-template <int dim>
-class Solid
-{
-public:
- Solid (const std::string & input_file);
- virtual ~Solid (void);
- void run (void);
+ template <int dim>
+ class Solid
+ {
+ public:
+ Solid (const std::string & input_file);
+ virtual ~Solid (void);
+ void run (void);
+
+ private:
+
+ // === DATA STRUCTS ===
+
+ struct PerTaskData_K
+ {
+ FullMatrix<double> cell_matrix;
+ std::vector<unsigned int> local_dof_indices;
+
+ PerTaskData_K (const unsigned int dofs_per_cell)
+ :
+ cell_matrix (dofs_per_cell,
+ dofs_per_cell),
+ local_dof_indices (dofs_per_cell)
+ { }
+
+ void reset (void) {
+ cell_matrix = 0.0;
+ }
+ };
-private:
+ struct ScratchData_K
+ {
+ FEValues <dim> fe_values_ref;
- // === DATA STRUCTS ===
+ std::vector < std::vector< double > > Nx;
+ std::vector < std::vector< Tensor<2, dim> > > grad_Nx;
+ std::vector < std::vector< SymmetricTensor<2, dim> > > symm_grad_Nx;
- struct PerTaskData_K
- {
- FullMatrix<double> cell_matrix;
- std::vector<unsigned int> local_dof_indices;
-
- PerTaskData_K (const unsigned int dofs_per_cell)
- :
- cell_matrix (dofs_per_cell,
- dofs_per_cell),
- local_dof_indices (dofs_per_cell)
- { }
-
- void reset (void) {
- cell_matrix = 0.0;
- }
- };
-
- struct ScratchData_K
- {
- FEValues <dim> fe_values_ref;
-
- std::vector < std::vector< double > > Nx;
- std::vector < std::vector< Tensor<2, dim> > > grad_Nx;
- std::vector < std::vector< SymmetricTensor<2, dim> > > symm_grad_Nx;
-
- ScratchData_K ( const FiniteElement <dim> & fe_cell,
- const QGauss <dim> & qf_cell,
- const UpdateFlags uf_cell)
- :
- fe_values_ref (fe_cell,
- qf_cell,
- uf_cell),
- Nx (qf_cell.size(),
- std::vector< double >(fe_cell.dofs_per_cell)),
- grad_Nx (qf_cell.size(),
- std::vector< Tensor<2, dim> >(fe_cell.dofs_per_cell)),
- symm_grad_Nx (qf_cell.size(),
- std::vector< SymmetricTensor<2, dim> >(fe_cell.dofs_per_cell))
- { }
-
- ScratchData_K ( const ScratchData_K & rhs ) :
- fe_values_ref ( rhs.fe_values_ref.get_fe(),
- rhs.fe_values_ref.get_quadrature(),
- rhs.fe_values_ref.get_update_flags() ),
- Nx (rhs.Nx),
- grad_Nx (rhs.grad_Nx),
- symm_grad_Nx (rhs.symm_grad_Nx)
- { }
-
- void reset (void) {
+ ScratchData_K ( const FiniteElement <dim> & fe_cell,
+ const QGauss <dim> & qf_cell,
+ const UpdateFlags uf_cell)
+ :
+ fe_values_ref (fe_cell,
+ qf_cell,
+ uf_cell),
+ Nx (qf_cell.size(),
+ std::vector< double >(fe_cell.dofs_per_cell)),
+ grad_Nx (qf_cell.size(),
+ std::vector< Tensor<2, dim> >(fe_cell.dofs_per_cell)),
+ symm_grad_Nx (qf_cell.size(),
+ std::vector< SymmetricTensor<2, dim> >(fe_cell.dofs_per_cell))
+ { }
+
+ ScratchData_K ( const ScratchData_K & rhs ) :
+ fe_values_ref ( rhs.fe_values_ref.get_fe(),
+ rhs.fe_values_ref.get_quadrature(),
+ rhs.fe_values_ref.get_update_flags() ),
+ Nx (rhs.Nx),
+ grad_Nx (rhs.grad_Nx),
+ symm_grad_Nx (rhs.symm_grad_Nx)
+ { }
+
+ void reset (void) {
for (unsigned int q_point=0; q_point < grad_Nx.size(); ++q_point) {
- for (unsigned int k=0; k < Nx.size(); ++k) {
- Nx[q_point][k] = 0.0;
- grad_Nx[q_point][k] = 0.0;
- symm_grad_Nx[q_point][k] = 0.0;
- }
+ for (unsigned int k=0; k < Nx.size(); ++k) {
+ Nx[q_point][k] = 0.0;
+ grad_Nx[q_point][k] = 0.0;
+ symm_grad_Nx[q_point][k] = 0.0;
+ }
}
- }
+ }
- };
-
- struct PerTaskData_F
- {
- Vector<double> cell_rhs;
- std::vector<unsigned int> local_dof_indices;
-
- PerTaskData_F (const unsigned int dofs_per_cell)
- :
- cell_rhs (dofs_per_cell),
- local_dof_indices (dofs_per_cell)
- { }
-
- void reset (void) { cell_rhs = 0.0; }
- };
+ };
- struct ScratchData_F
- {
- FEValues <dim> fe_values_ref;
- FEFaceValues <dim> fe_face_values_ref;
-
- std::vector < std::vector< double > > Nx;
- std::vector < std::vector< SymmetricTensor<2, dim> > > symm_grad_Nx;
- std::vector< Vector<double> > rhs_values;
-
- // Solution data
- std::vector< std::vector<Tensor <1,dim> > > solution_grads;
-
- ScratchData_F ( const FiniteElement <dim> & fe_cell,
- const QGauss <dim> & qf_cell,
- const UpdateFlags uf_cell,
- const QGauss <dim-1> & qf_face,
- const UpdateFlags uf_face)
- :
- fe_values_ref (fe_cell,
- qf_cell,
- uf_cell),
- fe_face_values_ref (fe_cell,
- qf_face,
- uf_face),
- Nx (qf_cell.size(),
- std::vector< double >(fe_cell.dofs_per_cell)),
- symm_grad_Nx (qf_cell.size(),
- std::vector< SymmetricTensor<2, dim> >(fe_cell.dofs_per_cell)),
- rhs_values (qf_cell.size(),
- Vector<double>(dim))
- { }
-
- ScratchData_F ( const ScratchData_F & rhs )
- :
- fe_values_ref ( rhs.fe_values_ref.get_fe(),
- rhs.fe_values_ref.get_quadrature(),
- rhs.fe_values_ref.get_update_flags() ),
- fe_face_values_ref ( rhs.fe_face_values_ref.get_fe(),
- rhs.fe_face_values_ref.get_quadrature(),
- rhs.fe_face_values_ref.get_update_flags() ),
- Nx (rhs.Nx),
- symm_grad_Nx (rhs.symm_grad_Nx),
- rhs_values (rhs.rhs_values)
- { }
-
- void reset (void) {
- for (unsigned int q_point=0; q_point < symm_grad_Nx.size(); ++q_point) {
- for (unsigned int k=0; k < symm_grad_Nx[q_point].size(); ++k) {
- Nx[q_point][k] = 0.0;
- symm_grad_Nx[q_point][k] = 0.0;
- rhs_values[q_point] = 0.0;
- }
- }
- }
+ struct PerTaskData_F
+ {
+ Vector<double> cell_rhs;
+ std::vector<unsigned int> local_dof_indices;
- };
+ PerTaskData_F (const unsigned int dofs_per_cell)
+ :
+ cell_rhs (dofs_per_cell),
+ local_dof_indices (dofs_per_cell)
+ { }
- struct PerTaskData_SC
- {
- FullMatrix<double> cell_matrix;
- std::vector<unsigned int> local_dof_indices;
-
- // Calculation matrices (auto resized)
- FullMatrix<double> K_orig;
- FullMatrix<double> K_pu;
- FullMatrix<double> K_pt;
- FullMatrix<double> K_tt;
- // Calculation matrices (manual resized)
- FullMatrix<double> K_pt_inv;
- FullMatrix<double> K_tt_inv;
- FullMatrix<double> K_con;
- FullMatrix<double> A;
- FullMatrix<double> B;
- FullMatrix<double> C;
-
- PerTaskData_SC (const unsigned int & dofs_per_cell,
- const unsigned int & n_u,
- const unsigned int & n_p,
- const unsigned int & n_t)
- :
- cell_matrix (dofs_per_cell,
- dofs_per_cell),
- local_dof_indices (dofs_per_cell),
- K_pt_inv (n_t, n_p),
- K_tt_inv (n_t, n_t),
- K_con (n_u, n_u),
- A (n_t, n_u),
- B (n_t, n_u),
- C (n_p, n_u)
- { }
-
- // Choose not to reset any data
- // The matrix extraction and replacement tools will take care of this
- void reset(void) { }
- };
+ void reset (void) { cell_rhs = 0.0; }
+ };
- // Dummy struct for TBB
- struct ScratchData_SC
- {
- ScratchData_SC (void) { }
- ScratchData_SC (const ScratchData_SC & rhs) { }
- void reset (void) { }
- };
+ struct ScratchData_F
+ {
+ FEValues <dim> fe_values_ref;
+ FEFaceValues <dim> fe_face_values_ref;
- // Dummy struct for TBB
- struct PerTaskData_UQPH
- {
- PerTaskData_UQPH (void) { }
- void reset(void) { }
- };
+ std::vector < std::vector< double > > Nx;
+ std::vector < std::vector< SymmetricTensor<2, dim> > > symm_grad_Nx;
+ std::vector< Vector<double> > rhs_values;
- struct ScratchData_UQPH
- {
- FEValues<dim> fe_values_ref;
- std::vector< Tensor< 2, dim> > solution_grads_u_total;
- std::vector <double> solution_values_p_total;
- std::vector <double> solution_values_t_total;
- const BlockVector <double> & solution_total;
+ // Solution data
+ std::vector< std::vector<Tensor <1,dim> > > solution_grads;
- ScratchData_UQPH (const FiniteElement <dim> & fe_cell,
+ ScratchData_F ( const FiniteElement <dim> & fe_cell,
const QGauss <dim> & qf_cell,
const UpdateFlags uf_cell,
- const BlockVector <double> & solution_total)
- :
- fe_values_ref (fe_cell,
- qf_cell,
- uf_cell),
- solution_grads_u_total (qf_cell.size()),
- solution_values_p_total (qf_cell.size()),
- solution_values_t_total (qf_cell.size()),
- solution_total (solution_total)
- { }
-
- ScratchData_UQPH (const ScratchData_UQPH & rhs)
- :
- fe_values_ref (rhs.fe_values_ref.get_fe(),
- rhs.fe_values_ref.get_quadrature(),
- rhs.fe_values_ref.get_update_flags()),
- solution_grads_u_total (rhs.solution_grads_u_total),
- solution_values_p_total (rhs.solution_values_p_total),
- solution_values_t_total (rhs.solution_values_t_total),
- solution_total (rhs.solution_total)
- { }
-
- void reset (void)
- {
- // Is this necessary? Won't the call to fe_values.get_gradient overwrite this data?
- for (unsigned int q=0; q < qf_cell.size(); ++q)
+ const QGauss <dim-1> & qf_face,
+ const UpdateFlags uf_face)
+ :
+ fe_values_ref (fe_cell,
+ qf_cell,
+ uf_cell),
+ fe_face_values_ref (fe_cell,
+ qf_face,
+ uf_face),
+ Nx (qf_cell.size(),
+ std::vector< double >(fe_cell.dofs_per_cell)),
+ symm_grad_Nx (qf_cell.size(),
+ std::vector< SymmetricTensor<2, dim> >(fe_cell.dofs_per_cell)),
+ rhs_values (qf_cell.size(),
+ Vector<double>(dim))
+ { }
+
+ ScratchData_F ( const ScratchData_F & rhs )
+ :
+ fe_values_ref ( rhs.fe_values_ref.get_fe(),
+ rhs.fe_values_ref.get_quadrature(),
+ rhs.fe_values_ref.get_update_flags() ),
+ fe_face_values_ref ( rhs.fe_face_values_ref.get_fe(),
+ rhs.fe_face_values_ref.get_quadrature(),
+ rhs.fe_face_values_ref.get_update_flags() ),
+ Nx (rhs.Nx),
+ symm_grad_Nx (rhs.symm_grad_Nx),
+ rhs_values (rhs.rhs_values)
+ { }
+
+ void reset (void) {
+ for (unsigned int q_point=0; q_point < symm_grad_Nx.size(); ++q_point) {
+ for (unsigned int k=0; k < symm_grad_Nx[q_point].size(); ++k) {
+ Nx[q_point][k] = 0.0;
+ symm_grad_Nx[q_point][k] = 0.0;
+ rhs_values[q_point] = 0.0;
+ }
+ }
+ }
+
+ };
+
+ struct PerTaskData_SC
+ {
+ FullMatrix<double> cell_matrix;
+ std::vector<unsigned int> local_dof_indices;
+
+ // Calculation matrices (auto resized)
+ FullMatrix<double> K_orig;
+ FullMatrix<double> K_pu;
+ FullMatrix<double> K_pt;
+ FullMatrix<double> K_tt;
+ // Calculation matrices (manual resized)
+ FullMatrix<double> K_pt_inv;
+ FullMatrix<double> K_tt_inv;
+ FullMatrix<double> K_con;
+ FullMatrix<double> A;
+ FullMatrix<double> B;
+ FullMatrix<double> C;
+
+ PerTaskData_SC (const unsigned int & dofs_per_cell,
+ const unsigned int & n_u,
+ const unsigned int & n_p,
+ const unsigned int & n_t)
+ :
+ cell_matrix (dofs_per_cell,
+ dofs_per_cell),
+ local_dof_indices (dofs_per_cell),
+ K_pt_inv (n_t, n_p),
+ K_tt_inv (n_t, n_t),
+ K_con (n_u, n_u),
+ A (n_t, n_u),
+ B (n_t, n_u),
+ C (n_p, n_u)
+ { }
+
+ // Choose not to reset any data
+ // The matrix extraction and replacement tools will take care of this
+ void reset(void) { }
+ };
+
+ // Dummy struct for TBB
+ struct ScratchData_SC
+ {
+ ScratchData_SC (void) { }
+ ScratchData_SC (const ScratchData_SC & rhs) { }
+ void reset (void) { }
+ };
+
+ // Dummy struct for TBB
+ struct PerTaskData_UQPH
+ {
+ PerTaskData_UQPH (void) { }
+ void reset(void) { }
+ };
+
+ struct ScratchData_UQPH
+ {
+ FEValues<dim> fe_values_ref;
+ std::vector< Tensor< 2, dim> > solution_grads_u_total;
+ std::vector <double> solution_values_p_total;
+ std::vector <double> solution_values_t_total;
+ const BlockVector <double> & solution_total;
+
+ ScratchData_UQPH (const FiniteElement <dim> & fe_cell,
+ const QGauss <dim> & qf_cell,
+ const UpdateFlags uf_cell,
+ const BlockVector <double> & solution_total)
+ :
+ fe_values_ref (fe_cell,
+ qf_cell,
+ uf_cell),
+ solution_grads_u_total (qf_cell.size()),
+ solution_values_p_total (qf_cell.size()),
+ solution_values_t_total (qf_cell.size()),
+ solution_total (solution_total)
+ { }
+
+ ScratchData_UQPH (const ScratchData_UQPH & rhs)
+ :
+ fe_values_ref (rhs.fe_values_ref.get_fe(),
+ rhs.fe_values_ref.get_quadrature(),
+ rhs.fe_values_ref.get_update_flags()),
+ solution_grads_u_total (rhs.solution_grads_u_total),
+ solution_values_p_total (rhs.solution_values_p_total),
+ solution_values_t_total (rhs.solution_values_t_total),
+ solution_total (rhs.solution_total)
+ { }
+
+ void reset (void)
{
- solution_grads_u_total[q] = 0.0;
- solution_values_p_total[q] = 0.0;
- solution_values_t_total[q] = 0.0;
+ // Is this necessary? Won't the call to fe_values.get_gradient overwrite this data?
+ for (unsigned int q=0; q < qf_cell.size(); ++q)
+ {
+ solution_grads_u_total[q] = 0.0;
+ solution_values_p_total[q] = 0.0;
+ solution_values_t_total[q] = 0.0;
+ }
}
- }
- };
-
- // === METHODS ===
-
- /// \brief Print out a greeting for the user
- void make_grid (void);
- /// \brief Setup the Finite Element system to be solved
- void system_setup (void);
- void determine_component_extractors(void);
-
- /// \brief Assemble the system and right hand side matrices using multi-threading
- void assemble_system_K (void);
- void assemble_system_K_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_K & scratch,
- PerTaskData_K & data);
- void copy_local_to_global_K (const PerTaskData_K & data);
- void assemble_system_F (void);
- void assemble_system_F_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_F & scratch,
- PerTaskData_F & data);
- void copy_local_to_global_F (const PerTaskData_F & data);
- void assemble_SC (void);
- void assemble_SC_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_SC & scratch,
- PerTaskData_SC & data);
- void copy_local_to_global_SC (const PerTaskData_SC & data);
- /// \brief Apply Dirichlet boundary values
- void make_constraints (const int & it_nr,
- ConstraintMatrix & constraints);
-
- // /// \brief Setup the quadrature point history for each cell
- void setup_qph(void);
- // /// \brief Update the quadrature points stress and strain values, and fibre directions
- void update_qph_incremental ( const BlockVector <double> & solution_delta );
- void update_qph_incremental_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_UQPH & scratch,
- PerTaskData_UQPH & data);
- void copy_local_to_global_UQPH (const PerTaskData_UQPH & data) {}
- /// \brief Solve for the displacement using a Newton-Rhapson method
- void solve_nonlinear_timestep (BlockVector <double> & solution_delta);
- void solve_linear_system (BlockVector <double> & newton_update);
-
- /// \brief Error measurement
- void get_error_res (const BlockVector <double> & residual, BlockVector <double> & error_res);
- void get_error_update (const BlockVector <double> & newton_update, BlockVector <double> & error_update);
- double get_error_dil (void);
-
- // Solution
- BlockVector <double> get_solution_total (const BlockVector <double> & solution_delta);
-
- // Postprocessing
- void output_results(void);
-
- // === ATTRIBUTES ===
- // Parameters
- Parameters::AllParameters parameters;
-
- // Geometry
- Triangulation<dim> triangulation; // Describes the triangulation
-
- // Time
- Time time;
- TimerOutput timer;
-
- // === Quadrature points ===
- std::vector< PointHistory <dim> > quadrature_point_history; // Quadrature point history
-
- // === Finite element system ===
- DoFHandler<dim> dof_handler_ref; // Describes the degrees of freedom
- const unsigned int degree;
- const FESystem<dim> fe; // Describes the global FE system
-
- unsigned int dofs_per_cell; // Number of degrees of freedom on each cell
- const FEValuesExtractors::Vector u_fe;
- const FEValuesExtractors::Scalar p_fe;
- const FEValuesExtractors::Scalar t_fe;
-
- // Block description
- static const unsigned int n_blocks = 3;
- static const unsigned int n_components = dim + 2;
- static const unsigned int first_u_component = 0;
- static const unsigned int p_component = dim;
- static const unsigned int t_component = dim + 1;
-
- enum {u_dof=0 , p_dof, t_dof};
- std::vector<unsigned int> dofs_per_block;
- std::vector<unsigned int> element_indices_u;
- std::vector<unsigned int> element_indices_p;
- std::vector<unsigned int> element_indices_t;
-
- // === Quadrature ===
- QGauss<dim> qf_cell; // Cell quadrature formula
- QGauss<dim-1> qf_face; // Face quadrature formula
- unsigned int n_q_points; // Number of quadrature points in a cell
- unsigned int n_q_points_f; // Number of quadrature points in a face
-
- // === Stiffness matrix setup ====
- ConstraintMatrix constraints; // Matrix to keep track of all constraints
- BlockSparsityPattern sparsity_pattern; // Sparsity pattern for the stiffness matrix
- BlockSparseMatrix <double> tangent_matrix; // Global stiffness matrix
- BlockVector <double> residual; // Holds the residual vector
- BlockVector <double> solution_n; // Holds the solution vector: Total displacement over all time-steps
-};
+ };
+
+ // === METHODS ===
+
+ /// \brief Print out a greeting for the user
+ void make_grid (void);
+ /// \brief Setup the Finite Element system to be solved
+ void system_setup (void);
+ void determine_component_extractors(void);
+
+ /// \brief Assemble the system and right hand side matrices using multi-threading
+ void assemble_system_K (void);
+ void assemble_system_K_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_K & scratch,
+ PerTaskData_K & data);
+ void copy_local_to_global_K (const PerTaskData_K & data);
+ void assemble_system_F (void);
+ void assemble_system_F_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_F & scratch,
+ PerTaskData_F & data);
+ void copy_local_to_global_F (const PerTaskData_F & data);
+ void assemble_SC (void);
+ void assemble_SC_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_SC & scratch,
+ PerTaskData_SC & data);
+ void copy_local_to_global_SC (const PerTaskData_SC & data);
+ /// \brief Apply Dirichlet boundary values
+ void make_constraints (const int & it_nr,
+ ConstraintMatrix & constraints);
+
+ // /// \brief Setup the quadrature point history for each cell
+ void setup_qph(void);
+ // /// \brief Update the quadrature points stress and strain values, and fibre directions
+ void update_qph_incremental ( const BlockVector <double> & solution_delta );
+ void update_qph_incremental_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_UQPH & scratch,
+ PerTaskData_UQPH & data);
+ void copy_local_to_global_UQPH (const PerTaskData_UQPH & data) {}
+ /// \brief Solve for the displacement using a Newton-Rhapson method
+ void solve_nonlinear_timestep (BlockVector <double> & solution_delta);
+ void solve_linear_system (BlockVector <double> & newton_update);
+
+ /// \brief Error measurement
+ void get_error_res (const BlockVector <double> & residual, BlockVector <double> & error_res);
+ void get_error_update (const BlockVector <double> & newton_update, BlockVector <double> & error_update);
+ double get_error_dil (void);
+
+ // Solution
+ BlockVector <double> get_solution_total (const BlockVector <double> & solution_delta);
+
+ // Postprocessing
+ void output_results(void);
+
+ // === ATTRIBUTES ===
+ // Parameters
+ Parameters::AllParameters parameters;
+
+ // Geometry
+ Triangulation<dim> triangulation; // Describes the triangulation
+
+ // Time
+ Time time;
+ TimerOutput timer;
+
+ // === Quadrature points ===
+ std::vector< PointHistory <dim> > quadrature_point_history; // Quadrature point history
+
+ // === Finite element system ===
+ DoFHandler<dim> dof_handler_ref; // Describes the degrees of freedom
+ const unsigned int degree;
+ const FESystem<dim> fe; // Describes the global FE system
+
+ unsigned int dofs_per_cell; // Number of degrees of freedom on each cell
+ const FEValuesExtractors::Vector u_fe;
+ const FEValuesExtractors::Scalar p_fe;
+ const FEValuesExtractors::Scalar t_fe;
+
+ // Block description
+ static const unsigned int n_blocks = 3;
+ static const unsigned int n_components = dim + 2;
+ static const unsigned int first_u_component = 0;
+ static const unsigned int p_component = dim;
+ static const unsigned int t_component = dim + 1;
+
+ enum {u_dof=0 , p_dof, t_dof};
+ std::vector<unsigned int> dofs_per_block;
+ std::vector<unsigned int> element_indices_u;
+ std::vector<unsigned int> element_indices_p;
+ std::vector<unsigned int> element_indices_t;
+
+ // === Quadrature ===
+ QGauss<dim> qf_cell; // Cell quadrature formula
+ QGauss<dim-1> qf_face; // Face quadrature formula
+ unsigned int n_q_points; // Number of quadrature points in a cell
+ unsigned int n_q_points_f; // Number of quadrature points in a face
+
+ // === Stiffness matrix setup ====
+ ConstraintMatrix constraints; // Matrix to keep track of all constraints
+ BlockSparsityPattern sparsity_pattern; // Sparsity pattern for the stiffness matrix
+ BlockSparseMatrix <double> tangent_matrix; // Global stiffness matrix
+ BlockVector <double> residual; // Holds the residual vector
+ BlockVector <double> solution_n; // Holds the solution vector: Total displacement over all time-steps
+ };
// @sect3{Implementation of the <code>Solid</code> class}
// @sect4{Public interface}
-template <int dim>
-Solid<dim>::Solid (const std::string & input_file)
- :
- parameters (input_file),
- triangulation (Triangulation<dim>::maximum_smoothing),
- time (parameters.end_time, parameters.delta_t),
- timer (std::cout,
- TimerOutput::summary,
- TimerOutput::wall_times),
- dof_handler_ref (triangulation),
- degree (parameters.poly_degree),
- fe (FE_Q<dim>(parameters.poly_degree), dim, // displacement
- FE_DGPMonomial<dim>(parameters.poly_degree-1), 1, // pressure
- FE_DGPMonomial<dim>(parameters.poly_degree-1), 1), // dilatation
- u_fe (first_u_component),
- p_fe (p_component),
- t_fe (t_component),
- dofs_per_block (n_blocks),
- qf_cell (parameters.quad_order),
- qf_face (parameters.quad_order)
-{
+ template <int dim>
+ Solid<dim>::Solid (const std::string & input_file)
+ :
+ parameters (input_file),
+ triangulation (Triangulation<dim>::maximum_smoothing),
+ time (parameters.end_time, parameters.delta_t),
+ timer (std::cout,
+ TimerOutput::summary,
+ TimerOutput::wall_times),
+ dof_handler_ref (triangulation),
+ degree (parameters.poly_degree),
+ fe (FE_Q<dim>(parameters.poly_degree), dim, // displacement
+ FE_DGPMonomial<dim>(parameters.poly_degree-1), 1, // pressure
+ FE_DGPMonomial<dim>(parameters.poly_degree-1), 1), // dilatation
+ u_fe (first_u_component),
+ p_fe (p_component),
+ t_fe (t_component),
+ dofs_per_block (n_blocks),
+ qf_cell (parameters.quad_order),
+ qf_face (parameters.quad_order)
+ {
n_q_points = qf_cell.size();
n_q_points_f = qf_face.size();
dofs_per_cell = fe.dofs_per_cell;
determine_component_extractors();
-}
+ }
-template <int dim>
-Solid<dim>::~Solid (void)
-{
+ template <int dim>
+ Solid<dim>::~Solid (void)
+ {
dof_handler_ref.clear ();
-}
+ }
-template <int dim>
-void Solid<dim>::run (void)
-{
- // Pre-processing
+ template <int dim>
+ void Solid<dim>::run (void)
+ {
+ // Pre-processing
make_grid ();
system_setup ();
output_results (); // Output initial grid position
solution_delta.collect_sizes ();
while (time.current() <= time.end()) {
- solution_delta = 0.0;
+ solution_delta = 0.0;
- // Solve step and update total solution vector
- solve_nonlinear_timestep (solution_delta);
- solution_n += solution_delta;
+ // Solve step and update total solution vector
+ solve_nonlinear_timestep (solution_delta);
+ solution_n += solution_delta;
- output_results ();
- time.increment();
+ output_results ();
+ time.increment();
}
-}
+ }
// @sect4{Solid::make_grid}
-template <int dim>
-void Solid<dim>::make_grid (void)
-{
+ template <int dim>
+ void Solid<dim>::make_grid (void)
+ {
GridGenerator::hyper_rectangle ( triangulation,
- Point<dim> (0.0, 0.0, 0.0),
- Point<dim> (1.0, 1.0, 1.0),
- true );
+ Point<dim> (0.0, 0.0, 0.0),
+ Point<dim> (1.0, 1.0, 1.0),
+ true );
GridTools::scale (parameters.scale, triangulation);
- // Need to refine at least once for the indentation problem
+ // Need to refine at least once for the indentation problem
if (parameters.global_refinement == 0) triangulation.refine_global (1);
else triangulation.refine_global (parameters.global_refinement);
- // Apply different BC's to a patch on the top surface
+ // Apply different BC's to a patch on the top surface
typename Triangulation<dim>::active_cell_iterator
- cell = triangulation.begin_active(),
- endc = triangulation.end();
+ cell = triangulation.begin_active(),
+ endc = triangulation.end();
for (; cell!=endc; ++cell)
- {
+ {
if (cell->at_boundary() == true) {
- for (unsigned int face=0; face < GeometryInfo<dim>::faces_per_cell; ++face) {
- // Find faces on the +y surface
- if ( cell->face(face)->at_boundary() == true
- && cell->face(face)->center()[2] == 1.0*parameters.scale)
- {
- if ( cell->face(face)->center()[0] < 0.5*parameters.scale
- && cell->face(face)->center()[1] < 0.5*parameters.scale)
- {
- cell->face(face)->set_boundary_indicator (6); // Set a new boundary id on a patch
- }
- }
- }
+ for (unsigned int face=0; face < GeometryInfo<dim>::faces_per_cell; ++face) {
+ // Find faces on the +y surface
+ if ( cell->face(face)->at_boundary() == true
+ && cell->face(face)->center()[2] == 1.0*parameters.scale)
+ {
+ if ( cell->face(face)->center()[0] < 0.5*parameters.scale
+ && cell->face(face)->center()[1] < 0.5*parameters.scale)
+ {
+ cell->face(face)->set_boundary_indicator (6); // Set a new boundary id on a patch
+ }
+ }
+ }
}
- }
-}
+ }
+ }
// @sect4{Solid::system_setup}
-template <int dim>
-void Solid<dim>::system_setup (void)
-{
+ template <int dim>
+ void Solid<dim>::system_setup (void)
+ {
timer.enter_subsection ("Setup system");
- // Number of components per block
+ // Number of components per block
std::vector<unsigned int> block_component (n_components, u_dof); // Displacement
block_component[p_component] = p_dof; // Pressure
block_component[t_component] = t_dof; // Dilatation
- // Setup DOF handler
+ // Setup DOF handler
dof_handler_ref.distribute_dofs (fe);
DoFRenumbering::Cuthill_McKee (dof_handler_ref);
DoFRenumbering::component_wise (dof_handler_ref, block_component);
- // Count number of dofs per block
+ // Count number of dofs per block
DoFTools::count_dofs_per_block (dof_handler_ref, dofs_per_block, block_component);
std::cout
- << "Triangulation:"
- << "\n\t Number of active cells: " << triangulation.n_active_cells()
- << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
- << std::endl;
-
- // the global system matrix will have the following structure
- // | K'_uu | K_up | 0 | | dU_u | | dR_u |
- // K = | K_pu | K_tt^-1 | K_pt^-1 | , dU = | dU_p | , dR = | dR_p |
- // | 0 | K_tp | K_tt | | dU_t | | dR_t |
- // reflect this structure in the sparsity pattern
+ << "Triangulation:"
+ << "\n\t Number of active cells: " << triangulation.n_active_cells()
+ << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs()
+ << std::endl;
+
+ // the global system matrix will have the following structure
+ // | K'_uu | K_up | 0 | | dU_u | | dR_u |
+ // K = | K_pu | K_tt^-1 | K_pt^-1 | , dU = | dU_p | , dR = | dR_p |
+ // | 0 | K_tp | K_tt | | dU_t | | dR_t |
+ // reflect this structure in the sparsity pattern
Table<2,DoFTools::Coupling> coupling (n_components, n_components);
for (unsigned int ii = 0; ii < n_components; ++ii) {
- for (unsigned int jj = ii; jj < n_components; ++jj) {
- if ((ii < p_component) && (jj == t_component)) {
- coupling[jj][ii] = DoFTools::none;
- coupling[ii][jj] = DoFTools::none;
- }
- else {
- coupling[ii][jj] = DoFTools::always;
- coupling[jj][ii] = DoFTools::always;
- }
- }
+ for (unsigned int jj = ii; jj < n_components; ++jj) {
+ if ((ii < p_component) && (jj == t_component)) {
+ coupling[jj][ii] = DoFTools::none;
+ coupling[ii][jj] = DoFTools::none;
+ }
+ else {
+ coupling[ii][jj] = DoFTools::always;
+ coupling[jj][ii] = DoFTools::always;
+ }
+ }
}
- // Setup system matrix
+ // Setup system matrix
tangent_matrix.clear ();
{
- const unsigned int n_dofs_u = dofs_per_block[u_dof];
- const unsigned int n_dofs_p = dofs_per_block[p_dof];
- const unsigned int n_dofs_t = dofs_per_block[t_dof];
+ const unsigned int n_dofs_u = dofs_per_block[u_dof];
+ const unsigned int n_dofs_p = dofs_per_block[p_dof];
+ const unsigned int n_dofs_t = dofs_per_block[t_dof];
- BlockCompressedSimpleSparsityPattern csp (n_blocks, n_blocks);
+ BlockCompressedSimpleSparsityPattern csp (n_blocks, n_blocks);
- csp.block(u_dof,u_dof).reinit (n_dofs_u, n_dofs_u);
- csp.block(u_dof,p_dof).reinit (n_dofs_u, n_dofs_p);
- csp.block(u_dof,t_dof).reinit (n_dofs_u, n_dofs_t);
+ csp.block(u_dof,u_dof).reinit (n_dofs_u, n_dofs_u);
+ csp.block(u_dof,p_dof).reinit (n_dofs_u, n_dofs_p);
+ csp.block(u_dof,t_dof).reinit (n_dofs_u, n_dofs_t);
- csp.block(p_dof,u_dof).reinit (n_dofs_p, n_dofs_u);
- csp.block(p_dof,p_dof).reinit (n_dofs_p, n_dofs_p);
- csp.block(p_dof,t_dof).reinit (n_dofs_p, n_dofs_t);
+ csp.block(p_dof,u_dof).reinit (n_dofs_p, n_dofs_u);
+ csp.block(p_dof,p_dof).reinit (n_dofs_p, n_dofs_p);
+ csp.block(p_dof,t_dof).reinit (n_dofs_p, n_dofs_t);
- csp.block(t_dof,u_dof).reinit (n_dofs_t, n_dofs_u);
- csp.block(t_dof,p_dof).reinit (n_dofs_t, n_dofs_p);
- csp.block(t_dof,t_dof).reinit (n_dofs_t, n_dofs_t);
- csp.collect_sizes();
+ csp.block(t_dof,u_dof).reinit (n_dofs_t, n_dofs_u);
+ csp.block(t_dof,p_dof).reinit (n_dofs_t, n_dofs_p);
+ csp.block(t_dof,t_dof).reinit (n_dofs_t, n_dofs_t);
+ csp.collect_sizes();
- DoFTools::make_sparsity_pattern (dof_handler_ref, csp);
- // DoFTools::make_sparsity_pattern (dof_handler_ref, csp, constraints, false);
- // DoFTools::make_sparsity_pattern (dof_handler_ref, coupling, csp, constraints, false);
- sparsity_pattern.copy_from (csp);
+ DoFTools::make_sparsity_pattern (dof_handler_ref, csp);
+ // DoFTools::make_sparsity_pattern (dof_handler_ref, csp, constraints, false);
+ // DoFTools::make_sparsity_pattern (dof_handler_ref, coupling, csp, constraints, false);
+ sparsity_pattern.copy_from (csp);
}
tangent_matrix.reinit (sparsity_pattern);
- // Setup storage vectors
+ // Setup storage vectors
residual.reinit (dofs_per_block);
residual.collect_sizes ();
solution_n.collect_sizes ();
solution_n.block(t_dof) = 1.0; // Dilatation is 1 in the initial configuration
- // Set up the quadrature point history
+ // Set up the quadrature point history
setup_qph ();
timer.leave_subsection();
-}
+ }
// A way to extract subblocks from the matrix
-template <int dim>
-void Solid<dim>::determine_component_extractors(void)
-{
+ template <int dim>
+ void Solid<dim>::determine_component_extractors(void)
+ {
element_indices_u.clear();
element_indices_p.clear();
element_indices_t.clear();
for (unsigned int k=0; k < fe.dofs_per_cell; ++k) {
- // 0 = u, 1 = p, 2 = dilatation interpolation fields
- const unsigned int k_group = fe.system_to_base_index(k).first.first;
- if (k_group == u_dof) {
- element_indices_u.push_back(k);
- }
- else if (k_group == p_dof) {
- element_indices_p.push_back(k);
- }
- else if (k_group == t_dof) {
- element_indices_t.push_back(k);
- }
- else {
- Assert (k_group <= t_dof, ExcInternalError());
- }
+ // 0 = u, 1 = p, 2 = dilatation interpolation fields
+ const unsigned int k_group = fe.system_to_base_index(k).first.first;
+ if (k_group == u_dof) {
+ element_indices_u.push_back(k);
+ }
+ else if (k_group == p_dof) {
+ element_indices_p.push_back(k);
+ }
+ else if (k_group == t_dof) {
+ element_indices_t.push_back(k);
+ }
+ else {
+ Assert (k_group <= t_dof, ExcInternalError());
+ }
}
-}
+ }
// @sect4{Solid::setup_qph}
-template <int dim>
-void Solid<dim>::setup_qph (void)
-{
+ template <int dim>
+ void Solid<dim>::setup_qph (void)
+ {
std::cout << " Setting up quadrature point data..." << std::endl;
{
- typename Triangulation<dim>::active_cell_iterator
- cell = triangulation.begin_active(),
- endc = triangulation.end();
-
- unsigned int our_cells = 0;
- for (; cell != endc; ++cell) {
- cell->clear_user_pointer();
- ++our_cells;
- }
-
- {
- std::vector<PointHistory <dim> > tmp;
- tmp.swap(quadrature_point_history);
- }
-
- quadrature_point_history.resize(our_cells * n_q_points);
-
- unsigned int history_index = 0;
- for (cell = triangulation.begin_active(); cell != endc; ++cell) {
- cell->set_user_pointer(&quadrature_point_history[history_index]);
- history_index += n_q_points;
- }
-
- Assert(history_index == quadrature_point_history.size(), ExcInternalError());
+ typename Triangulation<dim>::active_cell_iterator
+ cell = triangulation.begin_active(),
+ endc = triangulation.end();
+
+ unsigned int our_cells = 0;
+ for (; cell != endc; ++cell) {
+ cell->clear_user_pointer();
+ ++our_cells;
+ }
+
+ {
+ std::vector<PointHistory <dim> > tmp;
+ tmp.swap(quadrature_point_history);
+ }
+
+ quadrature_point_history.resize(our_cells * n_q_points);
+
+ unsigned int history_index = 0;
+ for (cell = triangulation.begin_active(); cell != endc; ++cell) {
+ cell->set_user_pointer(&quadrature_point_history[history_index]);
+ history_index += n_q_points;
+ }
+
+ Assert(history_index == quadrature_point_history.size(), ExcInternalError());
}
- // Setup initial data
+ // Setup initial data
typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler_ref.begin_active(),
- endc = dof_handler_ref.end();
+ cell = dof_handler_ref.begin_active(),
+ endc = dof_handler_ref.end();
for (; cell != endc; ++cell) {
- PointHistory<dim>* lqph = reinterpret_cast<PointHistory<dim>*> (cell->user_pointer());
- Assert(lqph >= &quadrature_point_history.front(), ExcInternalError());
- Assert(lqph < &quadrature_point_history.back(), ExcInternalError());
-
- // Setup any initial information at displacement gauss points
- for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) {
- lqph[q_point].setup_lqp( parameters );
- }
+ PointHistory<dim>* lqph = reinterpret_cast<PointHistory<dim>*> (cell->user_pointer());
+ Assert(lqph >= &quadrature_point_history.front(), ExcInternalError());
+ Assert(lqph < &quadrature_point_history.back(), ExcInternalError());
+
+ // Setup any initial information at displacement gauss points
+ for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) {
+ lqph[q_point].setup_lqp( parameters );
+ }
}
-}
+ }
// @sect4{Solid::update_qph_incremental}
-template <int dim>
-void Solid<dim>::update_qph_incremental (const BlockVector <double> & solution_delta)
-{
+ template <int dim>
+ void Solid<dim>::update_qph_incremental (const BlockVector <double> & solution_delta)
+ {
timer.enter_subsection("Update QPH data");
std::cout << "Update QPH data..."<< std::endl;
- // Get total solution as it stands at this update increment
+ // Get total solution as it stands at this update increment
const BlockVector <double> solution_total = get_solution_total(solution_delta);
const UpdateFlags uf_UQPH ( update_values | update_gradients );
PerTaskData_UQPH per_task_data_UQPH;
solution_total);
WorkStream::run ( dof_handler_ref.begin_active(),
- dof_handler_ref.end(),
- *this,
- &Solid::update_qph_incremental_one_cell,
- &Solid::copy_local_to_global_UQPH,
- scratch_data_UQPH,
- per_task_data_UQPH);
+ dof_handler_ref.end(),
+ *this,
+ &Solid::update_qph_incremental_one_cell,
+ &Solid::copy_local_to_global_UQPH,
+ scratch_data_UQPH,
+ per_task_data_UQPH);
timer.leave_subsection();
-}
+ }
-template <int dim>
-void Solid<dim>::update_qph_incremental_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_UQPH & scratch,
- PerTaskData_UQPH & data)
-{
+ template <int dim>
+ void Solid<dim>::update_qph_incremental_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_UQPH & scratch,
+ PerTaskData_UQPH & data)
+ {
PointHistory<dim>* lqph = reinterpret_cast<PointHistory<dim>*> (cell->user_pointer());
Assert(lqph >= &quadrature_point_history.front(), ExcInternalError());
Assert(lqph < &quadrature_point_history.back(), ExcInternalError());
Assert(scratch.solution_values_p_total.size() == n_q_points, ExcInternalError());
Assert(scratch.solution_values_t_total.size() == n_q_points, ExcInternalError());
- // Find the values and gradients at quadrature points inside the current cell
+ // Find the values and gradients at quadrature points inside the current cell
scratch.fe_values_ref.reinit(cell);
scratch.fe_values_ref[u_fe].get_function_gradients (scratch.solution_total, scratch.solution_grads_u_total);
scratch.fe_values_ref[p_fe].get_function_values (scratch.solution_total, scratch.solution_values_p_total);
scratch.fe_values_ref[t_fe].get_function_values (scratch.solution_total,scratch. solution_values_t_total);
- // === UPDATE DATA AT EACH GAUSS POINT ===
- // Update displacement and deformation gradient at all quadrature points
+ // === UPDATE DATA AT EACH GAUSS POINT ===
+ // Update displacement and deformation gradient at all quadrature points
for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) {
- lqph[q_point].update_values (scratch.solution_grads_u_total [q_point],
- scratch.solution_values_p_total[q_point],
- scratch.solution_values_t_total[q_point]);
+ lqph[q_point].update_values (scratch.solution_grads_u_total [q_point],
+ scratch.solution_values_p_total[q_point],
+ scratch.solution_values_t_total[q_point]);
}
-}
+ }
// @sect4{Solid::solve_nonlinear_timestep}
-template <int dim>
-void Solid<dim>::solve_nonlinear_timestep (BlockVector <double> & solution_delta)
-{
- // timer.enter_subsection("Nonlinear solver");
+ template <int dim>
+ void Solid<dim>::solve_nonlinear_timestep (BlockVector <double> & solution_delta)
+ {
+ // timer.enter_subsection("Nonlinear solver");
std::cout
- << "Timestep " << time.get_timestep()
- << std::endl;
+ << "Timestep " << time.get_timestep()
+ << std::endl;
- // Newton update vector
+ // Newton update vector
BlockVector <double> newton_update (dofs_per_block);
newton_update.collect_sizes ();
- // Solution error vectors
+ // Solution error vectors
BlockVector <double> soln_error_res (dofs_per_block); // Holds the true residual vector
BlockVector <double> soln_error_update (dofs_per_block); // Holds the update error vector
soln_error_res.collect_sizes ();
double res_u = 0.0, res_f = 0.0;
double res_u_0 = 1.0, res_f_0 = 1.0;
for (unsigned int it_nr=0; it_nr < parameters.max_iterations_NR; ++ it_nr)
- {
+ {
std::cout
- << std::endl
- << "Newton iteration: " << it_nr
- << std::endl;
+ << std::endl
+ << "Newton iteration: " << it_nr
+ << std::endl;
tangent_matrix = 0.0;
residual = 0.0;
- // Check residual
+ // Check residual
make_constraints (it_nr, constraints); // Make boundary conditions
assemble_system_F (); // Assemble RHS
get_error_res(residual, soln_error_res);
- // Residual scaling factors
+ // Residual scaling factors
res_f = soln_error_res.block(u_dof).l2_norm();
if (it_nr == 0) res_f_0 = res_f;
- // Check for solution convergence
+ // Check for solution convergence
if ( it_nr > 0
- && res_u/res_u_0 <= parameters.tol_u
- && res_f/res_f_0 <= parameters.tol_f)
- {
+ && res_u/res_u_0 <= parameters.tol_u
+ && res_f/res_f_0 <= parameters.tol_f)
+ {
std::cout
- << std::endl
- << "Solution for timestep " << time.get_timestep()
- << " converged on Newton iteration " << it_nr-1 << "."
- << std::endl
- << "Relative displacement error: " << res_u/res_u_0
- << "\t Relative force error: " << res_f/res_f_0
- << "\t Dilatation error: " << get_error_dil()
- << std::endl << std::endl;
-
- // timer.leave_subsection();
+ << std::endl
+ << "Solution for timestep " << time.get_timestep()
+ << " converged on Newton iteration " << it_nr-1 << "."
+ << std::endl
+ << "Relative displacement error: " << res_u/res_u_0
+ << "\t Relative force error: " << res_f/res_f_0
+ << "\t Dilatation error: " << get_error_dil()
+ << std::endl << std::endl;
+
+ // timer.leave_subsection();
return;
- }
+ }
- // No convergence -> continue with calculations
- // Assemble stiffness matrix
+ // No convergence -> continue with calculations
+ // Assemble stiffness matrix
assemble_system_K ();
- // Do the static condensation to make K'_uu, and put K_pt^{-1}
- // in the K_pt block and K_tt^{-1} in the K_pp block
+ // Do the static condensation to make K'_uu, and put K_pt^{-1}
+ // in the K_pt block and K_tt^{-1} in the K_pp block
assemble_SC();
- // Do the static condensation to make K'_uu, and put K_pt^{-1}
- // in the K_pt block and K_tt^{-1} in the K_pp block
+ // Do the static condensation to make K'_uu, and put K_pt^{-1}
+ // in the K_pt block and K_tt^{-1} in the K_pp block
assemble_SC();
constraints.condense (tangent_matrix, residual); // Apply BC's
solve_linear_system (newton_update);
constraints.distribute(newton_update); // Populate the constrained DOF's with their values
- // Newton update error
+ // Newton update error
get_error_update(newton_update, soln_error_update);
res_u = soln_error_update.block(u_dof).l2_norm();
- // Residual scaling factors
+ // Residual scaling factors
if (it_nr == 0) res_u_0 = res_u;
std::cout
- << "Nonlinear system error: "
- << std::endl << std::scientific
- << " Solution update \t ||dU||: " << soln_error_update.l2_norm()
- << "\t ||dU_u||: " << soln_error_update.block(u_dof).l2_norm()
- << "\t ||dU_p||: " << soln_error_update.block(p_dof).l2_norm()
- << "\t ||dU_t||: " << soln_error_update.block(t_dof).l2_norm()
- << std::endl;
+ << "Nonlinear system error: "
+ << std::endl << std::scientific
+ << " Solution update \t ||dU||: " << soln_error_update.l2_norm()
+ << "\t ||dU_u||: " << soln_error_update.block(u_dof).l2_norm()
+ << "\t ||dU_p||: " << soln_error_update.block(p_dof).l2_norm()
+ << "\t ||dU_t||: " << soln_error_update.block(t_dof).l2_norm()
+ << std::endl;
std::cout << std::scientific
<< " Residual \t ||dF||: " << soln_error_res.l2_norm()
<< "\t ||dR_u||: " << soln_error_res.block(u_dof).l2_norm()
<< "\t Dilatation error: " << get_error_dil()
<< std::endl;
- // Update and continue iterating
+ // Update and continue iterating
solution_delta += newton_update; // Update current solution
update_qph_incremental (solution_delta); // Update quadrature point information
- }
+ }
throw(ExcMessage("No convergence in nonlinear solver!"));
-}
+ }
-template <int dim>
-void Solid<dim>::get_error_res (const BlockVector <double> & residual, BlockVector <double> & error_res)
-{
+ template <int dim>
+ void Solid<dim>::get_error_res (const BlockVector <double> & residual, BlockVector <double> & error_res)
+ {
for (unsigned int i=0; i < dof_handler_ref.n_dofs(); ++i)
- if (!constraints.is_constrained(i))
- error_res(i) = residual(i);
-}
+ if (!constraints.is_constrained(i))
+ error_res(i) = residual(i);
+ }
-template <int dim>
-void Solid<dim>::get_error_update (const BlockVector <double> & newton_update, BlockVector <double> & error_update)
-{
+ template <int dim>
+ void Solid<dim>::get_error_update (const BlockVector <double> & newton_update, BlockVector <double> & error_update)
+ {
for (unsigned int i=0; i < dof_handler_ref.n_dofs(); ++i)
- if (!constraints.is_constrained(i))
- error_update(i) = newton_update(i);
-}
+ if (!constraints.is_constrained(i))
+ error_update(i) = newton_update(i);
+ }
-template <int dim>
-double Solid<dim>::get_error_dil (void)
-{
+ template <int dim>
+ double Solid<dim>::get_error_dil (void)
+ {
double v_e = 0.0; // Volume in current configuration
double V_e = 0.0; // Volume in reference configuration
FEValues<dim> fe_values_ref (fe, qf_cell, update_JxW_values);
typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler_ref.begin_active(),
- endc = dof_handler_ref.end();
+ cell = dof_handler_ref.begin_active(),
+ endc = dof_handler_ref.end();
for (; cell != endc; ++cell) {
- fe_values_ref.reinit (cell);
- PointHistory<dim>* lqph = reinterpret_cast<PointHistory<dim>*> (cell->user_pointer());
- Assert(lqph >= &quadrature_point_history.front(), ExcInternalError());
- Assert(lqph < &quadrature_point_history.back(), ExcInternalError());
-
- for (unsigned int q_point=0; q_point < n_q_points; ++q_point) {
- v_e += lqph[q_point].get_dilatation() * fe_values_ref.JxW(q_point);
- V_e += fe_values_ref.JxW(q_point);
- }
+ fe_values_ref.reinit (cell);
+ PointHistory<dim>* lqph = reinterpret_cast<PointHistory<dim>*> (cell->user_pointer());
+ Assert(lqph >= &quadrature_point_history.front(), ExcInternalError());
+ Assert(lqph < &quadrature_point_history.back(), ExcInternalError());
+
+ for (unsigned int q_point=0; q_point < n_q_points; ++q_point) {
+ v_e += lqph[q_point].get_dilatation() * fe_values_ref.JxW(q_point);
+ V_e += fe_values_ref.JxW(q_point);
+ }
}
return std::abs((v_e - V_e)/V_e); // Difference between initial and current volume
-}
+ }
// Solution (valid at any Newton step)
-template <int dim>
-BlockVector <double> Solid<dim>::get_solution_total (const BlockVector <double> & solution_delta)
-{
+ template <int dim>
+ BlockVector <double> Solid<dim>::get_solution_total (const BlockVector <double> & solution_delta)
+ {
BlockVector <double> solution_total (solution_n);
solution_total += solution_delta;
return solution_total;
-}
+ }
// @sect4{Solid::solve_linear_system}
-template <int dim>
-void Solid<dim>::solve_linear_system (BlockVector <double> & newton_update)
-{
+ template <int dim>
+ void Solid<dim>::solve_linear_system (BlockVector <double> & newton_update)
+ {
std::cout << "Solve linear system..." << std::endl;
BlockVector <double> A (dofs_per_block);
A.collect_sizes ();
B.collect_sizes ();
- // | K'_uu | K_up | 0 | | dU_u | | dR_u |
- // K = | K_pu | K_tt^-1 | K_pt^-1 | , dU = | dU_p | , dR = | dR_p |
- // | 0 | K_tp | K_tt | | dU_t | | dR_t |
+ // | K'_uu | K_up | 0 | | dU_u | | dR_u |
+ // K = | K_pu | K_tt^-1 | K_pt^-1 | , dU = | dU_p | , dR = | dR_p |
+ // | 0 | K_tp | K_tt | | dU_t | | dR_t |
- // Solve for du
+ // Solve for du
{
- // K'uu du = Ru − Kup Ktp^-1 (Rt − Ktt Kpt^{-1} Rp)
- tangent_matrix.block(p_dof, t_dof).vmult(A.block(t_dof), residual.block(p_dof));
- tangent_matrix.block(t_dof, t_dof).vmult (B.block(t_dof), A.block(t_dof));
- A.block(t_dof).equ(1.0, residual.block(t_dof), -1.0, B.block(t_dof));
- tangent_matrix.block(p_dof, t_dof).Tvmult(A.block(p_dof), A.block(t_dof));
- tangent_matrix.block(u_dof, p_dof).vmult(A.block(u_dof), A.block(p_dof));
- residual.block(u_dof) -= A.block(u_dof);
+ // K'uu du = Ru − Kup Ktp^-1 (Rt − Ktt Kpt^{-1} Rp)
+ tangent_matrix.block(p_dof, t_dof).vmult(A.block(t_dof), residual.block(p_dof));
+ tangent_matrix.block(t_dof, t_dof).vmult (B.block(t_dof), A.block(t_dof));
+ A.block(t_dof).equ(1.0, residual.block(t_dof), -1.0, B.block(t_dof));
+ tangent_matrix.block(p_dof, t_dof).Tvmult(A.block(p_dof), A.block(t_dof));
+ tangent_matrix.block(u_dof, p_dof).vmult(A.block(u_dof), A.block(p_dof));
+ residual.block(u_dof) -= A.block(u_dof);
- timer.enter_subsection("Linear solver");
- if (parameters.type_lin == "CG")
+ timer.enter_subsection("Linear solver");
+ if (parameters.type_lin == "CG")
{
- const int solver_its = tangent_matrix.block(u_dof, u_dof).m() * parameters.max_iterations_lin;
- const double tol_sol = parameters.tol_lin * residual.block(u_dof).l2_norm();
+ const int solver_its = tangent_matrix.block(u_dof, u_dof).m() * parameters.max_iterations_lin;
+ const double tol_sol = parameters.tol_lin * residual.block(u_dof).l2_norm();
- SolverControl solver_control (solver_its , tol_sol);
+ SolverControl solver_control (solver_its , tol_sol);
- GrowingVectorMemory < Vector<double> > GVM;
- SolverCG < Vector<double> > solver_CG (solver_control, GVM);
+ GrowingVectorMemory < Vector<double> > GVM;
+ SolverCG < Vector<double> > solver_CG (solver_control, GVM);
- // SSOR -> much better than Jacobi for symmetric systems
- PreconditionSSOR <SparseMatrix<double> > preconditioner;
- preconditioner.initialize (tangent_matrix.block(u_dof, u_dof), parameters.ssor_relaxation);
+ // SSOR -> much better than Jacobi for symmetric systems
+ PreconditionSSOR <SparseMatrix<double> > preconditioner;
+ preconditioner.initialize (tangent_matrix.block(u_dof, u_dof), parameters.ssor_relaxation);
- solver_CG.solve (tangent_matrix.block(u_dof, u_dof),
- newton_update.block(u_dof),
- residual.block(u_dof),
- preconditioner);
+ solver_CG.solve (tangent_matrix.block(u_dof, u_dof),
+ newton_update.block(u_dof),
+ residual.block(u_dof),
+ preconditioner);
- std::cout
- << "\t Iterations: " << solver_control.last_step()
- << "\n\t Residual: " << solver_control.last_value()
- << std::endl;
+ std::cout
+ << "\t Iterations: " << solver_control.last_step()
+ << "\n\t Residual: " << solver_control.last_value()
+ << std::endl;
}
- else if (parameters.type_lin == "Direct")
+ else if (parameters.type_lin == "Direct")
{
- SparseDirectUMFPACK A_direct;
- A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
- A_direct.vmult (newton_update.block(u_dof),
- residual.block(u_dof));
+ SparseDirectUMFPACK A_direct;
+ A_direct.initialize(tangent_matrix.block(u_dof, u_dof));
+ A_direct.vmult (newton_update.block(u_dof),
+ residual.block(u_dof));
}
- else throw (ExcMessage("Linear solver type not implemented"));
- timer.leave_subsection();
+ else throw (ExcMessage("Linear solver type not implemented"));
+ timer.leave_subsection();
}
timer.enter_subsection("Linear solver postprocessing");
- // Postprocess for dp
+ // Postprocess for dp
{
- // dp = Ktp^{-1} ( Rt − Ktt Kpt^{-1} (Rp − Kpu du) )
- tangent_matrix.block(p_dof, u_dof).vmult (A.block(p_dof), newton_update.block(u_dof));
- B.block(p_dof).equ(1.0, residual.block(p_dof), -1.0, A.block(p_dof));
- tangent_matrix.block(p_dof, t_dof).vmult(A.block(t_dof), B.block(p_dof));
- tangent_matrix.block(t_dof, t_dof).vmult(B.block(t_dof), A.block(t_dof));
- A.block(t_dof).equ (1.0, residual.block(t_dof), -1.0, B.block(t_dof));
- tangent_matrix.block(p_dof, t_dof).Tvmult (newton_update.block(p_dof), A.block(t_dof));
+ // dp = Ktp^{-1} ( Rt − Ktt Kpt^{-1} (Rp − Kpu du) )
+ tangent_matrix.block(p_dof, u_dof).vmult (A.block(p_dof), newton_update.block(u_dof));
+ B.block(p_dof).equ(1.0, residual.block(p_dof), -1.0, A.block(p_dof));
+ tangent_matrix.block(p_dof, t_dof).vmult(A.block(t_dof), B.block(p_dof));
+ tangent_matrix.block(t_dof, t_dof).vmult(B.block(t_dof), A.block(t_dof));
+ A.block(t_dof).equ (1.0, residual.block(t_dof), -1.0, B.block(t_dof));
+ tangent_matrix.block(p_dof, t_dof).Tvmult (newton_update.block(p_dof), A.block(t_dof));
}
- // Postprocess for dt
+ // Postprocess for dt
{
- // dt = Ktt^{-1} (Rt − Ktp dp)
- tangent_matrix.block(t_dof, p_dof).vmult (A.block(t_dof), newton_update.block(p_dof));
- residual.block(t_dof) -= A.block(t_dof);
- tangent_matrix.block(p_dof, p_dof).vmult (newton_update.block(t_dof), residual.block(t_dof));
+ // dt = Ktt^{-1} (Rt − Ktp dp)
+ tangent_matrix.block(t_dof, p_dof).vmult (A.block(t_dof), newton_update.block(p_dof));
+ residual.block(t_dof) -= A.block(t_dof);
+ tangent_matrix.block(p_dof, p_dof).vmult (newton_update.block(t_dof), residual.block(t_dof));
}
timer.leave_subsection();
-}
+ }
// @sect4{Solid::assemble_system_K}
-template <int dim>
-void Solid<dim>::assemble_system_K (void)
-{
+ template <int dim>
+ void Solid<dim>::assemble_system_K (void)
+ {
timer.enter_subsection("Assemble system matrix");
std::cout << "Assemble system matrix..."<< std::endl;
ScratchData_K scratch_data (fe, qf_cell, uf_cell);
WorkStream::run ( dof_handler_ref.begin_active(),
- dof_handler_ref.end(),
- *this,
- &Solid::assemble_system_K_one_cell,
- &Solid::copy_local_to_global_K,
- scratch_data,
- per_task_data);
+ dof_handler_ref.end(),
+ *this,
+ &Solid::assemble_system_K_one_cell,
+ &Solid::copy_local_to_global_K,
+ scratch_data,
+ per_task_data);
timer.leave_subsection();
-}
+ }
-template <int dim>
-void Solid<dim>::copy_local_to_global_K (const PerTaskData_K & data)
-{
- // Add the local contribution to the system matrix
+ template <int dim>
+ void Solid<dim>::copy_local_to_global_K (const PerTaskData_K & data)
+ {
+ // Add the local contribution to the system matrix
for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- tangent_matrix.add (data.local_dof_indices[i],
- data.local_dof_indices[j],
- data.cell_matrix(i,j));
-}
-
-template <int dim>
-void Solid<dim>::assemble_system_K_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_K & scratch,
- PerTaskData_K & data)
-{
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ tangent_matrix.add (data.local_dof_indices[i],
+ data.local_dof_indices[j],
+ data.cell_matrix(i,j));
+ }
+
+ template <int dim>
+ void Solid<dim>::assemble_system_K_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_K & scratch,
+ PerTaskData_K & data)
+ {
data.reset(); // Reset data in the PerTaskData_K storage unit
scratch.reset(); // Reset data in the Scratch storage unit
scratch.fe_values_ref.reinit (cell);
cell->get_dof_indices (data.local_dof_indices); // Find out which global numbers the degrees of freedom on this cell have
PointHistory<dim> *lqph = reinterpret_cast<PointHistory<dim>*>(cell->user_pointer());
- // Set up cell shape function gradients
+ // Set up cell shape function gradients
static const SymmetricTensor<2, dim> I = unit_symmetric_tensor <dim> ();
for (unsigned int q_point=0; q_point < n_q_points; ++q_point) {
- const Tensor<2, dim> F_inv = lqph[q_point].get_F_inv();
+ const Tensor<2, dim> F_inv = lqph[q_point].get_F_inv();
- for (unsigned int k=0; k< dofs_per_cell; ++k) {
- const unsigned int k_group = fe.system_to_base_index(k).first.first;
+ for (unsigned int k=0; k< dofs_per_cell; ++k) {
+ const unsigned int k_group = fe.system_to_base_index(k).first.first;
- if (k_group == u_dof) {
- scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv;
- scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
- }
- else if (k_group == p_dof) {
- scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k, q_point);
- }
- else if (k_group == t_dof) {
- scratch.Nx[q_point][k] = scratch.fe_values_ref[t_fe].value(k, q_point);
- }
- else {
- Assert (k_group <= t_dof, ExcInternalError());
- }
+ if (k_group == u_dof) {
+ scratch.grad_Nx[q_point][k] = scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv;
+ scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.grad_Nx[q_point][k]);
+ }
+ else if (k_group == p_dof) {
+ scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k, q_point);
+ }
+ else if (k_group == t_dof) {
+ scratch.Nx[q_point][k] = scratch.fe_values_ref[t_fe].value(k, q_point);
}
+ else {
+ Assert (k_group <= t_dof, ExcInternalError());
+ }
+ }
}
- // Build cell stiffness matrix
- // Global and local system matrices are symmetric
- // => Take advantage of this: Build only the lower half of the local matrix
- // Only assemble 1/2 of the K_uu, K_pp = 0, K_tt blocks and the whole K_pt, K_ut, K_up blocks
+ // Build cell stiffness matrix
+ // Global and local system matrices are symmetric
+ // => Take advantage of this: Build only the lower half of the local matrix
+ // Only assemble 1/2 of the K_uu, K_pp = 0, K_tt blocks and the whole K_pt, K_ut, K_up blocks
for (unsigned int q_point=0; q_point < n_q_points; ++q_point) {
- const Tensor <2,dim> T = static_cast < Tensor<2, dim> > (lqph[q_point].get_T_iso() + lqph[q_point].get_T_vol());
- const SymmetricTensor <4,dim> C = lqph[q_point].get_C_iso() + lqph[q_point].get_C_vol();
- const double C_v = lqph[q_point].get_d2U_dtheta2();
- const double J = lqph[q_point].get_J();
-
- const std::vector<double> & N = scratch.Nx[q_point];
- const std::vector< SymmetricTensor <2,dim> > & symm_B = scratch.symm_grad_Nx[q_point];
- const std::vector< Tensor <2,dim> > & B = scratch.grad_Nx[q_point];
- const double & JxW = scratch.fe_values_ref.JxW(q_point);
-
- for (unsigned int i=0; i < dofs_per_cell; ++i) {
-
- const unsigned int component_i = fe.system_to_component_index(i).first;
- const unsigned int i_group = fe.system_to_base_index(i).first.first;
-
- // Only assemble the lower diagonal part of the local matrix
- for (unsigned int j=0; j <= i; ++j) {
-
- const unsigned int component_j = fe.system_to_component_index(j).first;
- const unsigned int j_group = fe.system_to_base_index(j).first.first;
-
- if ( (i_group == j_group) && (i_group == u_dof ) ) {
- data.cell_matrix(i,j)
- += ( symm_B[i] * C * symm_B[j] // Material stiffness
- + ( component_i == component_j ?
- B[i][component_i] * T * B[j][component_j] :
- 0.0 ) // Geometric stiffness. Only add this along local diagonals
- ) * JxW; // K_uu
- }
- else if ( (i_group == p_dof) && (j_group == u_dof) ) {
- data.cell_matrix(i,j) += N[i]*J*(symm_B[j]*I)*JxW; // K_pu
- }
- else if ( (i_group == t_dof) && (j_group == p_dof) ) {
- data.cell_matrix(i,j) -= N[i]*N[j]*JxW; // K_tp
- }
- else if ( (i_group == j_group) && (i_group == t_dof) ) {
- data.cell_matrix(i,j) += N[i]*C_v*N[j]*JxW; // K_tt
- }
- else Assert ((i_group <= t_dof) && (j_group <= t_dof), ExcInternalError());
- } // END j LOOP
- } // END i LOOP
+ const Tensor <2,dim> T = static_cast < Tensor<2, dim> > (lqph[q_point].get_T_iso() + lqph[q_point].get_T_vol());
+ const SymmetricTensor <4,dim> C = lqph[q_point].get_C_iso() + lqph[q_point].get_C_vol();
+ const double C_v = lqph[q_point].get_d2U_dtheta2();
+ const double J = lqph[q_point].get_J();
+
+ const std::vector<double> & N = scratch.Nx[q_point];
+ const std::vector< SymmetricTensor <2,dim> > & symm_B = scratch.symm_grad_Nx[q_point];
+ const std::vector< Tensor <2,dim> > & B = scratch.grad_Nx[q_point];
+ const double & JxW = scratch.fe_values_ref.JxW(q_point);
+
+ for (unsigned int i=0; i < dofs_per_cell; ++i) {
+
+ const unsigned int component_i = fe.system_to_component_index(i).first;
+ const unsigned int i_group = fe.system_to_base_index(i).first.first;
+
+ // Only assemble the lower diagonal part of the local matrix
+ for (unsigned int j=0; j <= i; ++j) {
+
+ const unsigned int component_j = fe.system_to_component_index(j).first;
+ const unsigned int j_group = fe.system_to_base_index(j).first.first;
+
+ if ( (i_group == j_group) && (i_group == u_dof ) ) {
+ data.cell_matrix(i,j)
+ += ( symm_B[i] * C * symm_B[j] // Material stiffness
+ + ( component_i == component_j ?
+ B[i][component_i] * T * B[j][component_j] :
+ 0.0 ) // Geometric stiffness. Only add this along local diagonals
+ ) * JxW; // K_uu
+ }
+ else if ( (i_group == p_dof) && (j_group == u_dof) ) {
+ data.cell_matrix(i,j) += N[i]*J*(symm_B[j]*I)*JxW; // K_pu
+ }
+ else if ( (i_group == t_dof) && (j_group == p_dof) ) {
+ data.cell_matrix(i,j) -= N[i]*N[j]*JxW; // K_tp
+ }
+ else if ( (i_group == j_group) && (i_group == t_dof) ) {
+ data.cell_matrix(i,j) += N[i]*C_v*N[j]*JxW; // K_tt
+ }
+ else Assert ((i_group <= t_dof) && (j_group <= t_dof), ExcInternalError());
+ } // END j LOOP
+ } // END i LOOP
} // END q_point LOOP
- // Global and local system matrices are symmetric
- // => Copy the upper half of the local matrix in the bottom half of the local matrix
+ // Global and local system matrices are symmetric
+ // => Copy the upper half of the local matrix in the bottom half of the local matrix
for (unsigned int i=0; i<dofs_per_cell; ++i) {
- for (unsigned int j=i+1; j<dofs_per_cell; ++j) {
- data.cell_matrix(i,j) = data.cell_matrix(j,i);
- }
+ for (unsigned int j=i+1; j<dofs_per_cell; ++j) {
+ data.cell_matrix(i,j) = data.cell_matrix(j,i);
+ }
}
-}
+ }
// @sect4{Solid::assemble_system_F}
-template <int dim>
-void Solid<dim>::assemble_system_F (void)
-{
+ template <int dim>
+ void Solid<dim>::assemble_system_F (void)
+ {
timer.enter_subsection("Assemble system RHS");
std::cout << "Assemble system RHS..."<< std::endl;
uf_face);
WorkStream::run ( dof_handler_ref.begin_active(),
- dof_handler_ref.end(),
- *this,
- &Solid::assemble_system_F_one_cell,
- &Solid::copy_local_to_global_F,
- scratch_data,
- per_task_data );
+ dof_handler_ref.end(),
+ *this,
+ &Solid::assemble_system_F_one_cell,
+ &Solid::copy_local_to_global_F,
+ scratch_data,
+ per_task_data );
timer.leave_subsection();
-}
+ }
-template <int dim>
-void Solid<dim>::copy_local_to_global_F (const PerTaskData_F & data)
-{
- // Add the local contribution to the system RHS vector
+ template <int dim>
+ void Solid<dim>::copy_local_to_global_F (const PerTaskData_F & data)
+ {
+ // Add the local contribution to the system RHS vector
for (unsigned int i=0; i<dofs_per_cell; ++i) {
- residual(data.local_dof_indices[i]) += data.cell_rhs(i);
+ residual(data.local_dof_indices[i]) += data.cell_rhs(i);
}
-}
+ }
-template <int dim>
-void Solid<dim>::assemble_system_F_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_F & scratch,
- PerTaskData_F & data)
-{
+ template <int dim>
+ void Solid<dim>::assemble_system_F_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_F & scratch,
+ PerTaskData_F & data)
+ {
data.reset(); // Reset data in the PerTaskData_K storage unit
scratch.reset(); // Reset data in the ScratchData_F storage unit
scratch.fe_values_ref.reinit (cell);
cell->get_dof_indices (data.local_dof_indices); // Find out which global numbers the degrees of freedom on this cell have
PointHistory<dim> *lqph = reinterpret_cast<PointHistory<dim>*>(cell->user_pointer());
- // Precompute some data
+ // Precompute some data
for (unsigned int q_point=0; q_point < n_q_points; ++q_point) {
- const Tensor<2, dim> F_inv = lqph[q_point].get_F_inv();
+ const Tensor<2, dim> F_inv = lqph[q_point].get_F_inv();
- for (unsigned int k=0; k<dofs_per_cell; ++k) {
- const unsigned int k_group = fe.system_to_base_index(k).first.first;
+ for (unsigned int k=0; k<dofs_per_cell; ++k) {
+ const unsigned int k_group = fe.system_to_base_index(k).first.first;
- if (k_group == u_dof) {
- scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv);
- }
- else if (k_group == p_dof) {
- scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k, q_point);
- }
- else if (k_group == t_dof) {
- scratch.Nx[q_point][k] = scratch.fe_values_ref[t_fe].value(k, q_point);
- }
- else Assert (k_group <= t_dof, ExcInternalError());
+ if (k_group == u_dof) {
+ scratch.symm_grad_Nx[q_point][k] = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point) * F_inv);
}
+ else if (k_group == p_dof) {
+ scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k, q_point);
+ }
+ else if (k_group == t_dof) {
+ scratch.Nx[q_point][k] = scratch.fe_values_ref[t_fe].value(k, q_point);
+ }
+ else Assert (k_group <= t_dof, ExcInternalError());
+ }
}
- // Assembly for residual contribution
+ // Assembly for residual contribution
for (unsigned int q_point=0; q_point < n_q_points; ++q_point) {
- const SymmetricTensor <2,dim> T = lqph[q_point].get_T_iso() + lqph[q_point].get_T_vol();
- const double J = lqph[q_point].get_J();
- const double D = lqph[q_point].get_dilatation();
- const double p = lqph[q_point].get_pressure();
- const double p_star = lqph[q_point].get_dU_dtheta();
+ const SymmetricTensor <2,dim> T = lqph[q_point].get_T_iso() + lqph[q_point].get_T_vol();
+ const double J = lqph[q_point].get_J();
+ const double D = lqph[q_point].get_dilatation();
+ const double p = lqph[q_point].get_pressure();
+ const double p_star = lqph[q_point].get_dU_dtheta();
- const std::vector< double > & N = scratch.Nx[q_point];
- const std::vector< SymmetricTensor <2,dim> > & symm_B = scratch.symm_grad_Nx[q_point];
- const double JxW = scratch.fe_values_ref.JxW(q_point);
+ const std::vector< double > & N = scratch.Nx[q_point];
+ const std::vector< SymmetricTensor <2,dim> > & symm_B = scratch.symm_grad_Nx[q_point];
+ const double JxW = scratch.fe_values_ref.JxW(q_point);
- for (unsigned int i=0; i<dofs_per_cell; ++i) {
- const unsigned int i_group = fe.system_to_base_index(i).first.first;
+ for (unsigned int i=0; i<dofs_per_cell; ++i) {
+ const unsigned int i_group = fe.system_to_base_index(i).first.first;
- if (i_group == u_dof) {
- data.cell_rhs(i) -= ( symm_B[i]*T )*JxW; // R_u
- }
- else if (i_group == p_dof ) {
- data.cell_rhs(i) -= N[i]*(J - D)*JxW; // R_p
- }
- else if ( i_group == t_dof) {
- data.cell_rhs(i) -= N[i]*(p_star-p)*JxW; // R_t
- }
- else Assert (i_group <= t_dof, ExcInternalError());
- } // END i LOOP
+ if (i_group == u_dof) {
+ data.cell_rhs(i) -= ( symm_B[i]*T )*JxW; // R_u
+ }
+ else if (i_group == p_dof ) {
+ data.cell_rhs(i) -= N[i]*(J - D)*JxW; // R_p
+ }
+ else if ( i_group == t_dof) {
+ data.cell_rhs(i) -= N[i]*(p_star-p)*JxW; // R_t
+ }
+ else Assert (i_group <= t_dof, ExcInternalError());
+ } // END i LOOP
} // END q_point LOOP
- // Assembly for Neumann RHS contribution
+ // Assembly for Neumann RHS contribution
if (cell->at_boundary() == true)
- {
+ {
static const Tensor <2, dim> I = static_cast < Tensor <2, dim> > ( unit_symmetric_tensor <dim> () );
for (unsigned int face=0; face < GeometryInfo<dim>::faces_per_cell; ++face)
- {
+ {
if ( cell->face(face)->at_boundary() == true
&& cell->face(face)->boundary_indicator() == 6 )
- {
+ {
scratch.fe_face_values_ref.reinit (cell, face);
for (unsigned int f_q_point=0; f_q_point < n_q_points_f; ++f_q_point)
- {
+ {
const Tensor <1, dim> & N = scratch.fe_face_values_ref.normal_vector(f_q_point);
- // Traction in reference configuration
- // t_0 = p*N
+ // Traction in reference configuration
+ // t_0 = p*N
static const double p0 = -4.0/(parameters.scale*parameters.scale); // Reference pressure of 4 Pa
const double time_ramp = (time.current() / time.end()); // Linearly ramp up the pressure with time
const double pressure = p0 * parameters.p_p0 * time_ramp;
const Tensor <1,dim> traction = pressure * N;
for (unsigned int i=0; i < dofs_per_cell; ++i) {
- // Determine the dimensional component that matches the dof component (i.e. i % dim)
- const unsigned int i_group = fe.system_to_base_index(i).first.first;
-
- if (i_group == u_dof) {
- const unsigned int component_i = fe.system_to_component_index(i).first;
- const double & Ni = scratch.fe_face_values_ref.shape_value(i,f_q_point);
- const double & JxW = scratch.fe_face_values_ref.JxW(f_q_point);
-
- // Add traction vector contribution to the local RHS vector (displacement dofs only)
- data.cell_rhs(i) += (Ni * traction[component_i]) // Contribution from external forces
- * JxW;
- }
+ // Determine the dimensional component that matches the dof component (i.e. i % dim)
+ const unsigned int i_group = fe.system_to_base_index(i).first.first;
+
+ if (i_group == u_dof) {
+ const unsigned int component_i = fe.system_to_component_index(i).first;
+ const double & Ni = scratch.fe_face_values_ref.shape_value(i,f_q_point);
+ const double & JxW = scratch.fe_face_values_ref.JxW(f_q_point);
+
+ // Add traction vector contribution to the local RHS vector (displacement dofs only)
+ data.cell_rhs(i) += (Ni * traction[component_i]) // Contribution from external forces
+ * JxW;
+ }
} // END i LOOP
- } // END face q_point LOOP
- } // END at boundary check LOOP
+ } // END face q_point LOOP
+ } // END at boundary check LOOP
- } // END face LOOP
- }
-}
+ } // END face LOOP
+ }
+ }
// @sect4{Solid::assemble_system_SC}
-template <int dim>
-void Solid<dim>::assemble_SC (void)
-{
+ template <int dim>
+ void Solid<dim>::assemble_SC (void)
+ {
timer.enter_subsection("Perform static condensation");
PerTaskData_SC per_task_data (dofs_per_cell,
ScratchData_SC scratch_data;
WorkStream::run ( dof_handler_ref.begin_active(),
- dof_handler_ref.end(),
- *this,
- &Solid::assemble_SC_one_cell,
- &Solid::copy_local_to_global_SC,
- scratch_data,
- per_task_data );
+ dof_handler_ref.end(),
+ *this,
+ &Solid::assemble_SC_one_cell,
+ &Solid::copy_local_to_global_SC,
+ scratch_data,
+ per_task_data );
timer.leave_subsection();
-}
+ }
-template <int dim>
-void Solid<dim>::copy_local_to_global_SC (const PerTaskData_SC & data)
-{
- // Add the local contribution to the system matrix
+ template <int dim>
+ void Solid<dim>::copy_local_to_global_SC (const PerTaskData_SC & data)
+ {
+ // Add the local contribution to the system matrix
for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- tangent_matrix.add (data.local_dof_indices[i],
- data.local_dof_indices[j],
- data.cell_matrix(i,j));
-}
-
-template <int dim>
-void Solid<dim>::assemble_SC_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
- ScratchData_SC & scratch,
- PerTaskData_SC & data)
-{
+ for (unsigned int j=0; j<dofs_per_cell; ++j)
+ tangent_matrix.add (data.local_dof_indices[i],
+ data.local_dof_indices[j],
+ data.cell_matrix(i,j));
+ }
+
+ template <int dim>
+ void Solid<dim>::assemble_SC_one_cell (const typename DoFHandler<dim>::active_cell_iterator & cell,
+ ScratchData_SC & scratch,
+ PerTaskData_SC & data)
+ {
data.reset();
scratch.reset();
cell->get_dof_indices (data.local_dof_indices); // Find out which global numbers the degrees of freedom on this cell have
- // The local stifness matrix K_e is:
- // | K_uu | K_up | 0 |
- // | K_pu | 0 | K_pt |
- // | 0 | K_tp | K_tt |
- //
- // We are going to exploit the zeros for post-processing as:
- // | K'_uu | K_up | 0 |
- // | K_pu | K_tt^-1 | K_pt^-1 |
- // | 0 | K_tp | K_tt |
- // with K'_uu = K_uu + Kup Ktp^{-1} Ktt Kpt^{-1} Kpu
-
- // NOTE:
- // GLOBAL Data already exists in the K_uu, K_pt, K_tp subblocks
- //
- // For the K_uu block in particular, this means that contributions have been
- // added from the surrounding cells, so we need to be careful when we manipulate this block.
- // We can't just erase the subblocks and
- // Additionally the copy_local_to_global operation is a "+=" operation -> need to take this
- // into account
- //
- // So the intermediate matrix that we need to get from what we have in K_uu and what we
- // are actually wanting is:
- // | K'_uu - K_uu | 0 | 0 |
- // | 0 | K_tt^-1 | K_pt^-1 - K_pt |
- // | 0 | 0 | 0 |
- //
- // Strategy to get the subblocks we want:
- // K'_uu: Since we don't have access to K_uu^h, but we know its contribution is added to the global
- // K_uu matrix, we just want to add the element wise static-condensation
- // K'_uu^h = K_uu^h + K_up^h K_tp^{-1}^h K_tt^h K_pt^{-1}^h K_pu^h
- // Since we already have K_uu^h in the system matrix, we just need to do the following
- // K'_uu^h == (K_uu^h += K_up^h K_tp^{-1}^h K_tt^h K_pt^{-1}^h K_pu^h)
- // K_pt^-1: Similarly, K_pt exists in the subblock. Since the copy operation is a += operation, we need
- // to subtract the existing K_pt submatrix in addition to "adding" that which we wish to
- // replace it with.
- // K_tp^-1: Same as above
- // K_tt^-1: Nothing exists in the original K_pp subblock, so we can just add this contribution as is.
-
- // Extract element data from the system matrix
+ // The local stifness matrix K_e is:
+ // | K_uu | K_up | 0 |
+ // | K_pu | 0 | K_pt |
+ // | 0 | K_tp | K_tt |
+ //
+ // We are going to exploit the zeros for post-processing as:
+ // | K'_uu | K_up | 0 |
+ // | K_pu | K_tt^-1 | K_pt^-1 |
+ // | 0 | K_tp | K_tt |
+ // with K'_uu = K_uu + Kup Ktp^{-1} Ktt Kpt^{-1} Kpu
+
+ // NOTE:
+ // GLOBAL Data already exists in the K_uu, K_pt, K_tp subblocks
+ //
+ // For the K_uu block in particular, this means that contributions have been
+ // added from the surrounding cells, so we need to be careful when we manipulate this block.
+ // We can't just erase the subblocks and
+ // Additionally the copy_local_to_global operation is a "+=" operation -> need to take this
+ // into account
+ //
+ // So the intermediate matrix that we need to get from what we have in K_uu and what we
+ // are actually wanting is:
+ // | K'_uu - K_uu | 0 | 0 |
+ // | 0 | K_tt^-1 | K_pt^-1 - K_pt |
+ // | 0 | 0 | 0 |
+ //
+ // Strategy to get the subblocks we want:
+ // K'_uu: Since we don't have access to K_uu^h, but we know its contribution is added to the global
+ // K_uu matrix, we just want to add the element wise static-condensation
+ // K'_uu^h = K_uu^h + K_up^h K_tp^{-1}^h K_tt^h K_pt^{-1}^h K_pu^h
+ // Since we already have K_uu^h in the system matrix, we just need to do the following
+ // K'_uu^h == (K_uu^h += K_up^h K_tp^{-1}^h K_tt^h K_pt^{-1}^h K_pu^h)
+ // K_pt^-1: Similarly, K_pt exists in the subblock. Since the copy operation is a += operation, we need
+ // to subtract the existing K_pt submatrix in addition to "adding" that which we wish to
+ // replace it with.
+ // K_tp^-1: Same as above
+ // K_tt^-1: Nothing exists in the original K_pp subblock, so we can just add this contribution as is.
+
+ // Extract element data from the system matrix
AdditionalTools::extract_submatrix(data.local_dof_indices,
data.local_dof_indices,
data.K_orig,
data.K_tt);
- // Place K_pt^-1 in the K_pt block
+ // Place K_pt^-1 in the K_pt block
data.K_pt_inv.invert(data.K_pt);
data.K_pt_inv.add (-1.0, data.K_pt);
AdditionalTools::replace_submatrix(element_indices_p,
data.K_pt_inv,
data.cell_matrix);
- // Place K_tt^-1 in the K_pp block
+ // Place K_tt^-1 in the K_pp block
data.K_tt_inv.invert(data.K_tt);
AdditionalTools::replace_submatrix(element_indices_p,
element_indices_p,
data.K_tt_inv,
data.cell_matrix);
- // Make condensation terms to add to the K_uu block
+ // Make condensation terms to add to the K_uu block
data.K_pt_inv.mmult(data.A, data.K_pu);
data.K_tt.mmult(data.B, data.A);
data.K_pt_inv.Tmmult(data.C, data.B); // Symmetric matrix
element_indices_u,
data.K_con,
data.cell_matrix);
-}
+ }
// @sect4{Solid::make_constraints}
-template <int dim>
-void Solid<dim>::make_constraints (const int & it_nr,
- ConstraintMatrix & constraints)
-{
+ template <int dim>
+ void Solid<dim>::make_constraints (const int & it_nr,
+ ConstraintMatrix & constraints)
+ {
std::cout << "Make constraints..."<< std::endl;
constraints.clear();
const bool apply_dirichlet_bc = (it_nr == 0);
- // Boundary conditions:
- // b_id 0: -x face: Zero x-component of displacement : Symmetry plane
- // b_id 2: -y face: Zero y-component of displacement : Symmetry plane
- // b_id 4: -z face: Zero z-component of displacement : Symmetry plane
+ // Boundary conditions:
+ // b_id 0: -x face: Zero x-component of displacement : Symmetry plane
+ // b_id 2: -y face: Zero y-component of displacement : Symmetry plane
+ // b_id 4: -z face: Zero z-component of displacement : Symmetry plane
- // b_id 5: +z face: Zero x-component and Zero y-component
- // b_id 6: Applied pressure face: Zero x-component and Zero y-component
- // b_id 1: +x face: Traction free
- // b_id 3: +y face: Traction free
+ // b_id 5: +z face: Zero x-component and Zero y-component
+ // b_id 6: Applied pressure face: Zero x-component and Zero y-component
+ // b_id 1: +x face: Traction free
+ // b_id 3: +y face: Traction free
{
- const int boundary_id = 0;
+ const int boundary_id = 0;
- std::vector< bool > components (n_components, false);
- components[0] = true;
+ std::vector< bool > components (n_components, false);
+ components[0] = true;
- if (apply_dirichlet_bc == true) {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
- else {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
+ if (apply_dirichlet_bc == true) {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
+ else {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
}
{
- const int boundary_id = 2;
+ const int boundary_id = 2;
- std::vector< bool > components (n_components, false);
- components[1] = true;
+ std::vector< bool > components (n_components, false);
+ components[1] = true;
- if (apply_dirichlet_bc == true) {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
- else {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
+ if (apply_dirichlet_bc == true) {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
+ else {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
}
{
- const int boundary_id = 4;
- std::vector< bool > components (n_components, false);
- components[2] = true;
-
- if (apply_dirichlet_bc == true) {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
- else {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
+ const int boundary_id = 4;
+ std::vector< bool > components (n_components, false);
+ components[2] = true;
+
+ if (apply_dirichlet_bc == true) {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
+ else {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
}
{
- const int boundary_id = 5;
- std::vector< bool > components (n_components, true);
- components[2] = false;
-
- if (apply_dirichlet_bc == true) {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
- else {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
+ const int boundary_id = 5;
+ std::vector< bool > components (n_components, true);
+ components[2] = false;
+
+ if (apply_dirichlet_bc == true) {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
+ else {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
}
{
- const int boundary_id = 6;
- std::vector< bool > components (n_components, true);
- components[2] = false;
-
- if (apply_dirichlet_bc == true) {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
- else {
- VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
- }
+ const int boundary_id = 6;
+ std::vector< bool > components (n_components, true);
+ components[2] = false;
+
+ if (apply_dirichlet_bc == true) {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
+ else {
+ VectorTools::interpolate_boundary_values ( dof_handler_ref, boundary_id, ZeroFunction<dim>(n_components), constraints, components );
+ }
}
constraints.close();
-}
+ }
// @sect4{Solid::output_results}
-template <int dim>
-void Solid<dim>::output_results(void)
-{
+ template <int dim>
+ void Solid<dim>::output_results(void)
+ {
DataOut<dim> data_out;
std::vector<DataComponentInterpretation::DataComponentInterpretation> data_component_interpretation (dim, DataComponentInterpretation::component_is_part_of_vector);
data_out.add_data_vector (solution_n,
solution_name,
DataOut<dim>::type_dof_data, data_component_interpretation);
- // MappingQEulerian<dim> q_mapping (degree, solution_n.block(u_dof), dof_handler_ref);
- // MappingQEulerian<dim> q_mapping (degree, solution_n, dof_handler_ref);
+ // MappingQEulerian<dim> q_mapping (degree, solution_n.block(u_dof), dof_handler_ref);
+ // MappingQEulerian<dim> q_mapping (degree, solution_n, dof_handler_ref);
Vector<double> soln;
soln.reinit(solution_n.size());
for (unsigned int i=0; i < soln.size(); ++i) soln(i) = solution_n(i);
std::ofstream output (filename.str().c_str());
data_out.write_vtk (output);
+ }
}
+
// @sect3{Main function}
int main ()
{
- try
+ try
{
- deallog.depth_console (0);
+ using namespace dealii;
+ using namespace Step44;
- Solid<3> solid_3d ("parameters.prm");
- solid_3d.run();
+ deallog.depth_console (0);
+
+ Solid<3> solid_3d ("parameters.prm");
+ solid_3d.run();
}
- catch (std::exception &exc)
+ catch (std::exception &exc)
{
- std::cerr << std::endl << std::endl
- << "----------------------------------------------------"
- << std::endl;
- std::cerr << "Exception on processing: " << std::endl
- << exc.what() << std::endl
- << "Aborting!" << std::endl
- << "----------------------------------------------------"
- << std::endl;
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Exception on processing: " << std::endl
+ << exc.what() << std::endl
+ << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
- return 1;
+ return 1;
}
- catch (...)
+ catch (...)
{
- std::cerr << std::endl << std::endl
- << "----------------------------------------------------"
- << std::endl;
- std::cerr << "Unknown exception!" << std::endl
- << "Aborting!" << std::endl
- << "----------------------------------------------------"
- << std::endl;
- return 1;
+ std::cerr << std::endl << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ std::cerr << "Unknown exception!" << std::endl
+ << "Aborting!" << std::endl
+ << "----------------------------------------------------"
+ << std::endl;
+ return 1;
}
- return 0;
+ return 0;
}