Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SPlisHSPlasH/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,14 @@ set(ELASTICITY_HEADER_FILES
Elasticity/Elasticity_Becker2009.h
Elasticity/Elasticity_Peer2018.h
Elasticity/Elasticity_Kugelstadt2021.h
Elasticity/Elasticity_Kee2023.h
)

set(ELASTICITY_SOURCE_FILES
Elasticity/Elasticity_Becker2009.cpp
Elasticity/Elasticity_Peer2018.cpp
Elasticity/Elasticity_Kugelstadt2021.cpp
Elasticity/Elasticity_Kee2023.cpp
)

set(UTILS_HEADER_FILES
Expand Down
2,845 changes: 2,845 additions & 0 deletions SPlisHSPlasH/Elasticity/Elasticity_Kee2023.cpp

Large diffs are not rendered by default.

235 changes: 235 additions & 0 deletions SPlisHSPlasH/Elasticity/Elasticity_Kee2023.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#ifndef __Elasticity_Kee2023_h__
#define __Elasticity_Kee2023_h__

#include "SPlisHSPlasH/Common.h"
#include "SPlisHSPlasH/FluidModel.h"
#include "SPlisHSPlasH/NonPressureForceBase.h"
#if USE_AVX
#include "SPlisHSPlasH/Utilities/AVX_math.h"
#include "SPlisHSPlasH/Utilities/CholeskyAVXSolver.h"
#endif


namespace SPH
{
/** \brief This class implements the elasticity solver
* by Kee et al. [K23].
*
* References:
* - [K23] Kee et al..
* Kee 2023.
*
*/
class Elasticity_Kee2023 : public NonPressureForceBase
{
protected:

struct Factorization
{
Real m_dt;
Real m_mu;
Eigen::SparseMatrix<Real, Eigen::RowMajor> m_DT_K;
Eigen::SparseMatrix<Real, Eigen::RowMajor> m_D;
Eigen::SparseMatrix<Real, Eigen::ColMajor> m_matHTH;

#ifdef USE_AVX
CholeskyAVXSolver *m_cholesky;
Factorization() { m_cholesky = nullptr; }
~Factorization() { delete m_cholesky; }
#else
Factorization() {}
~Factorization() {}
// L-BFGS prefactored Cholesky (N×N, constant proxy)
Eigen::SparseMatrix<Real, Eigen::ColMajor> m_matL;
Eigen::SparseMatrix<Real, Eigen::ColMajor> m_matLT;
Eigen::VectorXi m_permInd;
Eigen::VectorXi m_permInvInd;
#endif
};

struct ElasticObject
{
std::string m_md5;
std::vector<unsigned int> m_particleIndices;
unsigned int m_nFixed;

std::shared_ptr<Factorization> m_factorization;
#ifdef USE_AVX
VectorXr m_rhs;
VectorXr m_sol;
std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_dx;
std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_f_avx;
std::vector<Scalarf8, AlignmentAllocator<Scalarf8, 32>> m_sol_avx;
std::vector<Quaternion8f, AlignmentAllocator<Quaternion8f, 32>> m_quats_avx;
#else
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_f;
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_xk;
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_xTilde;
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_dx;
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_dx_perm;
std::vector<Quaternionr, Eigen::aligned_allocator<Quaternionr>> m_quats;
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_gradient; // gradient at current xk (for line search)

// Newton: per-particle 9×9 Hessian (K_i = d²ψ/dvec(F)²)
std::vector<Eigen::Matrix<Real, 9, 9>> m_hessian9x9;

// Newton PCG workspace
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_r; // residual
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_p; // search direction
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_Ap; // A * p
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_pcg_z; // preconditioned residual
std::vector<Matrix3r, Eigen::aligned_allocator<Matrix3r>> m_pcg_precond; // block-diagonal preconditioner (inverted 3x3 blocks)

// L-BFGS secant history (circular queue)
std::vector<std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>> m_lbfgs_s; // position differences s_k = x_{k+1} - x_k
std::vector<std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>> m_lbfgs_y; // gradient differences y_k = g_{k+1} - g_k
std::vector<Real> m_lbfgs_rho; // 1 / (y_k^T s_k)
std::vector<Real> m_lbfgs_alpha; // temporary for two-loop recursion
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_lbfgs_last_sol; // previous sol for s_k computation
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>> m_lbfgs_q; // temporary for two-loop recursion
int m_lbfgs_count = 0; // number of stored secant pairs
#endif

ElasticObject() { m_factorization = nullptr; }
~ElasticObject() { m_factorization = nullptr; }
};

Real m_youngsModulus;
Real m_poissonRatio;
Vector3r m_fixedBoxMin;
Vector3r m_fixedBoxMax;
Vector3r m_fixedBox2Min;
Vector3r m_fixedBox2Max;

// initial particle indices, used to access their original positions
std::vector<unsigned int> m_current_to_initial_index;
std::vector<unsigned int> m_initial_to_current_index;
// initial particle neighborhood
std::vector<std::vector<unsigned int>> m_initialNeighbors;
// volumes in rest configuration
std::vector<Real> m_restVolumes;
std::vector<Matrix3r> m_rotations;
std::vector<Real> m_stress;
std::vector<int> m_fixedGroupId; // 0: free, 1: box1, 2: box2
std::vector<Matrix3r> m_L;
std::vector<Matrix3r> m_F;
std::vector<Matrix3r> m_PL;
Real m_alpha;
int m_maxNeighbors;
int m_solverType; // 0: Newton, 1: LBFGS
int m_lbfgsWindowSize;
int m_materialType; // 0: Stable Neo-Hookean, 1: Co-rotated
int m_maxIter;
Real m_maxError;
int m_maxIterCG; // max CG iterations for Newton linear solve
Real m_tolCG; // CG convergence tolerance
int m_maxLSIter;
Real m_lsArmijoParam;
Real m_lsBeta;
bool m_useLineSearch;
unsigned int m_totalNeighbors;
std::vector<ElasticObject*> m_objects;
Real m_lambda;
Real m_mu;

#ifdef USE_AVX
typedef Eigen::SimplicialLLT<Eigen::SparseMatrix<double>, Eigen::Lower, Eigen::AMDOrdering<int>> SolverLLT;
#else
typedef Eigen::SimplicialLLT<Eigen::SparseMatrix<double>, Eigen::Lower, Eigen::AMDOrdering<int>> SolverLLT;
#endif

void determineFixedParticles();
std::string computeMD5(const unsigned int objIndex);
void initValues();
void initSystem();
void initFactorization(std::shared_ptr<Factorization> factorization, std::vector<unsigned int> &particleIndices, const unsigned int nFixed, const Real dt, const Real mu);
void findObjects();
void computeMatrixL();

void stepElasticitySolver();

#ifndef USE_AVX
void computeXTilde(ElasticObject* obj);
void updateVelocity(ElasticObject* obj, const std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>& xk, Real fdt);
Real computeEnergy(ElasticObject* obj);
Real computePsi(const Matrix3r& F, const Matrix3r& R) const;
Real computeEnergyAndGradient(ElasticObject* obj);
void computeHessian(ElasticObject* obj);
void computeCorotatedHessian9x9(ElasticObject* obj);
void computeStableNeoHookeanHessian9x9(ElasticObject* obj);
void computeNewtonPreconditioner(ElasticObject* obj);
void newtonMatvec(ElasticObject* obj, const std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>& x,
std::vector<Vector3r, Eigen::aligned_allocator<Vector3r>>& Ax);
int matFreePCG(ElasticObject* obj);
void prefactorizedLLTSolve(ElasticObject* obj);
Real newtonSolve(ElasticObject* obj, int& cgIter);
Real lbfgsSolve(ElasticObject* obj);
Real lineSearch(ElasticObject* obj, Real energy, int& lsIter);
#endif

Matrix3r computeP(const Matrix3r& F, const Matrix3r& R) const;

virtual void initParameters();
/** This function is called after the simulation scene is loaded and all
* parameters are initialized. While reading a scene file several parameters
* can change. The deferred init function should initialize all values which
* depend on these parameters.
*/
virtual void deferredInit();

//////////////////////////////////////////////////////////////////////////
// multiplication of symmetric matrix, represented by a 6D vector, and a
// 3D vector
//////////////////////////////////////////////////////////////////////////
FORCE_INLINE void symMatTimesVec(const Vector6r & M, const Vector3r & v, Vector3r &res)
{
res[0] = M[0] * v[0] + M[3] * v[1] + M[4] * v[2];
res[1] = M[3] * v[0] + M[1] * v[1] + M[5] * v[2];
res[2] = M[4] * v[0] + M[5] * v[1] + M[2] * v[2];
}

void rotationMatricesToAVXQuaternions();

public:
static std::string METHOD_NAME;
static int YOUNGS_MODULUS;
static int POISSON_RATIO;
static int FIXED_BOX_MIN;
static int FIXED_BOX_MAX;
static int FIXED_BOX2_MIN;
static int FIXED_BOX2_MAX;
static int ALPHA;
static int MAX_NEIGHBORS;
static int SOLVER_TYPE;
static int LBFGS_WINDOW_SIZE;
static int MATERIAL_TYPE;
static int MAX_ITER;
static int MAX_ERROR;
static int MAX_ITER_CG;
static int TOL_CG;
static int MAX_LS_ITER;
static int LS_ARMIJO_PARAM;
static int LS_BETA;
static int USE_LINE_SEARCH;

static int ENUM_SOLVER_NEWTON;
static int ENUM_SOLVER_LBFGS;
static int ENUM_MATERIAL_STABLE_NEOHOOKEAN;
static int ENUM_MATERIAL_COROTATED;

Elasticity_Kee2023(FluidModel *model);
virtual ~Elasticity_Kee2023(void);

static NonPressureForceBase* creator(FluidModel* model) { return new Elasticity_Kee2023(model); }
virtual std::string getMethodName() { return METHOD_NAME; }

virtual void step();
virtual void reset();
virtual void performNeighborhoodSearchSort();

virtual void saveState(BinaryFileWriter &binWriter);
virtual void loadState(BinaryFileReader &binReader);
};
}

#endif
23 changes: 23 additions & 0 deletions SPlisHSPlasH/Elasticity/Elasticity_Kugelstadt2021.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,29 @@ void Elasticity_Kugelstadt2021::initValues()
}
}

