Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
73a85d6
Add a fixed-size fast path to double_dot_product
dseyler Aug 24, 2026
9ee0f62
Updated documentation for double dot product
dseyler Aug 27, 2026
6edb73d
Compute dyadic product with eigen
dseyler Aug 26, 2026
85d72ec
Updated documentation and removed references to Fortran
dseyler Aug 27, 2026
1e770a1
Compute fixed-size tensor products with Eigen instead of nested loops
dseyler Aug 30, 2026
c3b3303
Templated viscous stress functions on nsd and implemented fixed-size …
dseyler Aug 25, 2026
4915b39
Updated documentation
dseyler Aug 27, 2026
1c22dad
Trimmed comments from viscous stress functions
dseyler Aug 30, 2026
f2f6198
trim comments and assume 27-node element bound rather than checking p…
dseyler Aug 31, 2026
2f45845
Polished documentations, variable names, and types
dseyler Aug 31, 2026
83a4ad2
Removed column hoisting, which had no impact on runtime
dseyler Aug 31, 2026
42a5f4a
Dispatch by nsd within compute_visc_stress_and_tangent
dseyler Sep 2, 2026
990ccb5
Reuse viscosity across gauss points for linear elements. lShpF correc…
dseyler Sep 2, 2026
f0e2ac0
Reverted minor optimizations that made code less readable
dseyler Sep 4, 2026
2b71444
Merge remote-tracking branch 'origin/main' into perf/visc-stress-temp…
dseyler Sep 4, 2026
2db0a06
Merge branch 'main' into perf/visc-stress-templated
aabrown100-git Sep 8, 2026
8e25ed2
Declare viscous buffers as statics. Moved viscosity models into anony…
dseyler Sep 17, 2026
b71a6c2
merge remote-tracking branch 'fork/perf/visc-stress-templated' into p…
dseyler Sep 17, 2026
9104cea
Merge remote-tracking branch 'origin/main' into perf/visc-stress-temp…
dseyler Sep 17, 2026
952b7fc
recompute_visc flag checked in struct / ustruct instead of passed int…
dseyler Sep 17, 2026
f59e22f
Merge branch 'perf/fixed-size-tensor-ops' into perf/visc-stress-templ…
dseyler Sep 17, 2026
1ecce85
Added nsd-templated compute_pk2cc overloads taking Eigen matrices
dseyler Sep 18, 2026
fc4511a
Keep F, vx, S0, Dm, P and Svis as fixed-size Eigen matrices
dseyler Sep 21, 2026
6f2ab9d
Merge remote-tracking branch 'origin/main' into perf/visc-stress-temp…
dseyler Sep 21, 2026
368df7c
Fixed typos
dseyler Sep 21, 2026
8ea22ae
max number of nodes = 27 defined in consts.h
dseyler Sep 21, 2026
8dc84f5
Consolidate Matrix aliases into mat_fun
dseyler Sep 21, 2026
c6d3727
refactor Bm as per-node Eigen blocks
dseyler Sep 22, 2026
0cbfa49
Hoisted and collapsed geometric stiffness computation with Eigen and …
dseyler Sep 22, 2026
3084c8a
Removed unused locals in ustruct and collapsed more arithmetic with e…
dseyler Sep 22, 2026
f8a9e76
Collapse rMNqx and rMNwx with Eigen
dseyler Sep 22, 2026
0c401dd
Collapsed interpolated quantitied and gradients for readability using…
dseyler Sep 22, 2026
d424f50
condensed strain displacmeent computation
dseyler Sep 22, 2026
cc08b5e
Implemented eigen view helper in viscosity models
dseyler Sep 22, 2026
d2590a6
add compound assignments for consistency
dseyler Sep 22, 2026
a22846f
reverted two argument f + s to fl for fiber matrix
dseyler Sep 22, 2026
3c04e25
removed dead locals and bare declarations
dseyler Sep 22, 2026
ae0043b
aligned compute_pk2cc nsd branches for consistency
dseyler Sep 22, 2026
a061129
Added comments to dyadic_product, eigen_view, and Kvis buffers
dseyler Sep 22, 2026
793c716
Removed dead locals and moved declarations closer to use
dseyler Sep 22, 2026
28091da
Organized prestress matrix to show voigt symmetry
dseyler Sep 22, 2026
424ea14
Refactored viscosity to simplify call site and hide Kvis_u, Kvis_v re…
dseyler Sep 22, 2026
d55b935
Moved Eigen declarations closer to use
dseyler Sep 23, 2026
66720df
Hoisted Fiber matrix construction out of gauss loop and cleaned up so…
dseyler Sep 23, 2026
48ed4b7
Remove dead mat_fun functions
dseyler Sep 23, 2026
888f1a5
Cleaned up comments and out-of-scope edits
dseyler Sep 23, 2026
43f93a9
Addressed review comments (constexpr, docs, mutable eigen view)
dseyler Sep 24, 2026
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
18 changes: 14 additions & 4 deletions Code/Source/solver/fsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So
std::array<fsType,2> fs_1;
fs::get_thood_fs(com_mod, fs_1, lM, vmsStab, 1);

