1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 
19 #ifndef itkFEMSolverCrankNicolson_h
20 #define itkFEMSolverCrankNicolson_h
21 
22 #include "itkFEMSolver.h"
23 #include "itkFEMElementBase.h"
24 #include "itkFEMMaterialBase.h"
25 #include "itkFEMLoadBase.h"
26 #include "itkFEMLinearSystemWrapperVNL.h"
27 
28 #include "vnl/vnl_sparse_matrix.h"
29 #include "vnl/vnl_matrix.h"
30 #include "vnl/vnl_vector.h"
31 #include "vnl/algo/vnl_svd.h"
32 #include "vnl/algo/vnl_cholesky.h"
33 #include <vnl/vnl_sparse_matrix_linear_system.h>
34 #include <cmath>
35 
36 namespace itk
37 {
38 namespace fem
39 {
40 /**
41  * \class SolverCrankNicolson
42  * \brief FEM Solver for time dependent problems; uses Crank-Nicolson implicit discretization scheme.
43  *
44  * This is the main class used for solving FEM time-dependent problems.
45  * It solves the following problem:
46  *
47  * \f[
48  *      ( M + \alpha*dt* K )*U_t=(M - (1.- \alpha)*dt* K)* U_{t-1} + dt*(\alpha*f_{n+1} + (1-\alpha)*f_n)
49  * \f]
50  *
51  * which is the Crank-Nicolson formulation of the static problem if \f$\alpha=0.5\f$.
52  * The static solution is gained if :
53  *      \f$\rho = 0.0\f$;   \f$\alpha = 1.0\f$;  \f$dt = 1.0\f$;
54  * Practically, it is good to set rho to something small (for the itpack solver).
55  * The advantage of choosing \f$\alpha=0.5\f$ is that the solution is then stable for any
56  * choice of time step, dt.  This class inherits and uses most of the Solver class
57  * functionality.
58  *
59  * Updated: The calls to to AssembleKandM (or AssembleK) and
60  * AssembleFforTimeStep (or AssembleF) are now handled internally
61  * by calling Update().
62  *
63  * FIXME:
64  * 1) We should also account for the contribution to the force from essential BCs.
65  * Basically there are terms involving  \f$ M * (\dot g_b) \f$  and  \f$ K * g_b \f$
66  * where\f$ g_b\f$ is the essential BC vector.
67  * \ingroup ITKFEM
68  */
69 
70 template <unsigned int TDimension = 3>
71 class ITK_TEMPLATE_EXPORT SolverCrankNicolson : public Solver<TDimension>
72 {
73 public:
74   ITK_DISALLOW_COPY_AND_ASSIGN(SolverCrankNicolson);
75 
76   using Self = SolverCrankNicolson;
77   using Superclass = Solver<TDimension>;
78   using Pointer = SmartPointer<Self>;
79   using ConstPointer = SmartPointer<const Self>;
80 
81   /** Method for creation through the object factory. */
82   itkNewMacro(Self);
83 
84   /** Run-time type information (and related methods) */
85   itkTypeMacro(SolverCrankNicolson, Solver<TDimension> );
86 
87   using Float = Element::Float;
88 
89   /** Get/Set the use of the Mass Matrix for the solution. */
90   itkSetMacro(UseMassMatrix, bool);
91   itkGetMacro(UseMassMatrix, bool);
92 
93   /** Get the number of iterations run for the solver. */
94   itkGetConstMacro(Iterations, unsigned int);
95 
96   /**
97    * Reset the number of iterations for the solver. This
98    * will prompt the Solver to Assemble the master stiffness
99    * and mass matrix again. This is only generated before the
100    * first iteration.
101    */
ResetIterations()102   void ResetIterations()
103   {
104     m_Iterations = 0;
105   }
106 
107   /**
108    * Add solution vector u to the corresponding nodal values, which are
109    * stored in node objects. This is standard post processing of the solution
110    */
111   void AddToDisplacements(Float optimum = 1.0);
112 
113   void AverageLastTwoDisplacements(Float t = 0.5);
114 
115   void ZeroVector(int which = 0);
116 
117   void PrintDisplacements();
118 
119   void PrintForce();
120 
121   /** Get the index for the current solution. */
122   itkGetMacro(TotalSolutionIndex, unsigned int);
123 
124   /** Get the index for the previous solution. */
125   itkGetMacro(SolutionTMinus1Index, unsigned int);
126 
127   /** Set stability step for the solution. Initialized to 0.5. */
128   itkSetMacro(Alpha, Float);
129   itkGetMacro(Alpha, Float);
130 
131   /** Set density constant.  */
132   itkSetMacro(Rho, Float);
133   itkGetMacro(Rho, Float);
134 
135   /** Returns the time step used for dynamic problems. */
GetTimeStep()136   Float GetTimeStep() const override
137   {
138     return m_TimeStep;
139   }
140 
141   /**
142    * Sets the time step used for dynamic problems.
143    *
144    * \param dt New time step.
145    */
SetTimeStep(Float dt)146   void SetTimeStep(Float dt) override
147   {
148     m_TimeStep = dt;
149   }
150 
151   /** Compute the current state of the right hand side and store the current force
152    * for the next iteration.
153    */
154   void RecomputeForceVector(unsigned int index);
155 
156   /** Finds a triplet that brackets the energy minimum. From Numerical
157   * Recipes.*/
158   void FindBracketingTriplet(Float *a, Float *b, Float *c);
159 
160   /** Finds the optimum value between the last two solutions
161    * and sets the current solution to that value.  Uses Evaluate Residual;
162    */
163   Float GoldenSection(Float tol = 0.01, unsigned int MaxIters = 25);
164 
165   /* Brents method from Numerical Recipes. */
166   Float BrentsMethod(Float tol = 0.01, unsigned int MaxIters = 25);
167 
168   Float EvaluateResidual(Float t = 1.0);
169 
170   Float GetDeformationEnergy(Float t = 1.0);
171 
GSSign(Float a,Float b)172   inline Float GSSign(Float a, Float b)
173   {
174     return b > 0.0 ? std::fabs(a) : -1. * std::fabs(a);
175   }
GSMax(Float a,Float b)176   inline Float GSMax(Float a, Float b)
177   {
178     return a > b ? a : b;
179   }
180 
181   void SetEnergyToMin(Float xmin);
182 
GetLinearSystem()183   inline LinearSystemWrapper * GetLinearSystem()
184   {
185     return this->m_LinearSystem;
186   }
187 
GetCurrentMaxSolution()188   Float GetCurrentMaxSolution()
189   {
190     return m_CurrentMaxSolution;
191   }
192 
193   /** Compute and print the minimum and maximum of the total solution
194    * and the last solution values. */
195   void PrintMinMaxOfSolution();
196 
197 protected:
198 
199   /**
200   * Default constructor. Sets the indices for the matrix and vector storage.
201   * Time step and other parameters are also initialized.
202   */
203   SolverCrankNicolson();
~SolverCrankNicolson()204   ~SolverCrankNicolson() override {}
205 
206   /** Method invoked by the pipeline in order to trigger the computation of
207    * the registration. */
208   void GenerateData() override;
209 
210   /**
211    * Solve for the displacement vector u at a given time.  Update the total solution as well.
212    */
213   void RunSolver() override;
214 
215   /**
216    * Helper initialization function before assembly but after generate GFN.
217    */
218   void InitializeForSolution();
219 
220   /**
221    * Assemble the master stiffness and mass matrix. We actually assemble
222    * the right hand side and left hand side of the implicit scheme equation.
223    * MFCs are applied to K.
224    */
225   void AssembleKandM();
226 
227   /**
228    * Assemble the master force vector at a given time.
229    *
230    * \param dim This is a parameter that can be passed to the function and is
231                 normally used with isotropic elements to specify the
232                 dimension for which the master force vector should be assembled.
233    */
234   void AssembleFforTimeStep(int dim = 0);
235 
236   Float m_TimeStep;
237   Float m_Rho;
238   Float m_Alpha;
239   Float m_CurrentMaxSolution;
240 
241   bool         m_UseMassMatrix;
242   unsigned int m_Iterations;
243 
244   unsigned int m_ForceTIndex;
245   unsigned int m_ForceTotalIndex;
246   unsigned int m_ForceTMinus1Index;
247   unsigned int m_SolutionTIndex;
248   unsigned int m_SolutionTMinus1Index;
249   unsigned int m_SolutionVectorTMinus1Index;
250   unsigned int m_TotalSolutionIndex;
251   unsigned int m_DifferenceMatrixIndex;
252   unsigned int m_SumMatrixIndex;
253   unsigned int m_DiffMatrixBySolutionTMinus1Index;
254 };
255 } // end namespace fem
256 } // end namespace itk
257 
258 #ifndef ITK_MANUAL_INSTANTIATION
259 #include "itkFEMSolverCrankNicolson.hxx"
260 #endif
261 
262 #endif // itkFEMSolverCrankNicolson_h
263