// Check neighbor list symmetry
{
unsigned int maxActualNeighbors = 0;
int asymmetricPairs = 0;
for (unsigned int i = 0; i < numParticles; i++)
{
if (m_initialNeighbors[i].size() > maxActualNeighbors)
maxActualNeighbors = (unsigned int)m_initialNeighbors[i].size();
for (size_t jn = 0; jn < m_initialNeighbors[i].size(); jn++)
{
unsigned int j = m_initialNeighbors[i][jn];
bool found = false;
for (size_t kn = 0; kn < m_initialNeighbors[j].size(); kn++)
{
if (m_initialNeighbors[j][kn] == i) { found = true; break; }
}
if (!found) asymmetricPairs++;
}
}
LOG_INFO << "Neighbor stats: maxNeighbors=" << maxActualNeighbors
<< ", asymmetricPairs=" << asymmetricPairs;
}

// mark all particles in the bounding box as fixed
determineFixedParticles();

Expand Down
2 changes: 2 additions & 0 deletions SPlisHSPlasH/NonPressureForceRegistration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "Elasticity/Elasticity_Becker2009.h"
#include "Elasticity/Elasticity_Peer2018.h"
#include "Elasticity/Elasticity_Kugelstadt2021.h"
#include "Elasticity/Elasticity_Kee2023.h"

#include "SurfaceTension/SurfaceTension_Becker2007.h"
#include "SurfaceTension/SurfaceTension_Akinci2013.h"
Expand All @@ -39,6 +40,7 @@ void Simulation::registerNonpressureForces()
addElasticityMethod(Elasticity_Becker2009::METHOD_NAME, Elasticity_Becker2009::creator);
addElasticityMethod(Elasticity_Peer2018::METHOD_NAME, Elasticity_Peer2018::creator);
addElasticityMethod(Elasticity_Kugelstadt2021::METHOD_NAME, Elasticity_Kugelstadt2021::creator);
addElasticityMethod(Elasticity_Kee2023::METHOD_NAME, Elasticity_Kee2023::creator);

addSurfaceTensionMethod("None", [](FluidModel*) -> NonPressureForceBase* { return nullptr; });
addSurfaceTensionMethod(SurfaceTension_Becker2007::METHOD_NAME, SurfaceTension_Becker2007::creator);
Expand Down
Loading