// Viscous response for the solid element routines
Array<double> Svis(nsd,nsd);
Array3<double> Kvis_u(nsd*nsd,fs_1[0].eNoN,fs_1[0].eNoN);
Array3<double> Kvis_v(nsd*nsd,fs_1[0].eNoN,fs_1[0].eNoN);

std::array<fsType,2> fs_2;
fs::get_thood_fs(com_mod, fs_2, lM, vmsStab, 2);

Expand Down Expand Up @@ -186,7 +191,10 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So
}
}

if (g == 0 || !fs_1[0].lShpF) {
// Viscosity is constant at all Gauss points for linear elements

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor suggestion:

Suggested change
// Viscosity is constant at all Gauss points for linear elements
// Viscosity is constant at all Gauss points for linear tetrahedral elements

Linear hexahedral elements are actually bi- or trilinear, so I don't think that this would apply to them, and I think it's a good idea to leave a reminder in the comment.

(I know that technically those are not linear elements, but they are sometime called that, so it might be better to be redundantly explicit just in case).

const bool recompute_visc = (g == 0 || !fs_1[0].lShpF);

if (recompute_visc) {
auto Nx = fs_1[0].Nx.rslice(g);
nn::gnn(fs_1[0].eNoN, nsd, nsd, Nx, xwl, Nwx, Jac, ksix);
if (utils::is_zero(Jac)) {
Expand Down Expand Up @@ -220,7 +228,8 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So
auto N0 = fs_1[0].N.col(g);
struct_ns::struct_3d(com_mod, cep_mod, fs_1[0].eNoN, nFn, w, N0,
Nwx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l_f,
ya_l_s, ya_l_n, lR, lK);
ya_l_s, ya_l_n, lR, lK,
Svis, Kvis_u, Kvis_v, recompute_visc);
} break;
case Equation_lElas:
throw std::runtime_error("[construct_fsi] LELAS3D not implemented");
Expand All @@ -233,7 +242,7 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So
ustruct::ustruct_3d_m(com_mod, cep_mod, vmsStab, fs_1[0].eNoN,
fs_1[1].eNoN, nFn, w, Jac, N0, N1, Nwx, al,
yl, dl, bfl, fN, ya_l_f, ya_l_s, ya_l_n, lR,
lK, lKd);
lK, lKd, Svis, Kvis_u, Kvis_v, recompute_visc);
break;
}

Expand All @@ -256,7 +265,8 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So
auto N0 = fs_1[0].N.col(g);
struct_ns::struct_2d(com_mod, cep_mod, fs_1[0].eNoN, nFn, w, N0,
Nwx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l_f,
ya_l_s, ya_l_n, lR, lK);
ya_l_s, ya_l_n, lR, lK,
Svis, Kvis_u, Kvis_v, recompute_visc);
} break;

case Equation_ustruct:
Expand Down
29 changes: 29 additions & 0 deletions Code/Source/solver/mat_fun.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,35 @@ namespace mat_fun {
}

Array<double> mat_symm(const Array<double>& A, const int nd);

