|
| 1 | +#ifndef __Elasticity_Kee2023_h__ |
| 2 | +#define __Elasticity_Kee2023_h__ |
| 3 | + |
| 4 | +#include "SPlisHSPlasH/Common.h" |
| 5 | +#include "SPlisHSPlasH/FluidModel.h" |
| 6 | +#include "SPlisHSPlasH/NonPressureForceBase.h" |
| 7 | +#if USE_AVX |
| 8 | +#include "SPlisHSPlasH/Utilities/AVX_math.h" |
| 9 | +#include "SPlisHSPlasH/Utilities/CholeskyAVXSolver.h" |
| 10 | +#endif |
| 11 | + |
| 12 | + |
| 13 | +namespace SPH |
| 14 | +{ |
| 15 | + /** \brief This class implements the elasticity solver |
| 16 | + * by Kee et al. [K23]. |
| 17 | + * |
| 18 | + * References: |
| 19 | + * - [K23] Kee et al.. |
| 20 | + * Kee 2023. |
| 21 | + * |
| 22 | + */ |
| 23 | + class Elasticity_Kee2023 : public NonPressureForceBase |
| 24 | + { |
| 25 | + protected: |
| 26 | + |
| 27 | + struct Factorization |
| 28 | + { |
| 29 | + Real m_dt; |
| 30 | + Real m_mu; |
| 31 | + Eigen::SparseMatrix<Real, Eigen::RowMajor> m_DT_K; |
| 32 | + Eigen::SparseMatrix<Real, Eigen::RowMajor> m_D; |
| 33 | + Eigen::SparseMatrix<Real, Eigen::ColMajor> m_matHTH; |
| 34 | + |
| 35 | +#ifdef USE_AVX |
| 36 | + CholeskyAVXSolver *m_cholesky; |
| 37 | + Factorization() { m_cholesky = nullptr; } |
| 38 | + ~Factorization() { delete m_cholesky; } |
| 39 | +#else |
| 40 | + Factorization() {} |
| 41 | + ~Factorization() {} |
| 42 | + // L-BFGS prefactored Cholesky (N×N, constant proxy) |
| 43 | + Eigen::SparseMatrix<Real, Eigen::ColMajor> m_matL; |
| 44 | + Eigen::SparseMatrix<Real, Eigen::ColMajor> m_matLT; |
| 45 | + Eigen::VectorXi m_permInd; |
| 46 | + Eigen::VectorXi m_permInvInd; |
| 47 | +#endif |
| 48 | + }; |
| 49 | + |
| 50 | + struct ElasticObject |
| 51 | + { |
| 52 | + std::string m_md5; |
| 53 | + std::vector<unsigned int> m_particleIndices; |
| 54 | + unsigned int m_nFixed; |
| 55 | + std::shared_ptr<Factorization> m_factorization; |
| 56 | + |
| 57 | + // Newton: per-particle 9×9 Hessian and block-diagonal preconditioner. |
| 58 | + // Same type in both builds (SVD is always scalar). |
| 59 | + std::vector<Eigen::Matrix<Real, 9, 9>> m_hessian9x9; |
| 60 | + std::vector<Matrix3r, Eigen::aligned_allocator<Matrix3r>> m_pcg_precond; |
| 61 | + |
| 62 | +#ifdef USE_AVX |
| 63 | + // AVX state (Scalarf8, 3 active lanes x/y/z). |
| 64 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_f_avx; // F = D·xk workspace (3*numParticles) |
| 65 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_xk_avx; // current iterate |
| 66 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_xTilde_avx; // inertial target |
| 67 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_dx_avx; // solver step (also LLT solve RHS/result) |
| 68 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_gradient_avx; // ∇E at xk |
| 69 | + |
| 70 | + // Newton CG workspace (Scalarf8, coord-packed x/y/z in lanes 0/1/2) |
| 71 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_pcg_r_avx; |
| 72 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_pcg_p_avx; |
| 73 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_pcg_Ap_avx; |
| 74 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_pcg_z_avx; |
| 75 | + |
| 76 | + // L-BFGS secant history |
| 77 | + std::vector<std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>>> m_lbfgs_s_avx; |
| 78 | + std::vector<std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>>> m_lbfgs_y_avx; |
| 79 | + std::vector<Real> m_lbfgs_rho; |
| 80 | + std::vector<Real> m_lbfgs_alpha; |
| 81 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_lbfgs_last_sol_avx; |
| 82 | + std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_lbfgs_q_avx; |
| 83 | + int m_lbfgs_count = 0; |
| 84 | +#else |
| 85 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_f; // F = D·xk workspace |
| 86 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_xk; // current iterate |
| 87 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_xTilde; // inertial target |
| 88 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_dx; // Newton/LBFGS step (also LLT solve RHS/result) |
| 89 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_gradient; // ∇E at xk |
| 90 | + |
| 91 | + // Newton PCG workspace |
| 92 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_r; |
| 93 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_p; |
| 94 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_Ap; |
| 95 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_z; |
| 96 | + |
| 97 | + // L-BFGS secant history |
| 98 | + std::vector<std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>> m_lbfgs_s; |
| 99 | + std::vector<std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>> m_lbfgs_y; |
| 100 | + std::vector<Real> m_lbfgs_rho; |
| 101 | + std::vector<Real> m_lbfgs_alpha; |
| 102 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_lbfgs_last_sol; |
| 103 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_lbfgs_q; |
| 104 | + int m_lbfgs_count = 0; |
| 105 | + |
| 106 | + // Permutation workspace for scalar LLT (manual forward/backward sub) |
| 107 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_dx_perm; |
| 108 | +#endif |
| 109 | + |
| 110 | + ElasticObject() { m_factorization = nullptr; } |
| 111 | + ~ElasticObject() { m_factorization = nullptr; } |
| 112 | + }; |
| 113 | + |
| 114 | + Real m_youngsModulus; |
| 115 | + Real m_poissonRatio; |
| 116 | + Vector3r m_fixedBoxMin; |
| 117 | + Vector3r m_fixedBoxMax; |
| 118 | + Vector3r m_fixedBox2Min; |
| 119 | + Vector3r m_fixedBox2Max; |
| 120 | + |
| 121 | + // initial particle indices, used to access their original positions |
| 122 | + std::vector<unsigned int> m_current_to_initial_index; |
| 123 | + std::vector<unsigned int> m_initial_to_current_index; |
| 124 | + // initial particle neighborhood |
| 125 | + std::vector<std::vector<unsigned int>> m_initialNeighbors; |
| 126 | + // volumes in rest configuration |
| 127 | + std::vector<Real> m_restVolumes; |
| 128 | + std::vector<Matrix3r> m_rotations; |
| 129 | + std::vector<Real> m_stress; |
| 130 | + std::vector<int> m_fixedGroupId; // 0: free, 1: box1, 2: box2 |
| 131 | + std::vector<Matrix3r> m_L; |
| 132 | + std::vector<Matrix3r> m_F; |
| 133 | + std::vector<Matrix3r> m_PL; |
| 134 | + Real m_alpha; |
| 135 | + int m_maxNeighbors; |
| 136 | + int m_solverType; // 0: Newton, 1: LBFGS |
| 137 | + int m_lbfgsWindowSize; |
| 138 | + int m_materialType; // 0: Stable Neo-Hookean, 1: Co-rotated |
| 139 | + int m_maxIter; |
| 140 | + Real m_maxError; |
| 141 | + int m_maxIterCG; // max CG iterations for Newton linear solve |
| 142 | + Real m_tolCG; // CG convergence tolerance |
| 143 | + int m_maxLSIter; |
| 144 | + Real m_lsArmijoParam; |
| 145 | + Real m_lsBeta; |
| 146 | + bool m_useLineSearch; |
| 147 | + unsigned int m_totalNeighbors; |
| 148 | + std::vector<ElasticObject*> m_objects; |
| 149 | + Real m_lambda; |
| 150 | + Real m_mu; |
| 151 | + |
| 152 | + // Precomputed V_j * gradW(xi0 - xj0) per neighbor pair. |
| 153 | + // Scalar format (always available, used by Newton CG). |
| 154 | + std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_precomp_V_gradW; |
| 155 | + std::vector<unsigned int> m_precomputed_indices; |
| 156 | +#ifdef USE_AVX |
| 157 | + // AVX-packed format (8 neighbors per entry, used by L-BFGS force loops). |
| 158 | + std::vector<Vector3f8, Eigen::aligned_allocator<Vector3f8>> m_precomp_V_gradW8; |
| 159 | + std::vector<unsigned int> m_precomputed_indices8; |
| 160 | +#endif |
| 161 | + |
| 162 | +#ifdef USE_AVX |
| 163 | + typedef Eigen::SimplicialLLT<Eigen::SparseMatrix<double>, Eigen::Lower, Eigen::AMDOrdering<int>> SolverLLT; |
| 164 | +#else |
| 165 | + typedef Eigen::SimplicialLLT<Eigen::SparseMatrix<double>, Eigen::Lower, Eigen::AMDOrdering<int>> SolverLLT; |
| 166 | +#endif |
| 167 | + |
| 168 | + void determineFixedParticles(); |
| 169 | + std::string computeMD5(const unsigned int objIndex); |
| 170 | + void initValues(); |
| 171 | + void initSystem(); |
| 172 | + void initFactorization(std::shared_ptr<Factorization> factorization, std::vector<unsigned int> &particleIndices, const unsigned int nFixed, const Real dt, const Real mu); |
| 173 | + void findObjects(); |
| 174 | + void computeMatrixL(); |
| 175 | + void precomputeValues(); |
| 176 | + |
| 177 | + void stepElasticitySolver(); |
| 178 | + |
| 179 | + void computeXTilde(ElasticObject* obj); |
| 180 | + void updateVelocity(ElasticObject* obj, Real fdt); |
| 181 | + Real computeEnergy(ElasticObject* obj); |
| 182 | + Real computePsi(const Matrix3r& F, const Matrix3r& R) const; |
| 183 | + Real computeEnergyAndGradient(ElasticObject* obj); |
| 184 | + void computeHessian(ElasticObject* obj); |
| 185 | + void computeCorotatedHessian9x9(ElasticObject* obj); |
| 186 | + void computeStableNeoHookeanHessian9x9(ElasticObject* obj); |
| 187 | + void computeNewtonPreconditioner(ElasticObject* obj); |
| 188 | + void newtonMatvec(ElasticObject* obj); |
| 189 | + int matFreePCG(ElasticObject* obj); |
| 190 | + Real newtonSolve(ElasticObject* obj, int& cgIter); |
| 191 | + void prefactorizedLLTSolve(ElasticObject* obj); |
| 192 | + Real lbfgsSolve(ElasticObject* obj); |
| 193 | + Real lineSearch(ElasticObject* obj, Real energy, int& lsIter); |
| 194 | + |
| 195 | + Matrix3r computeP(const Matrix3r& F, const Matrix3r& R) const; |
| 196 | + |
| 197 | + virtual void initParameters(); |
| 198 | + /** This function is called after the simulation scene is loaded and all |
| 199 | + * parameters are initialized. While reading a scene file several parameters |
| 200 | + * can change. The deferred init function should initialize all values which |
| 201 | + * depend on these parameters. |
| 202 | + */ |
| 203 | + virtual void deferredInit(); |
| 204 | + |
| 205 | + ////////////////////////////////////////////////////////////////////////// |
| 206 | + // multiplication of symmetric matrix, represented by a 6D vector, and a |
| 207 | + // 3D vector |
| 208 | + ////////////////////////////////////////////////////////////////////////// |
| 209 | + FORCE_INLINE void symMatTimesVec(const Vector6r & M, const Vector3r & v, Vector3r &res) |
| 210 | + { |
| 211 | + res[0] = M[0] * v[0] + M[3] * v[1] + M[4] * v[2]; |
| 212 | + res[1] = M[3] * v[0] + M[1] * v[1] + M[5] * v[2]; |
| 213 | + res[2] = M[4] * v[0] + M[5] * v[1] + M[2] * v[2]; |
| 214 | + } |
| 215 | + |
| 216 | + void rotationMatricesToAVXQuaternions(); |
| 217 | + |
| 218 | + public: |
| 219 | + static std::string METHOD_NAME; |
| 220 | + static int YOUNGS_MODULUS; |
| 221 | + static int POISSON_RATIO; |
| 222 | + static int FIXED_BOX_MIN; |
| 223 | + static int FIXED_BOX_MAX; |
| 224 | + static int FIXED_BOX2_MIN; |
| 225 | + static int FIXED_BOX2_MAX; |
| 226 | + static int ALPHA; |
| 227 | + static int MAX_NEIGHBORS; |
| 228 | + static int SOLVER_TYPE; |
| 229 | + static int LBFGS_WINDOW_SIZE; |
| 230 | + static int MATERIAL_TYPE; |
| 231 | + static int MAX_ITER; |
| 232 | + static int MAX_ERROR; |
| 233 | + static int MAX_ITER_CG; |
| 234 | + static int TOL_CG; |
| 235 | + static int MAX_LS_ITER; |
| 236 | + static int LS_ARMIJO_PARAM; |
| 237 | + static int LS_BETA; |
| 238 | + static int USE_LINE_SEARCH; |
| 239 | + |
| 240 | + static int ENUM_SOLVER_NEWTON; |
| 241 | + static int ENUM_SOLVER_LBFGS; |
| 242 | + static int ENUM_MATERIAL_STABLE_NEOHOOKEAN; |
| 243 | + static int ENUM_MATERIAL_COROTATED; |
| 244 | + |
| 245 | + Elasticity_Kee2023(FluidModel *model); |
| 246 | + virtual ~Elasticity_Kee2023(void); |
| 247 | + |
| 248 | + static NonPressureForceBase* creator(FluidModel* model) { return new Elasticity_Kee2023(model); } |
| 249 | + virtual std::string getMethodName() { return METHOD_NAME; } |
| 250 | + |
| 251 | + virtual void step(); |
| 252 | + virtual void reset(); |
| 253 | + virtual void performNeighborhoodSearchSort(); |
| 254 | + |
| 255 | + virtual void saveState(BinaryFileWriter &binWriter); |
| 256 | + virtual void loadState(BinaryFileReader &binReader); |
| 257 | + }; |
| 258 | +} |
| 259 | + |
| 260 | +#endif |
0 commit comments