Optimize flex by pinning nodes in empty cells.
This change introduces an optimization for flexcomp objects defined by a mesh. It identifies grid cells that do not contain any mesh vertices and marks them as empty. Nodes that are exclusively part of empty cells are pinned, preventing them from moving. Stiffness computations are skipped for empty cells, reducing computational cost. The total mass is now distributed only among the non-pinned nodes. PiperOrigin-RevId: 902565735 Change-Id: Id0a9a685536d5e18a3e42124a25ab08ff3a918f2
This commit is contained in:
committed by
Copybara-Service
parent
fa7b36d111
commit
508e581ba9
@@ -743,7 +743,10 @@ void mj_instantiateEquality(const mjModel* m, mjData* d) {
|
||||
mjtNum* refpos_c = mjSTACKALLOC(d, 3*npc, mjtNum);
|
||||
for (int n = 0; n < npc; n++) {
|
||||
int gn = gindices[n];
|
||||
if (m->flex_centered[f]) {
|
||||
if (m->flex_centered[f] ||
|
||||
(m->flex_node[3*(gn + nstart)+0] == 0 &&
|
||||
m->flex_node[3*(gn + nstart)+1] == 0 &&
|
||||
m->flex_node[3*(gn + nstart)+2] == 0)) {
|
||||
mju_copy3(xpos_c + 3*n, d->xpos + 3*bodyid[gn]);
|
||||
} else {
|
||||
mju_mulMatVec3(xpos_c + 3*n, d->xmat + 9*bodyid[gn], m->flex_node + 3*(gn + nstart));
|
||||
|
||||
@@ -564,16 +564,13 @@ void mj_flex(const mjModel* m, mjData* d) {
|
||||
|
||||
// 0: vertices are the mesh vertices, 1: vertices are interpolated from nodal dofs
|
||||
if (m->flex_interp[f] == 0) {
|
||||
// centered: copy body position
|
||||
if (m->flex_centered[f]) {
|
||||
for (int i=vstart; i < vend; i++) {
|
||||
for (int i=vstart; i < vend; i++) {
|
||||
if (m->flex_centered[f] ||
|
||||
(m->flex_vert[3*i+0] == 0 &&
|
||||
m->flex_vert[3*i+1] == 0 &&
|
||||
m->flex_vert[3*i+2] == 0)) {
|
||||
mji_copy3(d->flexvert_xpos+3*i, d->xpos+3*m->flex_vertbodyid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// non-centered: map from local to global
|
||||
else {
|
||||
for (int i=vstart; i < vend; i++) {
|
||||
} else {
|
||||
mji_mulMatVec3(d->flexvert_xpos+3*i, d->xmat+9*m->flex_vertbodyid[i], m->flex_vert+3*i);
|
||||
mji_addTo3(d->flexvert_xpos+3*i, d->xpos+3*m->flex_vertbodyid[i]);
|
||||
}
|
||||
@@ -585,13 +582,14 @@ void mj_flex(const mjModel* m, mjData* d) {
|
||||
int nodenum = nend - nstart;
|
||||
mj_markStack(d);
|
||||
mjtNum* nodexpos = mjSTACKALLOC(d, 3*nodenum, mjtNum);
|
||||
if (m->flex_centered[f]) {
|
||||
for (int i=nstart; i < nend; i++) {
|
||||
mji_copy3(nodexpos + 3*(i-nstart), d->xpos + 3*m->flex_nodebodyid[i]);
|
||||
}
|
||||
} else {
|
||||
for (int i=nstart; i < nend; i++) {
|
||||
int j = i - nstart;
|
||||
for (int i=nstart; i < nend; i++) {
|
||||
int j = i - nstart;
|
||||
if (m->flex_centered[f] ||
|
||||
(m->flex_node[3*i+0] == 0 &&
|
||||
m->flex_node[3*i+1] == 0 &&
|
||||
m->flex_node[3*i+2] == 0)) {
|
||||
mji_copy3(nodexpos + 3*j, d->xpos + 3*m->flex_nodebodyid[i]);
|
||||
} else {
|
||||
mji_mulMatVec3(nodexpos + 3*j, d->xmat + 9*m->flex_nodebodyid[i], m->flex_node + 3*i);
|
||||
mji_addTo3(nodexpos + 3*j, d->xpos + 3*m->flex_nodebodyid[i]);
|
||||
}
|
||||
|
||||
@@ -988,28 +988,36 @@ void mj_local2Global(mjData* d, mjtNum xpos[3], mjtNum xmat[9],
|
||||
//-------------------------- miscellaneous utilities -----------------------------------------------
|
||||
|
||||
// gather global node positions and velocities
|
||||
void mju_flexGatherState(const mjModel* m, mjData* d, int f, mjtNum* xpos, mjtNum* vel) {
|
||||
void mju_flexGatherState(const mjModel* m, const mjData* d, int f, mjtNum* xpos, mjtNum* vel) {
|
||||
int nodenum = m->flex_nodenum[f];
|
||||
int nstart = m->flex_nodeadr[f];
|
||||
int* bodyid = m->flex_nodebodyid + m->flex_nodeadr[f];
|
||||
|
||||
// compute positions
|
||||
if (m->flex_centered[f]) {
|
||||
for (int i=0; i < nodenum; i++) {
|
||||
mju_copy3(xpos + 3*i, d->xpos + 3*bodyid[i]);
|
||||
if (vel) {
|
||||
mju_copy3(vel + 3*i, d->qvel + m->body_dofadr[bodyid[i]]);
|
||||
}
|
||||
// compute positions and velocities
|
||||
for (int i=0; i < nodenum; i++) {
|
||||
int bid = bodyid[i];
|
||||
if (m->flex_centered[f] ||
|
||||
(m->flex_node[3*(i+nstart)+0] == 0 &&
|
||||
m->flex_node[3*(i+nstart)+1] == 0 &&
|
||||
m->flex_node[3*(i+nstart)+2] == 0)) {
|
||||
mju_copy3(xpos + 3*i, d->xpos + 3*bid);
|
||||
} else {
|
||||
mju_mulMatVec3(xpos + 3*i, d->xmat + 9*bid, m->flex_node + 3*(i+nstart));
|
||||
mju_addTo3(xpos + 3*i, d->xpos + 3*bid);
|
||||
}
|
||||
} else {
|
||||
mjtNum screw[6];
|
||||
for (int i=0; i < nodenum; i++) {
|
||||
mju_mulMatVec3(xpos + 3*i, d->xmat + 9*bodyid[i], m->flex_node + 3*(i+nstart));
|
||||
mju_addTo3(xpos + 3*i, d->xpos + 3*bodyid[i]);
|
||||
if (vel) {
|
||||
mj_objectVelocity(m, d, mjOBJ_BODY, bodyid[i], screw, 0);
|
||||
mju_copy3(vel + 3*i, screw + 3);
|
||||
}
|
||||
|
||||
if (vel) {
|
||||
mjtNum body_vel[6];
|
||||
mj_objectVelocity(m, d, mjOBJ_BODY, bid, body_vel, 0); // returns [omega, v_CoM] in world frame
|
||||
|
||||
// linear velocity at CoM
|
||||
mju_copy3(vel + 3*i, body_vel + 3);
|
||||
|
||||
// add omega x (xpos - xipos)
|
||||
mjtNum r[3], cross[3];
|
||||
mju_sub3(r, xpos + 3*i, d->xipos + 3*bid);
|
||||
mju_cross(cross, body_vel, r);
|
||||
mju_addTo3(vel + 3*i, cross);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ MJAPI void mj_local2Global(mjData* d, mjtNum xpos[3], mjtNum xmat[9],
|
||||
//-------------------------- miscellaneous ---------------------------------------------------------
|
||||
|
||||
// gather global node positions and velocities
|
||||
MJAPI void mju_flexGatherState(const mjModel* m, mjData* d, int f, mjtNum* xpos, mjtNum* vel);
|
||||
MJAPI void mju_flexGatherState(const mjModel* m, const mjData* d, int f, mjtNum* xpos, mjtNum* vel);
|
||||
|
||||
// extract 6D force:torque for one contact, in contact frame
|
||||
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
||||
|
||||
@@ -880,17 +880,62 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
const int* dof_indices, int ndof, int nband) {
|
||||
int nv = m->nv;
|
||||
|
||||
// build global2local map for ADDH
|
||||
int* global2local = NULL;
|
||||
// compute upper bounds across all interpolated flexes
|
||||
int max_nodenum = 0;
|
||||
int max_npc = 0;
|
||||
for (int f = 0; f < m->nflex; f++) {
|
||||
if (!m->flex_interp[f]) continue;
|
||||
if (m->flex_rigid[f]) continue;
|
||||
int order = m->flex_interp[f];
|
||||
int npc = (order+1)*(order+1)*(order+1);
|
||||
if (npc > max_npc) max_npc = npc;
|
||||
if (m->flex_nodenum[f] > max_nodenum) max_nodenum = m->flex_nodenum[f];
|
||||
}
|
||||
|
||||
// nothing to do
|
||||
if (max_npc == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int max_dim_c = 3 * max_npc;
|
||||
|
||||
// single unconditional markStack
|
||||
mj_markStack(d);
|
||||
|
||||
// global2local map for ADDH
|
||||
int* global2local = mjSTACKALLOC(d, nv, int);
|
||||
if (op == mjFLEXOP_ADDH) {
|
||||
mj_markStack(d);
|
||||
global2local = mjSTACKALLOC(d, nv, int);
|
||||
mju_fillInt(global2local, -1, nv);
|
||||
for (int i=0; i<ndof; i++) {
|
||||
global2local[dof_indices[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// per-flex node positions (upper bound)
|
||||
mjtNum* xpos = mjSTACKALLOC(d, 3*max_nodenum, mjtNum);
|
||||
|
||||
// per-cell arrays (upper bound)
|
||||
mjtNum* xpos_c = mjSTACKALLOC(d, 3*max_npc, mjtNum);
|
||||
mjtNum* K_rot_cell = mjSTACKALLOC(d, max_dim_c*max_dim_c, mjtNum);
|
||||
|
||||
// sparse Jacobian for one cell (upper bound)
|
||||
int* J_rownnz = mjSTACKALLOC(d, max_dim_c, int);
|
||||
int* J_rowadr = mjSTACKALLOC(d, max_dim_c, int);
|
||||
mjtNum* J_val = mjSTACKALLOC(d, max_dim_c*nv, mjtNum);
|
||||
int* J_colind = mjSTACKALLOC(d, max_dim_c*nv, int);
|
||||
|
||||
// temp allocations for chain
|
||||
int* chain_colind = mjSTACKALLOC(d, nv, int);
|
||||
mjtNum* blk_jac = mjSTACKALLOC(d, 3*nv, mjtNum);
|
||||
|
||||
// ADDH-specific allocations (upper bound)
|
||||
mjtNum* J_reduced = NULL;
|
||||
mjtNum* KJ = NULL;
|
||||
if (op == mjFLEXOP_ADDH) {
|
||||
J_reduced = mjSTACKALLOC(d, max_dim_c*ndof, mjtNum);
|
||||
KJ = mjSTACKALLOC(d, max_dim_c*ndof, mjtNum);
|
||||
}
|
||||
|
||||
// loop over flexes
|
||||
for (int f=0; f < m->nflex; f++) {
|
||||
// only process flex_interp
|
||||
@@ -899,10 +944,10 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
}
|
||||
|
||||
// get stiffness and damping
|
||||
mjtNum* k = m->flex_stiffness + m->flex_stiffnessadr[f];
|
||||
mjtNum* K = m->flex_stiffness + m->flex_stiffnessadr[f];
|
||||
|
||||
// skip if rigid or no stiffness
|
||||
if (m->flex_rigid[f] || k[0] == 0) {
|
||||
if (m->flex_rigid[f] || K[0] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -926,27 +971,9 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
int cy = m->flex_cellnum[3*f+1];
|
||||
int cz = m->flex_cellnum[3*f+2];
|
||||
|
||||
int nodenum = m->flex_nodenum[f];
|
||||
int* bodyid = m->flex_nodebodyid + m->flex_nodeadr[f];
|
||||
|
||||
// standard stack allocation
|
||||
mj_markStack(d);
|
||||
mjtNum* xpos = mjSTACKALLOC(d, 3*nodenum, mjtNum);
|
||||
|
||||
// per-cell arrays
|
||||
int dim_c = 3 * npc;
|
||||
mjtNum* xpos_c = mjSTACKALLOC(d, 3*npc, mjtNum);
|
||||
mjtNum* K_rot_cell = mjSTACKALLOC(d, dim_c*dim_c, mjtNum);
|
||||
|
||||
// sparse Jacobian for one cell
|
||||
int* J_rownnz = mjSTACKALLOC(d, dim_c, int);
|
||||
int* J_rowadr = mjSTACKALLOC(d, dim_c, int);
|
||||
mjtNum* J_val = mjSTACKALLOC(d, dim_c*nv, mjtNum);
|
||||
int* J_colind = mjSTACKALLOC(d, dim_c*nv, int);
|
||||
|
||||
// temp allocations for chain
|
||||
int* chain_colind = mjSTACKALLOC(d, nv, int);
|
||||
mjtNum* blk_jac = mjSTACKALLOC(d, 3*nv, mjtNum);
|
||||
|
||||
// gather raw node positions (unrotated)
|
||||
mju_flexGatherState(m, d, f, xpos, NULL);
|
||||
@@ -956,6 +983,16 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
for (int ci = 0; ci < cx; ci++) {
|
||||
for (int cj = 0; cj < cy; cj++) {
|
||||
for (int ck = 0; ck < cz; ck++) {
|
||||
// get cell stiffness
|
||||
mjtNum* k_cell = K + cell_idx * 3*npc * 3*npc;
|
||||
|
||||
// skip empty cells: stiffness buffer is zero-initialized at compile time
|
||||
// (user_model.cc), and non-empty cells have strictly positive diagonal
|
||||
if (k_cell[0] == 0) {
|
||||
cell_idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// gather cell-local node positions
|
||||
int gindices[125]; // max npc = 125 for quadratic
|
||||
mjtNum quat[4];
|
||||
@@ -967,9 +1004,6 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
mju_quat2Mat(R, quat);
|
||||
mju_transpose(RT, R, 3, 3);
|
||||
|
||||
// get cell stiffness
|
||||
mjtNum* k_cell = k + cell_idx * 3*npc * 3*npc;
|
||||
|
||||
// compute K_rot_cell = RT * K_cell * R (block-wise)
|
||||
mju_zero(K_rot_cell, dim_c*dim_c);
|
||||
for (int a = 0; a < npc; a++) {
|
||||
@@ -1025,9 +1059,7 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
addJTBJ_mulSparse(m, d, res, vec, J_rownnz, J_rowadr, J_colind,
|
||||
J_val, K_rot_cell, dim_c);
|
||||
} else if (op == mjFLEXOP_ADDH) {
|
||||
mj_markStack(d);
|
||||
// H -= J_cell^T * K_rot_cell * J_cell (banded format)
|
||||
mjtNum* J_reduced = mjSTACKALLOC(d, dim_c*ndof, mjtNum);
|
||||
mju_zero(J_reduced, dim_c*ndof);
|
||||
|
||||
for (int i = 0; i < dim_c; i++) {
|
||||
@@ -1043,7 +1075,6 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
}
|
||||
|
||||
// KJ = K_rot_cell * J_reduced (dim_c x ndof)
|
||||
mjtNum* KJ = mjSTACKALLOC(d, dim_c*ndof, mjtNum);
|
||||
mju_mulMatMat(KJ, K_rot_cell, J_reduced, dim_c, dim_c, ndof);
|
||||
|
||||
// H[i,j] -= J_reduced[k,i] * KJ[k,j], store lower triangle in banded format
|
||||
@@ -1056,20 +1087,15 @@ static void mjd_flexInterp_kernel(const mjModel* m, mjData* d, mjtFlexOp op,
|
||||
res[i*nband + nband-1-(i-j)] -= val;
|
||||
}
|
||||
}
|
||||
mj_freeStack(d);
|
||||
}
|
||||
|
||||
cell_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mj_freeStack(d);
|
||||
}
|
||||
|
||||
if (op == mjFLEXOP_ADDH) {
|
||||
mj_freeStack(d); // free global2local
|
||||
}
|
||||
mj_freeStack(d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -273,6 +273,15 @@ static void mj_springdamper(const mjModel* m, mjData* d) {
|
||||
for (int ci = 0; ci < cx; ci++) {
|
||||
for (int cj = 0; cj < cy; cj++) {
|
||||
for (int ck = 0; ck < cz; ck++) {
|
||||
// get cell stiffness matrix
|
||||
mjtNum* k_cell = k + cell_idx * 3*npc * 3*npc;
|
||||
|
||||
// skip empty cells (zero stiffness)
|
||||
if (k_cell[0] == 0) {
|
||||
cell_idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// gather cell-local node data
|
||||
mjtNum quat[4];
|
||||
mju_flexGatherCellState(order, cy, cz, ci, cj, ck, xpos_g, vel_g, xpos0,
|
||||
@@ -289,9 +298,6 @@ static void mj_springdamper(const mjModel* m, mjData* d) {
|
||||
mji_addScl3(displ_c+3*n, xpos_c+3*n, xpos0_c+3*n, -1);
|
||||
}
|
||||
|
||||
// get cell stiffness matrix
|
||||
mjtNum* k_cell = k + cell_idx * 3*npc * 3*npc;
|
||||
|
||||
// compute force in corotational frame
|
||||
if (enbl_spring) {
|
||||
mju_mulMatVec(frc_c, k_cell, displ_c, 3*npc, 3*npc);
|
||||
@@ -332,12 +338,20 @@ static void mj_springdamper(const mjModel* m, mjData* d) {
|
||||
// apply accumulated forces to bodies
|
||||
for (int i = 0; i < nodenum; i++) {
|
||||
mju_scl3(dmp_g+3*i, dmp_g+3*i, m->flex_damping[f]);
|
||||
if (m->flex_centered[f]) {
|
||||
if (enbl_spring) mji_addTo3(d->qfrc_spring + m->body_dofadr[bodyid[i]], frc_g+3*i);
|
||||
if (enbl_damper) mji_addTo3(d->qfrc_damper + m->body_dofadr[bodyid[i]], dmp_g+3*i);
|
||||
int bid = bodyid[i];
|
||||
int nidx = i + m->flex_nodeadr[f];
|
||||
|
||||
// fast path: node at body origin (not pinned), direct DOF write
|
||||
if (m->body_dofnum[bid] > 0 &&
|
||||
(m->flex_centered[f] ||
|
||||
(m->flex_node[3*nidx+0] == 0 &&
|
||||
m->flex_node[3*nidx+1] == 0 &&
|
||||
m->flex_node[3*nidx+2] == 0))) {
|
||||
if (enbl_spring) mji_addTo3(d->qfrc_spring + m->body_dofadr[bid], frc_g+3*i);
|
||||
if (enbl_damper) mji_addTo3(d->qfrc_damper + m->body_dofadr[bid], dmp_g+3*i);
|
||||
} else {
|
||||
if (enbl_spring) mj_applyFT(m, d, frc_g+3*i, 0, xpos_g+3*i, bodyid[i], d->qfrc_spring);
|
||||
if (enbl_damper) mj_applyFT(m, d, dmp_g+3*i, 0, xpos_g+3*i, bodyid[i], d->qfrc_damper);
|
||||
if (enbl_spring) mj_applyFT(m, d, frc_g+3*i, 0, xpos_g+3*i, bid, d->qfrc_spring);
|
||||
if (enbl_damper) mj_applyFT(m, d, dmp_g+3*i, 0, xpos_g+3*i, bid, d->qfrc_damper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1459,11 +1459,18 @@ static void addFlexBvhGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
for (int i=0; i < NX; i++) {
|
||||
for (int j=0; j < NY; j++) {
|
||||
for (int k=0; k < NZ; k++) {
|
||||
int offset = 3*(i*NY*NZ + j*NZ + k);
|
||||
int n0 = i*NY*NZ + j*NZ + k;
|
||||
|
||||
// skip if this node is pinned (no joints on its body)
|
||||
if (m->body_jntnum[bodyid[n0]] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int offset = 3*n0;
|
||||
int offset1 = 3*((i+1)*NY*NZ + j*NZ + k);
|
||||
int offset2 = 3*(i*NY*NZ + (j+1)*NZ + k);
|
||||
int offset3 = 3*(i*NY*NZ + j*NZ + (k+1));
|
||||
if (i < NX-1) {
|
||||
if (i < NX-1 && m->body_jntnum[bodyid[(i+1)*NY*NZ + j*NZ + k]] > 0) {
|
||||
mjvGeom* thisgeom = acquireGeom(scn, i, mjCAT_DECOR, mjOBJ_UNKNOWN);
|
||||
if (!thisgeom) {
|
||||
return;
|
||||
@@ -1472,7 +1479,7 @@ static void addFlexBvhGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, xpos+offset, xpos+offset1);
|
||||
releaseGeom(&thisgeom, scn);
|
||||
}
|
||||
if (j < NY-1) {
|
||||
if (j < NY-1 && m->body_jntnum[bodyid[i*NY*NZ + (j+1)*NZ + k]] > 0) {
|
||||
mjvGeom* thisgeom = acquireGeom(scn, i, mjCAT_DECOR, mjOBJ_UNKNOWN);
|
||||
if (!thisgeom) {
|
||||
return;
|
||||
@@ -1481,7 +1488,7 @@ static void addFlexBvhGeoms(const mjModel* m, mjData* d, const mjvOption* vopt,
|
||||
mjv_connector(thisgeom, mjGEOM_LINE, 3, xpos+offset, xpos+offset2);
|
||||
releaseGeom(&thisgeom, scn);
|
||||
}
|
||||
if (k < NZ-1) {
|
||||
if (k < NZ-1 && m->body_jntnum[bodyid[i*NY*NZ + j*NZ + (k+1)]] > 0) {
|
||||
mjvGeom* thisgeom = acquireGeom(scn, i, mjCAT_DECOR, mjOBJ_UNKNOWN);
|
||||
if (!thisgeom) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user