/**
* @brief Symmetric part of a 2nd order tensor, 0.5 * (A + A^T).
*
* Fixed-size overload for the Eigen matrices used by the element kernels.
*
* @tparam nsd Number of spatial dimensions.
* @param[in] A Second order tensor.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor:

Suggested change
* @param[in] A Second order tensor.
* @param[in] A second order tensor.

Same for the function below.

* @return The symmetric part of A.
*/
template <int nsd>
Matrix<nsd> mat_symm(const Matrix<nsd>& A) {
return 0.5 * (A + A.transpose());
}

/**
* @brief Deviatoric part of a 2nd order tensor, A - tr(A)/nsd * I.
*
* Fixed-size overload for the Eigen matrices used by the element kernels.
*
* @tparam nsd Number of spatial dimensions.
* @param[in] A Second order tensor.
* @return The deviatoric part of A.
*/
template <int nsd>
Matrix<nsd> mat_dev(const Matrix<nsd>& A) {
return A - (A.trace() / nsd) * Matrix<nsd>::Identity();
}

Array<double> mat_symm_prod(const Vector<double>& u, const Vector<double>& v, const int nd);

double mat_trace(const Array<double>& A, const int nd);
Expand Down
206 changes: 106 additions & 100 deletions Code/Source/solver/mat_models.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1559,65 +1559,62 @@ void g_vol_pen(const ComMod& com_mod, const dmnType& lDmn, const double p,
}
}

namespace {

/// @brief Largest element node count the fixed-size views below allow (HEX27).
constexpr int MAX_ELEMENT_NODES = 27;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I have a clean solution to this, but: this number depends on the "largest" local finite element basis supported by the library, and this information is not logically pertinent to this module (the material model evaluation) but to the finite element basis module.

I think having this constant here introduces possible unexpected bugs (e.g. if larger finite element spaces are added in the future), and in general introduces implicit coupling between the two modules (implicit in the sense that the coupling is somewhat hidden).

One somewhat cleaner solution to this, I think, would be to move this definition to the basis function module (source/Code/FE/Basis). I am not sure what the appropriate file for that would be, but maybe @zasexton has a clearer picture.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. A max size of 27 is also set in fluid.cpp in two separate places. Would be nice to define this in a single place in the code to avoid bugs.


/// @brief A quantity carrying one nsd-vector per element node, so nsd x eNoN.
template <int nsd>
using NodalMatrix = Eigen::Matrix<double, nsd, Eigen::Dynamic, 0, nsd, MAX_ELEMENT_NODES>;

} // namespace

