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