Clean-up flex assumptions.
Do not allow a mix of `elastic2d != none` with `dof = trilinear` since the latter assumes 3d elasticity. Also, do not assume that `flex_interp > 0` in the engine. This will enable to use, e.g., `flex_interp = -1` to mean a linear surface finite element instead of a 3d finite element which is currently identified with `flex_interp = 1`. PiperOrigin-RevId: 903852035 Change-Id: Ia6290b4a05e9e510ffb7f36d141cd525b40d3110
This commit is contained in:
committed by
Copybara-Service
parent
863a084d7f
commit
a891782553
@@ -4328,7 +4328,7 @@ stress-strain relationship. See also :ref:`deformable <CDeformable>` objects and
|
|||||||
|
|
||||||
:at:`elastic2d`: :at-val:`[none, bend, stretch, both], "none"`
|
:at:`elastic2d`: :at-val:`[none, bend, stretch, both], "none"`
|
||||||
Elastic contribution to passive forces of 2D flexes. "none": none, "bend": bending only, "stretch": stretching only,
|
Elastic contribution to passive forces of 2D flexes. "none": none, "bend": bending only, "stretch": stretching only,
|
||||||
"both": bending and stretching.
|
"both": bending and stretching. Not yet supported by :ref:`dof<body-flexcomp-dof>` **trilinear** and **quadratic**.
|
||||||
|
|
||||||
.. _flex-contact:
|
.. _flex-contact:
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</body>
|
</body>
|
||||||
<flexcomp type="mesh" file="bunny.obj" pos="0 0 .1" dim="2" euler="90 0 0"
|
<flexcomp type="mesh" file="bunny.obj" pos="0 0 .1" dim="2" euler="90 0 0"
|
||||||
radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear">
|
radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear">
|
||||||
<elasticity young="1e3" poisson="0.1" damping="0.001" elastic2d="stretch"/>
|
<elasticity young="1e3" poisson="0.1" damping="0.001" elastic2d="none"/>
|
||||||
<contact selfcollide="none" internal="false"/>
|
<contact selfcollide="none" internal="false"/>
|
||||||
</flexcomp>
|
</flexcomp>
|
||||||
</worldbody>
|
</worldbody>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</body>
|
</body>
|
||||||
<flexcomp type="mesh" file="bunny.obj" pos="0 0 0" dim="2" euler="90 0 0" cellcount="3 3 3"
|
<flexcomp type="mesh" file="bunny.obj" pos="0 0 0" dim="2" euler="90 0 0" cellcount="3 3 3"
|
||||||
radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear">
|
radius=".001" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="trilinear">
|
||||||
<elasticity young="1e3" poisson="0.1" damping="0.01" elastic2d="stretch"/>
|
<elasticity young="1e3" poisson="0.1" damping="0.01" elastic2d="none"/>
|
||||||
<contact selfcollide="none" internal="false"/>
|
<contact selfcollide="none" internal="false"/>
|
||||||
</flexcomp>
|
</flexcomp>
|
||||||
</worldbody>
|
</worldbody>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</body>
|
</body>
|
||||||
<flexcomp type="mesh" file="bunny.obj" pos="0 0 -.01" dim="2" euler="90 0 0"
|
<flexcomp type="mesh" file="bunny.obj" pos="0 0 -.01" dim="2" euler="90 0 0"
|
||||||
radius=".002" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="quadratic">
|
radius=".002" rgba="0 .7 .7 1" mass=".05" name="softbody" dof="quadratic">
|
||||||
<elasticity young="1e3" poisson="0.1" damping="0.0001" elastic2d="stretch"/>
|
<elasticity young="1e3" poisson="0.1" damping="0.0001" elastic2d="none"/>
|
||||||
<contact selfcollide="none" internal="false"/>
|
<contact selfcollide="none" internal="false"/>
|
||||||
</flexcomp>
|
</flexcomp>
|
||||||
</worldbody>
|
</worldbody>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</body>
|
</body>
|
||||||
<flexcomp type="mesh" file="bunny_with_uv.obj" pos="0 0 .1" dim="2" euler="90 0 0"
|
<flexcomp type="mesh" file="bunny_with_uv.obj" pos="0 0 .1" dim="2" euler="90 0 0"
|
||||||
radius=".001" material="matsponge" mass=".05" name="softbody" dof="trilinear">
|
radius=".001" material="matsponge" mass=".05" name="softbody" dof="trilinear">
|
||||||
<elasticity young="1e3" poisson="0.1" damping="0.001" elastic2d="stretch"/>
|
<elasticity young="1e3" poisson="0.1" damping="0.001" elastic2d="none"/>
|
||||||
<contact selfcollide="none" internal="false"/>
|
<contact selfcollide="none" internal="false"/>
|
||||||
</flexcomp>
|
</flexcomp>
|
||||||
</worldbody>
|
</worldbody>
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ static int mj_vertBodyWeight(const mjModel* m, const mjData* d, int f, int* v,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int npc = (order+1)*(order+1)*(order+1); // number of nodes per cell
|
int npc = (order+1)*(order+1)*(order+1); // number of nodes per cell
|
||||||
|
|
||||||
// cell lookup: get local coords and node indices
|
// cell lookup: get local coords and node indices
|
||||||
@@ -709,6 +710,7 @@ void mj_instantiateEquality(const mjModel* m, mjData* d) {
|
|||||||
int f = id[0];
|
int f = id[0];
|
||||||
int nodenum = m->flex_nodenum[f];
|
int nodenum = m->flex_nodenum[f];
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
|
|
||||||
// skip if not interpolated (order == 0 or no nodes)
|
// skip if not interpolated (order == 0 or no nodes)
|
||||||
if (!order || !nodenum) {
|
if (!order || !nodenum) {
|
||||||
@@ -1676,6 +1678,7 @@ void mj_diagApprox(const mjModel* m, mjData* d) {
|
|||||||
int flex_id = m->eq_obj1id[id];
|
int flex_id = m->eq_obj1id[id];
|
||||||
int nstart = m->flex_nodeadr[flex_id];
|
int nstart = m->flex_nodeadr[flex_id];
|
||||||
int order = m->flex_interp[flex_id];
|
int order = m->flex_interp[flex_id];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int npc = (order+1)*(order+1)*(order+1);
|
int npc = (order+1)*(order+1)*(order+1);
|
||||||
|
|
||||||
// per-cell constraint count
|
// per-cell constraint count
|
||||||
@@ -2296,6 +2299,7 @@ static int mj_ne(const mjModel* m, mjData* d, int* nnz) {
|
|||||||
// per-cell strain constraints: each equality is one cell
|
// per-cell strain constraints: each equality is one cell
|
||||||
int f = id[0];
|
int f = id[0];
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
if (!order || !m->flex_nodenum[f]) {
|
if (!order || !m->flex_nodenum[f]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -595,7 +595,8 @@ void mj_flex(const mjModel* m, mjData* d) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int order = m->flex_interp[f];
|
int interp = m->flex_interp[f];
|
||||||
|
int order = interp < 0 ? -interp : interp;
|
||||||
int cx = m->flex_cellnum[3*f+0];
|
int cx = m->flex_cellnum[3*f+0];
|
||||||
int cy = m->flex_cellnum[3*f+1];
|
int cy = m->flex_cellnum[3*f+1];
|
||||||
int cz = m->flex_cellnum[3*f+2];
|
int cz = m->flex_cellnum[3*f+2];
|
||||||
@@ -2624,7 +2625,8 @@ void mj_rnePostConstraint(const mjModel* m, mjData* d) {
|
|||||||
case mjEQ_FLEXSTRAIN: {
|
case mjEQ_FLEXSTRAIN: {
|
||||||
// increment: trilinear uses 2 center (I1,J-1) + 3*ngauss shear, quadratic uses 6*ngauss
|
// increment: trilinear uses 2 center (I1,J-1) + 3*ngauss shear, quadratic uses 6*ngauss
|
||||||
k = m->eq_obj1id[id];
|
k = m->eq_obj1id[id];
|
||||||
int order = m->flex_interp[k];
|
int interp_k = m->flex_interp[k];
|
||||||
|
int order = interp_k < 0 ? -interp_k : interp_k;
|
||||||
int nodenum = m->flex_nodenum[k];
|
int nodenum = m->flex_nodenum[k];
|
||||||
if (order && nodenum) {
|
if (order && nodenum) {
|
||||||
int nquad = order + 1;
|
int nquad = order + 1;
|
||||||
|
|||||||
@@ -887,6 +887,7 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
|||||||
if (!m->flex_interp[f]) continue;
|
if (!m->flex_interp[f]) continue;
|
||||||
if (m->flex_rigid[f]) continue;
|
if (m->flex_rigid[f]) continue;
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int npc = (order+1)*(order+1)*(order+1);
|
int npc = (order+1)*(order+1)*(order+1);
|
||||||
if (npc > max_npc) max_npc = npc;
|
if (npc > max_npc) max_npc = npc;
|
||||||
if (m->flex_nodenum[f] > max_nodenum) max_nodenum = m->flex_nodenum[f];
|
if (m->flex_nodenum[f] > max_nodenum) max_nodenum = m->flex_nodenum[f];
|
||||||
@@ -966,6 +967,7 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int npc = (order+1)*(order+1)*(order+1);
|
int npc = (order+1)*(order+1)*(order+1);
|
||||||
int cx = m->flex_cellnum[3*f+0];
|
int cx = m->flex_cellnum[3*f+0];
|
||||||
int cy = m->flex_cellnum[3*f+1];
|
int cy = m->flex_cellnum[3*f+1];
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ static void mj_springdamper(const mjModel* m, mjData* d) {
|
|||||||
|
|
||||||
if (m->flex_interp[f]) {
|
if (m->flex_interp[f]) {
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int npc = (order+1)*(order+1)*(order+1); // nodes per cell
|
int npc = (order+1)*(order+1)*(order+1); // nodes per cell
|
||||||
int cx = m->flex_cellnum[3*f+0];
|
int cx = m->flex_cellnum[3*f+0];
|
||||||
int cy = m->flex_cellnum[3*f+1];
|
int cy = m->flex_cellnum[3*f+1];
|
||||||
|
|||||||
@@ -714,6 +714,7 @@ static void makeFlexBandwidth(mjModel* m, mjData* d) {
|
|||||||
for (int f = 0; f < m->nflex; f++) {
|
for (int f = 0; f < m->nflex; f++) {
|
||||||
if (!m->flex_interp[f]) continue;
|
if (!m->flex_interp[f]) continue;
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int nodeadr = m->flex_nodeadr[f];
|
int nodeadr = m->flex_nodeadr[f];
|
||||||
int nodenum = m->flex_nodenum[f];
|
int nodenum = m->flex_nodenum[f];
|
||||||
int cx = m->flex_cellnum[3*f+0];
|
int cx = m->flex_cellnum[3*f+0];
|
||||||
|
|||||||
@@ -864,6 +864,7 @@ int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt,
|
|||||||
if (m->flex_interp[i]) {
|
if (m->flex_interp[i]) {
|
||||||
mjtNum* coord = m->flex_vert0 + 3*(m->flex_vertadr[i] + vertid);
|
mjtNum* coord = m->flex_vert0 + 3*(m->flex_vertadr[i] + vertid);
|
||||||
int order = m->flex_interp[i];
|
int order = m->flex_interp[i];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int npc = (order+1)*(order+1)*(order+1);
|
int npc = (order+1)*(order+1)*(order+1);
|
||||||
|
|
||||||
// cell lookup: get local coords and node indices
|
// cell lookup: get local coords and node indices
|
||||||
|
|||||||
@@ -1452,6 +1452,7 @@ static void addFlexBvhGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
|||||||
int cy = m->flex_cellnum[3*f+1];
|
int cy = m->flex_cellnum[3*f+1];
|
||||||
int cz = m->flex_cellnum[3*f+2];
|
int cz = m->flex_cellnum[3*f+2];
|
||||||
int order = m->flex_interp[f];
|
int order = m->flex_interp[f];
|
||||||
|
order = order < 0 ? -order : order;
|
||||||
int NX = cx * order + 1;
|
int NX = cx * order + 1;
|
||||||
int NY = cy * order + 1;
|
int NY = cy * order + 1;
|
||||||
int NZ = cz * order + 1;
|
int NZ = cz * order + 1;
|
||||||
|
|||||||
+13
-3
@@ -4100,6 +4100,19 @@ void mjCFlex::Compile(const mjVFS* vfs) {
|
|||||||
}
|
}
|
||||||
nelem = (int)elem_.size()/(dim+1);
|
nelem = (int)elem_.size()/(dim+1);
|
||||||
|
|
||||||
|
// elastic2d checks
|
||||||
|
if (elastic2d) {
|
||||||
|
if (thickness <= 0) {
|
||||||
|
throw mjCError(this, "2d elasticity requires positive thickness");
|
||||||
|
}
|
||||||
|
if (interpolated) {
|
||||||
|
throw mjCError(this, "interpolated flex does not yet support 2d elasticity");
|
||||||
|
}
|
||||||
|
if (dim != 2 && !interpolated) {
|
||||||
|
throw mjCError(this, "2d elasticity requires 2d flex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set nvert, rigid, centered; check size
|
// set nvert, rigid, centered; check size
|
||||||
if (vert_.empty()) {
|
if (vert_.empty()) {
|
||||||
centered = true;
|
centered = true;
|
||||||
@@ -4342,9 +4355,6 @@ void mjCFlex::Compile(const mjVFS* vfs) {
|
|||||||
|
|
||||||
// bending stiffness (2D only)
|
// bending stiffness (2D only)
|
||||||
if (dim == 2 && (elastic2d == 1 || elastic2d == 3)) {
|
if (dim == 2 && (elastic2d == 1 || elastic2d == 3)) {
|
||||||
if (thickness < 0) {
|
|
||||||
throw mjCError(this, "thickness must be positive for bending stiffness");
|
|
||||||
}
|
|
||||||
bending.assign(nedge*17, 0);
|
bending.assign(nedge*17, 0);
|
||||||
|
|
||||||
for (unsigned int e = 0; e < nedge; e++) {
|
for (unsigned int e = 0; e < nedge; e++) {
|
||||||
|
|||||||
@@ -819,6 +819,58 @@ TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, Flex2DElasticityRequiresPositiveThickness) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<flexcomp name="f" type="grid" count="3 3 1" spacing="1 1 1" dim="2" dof="2d">
|
||||||
|
<elasticity young="1" thickness="0" elastic2d="bend"/>
|
||||||
|
</flexcomp>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
EXPECT_THAT(model, testing::IsNull());
|
||||||
|
EXPECT_THAT(error.data(),
|
||||||
|
HasSubstr("2d elasticity requires positive thickness"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, InterpolatedFlexDoesNotSupport2DElasticity) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<flexcomp name="f" type="grid" count="3 3 1" spacing="1 1 1" dim="2" dof="trilinear">
|
||||||
|
<contact selfcollide="none"/>
|
||||||
|
<elasticity young="1" thickness="1" elastic2d="bend"/>
|
||||||
|
</flexcomp>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
EXPECT_THAT(model, testing::IsNull());
|
||||||
|
EXPECT_THAT(
|
||||||
|
error.data(),
|
||||||
|
HasSubstr("interpolated flex does not yet support 2d elasticity"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, Flex2DElasticityRequires2DFlex) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<flexcomp name="f" type="grid" count="3 3 3" spacing="1 1 1" dim="3" dof="2d">
|
||||||
|
<elasticity young="1" thickness="1" elastic2d="bend"/>
|
||||||
|
</flexcomp>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
EXPECT_THAT(model, testing::IsNull());
|
||||||
|
EXPECT_THAT(error.data(), HasSubstr("2d elasticity requires 2d flex"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MjCMeshTest, VolumeNegativeThrowsError) {
|
TEST_F(MjCMeshTest, VolumeNegativeThrowsError) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
|
|||||||
Reference in New Issue
Block a user