/**
* @brief Get the viscous PK2 stress and corresponding tangent matrix contributions for a solid
* with a viscous pseudo-potential model.
* @brief Viscous PK2 stress and tangent contributions for the viscous
* pseudo-potential model.
*
* This is defined by a viscous pseuo-potential
* Psi = mu/2 * tr(E_dot^2)
* The viscous 2nd Piola-Kirchhoff stress is given by
* Svis = dPsi/dE_dot
* Svis = dPsi/dE_dot
* = mu * E_dot
* = mu * 1/2 * F^T * (grad(v) + grad(v)^T) * F
* = mu * 1/2 * ( (F^T * Grad(v)) + (F^T * Grad(v))^T )
*
*
* @tparam nsd Number of spatial dimensions
* @param mu Solid viscosity parameter
* @param eNoN Number of nodes in an element
* @param Nx Shape function gradient w.r.t. reference configuration coordinates (dN/dX)
* @param vx Velocity gradient matrix w.r.t reference configuration coordinates (dv/dX)
* @param F Deformation gradient matrix
* @param Svis Viscous 2nd Piola-Kirchhoff stress matrix
* @param Kvis_u Viscous tangent matrix contribution due to displacement
* @param Kvis_v Visous tangent matrix contribution due to velocity
* @param[in] mu Solid viscosity parameter
* @param[in] eNoN Number of nodes in an element
* @param[in] Nx Shape function gradient w.r.t. reference configuration coordinates (dN/dX)
* @param[in] vx Velocity gradient matrix w.r.t. reference configuration coordinates (dv/dX)
* @param[in] F Deformation gradient matrix
* @param[out] Svis Viscous 2nd Piola-Kirchhoff stress matrix
* @param[out] Kvis_u Viscous tangent matrix contribution due to displacement
* @param[out] Kvis_v Viscous tangent matrix contribution due to velocity
*/
void compute_visc_stress_potential(const double mu, const int eNoN, const Array<double>& Nx, const Array<double>& vx, const Array<double>& F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v) {

using namespace consts;
using namespace mat_fun;
using namespace utils;

// Number of spatial dimensions
int nsd = F.nrows();

// Initialize Svis, Kvis_u, Kvis_v to zero
Svis = 0.0;
Kvis_u = 0.0;
Kvis_v = 0.0;

template <int nsd>
void compute_visc_stress_potential(const double mu, const int eNoN, const Array<double>& Nx,
const Array<double>& vx, const Array<double>& F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I interpret this right, you removed the definition of this function and of compute_visc_stress_newton from the header because they are not meant to be called from outside this file.

If that is correct, I think it is a good decision, but I would also place both functions in the anonymous namespace above (my understanding of anonymous namespaces is that, among other things, they're a way of specifying that a certain name should only have internal visibility).

// Alias the caller's storage; no copies. Svis, Kvis_u and Kvis_v are
// written in full below, so they are not zeroed first.
Eigen::Map<const Matrix<nsd>> F_map(F.data());
Eigen::Map<const Matrix<nsd>> vx_map(vx.data());
Eigen::Map<const NodalMatrix<nsd>> Nx_map(Nx.data(), nsd, eNoN);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand this right, these lines are constructing Eigen views over the data stored by the arrays, so that the subsequent operations use Eigen for efficiency but don't do any copy.

If that is right,

  1. Would it make sense to change the code in the caller to directly use Eigen structures for the local data, instead of custom Arrays? This would be a somewhat larger change perhaps, but I think it might make sense, reduce the (presumably very small, if even detectable) overhead associated to the construction of these views, and make the code a bit simpler (this function would just compute the viscosity, without any boilerplate code related to the technicalities of conversion).
  2. Should you choose not to follow the above suggestion, I would recommend writing a small helper function to do this, as a form of synctactic sugar, to turn these lines into e.g. auto F_eigen = to_eigen_map(F).

A similar suggestion applies to compute_visc_stress_newton.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct. I think we should move toward option 1 eventually unless we plan to rewrite the Array class eventually. I sketched out a version of this branch that also changes all of 3d_struct() to use Eigen, and it simplifies many of the functions in mat_models.{cpp,h} because they already convert inputs to Eigen, and it shaves another 10% off the runtime. It would be a much larger PR (~500 lines changed), but I could fold that in here if you think that would be the better option.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR would be a good place to do that. What are other people's thoughts? @ktbolt @zasexton

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michelebucelli I say let's do it; good to move Eigen as far up as possible.


// Required intermediate terms for stress and tangent
auto Ft = transpose(F);
auto F_Ft = mat_mul(F, Ft);
auto Ft_vx = mat_mul(Ft, vx);
auto vxt = transpose(vx);
auto F_vxt = mat_mul(F, vxt);

//double F_Nx[nsd][eNoN] = {0}, vx_Nx[nsd][eNoN] = {0};
Array<double> F_Nx(nsd,eNoN), vx_Nx(nsd,eNoN);

for (int a = 0; a < eNoN; ++a) {
for (int i = 0; i < nsd; ++i) {
for (int j = 0; j < nsd; ++j) {
F_Nx(i,a) += F(i,j) * Nx(j,a);
vx_Nx(i,a) += vx(i,j) * Nx(j,a);
}
}
}
const Matrix<nsd> F_Ft = F_map * F_map.transpose();
const Matrix<nsd> Ft_vx = F_map.transpose() * vx_map;
const Matrix<nsd> F_vxt = F_map * vx_map.transpose();

// F_Nx(i,a) = sum_j F(i,j) * Nx(j,a), and likewise for vx.
const NodalMatrix<nsd> F_Nx = F_map * Nx_map;
const NodalMatrix<nsd> vx_Nx = vx_map * Nx_map;

// 2nd Piola-Kirchhoff stress due to viscosity
// Svis = mu * 1/2 * ( (F^T * dv/dX) + (F^T * dv/dX)^T )
Svis = mu * mat_symm(Ft_vx, nsd);
Eigen::Map<Matrix<nsd>> Svis_map(Svis.data());
Svis_map.noalias() = mu * mat_fun::mat_symm<nsd>(Ft_vx);

// Tangent matrix contributions due to viscosity
for (int b = 0; b < eNoN; ++b) {
Expand All @@ -1641,71 +1638,58 @@ void compute_visc_stress_potential(const double mu, const int eNoN, const Array<
/**
* @brief Get the viscous PK2 stress and corresponding tangent matrix contributions for a solid
* with a Newtonian fluid-like viscosity model.
*
* The viscous deviatoric Cauchy stress is given by
* sigma_vis_dev = 2 * mu * d_dev
* where d_dev = 1/2 * (grad(v) + grad(v)^T) - 1/3 * (div(v)) * I
* The viscous 2nd Piola-Kirchhoff stress is given by a pull-back operation
* Svis = 2 * mu * J * F^-1 * d_dev * F^-T
*
* Note, there is likely an error/bug in the tangent contributions that leads to suboptimal nonlinear convergence
*
*
* Note, there is likely an error/bug in the tangent contributions
* that leads to suboptimal nonlinear convergence.
*
* @tparam nsd Number of spatial dimensions
* @param mu Solid viscosity parameter
* @param eNoN Number of nodes in an element
* @param Nx Shape function gradient w.r.t. reference configuration coordinates (dN/dX)
* @param vx Velocity gradient matrix w.r.t reference configuration coordinates (dv/dX)
* @param F Deformation gradient matrix
* @param Svis Viscous 2nd Piola-Kirchhoff stress matrix
* @param Kvis_u Viscous tangent matrix contribution due to displacement
* @param Kvis_v Visous tangent matrix contribution due to velocity
* @param[in] mu Solid viscosity parameter
* @param[in] eNoN Number of nodes in an element
* @param[in] Nx Shape function gradient w.r.t. reference configuration coordinates (dN/dX)
* @param[in] vx Velocity gradient matrix w.r.t. reference configuration coordinates (dv/dX)
* @param[in] F Deformation gradient matrix
* @param[out] Svis Viscous 2nd Piola-Kirchhoff stress matrix
* @param[out] Kvis_u Viscous tangent matrix contribution due to displacement
* @param[out] Kvis_v Viscous tangent matrix contribution due to velocity
*/
void compute_visc_stress_newtonian(const double mu, const int eNoN, const Array<double>& Nx, const Array<double>& vx, const Array<double>& F,
template <int nsd>
void compute_visc_stress_newtonian(const double mu, const int eNoN, const Array<double>& Nx,
const Array<double>& vx, const Array<double>& F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v) {
using namespace consts;
using namespace mat_fun;
using namespace utils;

// Number of spatial dimensions
int nsd = F.nrows();

// Initialize Svis, Kvis_u, Kvis_v to zero
Svis = 0.0;
Kvis_u = 0.0;
Kvis_v = 0.0;

Eigen::Map<const Matrix<nsd>> F_map(F.data());
Eigen::Map<const Matrix<nsd>> vx_map(vx.data());
Eigen::Map<const NodalMatrix<nsd>> Nx_map(Nx.data(), nsd, eNoN);

// Get identity matrix, Jacobian, and F^-1
auto Idm = mat_id(nsd);
auto J = mat_det(F, nsd);
auto Fi = mat_inv(F, nsd);
const auto Idm = Matrix<nsd>::Identity();
const double J = F_map.determinant();
const Matrix<nsd> Fi = F_map.inverse();
Comment thread
dseyler marked this conversation as resolved.
Outdated

// Required intermediate terms for stress and tangent
// vx_Fi: Velocity gradient in current configuration
auto vx_Fi = mat_mul(vx, Fi);
auto vx_Fi_symm = mat_symm(vx_Fi, nsd);
// vx_Fi: Velocity gradient in current configuration
const Matrix<nsd> vx_Fi = vx_map * Fi;
const Matrix<nsd> vx_Fi_symm = mat_fun::mat_symm<nsd>(vx_Fi);
// ddev: Deviatoric part of rate of strain tensor
auto ddev = mat_dev(vx_Fi_symm, nsd);
//double Nx_Fi[nsd][eNoN] = {0}, ddev_Nx_Fi[nsd][eNoN] = {0}, vx_Fi_Nx_Fi[nsd][eNoN] = {0};
Array<double> Nx_Fi(nsd,eNoN), ddev_Nx_Fi(nsd,eNoN), vx_Fi_Nx_Fi(nsd,eNoN);
for (int a = 0; a < eNoN; ++a) {
for (int i = 0; i < nsd; ++i) {
for (int j = 0; j < nsd; ++j) {
Nx_Fi(i,a) += Nx(j,a) * Fi(j,i);
}
}
}
const Matrix<nsd> ddev = mat_fun::mat_dev<nsd>(vx_Fi_symm);

mat_mul(ddev, Nx_Fi, ddev_Nx_Fi);
mat_mul(vx_Fi, Nx_Fi, vx_Fi_Nx_Fi);
// Nx_Fi(i,a) = sum_j Nx(j,a) * Fi(j,i), which is Fi^T * Nx.
const NodalMatrix<nsd> Nx_Fi = Fi.transpose() * Nx_map;
const NodalMatrix<nsd> ddev_Nx_Fi = ddev * Nx_Fi;
const NodalMatrix<nsd> vx_Fi_Nx_Fi = vx_Fi * Nx_Fi;

// 2nd Piola-Kirchhoff stress due to viscosity
// Svis = 2 * mu * J * F^-1 * d_dev * F^-T
auto Fit = transpose(Fi);
auto ddev_Fit = mat_mul(ddev, Fit);
auto Fi_ddev_Fit = mat_mul(Fi, ddev_Fit);
Svis = 2.0 * mu * J * Fi_ddev_Fit;
Eigen::Map<Matrix<nsd>> Svis_map(Svis.data());
Svis_map.noalias() = (2.0 * mu * J) * (Fi * ddev * Fi.transpose());

// Tangent matrix contributions due to viscosity
double r2d = 2.0 / nsd;
constexpr double r2d = 2.0 / nsd;
for (int b = 0; b < eNoN; ++b) {
for (int a = 0; a < eNoN; ++a) {
double Nx_Fi_Nx_Fi = 0.0;
Expand All @@ -1718,7 +1702,7 @@ void compute_visc_stress_newtonian(const double mu, const int eNoN, const Array<
int ii = i * nsd + j;

// Derivative of the residual w.r.t displacement
Kvis_u(ii,a,b) = mu * J * (2.0 *
Kvis_u(ii,a,b) = mu * J * (2.0 *
(ddev_Nx_Fi(i,a) * Nx_Fi(j,b) - ddev_Nx_Fi(i,b) * Nx_Fi(j,a)) -
(Nx_Fi_Nx_Fi * vx_Fi(i,j) + Nx_Fi(i,b) * vx_Fi_Nx_Fi(j,a) -
r2d * Nx_Fi(i,a) * vx_Fi_Nx_Fi(j,b)));
Expand All @@ -1732,13 +1716,11 @@ void compute_visc_stress_newtonian(const double mu, const int eNoN, const Array<
}
}


/**
* @brief Get the solid viscous PK2 stress and corresponding tangent matrix contributions
* Calls the appropriate function based on the viscosity type, either viscous
* Calls the appropriate function based on the viscosity type, either viscous
* pseudo-potential or Newtonian viscosity model.
*
* @tparam nsd Number of spatial dimensions
*
* @param[in] lDmn Domain object
* @param[in] eNoN Number of nodes in an element
* @param[in] Nx Shape function gradient w.r.t. reference configuration coordinates (dN/dX)
Expand All @@ -1749,15 +1731,39 @@ void compute_visc_stress_newtonian(const double mu, const int eNoN, const Array<
* @param[out] Kvis_v Viscous tangent matrix contribution due to velocity
*/
void compute_visc_stress_and_tangent(const dmnType& lDmn, const int eNoN, const Array<double>& Nx, const Array<double>& vx, const Array<double>& F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v) {
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v,
const bool recompute_visc) {

switch (lDmn.solid_visc.viscType) {
case consts::SolidViscosityModelType::viscType_Newtonian:
compute_visc_stress_newtonian(lDmn.solid_visc.mu, eNoN, Nx, vx, F, Svis, Kvis_u, Kvis_v);
// Viscosity is constant at all Gauss points for linear elements
if (!recompute_visc) {
return;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it would be better if this check was performed by the caller of this function, rather than by the function itself.

In other words, I think this function should always do what its name says (compute the viscosity), and the caller, who is aware of the context, should be the one to decide whether the viscosity needs to be recomputed (and thus the function needs to be called) or not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Would you rather add recompute_visc as a new argument for the potential and Newtonian viscosity models? I placed it within each case rather than throwing an early return at the top of the function, in case a different viscosity model is added in the future that does differ between Gauss points within linear elements. Alternatively, could just scrap the recompute_visc change if the 5% runtime savings isn't worth the risks/complexity of the assembly routine being treated differently for certain element types

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The place where I think this would be cleanest (although maybe still not very clean 😅 ), right now, is to pass recompute_visc to struct_2d and struct_3d (as you do already), but not to mat_models::compute_visc_stress_and_tangent. So the if (recompute_visc) statement should wrap the call to compute_visc_stress_and_tangent, in my opinion.

It is true that this might break for future models (which is why I say it still isn't entirely clean). But then, should new viscosity models be implemented, it would be a good idea to give them an object-oriented refactoring, and perhaps this sort of caching could be encapsulated into the hypothetical viscous model class.

if (F.nrows() == 3) {
compute_visc_stress_newtonian<3>(lDmn.solid_visc.mu, eNoN, Nx, vx, F, Svis, Kvis_u, Kvis_v);
} else if (F.nrows() == 2) {
compute_visc_stress_newtonian<2>(lDmn.solid_visc.mu, eNoN, Nx, vx, F, Svis, Kvis_u, Kvis_v);
}
Comment thread
dseyler marked this conversation as resolved.
Outdated
break;

case consts::SolidViscosityModelType::viscType_Potential:
compute_visc_stress_potential(lDmn.solid_visc.mu, eNoN, Nx, vx, F, Svis, Kvis_u, Kvis_v);
// Viscosity is constant at all Gauss points for linear elements
if (!recompute_visc) {
return;
}
if (F.nrows() == 3) {
compute_visc_stress_potential<3>(lDmn.solid_visc.mu, eNoN, Nx, vx, F, Svis, Kvis_u, Kvis_v);
} else if (F.nrows() == 2) {
compute_visc_stress_potential<2>(lDmn.solid_visc.mu, eNoN, Nx, vx, F, Svis, Kvis_u, Kvis_v);
}
break;

default:
// No viscosity model for this domain.
Svis = 0.0;
Kvis_u = 0.0;
Kvis_v = 0.0;
break;
Comment thread
dseyler marked this conversation as resolved.
}
}
Expand Down
19 changes: 13 additions & 6 deletions Code/Source/solver/mat_models.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,21 @@ void compute_svol_p(const ComMod& com_mod, const CepMod& cep_mod, const stModelT
void g_vol_pen(const ComMod& com_mod, const dmnType& lDmn, const double p,
double& ro, double& bt, double& dro, double& dbt, const double Ja);

void compute_visc_stress_potential(const double mu, const int eNoN, const Array<double>& Nx, const double vx, const double F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v);

void compute_visc_stress_newtonian(const double mu, const int eNoN, const Array<double>& Nx, const Array<double>& vx, const Array<double>& F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v);

/// @brief Computes viscous PK2 stress and tangent
/// for the viscosity model configured for the domain.
///
/// @param[in] lDmn Domain, supplying the viscosity model and its parameters.
/// @param[in] eNoN Number of element nodes.
/// @param[in] Nx Shape function spatial derivatives.
/// @param[in] vx Velocity gradient.
/// @param[in] F Deformation gradient.
/// @param[out] Svis Viscous 2nd Piola-Kirchhoff stress.
/// @param[out] Kvis_u,Kvis_v Tangent contributions w.r.t. displacement and velocity.
/// @param[in] recompute False when the outputs are still valid from the previous call.
void compute_visc_stress_and_tangent(const dmnType& lDmn, const int eNoN, const Array<double>& Nx, const Array<double>& vx, const Array<double>& F,
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v);
Array<double>& Svis, Array3<double>& Kvis_u, Array3<double>& Kvis_v,
const bool recompute_visc);
};

#endif
Expand Down
Loading
Loading