-
Notifications
You must be signed in to change notification settings - Fork 61
Templated viscous stress functions + linear shape function fix #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 10 commits
73a85d6
9ee0f62
6edb73d
85d72ec
1e770a1
c3b3303
4915b39
1c22dad
f2f6198
2f45845
83a4ad2
42a5f4a
990ccb5
f0e2ac0
2b71444
2db0a06
8e25ed2
b71a6c2
9104cea
952b7fc
f59e22f
1ecce85
fc4511a
6f2ab9d
368df7c
8ea22ae
8dc84f5
c6d3727
0cbfa49
3084c8a
f8a9e76
0c401dd
d424f50
cc08b5e
d2590a6
a22846f
3c04e25
ae0043b
a061129
793c716
28091da
424ea14
d55b935
66720df
48ed4b7
888f1a5
43f93a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very minor:
Suggested change
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); | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 (
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand this right, these lines are constructing If that is right,
A similar suggestion applies to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
|
|
@@ -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(); | ||
|
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; | ||
|
|
@@ -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))); | ||
|
|
@@ -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) | ||
|
|
@@ -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; | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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); | ||
| } | ||
|
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; | ||
|
dseyler marked this conversation as resolved.
|
||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor suggestion:
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).