Skip to content

Commit 52824da

Browse files
yuvaltassacopybara-github
authored andcommitted
Fix interpolated flex stiffness and quadratic quadrature
Use the standard 2*mu symmetric-strain term for solid and membrane elements, and correct the three-point Gauss abscissae. Add analytical stretch, shear, and quadratic strain-energy checks for both interpolation orders and nonzero Poisson ratio. PiperOrigin-RevId: 994297589 Change-Id: I729122f2a5863542fc2961f0508a29044afd304b
1 parent cbd16fe commit 52824da

4 files changed

Lines changed: 79 additions & 7 deletions

File tree

‎doc/changelog.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ Changelog
55
Upcoming Version (not yet released)
66
-----------------------------------
77

8+
Engine
9+
^^^^^^
10+
- Corrected the shear contribution to solid and membrane stiffness for interpolated flexes, and the three-point Gauss
11+
quadrature used by quadratic flexes. Material parameters now produce the standard isotropic linear-elastic stiffness;
12+
existing models using these flexes can change behavior.
13+
814
Compiler
915
^^^^^^^^
1016
.. admonition:: Breaking API changes

‎src/user/user_mesh.cc‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3659,8 +3659,8 @@ void quadratureGaussLegendre(
36593659
weights[1] = dpdx;
36603660
} else {
36613661
points[0] = p0;
3662-
points[1] = -dpdx / sqrt(3. / 5.) + p0;
3663-
points[2] = dpdx / sqrt(3. / 5.) + p0;
3662+
points[1] = -dpdx * sqrt(3. / 5.) + p0;
3663+
points[2] = dpdx * sqrt(3. / 5.) + p0;
36643664
weights[0] = 8. / 9. * dpdx;
36653665
weights[1] = 5. / 9. * dpdx;
36663666
weights[2] = 5. / 9. * dpdx;
@@ -3791,7 +3791,7 @@ void inline ComputeLinearStiffness(
37913791
}
37923792

37933793
// tensor contraction of the gradients of elastic strains
3794-
// (d(F+F')/dx : d(F+F')/dx)
3794+
// lambda * div(u) * div(v) + 2*mu * sym(grad(u)) : sym(grad(v))
37953795
for (int i = 0; i < n; i++) {
37963796
for (int j = 0; j < n; j++) {
37973797
Matrix du;
@@ -3807,7 +3807,7 @@ void inline ComputeLinearStiffness(
38073807
dv[l][1] = invJ[1] * F[j][1];
38083808
dv[l][2] = invJ[2] * F[j][2];
38093809
K[ndof * (3 * i + k) + 3 * j + l] -= la * trace(du) * trace(dv) * dvol;
3810-
K[ndof * (3 * i + k) + 3 * j + l] -= mu * trace(inner(sym(du), sym(dv))) * dvol;
3810+
K[ndof * (3 * i + k) + 3 * j + l] -= 2 * mu * trace(inner(sym(du), sym(dv))) * dvol;
38113811
mjuu_zerovec(du[k].data(), 3);
38123812
mjuu_zerovec(dv[l].data(), 3);
38133813
}
@@ -3901,8 +3901,7 @@ void inline ComputeLinearStiffness2D(std::vector<double>& K,
39013901
dv[l][axis1] = invJ1 * F[j][1];
39023902

39033903
K[ndof * (3 * i + k) + 3 * j + l] -= la * trace(du) * trace(dv) * dvol;
3904-
// mu (not 2*mu): same convention as 3D ComputeLinearStiffness
3905-
K[ndof * (3 * i + k) + 3 * j + l] -= mu * trace(inner(sym(du), sym(dv))) * dvol;
3904+
K[ndof * (3 * i + k) + 3 * j + l] -= 2 * mu * trace(inner(sym(du), sym(dv))) * dvol;
39063905
mjuu_zerovec(du[k].data(), 3);
39073906
mjuu_zerovec(dv[l].data(), 3);
39083907
}

‎test/engine/engine_forward_test.cc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3493,7 +3493,7 @@ TEST_F(ForwardTest, FlexParentCoupling) {
34933493

34943494
// implicit and explicit flex damping legitimately differ at
34953495
// O(h*damping*K/M) in this comparison
3496-
EXPECT_LT(max_diff, MjTol(5e-5, 1.5e-2))
3496+
EXPECT_LT(max_diff, MjTol(1e-4, 1.5e-2))
34973497
<< "Implicit integrator should match Euler at small timestep";
34983498
}
34993499

‎test/user/user_mesh_test.cc‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,73 @@ TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
797797
EXPECT_LE(mju_abs(model->geom_size[2]), 1);
798798
}
799799

800+
// Check the compiled stiffness against continuum strain energies on a unit
801+
// cube. Affine fields test the Lamé coefficients; quadratic fields also test
802+
// quadrature.
803+
TEST_F(MjCMeshTest, InterpolatedFlexStrainEnergy) {
804+
for (bool membrane : {false, true}) {
805+
for (bool quadratic : {false, true}) {
806+
for (double poisson : {0.0, 0.25}) {
807+
SCOPED_TRACE(absl::StrFormat("membrane=%d quadratic=%d poisson=%g",
808+
membrane, quadratic, poisson));
809+
std::string xml =
810+
absl::StrFormat(R"(
811+
<mujoco>
812+
<worldbody>
813+
<flexcomp name="f" type="grid" count="3 3 3" spacing=".5 .5 .5" dim="3" dof="%s">
814+
<elasticity young="2" poisson="%g" thickness=".1" %s/>
815+
<contact selfcollide="none"/>
816+
</flexcomp>
817+
</worldbody>
818+
</mujoco>)",
819+
quadratic ? "quadratic" : "trilinear", poisson,
820+
membrane ? "elastic2d=\"stretch\"" : "");
821+
char error[1024];
822+
MjModelPtr model =
823+
LoadModelFromString(xml.c_str(), error, sizeof(error));
824+
ASSERT_THAT(model.get(), testing::NotNull()) << error;
825+
int order = quadratic ? 2 : 1;
826+
int nbasis = order + 1;
827+
int nnode = membrane ? nbasis * nbasis : nbasis * nbasis * nbasis;
828+
int ndof = 3 * nnode;
829+
double mu = 1 / (1 + poisson);
830+
double lambda = membrane
831+
? 2 * poisson / (1 - poisson * poisson)
832+
: 2 * poisson / ((1 + poisson) * (1 - 2 * poisson));
833+
for (int field = 0; field < (quadratic ? 3 : 2); ++field) {
834+
SCOPED_TRACE(absl::StrFormat("field=%d", field));
835+
// u_x=x, u_y=x, or u_x=x^2/2. For membranes use each face's in-plane
836+
// axes.
837+
for (int face = 0; face < (membrane ? 6 : 1); ++face) {
838+
int axis0 = membrane ? (face / 2 + 1) % 3 : 0;
839+
int axis1 = membrane ? (face / 2 + 2) % 3 : 1;
840+
std::vector<double> u(ndof, 0);
841+
for (int i = 0; i < nnode; ++i) {
842+
int ix = i / (membrane ? nbasis : nbasis * nbasis);
843+
double x = static_cast<double>(ix) / order - 0.5;
844+
u[3 * i + (field == 1 ? axis1 : axis0)] =
845+
field == 2 ? x * x / 2 : x;
846+
}
847+
const mjtNum* stiffness = model->flex_stiffness +
848+
model->flex_stiffnessadr[0] +
849+
face * ndof * ndof;
850+
double energy = 0;
851+
for (int i = 0; i < ndof; ++i) {
852+
for (int j = 0; j < ndof; ++j) {
853+
energy -= 0.5 * u[i] * stiffness[i * ndof + j] * u[j];
854+
}
855+
}
856+
double expected = 0.5 * (field == 1 ? mu : lambda + 2 * mu);
857+
if (field == 2) expected /= 12; // integral of x^2 on [-1/2, 1/2]
858+
if (membrane) expected *= 0.1; // thickness
859+
EXPECT_NEAR(energy, expected, MjTol(1e-12, 2e-6));
860+
}
861+
}
862+
}
863+
}
864+
}
865+
}
866+
800867
TEST_F(MjCMeshTest, Flex2DElasticityRequiresPositiveThickness) {
801868
static constexpr char xml[] = R"(
802869
<mujoco>

0 commit comments

Comments
 (0)
⌂ 换网址