1use super::multibody_link::{MultibodyLink, MultibodyLinkVec};
2use super::multibody_workspace::MultibodyWorkspace;
3use crate::alloc_prelude::*;
4use crate::dynamics::integration_parameters::SpringCoefficients;
5use crate::dynamics::solver::{GenericJointConstraint, WritebackId};
6use crate::dynamics::{
7 IntegrationParameters, RigidBodyHandle, RigidBodySet, RigidBodyType, RigidBodyVelocity,
8};
9use crate::math::{
10 ANG_DIM, AngDim, AngVector, DIM, DVector, Dim, Jacobian, Pose, Real, SPATIAL_DIM,
11 SimdAngVector, Vector,
12};
13use crate::prelude::MultibodyJoint;
14#[cfg(feature = "dim3")]
15use crate::utils::mat_to_na;
16use crate::utils::{AngularInertiaOps, CrossProduct, CrossProductMatrix, IndexMut2, vect_to_na};
17use na::{
18 self, DMatrix, DVectorView, DVectorViewMut, Dyn, LU, OMatrix, SMatrix, SVector, StorageMut,
19};
20
21#[cfg(doc)]
22use crate::prelude::{GenericJoint, RigidBody};
23
24#[repr(C)]
25#[derive(Copy, Clone, Debug, Default)]
26struct Force {
27 linear: Vector,
28 angular: AngVector,
29}
30
31impl Force {
32 fn new(linear: Vector, angular: AngVector) -> Self {
33 Self { linear, angular }
34 }
35
36 fn as_vector(&self) -> &SVector<Real, SPATIAL_DIM> {
37 unsafe { core::mem::transmute(self) }
38 }
39}
40
41#[cfg(feature = "dim2")]
42fn concat_rb_mass_matrix(mass: Vector, inertia: Real) -> SMatrix<Real, SPATIAL_DIM, SPATIAL_DIM> {
43 let mut result = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
44 result[(0, 0)] = mass.x;
45 result[(1, 1)] = mass.y;
46 result[(2, 2)] = inertia;
47 result
48}
49
50#[cfg(feature = "dim3")]
51fn concat_rb_mass_matrix(
52 mass: Vector,
53 inertia: na::Matrix3<Real>,
54) -> SMatrix<Real, SPATIAL_DIM, SPATIAL_DIM> {
55 let mut result = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
56 result[(0, 0)] = mass.x;
57 result[(1, 1)] = mass.y;
58 result[(2, 2)] = mass.z;
59 result
60 .fixed_view_mut::<ANG_DIM, ANG_DIM>(DIM, DIM)
61 .copy_from(&inertia);
62 result
63}
64
65#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
71#[derive(Copy, Clone, Debug)]
72pub struct MultibodyDofCoupling {
73 pub link1: usize,
75 pub dof1: usize,
78 pub axis1: usize,
81 pub link2: usize,
83 pub dof2: usize,
85 pub axis2: usize,
87 pub coeff: Real,
89 pub offset: Real,
91}
92
93#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
95#[derive(Clone, Debug)]
96pub struct Multibody {
97 pub(crate) links: MultibodyLinkVec,
99 pub(crate) velocities: DVector,
100 pub(crate) damping: DVector,
101 pub(crate) armature: DVector,
103 pub(crate) frictions: DVector,
105 pub(crate) accelerations: DVector,
106
107 body_jacobians: Vec<Jacobian<Real>>,
108 augmented_mass: DMatrix<Real>,
113 inv_augmented_mass: LU<Real, Dyn, Dyn>,
114 augmented_mass_indices: IndexSequence,
117
118 acc_augmented_mass: DMatrix<Real>,
119 acc_inv_augmented_mass: LU<Real, Dyn, Dyn>,
120
121 ndofs: usize,
122 pub(crate) root_is_dynamic: bool,
123 pub(crate) solver_id: u32,
124 self_contacts_enabled: bool,
125 couplings: Vec<MultibodyDofCoupling>,
129
130 workspace: MultibodyWorkspace,
134 coriolis_v: Vec<OMatrix<Real, Dim, Dyn>>,
135 coriolis_w: Vec<OMatrix<Real, AngDim, Dyn>>,
136 i_coriolis_dt: Jacobian<Real>,
137}
138impl Default for Multibody {
139 fn default() -> Self {
140 Multibody::new()
141 }
142}
143
144impl Multibody {
145 pub fn new() -> Self {
147 Self::with_self_contacts(true)
148 }
149
150 pub(crate) fn with_self_contacts(self_contacts_enabled: bool) -> Self {
151 Multibody {
152 links: MultibodyLinkVec(Vec::new()),
153 velocities: DVector::zeros(0),
154 damping: DVector::zeros(0),
155 armature: DVector::zeros(0),
156 frictions: DVector::zeros(0),
157 accelerations: DVector::zeros(0),
158 body_jacobians: Vec::new(),
159 augmented_mass: DMatrix::zeros(0, 0),
160 inv_augmented_mass: LU::new(DMatrix::zeros(0, 0)),
161 acc_augmented_mass: DMatrix::zeros(0, 0),
162 acc_inv_augmented_mass: LU::new(DMatrix::zeros(0, 0)),
163 augmented_mass_indices: IndexSequence::new(),
164 ndofs: 0,
165 solver_id: 0,
166 workspace: MultibodyWorkspace::new(),
167 coriolis_v: Vec::new(),
168 coriolis_w: Vec::new(),
169 i_coriolis_dt: Jacobian::zeros(0),
170 root_is_dynamic: false,
171 self_contacts_enabled,
172 couplings: Vec::new(),
173 }
175 }
176
177 pub(crate) fn with_root(handle: RigidBodyHandle, self_contacts_enabled: bool) -> Self {
178 let mut mb = Multibody::with_self_contacts(self_contacts_enabled);
179 mb.root_is_dynamic = true;
182 let joint = MultibodyJoint::free(Pose::IDENTITY);
183 mb.add_link(None, joint, handle);
184 mb
185 }
186
187 pub(crate) fn remove_link(self, to_remove: usize, joint_only: bool) -> Vec<Multibody> {
188 let mut result = vec![];
189 let mut link2mb = vec![usize::MAX; self.links.len()];
190 let mut link_id2new_id = vec![usize::MAX; self.links.len()];
191 let mut joint_replaced = vec![false; self.links.len()];
193
194 for (i, mut link) in self.links.0.into_iter().enumerate() {
196 let is_new_root = i == 0
197 || !joint_only && link.parent_internal_id == to_remove
198 || joint_only && i == to_remove;
199
200 if !joint_only && i == to_remove {
201 continue;
202 } else if is_new_root {
203 link2mb[i] = result.len();
204 result.push(Multibody::with_self_contacts(self.self_contacts_enabled));
205 } else {
206 link2mb[i] = link2mb[link.parent_internal_id]
207 }
208
209 let curr_mb = &mut result[link2mb[i]];
210 link_id2new_id[i] = curr_mb.links.len();
211
212 if is_new_root {
213 let joint = MultibodyJoint::fixed(*link.local_to_world());
214 link.joint = joint;
215 joint_replaced[i] = true;
216 }
217
218 curr_mb.ndofs += link.joint().ndofs();
219 curr_mb.links.push(link);
220 }
221
222 for mb in &mut result {
225 mb.grow_buffers(mb.ndofs, mb.links.len());
226 mb.workspace.resize(mb.links.len(), mb.ndofs);
227
228 let mut assembly_id = 0;
229 for (i, link) in mb.links.iter_mut().enumerate() {
230 let link_ndofs = link.joint().ndofs();
231 mb.velocities
232 .rows_mut(assembly_id, link_ndofs)
233 .copy_from(&self.velocities.rows(link.assembly_id, link_ndofs));
234 mb.damping
235 .rows_mut(assembly_id, link_ndofs)
236 .copy_from(&self.damping.rows(link.assembly_id, link_ndofs));
237 mb.armature
238 .rows_mut(assembly_id, link_ndofs)
239 .copy_from(&self.armature.rows(link.assembly_id, link_ndofs));
240 mb.frictions
241 .rows_mut(assembly_id, link_ndofs)
242 .copy_from(&self.frictions.rows(link.assembly_id, link_ndofs));
243 mb.accelerations
244 .rows_mut(assembly_id, link_ndofs)
245 .copy_from(&self.accelerations.rows(link.assembly_id, link_ndofs));
246
247 link.internal_id = i;
248 link.assembly_id = assembly_id;
249
250 link.parent_internal_id = if i != 0 {
253 link_id2new_id[link.parent_internal_id]
254 } else {
255 0
256 };
257 assembly_id += link_ndofs;
258 }
259 }
260
261 for coupling in &self.couplings {
263 let (l1, l2) = (coupling.link1, coupling.link2);
264 let kept = |l: usize| link_id2new_id[l] != usize::MAX && !joint_replaced[l];
265 if kept(l1) && kept(l2) && link2mb[l1] == link2mb[l2] {
266 result[link2mb[l1]].couplings.push(MultibodyDofCoupling {
267 link1: link_id2new_id[l1],
268 link2: link_id2new_id[l2],
269 ..*coupling
270 });
271 }
272 }
273
274 result
275 }
276
277 pub(crate) fn append(&mut self, mut rhs: Multibody, parent: usize, joint: MultibodyJoint) {
278 let joint_ndofs = joint.ndofs();
279 let rhs_root_ndofs = rhs.links[0].joint.ndofs();
280 let ndofs_before_append = self.velocities.len();
281 let base_internal_id = self.links.len();
282 let rhs_copy_shift = ndofs_before_append + joint_ndofs;
284 let rhs_copy_ndofs = rhs.ndofs - rhs_root_ndofs;
287
288 for link in &mut rhs.links.0[1..] {
290 link.assembly_id =
291 (link.assembly_id + ndofs_before_append + joint_ndofs) - rhs_root_ndofs;
292 link.internal_id += base_internal_id;
293 link.parent_internal_id += base_internal_id;
294 }
295
296 {
298 rhs.links[0].joint = joint;
299 rhs.links[0].assembly_id = ndofs_before_append;
300 rhs.links[0].internal_id = base_internal_id;
301 rhs.links[0].parent_internal_id = parent;
302 }
303
304 self.grow_buffers(rhs_copy_ndofs + joint_ndofs, rhs.links.len());
306
307 if rhs_copy_ndofs > 0 {
308 self.velocities
309 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
310 .copy_from(&rhs.velocities.rows(rhs_root_ndofs, rhs_copy_ndofs));
311 self.damping
312 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
313 .copy_from(&rhs.damping.rows(rhs_root_ndofs, rhs_copy_ndofs));
314 self.armature
315 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
316 .copy_from(&rhs.armature.rows(rhs_root_ndofs, rhs_copy_ndofs));
317 self.frictions
318 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
319 .copy_from(&rhs.frictions.rows(rhs_root_ndofs, rhs_copy_ndofs));
320 self.accelerations
321 .rows_mut(rhs_copy_shift, rhs_copy_ndofs)
322 .copy_from(&rhs.accelerations.rows(rhs_root_ndofs, rhs_copy_ndofs));
323 }
324
325 rhs.links[0]
327 .joint
328 .default_damping(&mut self.damping.rows_mut(ndofs_before_append, joint_ndofs));
329
330 self.links.append(&mut rhs.links);
331 self.ndofs = self.velocities.len();
332 self.workspace.resize(self.links.len(), self.ndofs);
333
334 self.couplings.extend(
336 rhs.couplings
337 .iter()
338 .filter(|c| c.link1 != 0 && c.link2 != 0)
339 .map(|c| MultibodyDofCoupling {
340 link1: c.link1 + base_internal_id,
341 link2: c.link2 + base_internal_id,
342 ..*c
343 }),
344 );
345 self.self_contacts_enabled &= rhs.self_contacts_enabled;
347 }
348
349 pub fn self_contacts_enabled(&self) -> bool {
354 self.self_contacts_enabled
355 }
356
357 pub fn set_self_contacts_enabled(&mut self, enabled: bool) {
362 self.self_contacts_enabled = enabled;
363 }
364
365 pub fn inv_augmented_mass(&self) -> &LU<Real, Dyn, Dyn> {
367 &self.inv_augmented_mass
368 }
369
370 #[inline]
372 pub fn root(&self) -> &MultibodyLink {
373 &self.links[0]
374 }
375
376 #[inline]
378 pub fn root_mut(&mut self) -> &mut MultibodyLink {
379 &mut self.links[0]
380 }
381
382 #[inline]
386 pub fn link(&self, id: usize) -> Option<&MultibodyLink> {
387 self.links.get(id)
388 }
389
390 #[inline]
394 pub fn link_mut(&mut self, id: usize) -> Option<&mut MultibodyLink> {
395 self.links.get_mut(id)
396 }
397
398 pub fn num_links(&self) -> usize {
400 self.links.len()
401 }
402
403 pub fn links(&self) -> impl Iterator<Item = &MultibodyLink> {
407 self.links.iter()
408 }
409
410 pub fn links_mut(&mut self) -> impl Iterator<Item = &mut MultibodyLink> {
414 self.links.iter_mut()
415 }
416
417 #[inline]
419 pub fn damping(&self) -> &DVector {
420 &self.damping
421 }
422
423 #[inline]
425 pub fn damping_mut(&mut self) -> &mut DVector {
426 &mut self.damping
427 }
428
429 #[inline]
432 pub fn armature(&self) -> &DVector {
433 &self.armature
434 }
435
436 #[inline]
439 pub fn armature_mut(&mut self) -> &mut DVector {
440 &mut self.armature
441 }
442
443 #[inline]
446 pub fn frictions(&self) -> &DVector {
447 &self.frictions
448 }
449
450 #[inline]
452 pub fn frictions_mut(&mut self) -> &mut DVector {
453 &mut self.frictions
454 }
455
456 pub(crate) fn add_link(
457 &mut self,
458 parent: Option<usize>, dof: MultibodyJoint,
460 body: RigidBodyHandle,
461 ) -> &mut MultibodyLink {
462 assert!(
463 parent.is_none() || !self.links.is_empty(),
464 "Multibody::build_body: invalid parent id."
465 );
466
467 let assembly_id = self.velocities.len();
471 let internal_id = self.links.len();
472
473 let ndofs = dof.ndofs();
477 self.grow_buffers(ndofs, 1);
478 self.ndofs += ndofs;
479
480 dof.default_damping(&mut self.damping.rows_mut(assembly_id, ndofs));
484
485 let local_to_parent = dof.body_to_parent();
489 let local_to_world;
490
491 let parent_internal_id;
492 if let Some(parent) = parent {
493 parent_internal_id = parent;
494 let parent_link = &mut self.links[parent_internal_id];
495 local_to_world = parent_link.local_to_world * local_to_parent;
496 } else {
497 parent_internal_id = 0;
498 local_to_world = local_to_parent;
499 }
500
501 let rb = MultibodyLink::new(
502 body,
503 internal_id,
504 assembly_id,
505 parent_internal_id,
506 dof,
507 local_to_world,
508 local_to_parent,
509 );
510
511 self.links.push(rb);
512 self.workspace.resize(self.links.len(), self.ndofs);
513
514 &mut self.links[internal_id]
515 }
516
517 fn grow_buffers(&mut self, ndofs: usize, num_jacobians: usize) {
518 let len = self.velocities.len();
519 self.velocities.resize_vertically_mut(len + ndofs, 0.0);
520 self.damping.resize_vertically_mut(len + ndofs, 0.0);
521 self.armature.resize_vertically_mut(len + ndofs, 0.0);
522 self.frictions.resize_vertically_mut(len + ndofs, 0.0);
523 self.accelerations.resize_vertically_mut(len + ndofs, 0.0);
524 self.body_jacobians
525 .extend((0..num_jacobians).map(|_| Jacobian::zeros(0)));
526 }
527
528 pub(crate) fn update_acceleration(&mut self, dt: Real, bodies: &RigidBodySet) {
529 if self.ndofs == 0 {
530 return; }
532
533 self.accelerations.fill(0.0);
534
535 let check_implicit_coriolis_divergence = dt * self.velocities.amax() >= 1.0e-3;
540 let mut pos_forces = DVector::zeros(if check_implicit_coriolis_divergence {
543 self.ndofs
544 } else {
545 0
546 });
547
548 for i in 0..self.links.len() {
550 let link = &self.links[i];
551 let rb = &bodies[link.rigid_body];
552
553 let mut acc = RigidBodyVelocity::zero();
554
555 if i != 0 {
556 let parent_id = link.parent_internal_id;
557 let parent_link = &self.links[parent_id];
558 let parent_rb = &bodies[parent_link.rigid_body];
559
560 acc += self.workspace.accs[parent_id];
561 acc.linvel += 2.0 * parent_rb.vels.angvel.gcross(link.joint_velocity.linvel);
564 #[cfg(feature = "dim3")]
565 {
566 acc.angvel += parent_rb.vels.angvel.cross(link.joint_velocity.angvel);
567 }
568
569 acc.linvel += parent_rb
570 .vels
571 .angvel
572 .gcross(parent_rb.vels.angvel.gcross(link.shift02));
573 acc.linvel += self.workspace.accs[parent_id].angvel.gcross(link.shift02);
574 }
575
576 acc.linvel += rb.vels.angvel.gcross(rb.vels.angvel.gcross(link.shift23));
577 acc.linvel += acc.angvel.gcross(link.shift23);
578
579 self.workspace.accs[i] = acc;
580
581 let gyroscopic;
584 let rb_inertia = rb.mprops.effective_angular_inertia();
585 let rb_mass = rb.mprops.effective_mass();
586
587 #[cfg(feature = "dim3")]
588 {
589 let angvel = rb.vels.angvel;
590 let inertia_times_angvel = rb_inertia * angvel;
591 gyroscopic = angvel.cross(inertia_times_angvel);
592 }
593 #[cfg(feature = "dim2")]
594 {
595 gyroscopic = 0.0;
596 }
597
598 let external_forces = Force::new(
599 rb.forces.force - rb_mass * acc.linvel,
600 rb.forces.torque - gyroscopic - rb_inertia * acc.angvel,
601 );
602 self.accelerations.gemv_tr(
603 1.0,
604 &self.body_jacobians[i],
605 external_forces.as_vector(),
606 1.0,
607 );
608
609 if check_implicit_coriolis_divergence {
610 let applied_forces = Force::new(rb.forces.force, rb.forces.torque);
611 pos_forces.gemv_tr(
612 1.0,
613 &self.body_jacobians[i],
614 applied_forces.as_vector(),
615 1.0,
616 );
617 }
618 }
619
620 self.accelerations
621 .cmpy(-1.0, &self.damping, &self.velocities, 1.0);
622
623 for li in 0..self.links.len() {
627 let mut idx = self.links[li].assembly_id;
628 let locked = self.links[li].joint.data.locked_axes.bits();
629 for a in 0..SPATIAL_DIM {
630 if (locked >> a) & 1 == 0 {
631 let k = self.links[li].joint.spring_stiffness[a];
632 if k != 0.0 {
633 let q = self.links[li].joint.coords[a];
634 let rest = self.links[li].joint.spring_ref[a];
635 let spring_pos_force = -k * (q - rest);
636 self.accelerations[idx] += spring_pos_force - k * dt * self.velocities[idx];
637 if check_implicit_coriolis_divergence {
638 pos_forces[idx] += spring_pos_force;
639 }
640 }
641 idx += 1;
642 }
643 }
644 }
645
646 let gen_forces = if check_implicit_coriolis_divergence {
649 self.accelerations.clone()
650 } else {
651 DVector::zeros(0)
652 };
653
654 self.augmented_mass_indices
655 .with_rearranged_rows_mut(&mut self.accelerations, |accs| {
656 self.acc_inv_augmented_mass.solve_mut(accs);
657 });
658
659 if check_implicit_coriolis_divergence {
660 self.free_velocity_energy_guard(dt, &gen_forces, &pos_forces);
661 }
662 }
663
664 fn free_velocity_energy_guard(&mut self, dt: Real, gen_forces: &DVector, pos_forces: &DVector) {
674 let eff_dim = self.augmented_mass_indices.dim_after_removal(self.ndofs);
675 if eff_dim == 0 {
676 return;
677 }
678
679 let mut v = self.velocities.clone();
682 let mut f = gen_forces.clone();
683 let mut f_pos = pos_forces.clone();
684 self.augmented_mass_indices.rearrange_rows(&mut v, true);
685 self.augmented_mass_indices.rearrange_rows(&mut f, true);
686 self.augmented_mass_indices.rearrange_rows(&mut f_pos, true);
687
688 let m = self.augmented_mass.view((0, 0), (eff_dim, eff_dim));
689 let v = v.rows(0, eff_dim);
690 let f = f.rows(0, eff_dim);
691 let f_pos = f_pos.rows(0, eff_dim);
692
693 let energy_delta_minus_work = |accelerations: &DVector| -> Option<Real> {
694 let mut a = accelerations.clone();
695 self.augmented_mass_indices.rearrange_rows(&mut a, true);
696 let a = a.rows(0, eff_dim).into_owned();
697 if !a.iter().all(|x| x.is_finite()) {
698 return None;
699 }
700 let m_a = m * &a;
701 let energy_delta = dt * v.dot(&m_a) + 0.5 * dt * dt * a.dot(&m_a);
702 let work = dt * v.dot(&f) + 0.5 * dt * dt * a.dot(&f_pos);
703 let kinetic_energy = 0.5 * v.dot(&(m * v));
706 let energy_margin = 1.0e-2 * kinetic_energy + 1.0e-8;
707 Some(energy_delta - work - energy_margin)
708 };
709
710 match energy_delta_minus_work(&self.accelerations) {
711 Some(excess) if excess <= 0.0 => (), _ => {
713 self.accelerations.copy_from(gen_forces);
716 self.augmented_mass_indices.with_rearranged_rows_mut(
717 &mut self.accelerations,
718 |accs| {
719 self.inv_augmented_mass.solve_mut(accs);
720 },
721 );
722
723 if !self.accelerations.iter().all(|x| x.is_finite()) {
727 self.accelerations.fill(0.0);
728 }
729 }
730 }
731 }
732
733 #[profiling::function]
735 pub(crate) fn update_velocities(&mut self, bodies: &mut RigidBodySet) {
736 let link = &mut self.links[0];
741 let joint_velocity = link
742 .joint
743 .jacobian_mul_coordinates(&self.velocities.as_slice()[link.assembly_id..]);
744
745 link.joint_velocity = joint_velocity;
746 bodies.index_mut_internal(link.rigid_body).vels = link.joint_velocity;
747
748 for i in 1..self.links.len() {
749 let (link, parent_link) = self.links.get_mut_with_parent(i);
750 let rb = &bodies[link.rigid_body];
751 let parent_rb = &bodies[parent_link.rigid_body];
752
753 let joint_velocity = link
754 .joint
755 .jacobian_mul_coordinates(&self.velocities.as_slice()[link.assembly_id..]);
756 link.joint_velocity = joint_velocity.transformed(
757 &(parent_link.local_to_world.rotation * link.joint.data.local_frame1.rotation),
758 );
759 let mut new_rb_vels = parent_rb.vels + link.joint_velocity;
760 let shift = rb.mprops.world_com - parent_rb.mprops.world_com;
761 new_rb_vels.linvel += parent_rb.vels.angvel.gcross(shift);
762 new_rb_vels.linvel += link.joint_velocity.angvel.gcross(link.shift23);
763
764 bodies.index_mut_internal(link.rigid_body).vels = new_rb_vels;
765 }
766 }
767
768 fn update_body_jacobians(&mut self) {
769 for i in 0..self.links.len() {
770 let link = &self.links[i];
771
772 if self.body_jacobians[i].ncols() != self.ndofs {
773 self.body_jacobians[i] = Jacobian::zeros(self.ndofs);
775 }
776
777 let parent_to_world;
778
779 if i != 0 {
780 let parent_id = link.parent_internal_id;
781 let parent_link = &self.links[parent_id];
782 parent_to_world = parent_link.local_to_world;
783
784 let (link_j, parent_j) = self.body_jacobians.index_mut_const(i, parent_id);
785 link_j.copy_from(parent_j);
786
787 {
788 let mut link_j_v = link_j.fixed_rows_mut::<DIM>(0);
789 let parent_j_w = parent_j.fixed_rows::<ANG_DIM>(DIM);
790
791 let shift_tr = vect_to_na(link.shift02).gcross_matrix_tr();
792 link_j_v.gemm(1.0, &shift_tr, &parent_j_w, 1.0);
793 }
794 } else {
795 self.body_jacobians[i].fill(0.0);
796 parent_to_world = Pose::IDENTITY;
797 }
798
799 let ndofs = link.joint.ndofs();
800 let mut tmp = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
801 let mut link_joint_j = tmp.columns_mut(0, ndofs);
802 let mut link_j_part = self.body_jacobians[i].columns_mut(link.assembly_id, ndofs);
803 link.joint.jacobian(
804 &(parent_to_world.rotation * link.joint.data.local_frame1.rotation),
805 &mut link_joint_j,
806 );
807 link_j_part += link_joint_j;
808
809 {
810 let link_j = &mut self.body_jacobians[i];
811 let (mut link_j_v, link_j_w) =
812 link_j.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
813 let shift_tr = vect_to_na(link.shift23).gcross_matrix_tr();
814 link_j_v.gemm(1.0, &shift_tr, &link_j_w, 1.0);
815 }
816 }
817 }
818
819 pub(crate) fn update_mass_matrix(&mut self, dt: Real, bodies: &RigidBodySet) {
820 if self.ndofs == 0 {
821 return; }
823
824 if self.augmented_mass.ncols() != self.ndofs {
825 self.augmented_mass = DMatrix::zeros(self.ndofs, self.ndofs);
827 self.acc_augmented_mass = DMatrix::zeros(self.ndofs, self.ndofs);
828 } else {
829 self.augmented_mass.fill(0.0);
830 self.acc_augmented_mass.fill(0.0);
831 }
832
833 self.augmented_mass_indices.clear();
834
835 let coriolis_ndofs = self.coriolis_v.first().map(|m| m.ncols());
837 if self.coriolis_v.len() != self.links.len() || coriolis_ndofs != Some(self.ndofs) {
838 self.coriolis_v = vec![OMatrix::<Real, Dim, Dyn>::zeros(self.ndofs); self.links.len()];
839 self.coriolis_w =
840 vec![OMatrix::<Real, AngDim, Dyn>::zeros(self.ndofs); self.links.len()];
841 self.i_coriolis_dt = Jacobian::zeros(self.ndofs);
842 }
843
844 let mut curr_assembly_id = 0;
845
846 for i in 0..self.links.len() {
847 let link = &self.links[i];
848 let rb = &bodies[link.rigid_body];
849 let rb_mass = rb.mprops.effective_mass();
850 let rb_inertia = rb.mprops.effective_angular_inertia().into_matrix();
851 let body_jacobian = &self.body_jacobians[i];
852
853 assert_eq!(
857 curr_assembly_id, link.assembly_id,
858 "Internal error: contiguity assumption on assembly_id does not hold."
859 );
860 curr_assembly_id += link.joint.ndofs();
861
862 if link.joint.kinematic {
863 for k in link.assembly_id..link.assembly_id + link.joint.ndofs() {
864 self.augmented_mass_indices.remove(k);
865 }
866 } else {
867 for k in link.assembly_id..link.assembly_id + link.joint.ndofs() {
868 self.augmented_mass_indices.keep(k);
869 }
870 }
871
872 #[allow(unused_mut)] let mut augmented_inertia = rb_inertia;
874
875 #[cfg(feature = "dim3")]
876 {
877 let gyroscopic_matrix = rb.vels.angvel.gcross_matrix() * rb_inertia
879 - (rb_inertia * rb.vels.angvel).gcross_matrix();
880
881 augmented_inertia += gyroscopic_matrix * dt;
882 }
883
884 #[allow(clippy::useless_conversion)] let rb_mass_matrix_wo_gyro = concat_rb_mass_matrix(rb_mass, rb_inertia.into());
888 #[allow(clippy::useless_conversion)] let rb_mass_matrix = concat_rb_mass_matrix(rb_mass, augmented_inertia.into());
890 self.augmented_mass
891 .quadform(1.0, &rb_mass_matrix_wo_gyro, body_jacobian, 1.0);
892 self.acc_augmented_mass
893 .quadform(1.0, &rb_mass_matrix, body_jacobian, 1.0);
894
895 let rb_j = &self.body_jacobians[i];
901 let rb_j_w = rb_j.fixed_rows::<ANG_DIM>(DIM);
902
903 let ndofs = link.joint.ndofs();
904
905 if i != 0 {
906 let parent_id = link.parent_internal_id;
907 let parent_link = &self.links[parent_id];
908 let parent_rb = &bodies[parent_link.rigid_body];
909 let parent_j = &self.body_jacobians[parent_id];
910 let parent_j_w = parent_j.fixed_rows::<ANG_DIM>(DIM);
911 let parent_w = SimdAngVector::<Real>::from(parent_rb.vels.angvel).gcross_matrix();
912
913 let (coriolis_v, parent_coriolis_v) = self.coriolis_v.index_mut2(i, parent_id);
914 let (coriolis_w, parent_coriolis_w) = self.coriolis_w.index_mut2(i, parent_id);
915
916 coriolis_v.copy_from(parent_coriolis_v);
917 coriolis_w.copy_from(parent_coriolis_w);
918
919 let shift_cross_tr = vect_to_na(link.shift02).gcross_matrix_tr();
921 coriolis_v.gemm(1.0, &shift_cross_tr, parent_coriolis_w, 1.0);
922
923 let dvel_cross = vect_to_na(
925 rb.vels.angvel.gcross(link.shift02) + 2.0 * link.joint_velocity.linvel,
926 )
927 .gcross_matrix_tr();
928 coriolis_v.gemm(1.0, &dvel_cross, &parent_j_w, 1.0);
929
930 coriolis_v.gemm(
932 1.0,
933 &vect_to_na(link.joint_velocity.linvel).gcross_matrix_tr(),
934 &parent_j_w,
935 1.0,
936 );
937 coriolis_v.gemm(1.0, &(parent_w * shift_cross_tr), &parent_j_w, 1.0);
938
939 #[cfg(feature = "dim3")]
940 {
941 let vel_wrt_joint_w = vect_to_na(link.joint_velocity.angvel).gcross_matrix();
942 coriolis_w.gemm(-1.0, &vel_wrt_joint_w, &parent_j_w, 1.0);
943 }
944
945 if !link.joint.kinematic {
947 let mut coriolis_v_part = coriolis_v.columns_mut(link.assembly_id, ndofs);
948
949 let mut tmp1 = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
950 let mut rb_joint_j = tmp1.columns_mut(0, ndofs);
951 link.joint.jacobian(
952 &(parent_link.local_to_world.rotation
953 * link.joint.data.local_frame1.rotation),
954 &mut rb_joint_j,
955 );
956
957 let rb_joint_j_v = rb_joint_j.fixed_rows::<DIM>(0);
958 coriolis_v_part.gemm(2.0, &parent_w, &rb_joint_j_v, 1.0);
959
960 #[cfg(feature = "dim3")]
961 {
962 let rb_joint_j_w = rb_joint_j.fixed_rows::<ANG_DIM>(DIM);
963 let mut coriolis_w_part = coriolis_w.columns_mut(link.assembly_id, ndofs);
964 coriolis_w_part.gemm(1.0, &parent_w, &rb_joint_j_w, 1.0);
965 }
966 }
967 } else {
968 self.coriolis_v[i].fill(0.0);
969 self.coriolis_w[i].fill(0.0);
970 }
971
972 let coriolis_v = &mut self.coriolis_v[i];
973 let coriolis_w = &mut self.coriolis_w[i];
974
975 {
976 let shift_cross_tr = vect_to_na(link.shift23).gcross_matrix_tr();
978 coriolis_v.gemm(1.0, &shift_cross_tr, coriolis_w, 1.0);
979
980 let dvel_cross = vect_to_na(rb.vels.angvel.gcross(link.shift23)).gcross_matrix_tr();
982 coriolis_v.gemm(1.0, &dvel_cross, &rb_j_w, 1.0);
983
984 coriolis_v.gemm(
986 1.0,
987 &(SimdAngVector::<Real>::from(rb.vels.angvel).gcross_matrix() * shift_cross_tr),
988 &rb_j_w,
989 1.0,
990 );
991 }
992
993 let coriolis_v = &mut self.coriolis_v[i];
994 let coriolis_w = &mut self.coriolis_w[i];
995
996 {
1000 let mut i_coriolis_dt_v = self.i_coriolis_dt.fixed_rows_mut::<DIM>(0);
1001 i_coriolis_dt_v.copy_from(coriolis_v);
1002 let rb_mass_dt = vect_to_na(rb_mass * dt);
1003 i_coriolis_dt_v
1004 .column_iter_mut()
1005 .for_each(|mut c| c.component_mul_assign(&rb_mass_dt));
1006 }
1007
1008 #[cfg(feature = "dim2")]
1009 {
1010 let mut i_coriolis_dt_w = self.i_coriolis_dt.fixed_rows_mut::<ANG_DIM>(DIM);
1011 i_coriolis_dt_w.zip_apply(coriolis_w, |o, x| *o = x * dt * rb_inertia);
1013 }
1014 #[cfg(feature = "dim3")]
1015 {
1016 let mut i_coriolis_dt_w = self.i_coriolis_dt.fixed_rows_mut::<ANG_DIM>(DIM);
1017 i_coriolis_dt_w.gemm(dt, &mat_to_na(rb_inertia), coriolis_w, 0.0);
1018 }
1019
1020 self.acc_augmented_mass
1021 .gemm_tr(1.0, rb_j, &self.i_coriolis_dt, 1.0);
1022 }
1023
1024 for i in 0..self.ndofs {
1033 let diag = self.damping[i] * dt + self.armature[i];
1034 self.acc_augmented_mass[(i, i)] += diag;
1035 self.augmented_mass[(i, i)] += diag;
1036 }
1037
1038 let dt2 = dt * dt;
1042 for li in 0..self.links.len() {
1043 let mut idx = self.links[li].assembly_id;
1044 let locked = self.links[li].joint.data.locked_axes.bits();
1045 for a in 0..SPATIAL_DIM {
1046 if (locked >> a) & 1 == 0 {
1047 let k = self.links[li].joint.spring_stiffness[a];
1048 if k != 0.0 {
1049 let d = k * dt2;
1050 self.acc_augmented_mass[(idx, idx)] += d;
1051 self.augmented_mass[(idx, idx)] += d;
1052 }
1053 idx += 1;
1054 }
1055 }
1056 }
1057
1058 let effective_dim = self
1059 .augmented_mass_indices
1060 .dim_after_removal(self.acc_augmented_mass.nrows());
1061
1062 self.augmented_mass_indices
1065 .rearrange_columns(&mut self.acc_augmented_mass, true);
1066 self.augmented_mass_indices
1067 .rearrange_columns(&mut self.augmented_mass, true);
1068
1069 self.augmented_mass_indices
1070 .rearrange_rows(&mut self.acc_augmented_mass, true);
1071 self.augmented_mass_indices
1072 .rearrange_rows(&mut self.augmented_mass, true);
1073
1074 self.acc_inv_augmented_mass = LU::new(
1076 self.acc_augmented_mass
1077 .view((0, 0), (effective_dim, effective_dim))
1078 .into_owned(),
1079 );
1080 self.inv_augmented_mass = LU::new(
1081 self.augmented_mass
1082 .view((0, 0), (effective_dim, effective_dim))
1083 .into_owned(),
1084 );
1085 }
1086
1087 pub fn dof_inverse_inertia(&mut self, bodies: &RigidBodySet) -> DVector {
1093 self.forward_kinematics(bodies, false);
1097 self.update_mass_matrix(0.0, bodies);
1098
1099 let n = self.ndofs;
1100 let mut out = DVector::zeros(n);
1101 if n == 0 {
1102 return out;
1103 }
1104 let mut e = DVector::zeros(n);
1108 for i in 0..n {
1109 e.fill(0.0);
1110 e[i] = 1.0;
1111 self.augmented_mass_indices
1112 .with_rearranged_rows_mut(&mut e, |b| {
1113 self.inv_augmented_mass.solve_mut(b);
1114 });
1115 out[i] = e[i];
1116 }
1117 out
1118 }
1119
1120 pub fn add_dof_coupling(&mut self, coupling: MultibodyDofCoupling) {
1124 self.couplings.push(coupling);
1125 }
1126
1127 pub fn couplings(&self) -> &[MultibodyDofCoupling] {
1133 &self.couplings
1134 }
1135
1136 pub fn remove_dof_coupling(&mut self, i: usize) -> Option<MultibodyDofCoupling> {
1140 (i < self.couplings.len()).then(|| self.couplings.remove(i))
1141 }
1142
1143 pub fn retain_dof_couplings(&mut self, f: impl FnMut(&MultibodyDofCoupling) -> bool) {
1145 self.couplings.retain(f);
1146 }
1147
1148 pub fn clear_dof_couplings(&mut self) {
1150 self.couplings.clear();
1151 }
1152
1153 pub(crate) fn num_friction_constraints(&self, link_id: usize) -> usize {
1156 let Some(link) = self.link(link_id) else {
1157 return 0;
1158 };
1159 let ndofs = link.joint().ndofs();
1160 (0..ndofs)
1161 .filter(|i| self.frictions[link.assembly_id + i] > 0.0)
1162 .count()
1163 }
1164
1165 pub(crate) fn num_couplings_owned_by(&self, owner_link: usize) -> usize {
1169 self.couplings
1170 .iter()
1171 .filter(|c| c.link1 == owner_link)
1172 .count()
1173 }
1174
1175 pub(crate) fn coupling_velocity_constraints(
1179 &self,
1180 owner_link: usize,
1181 params: &IntegrationParameters,
1182 mut j_id: usize,
1183 jacobians: &mut DVector,
1184 out: &mut [GenericJointConstraint],
1185 ) -> usize {
1186 let ndofs = self.ndofs;
1187 let erp_inv_dt = SpringCoefficients::<Real>::joint_defaults().erp_inv_dt(params.dt);
1188
1189 let mut i = 0;
1190 for c in self.couplings.iter().filter(|c| c.link1 == owner_link) {
1191 let g1 = self.links[c.link1].assembly_id + c.dof1;
1192 let g2 = self.links[c.link2].assembly_id + c.dof2;
1193 let q1 = self.links[c.link1].joint().coords()[c.axis1];
1194 let q2 = self.links[c.link2].joint().coords()[c.axis2];
1195
1196 jacobians.rows_mut(j_id, ndofs * 2).fill(0.0);
1198 jacobians[j_id + g2] += 1.0;
1199 jacobians[j_id + g1] -= c.coeff;
1200 for k in 0..ndofs {
1201 jacobians[j_id + ndofs + k] = jacobians[j_id + k];
1202 }
1203 self.inv_augmented_mass
1204 .solve_mut(&mut jacobians.rows_mut(j_id + ndofs, ndofs));
1205
1206 let lhs = jacobians[j_id + ndofs + g2] - c.coeff * jacobians[j_id + ndofs + g1];
1208
1209 let drift = q2 - c.coeff * q1 - c.offset;
1210
1211 out[i] = GenericJointConstraint {
1212 is_rigid_body1: false,
1213 solver_vel1: u32::MAX,
1214 ndofs1: 0,
1215 j_id1: 0,
1216 is_rigid_body2: false,
1217 solver_vel2: self.solver_id,
1218 ndofs2: ndofs,
1219 j_id2: j_id,
1220 joint_id: usize::MAX, impulse: 0.0,
1222 impulse_bounds: [-Real::MAX, Real::MAX],
1223 inv_lhs: crate::utils::inv(lhs),
1224 rhs: drift * erp_inv_dt,
1225 rhs_wo_bias: 0.0,
1226 cfm_coeff: 0.0,
1227 cfm_gain: 0.0,
1228 writeback_id: WritebackId::Dof(0),
1229 };
1230
1231 j_id += 2 * ndofs;
1232 i += 1;
1233 }
1234
1235 i
1236 }
1237
1238 #[inline]
1240 pub fn joint_velocity(&self, link: &MultibodyLink) -> DVectorView<'_, Real> {
1241 let ndofs = link.joint().ndofs();
1242 DVectorView::from_slice(
1243 &self.velocities.as_slice()[link.assembly_id..link.assembly_id + ndofs],
1244 ndofs,
1245 )
1246 }
1247
1248 #[inline]
1250 pub fn generalized_acceleration(&self) -> DVectorView<'_, Real> {
1251 self.accelerations.rows(0, self.ndofs)
1252 }
1253
1254 #[inline]
1256 pub fn generalized_velocity(&self) -> DVectorView<'_, Real> {
1257 self.velocities.rows(0, self.ndofs)
1258 }
1259
1260 #[inline]
1262 pub fn body_jacobian(&self, link_id: usize) -> &Jacobian<Real> {
1263 &self.body_jacobians[link_id]
1264 }
1265
1266 #[inline]
1268 pub fn generalized_velocity_mut(&mut self) -> DVectorViewMut<'_, Real> {
1269 self.velocities.rows_mut(0, self.ndofs)
1270 }
1271
1272 #[inline]
1273 pub(crate) fn integrate(&mut self, dt: Real) {
1274 for rb in self.links.iter_mut() {
1275 rb.joint
1276 .integrate(dt, &self.velocities.as_slice()[rb.assembly_id..])
1277 }
1278 }
1279
1280 pub fn apply_displacements(&mut self, disp: &[Real]) {
1285 for link in self.links.iter_mut() {
1286 link.joint.apply_displacement(&disp[link.assembly_id..])
1287 }
1288 }
1289
1290 pub(crate) fn update_root_type(&mut self, bodies: &RigidBodySet, take_body_pose: bool) {
1291 if let Some(rb) = bodies.get(self.links[0].rigid_body) {
1292 if rb.is_dynamic() != self.root_is_dynamic {
1293 let root_pose = if take_body_pose {
1294 *rb.position()
1295 } else {
1296 self.links[0].local_to_world
1297 };
1298
1299 if rb.is_dynamic() {
1300 let free_joint = MultibodyJoint::free(root_pose);
1301 let prev_root_ndofs = self.links[0].joint().ndofs();
1302 self.links[0].joint = free_joint;
1303 self.links[0].assembly_id = 0;
1304 self.ndofs += SPATIAL_DIM;
1305
1306 self.velocities = self.velocities.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1307 self.damping = self.damping.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1308 self.armature = self.armature.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1309 self.frictions = self.frictions.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1310 self.accelerations =
1311 self.accelerations.clone().insert_rows(0, SPATIAL_DIM, 0.0);
1312
1313 for link in &mut self.links[1..] {
1314 link.assembly_id += SPATIAL_DIM - prev_root_ndofs;
1315 }
1316 } else {
1317 assert!(self.velocities.len() >= SPATIAL_DIM);
1318 assert!(self.damping.len() >= SPATIAL_DIM);
1319 assert!(self.armature.len() >= SPATIAL_DIM);
1320 assert!(self.frictions.len() >= SPATIAL_DIM);
1321 assert!(self.accelerations.len() >= SPATIAL_DIM);
1322
1323 let fixed_joint = MultibodyJoint::fixed(root_pose);
1324 let prev_root_ndofs = self.links[0].joint().ndofs();
1325 self.links[0].joint = fixed_joint;
1326 self.links[0].assembly_id = 0;
1327 self.ndofs -= prev_root_ndofs;
1328
1329 if self.ndofs == 0 {
1330 self.velocities = DVector::zeros(0);
1331 self.damping = DVector::zeros(0);
1332 self.armature = DVector::zeros(0);
1333 self.frictions = DVector::zeros(0);
1334 self.accelerations = DVector::zeros(0);
1335 } else {
1336 self.velocities =
1337 self.velocities.index((prev_root_ndofs.., 0)).into_owned();
1338 self.damping = self.damping.index((prev_root_ndofs.., 0)).into_owned();
1339 self.armature = self.armature.index((prev_root_ndofs.., 0)).into_owned();
1340 self.frictions = self.frictions.index((prev_root_ndofs.., 0)).into_owned();
1341 self.accelerations = self
1342 .accelerations
1343 .index((prev_root_ndofs.., 0))
1344 .into_owned();
1345 }
1346
1347 for link in &mut self.links[1..] {
1348 link.assembly_id -= prev_root_ndofs;
1349 }
1350 }
1351
1352 self.root_is_dynamic = rb.is_dynamic();
1353 }
1354
1355 if take_body_pose {
1357 if self.links[0].joint.data.locked_axes.is_empty() {
1358 self.links[0].joint.set_free_pos(*rb.position());
1359 } else {
1360 self.links[0].joint.data.local_frame1 = *rb.position();
1361 }
1362 }
1363 }
1364 }
1365
1366 pub fn update_rigid_bodies(&self, bodies: &mut RigidBodySet, update_mass_properties: bool) {
1371 self.update_rigid_bodies_internal(bodies, update_mass_properties, false, true)
1372 }
1373
1374 pub(crate) fn update_rigid_bodies_internal(
1375 &self,
1376 bodies: &mut RigidBodySet,
1377 update_mass_properties: bool,
1378 update_next_positions_only: bool,
1379 change_tracking: bool,
1380 ) {
1381 for link in self.links.iter() {
1383 let rb = if change_tracking {
1384 bodies.get_mut_internal_with_modification_tracking(link.rigid_body)
1385 } else {
1386 bodies.get_mut_internal(link.rigid_body)
1387 };
1388
1389 if let Some(rb) = rb {
1390 rb.pos.next_position = link.local_to_world;
1391
1392 if !update_next_positions_only {
1393 rb.pos.position = link.local_to_world;
1394 }
1395
1396 if update_mass_properties {
1397 rb.mprops
1398 .update_world_mass_properties(rb.body_type, &link.local_to_world);
1399 }
1400 }
1401 }
1402 }
1403
1404 pub fn forward_kinematics(
1425 &mut self,
1426 bodies: &RigidBodySet,
1427 read_root_pose_from_rigid_body: bool,
1428 ) {
1429 self.update_root_type(bodies, read_root_pose_from_rigid_body);
1431
1432 {
1434 let link = &mut self.links[0];
1435 link.local_to_parent = link.joint.body_to_parent();
1436 link.local_to_world = link.local_to_parent;
1437 }
1438
1439 for i in 1..self.links.len() {
1441 let (link, parent_link) = self.links.get_mut_with_parent(i);
1442
1443 link.local_to_parent = link.joint.body_to_parent();
1444 link.local_to_world = parent_link.local_to_world * link.local_to_parent;
1445
1446 {
1447 let parent_rb = &bodies[parent_link.rigid_body];
1448 let link_rb = &bodies[link.rigid_body];
1449 let c0 = parent_link.local_to_world * parent_rb.mprops.local_mprops.local_com;
1450 let c2 = link.local_to_world * link.joint.data.local_frame2.translation;
1451 let c3 = link.local_to_world * link_rb.mprops.local_mprops.local_com;
1452
1453 link.shift02 = c2 - c0;
1454 link.shift23 = c3 - c2;
1455 }
1456
1457 assert_eq!(
1458 bodies[link.rigid_body].body_type,
1459 RigidBodyType::Dynamic,
1460 "A rigid-body that is not at the root of a multibody must be dynamic."
1461 );
1462 }
1463
1464 self.update_body_jacobians();
1468 }
1469
1470 pub fn kinematic_branch(&self, link_id: usize) -> Vec<usize> {
1472 let mut branch = vec![]; let mut curr_id = Some(link_id);
1474
1475 while let Some(id) = curr_id {
1476 branch.push(id);
1477 curr_id = self.links[id].parent_id();
1478 }
1479
1480 branch.reverse();
1481 branch
1482 }
1483
1484 pub fn forward_kinematics_single_link(
1492 &self,
1493 bodies: &RigidBodySet,
1494 link_id: usize,
1495 displacement: Option<&[Real]>,
1496 out_jacobian: Option<&mut Jacobian<Real>>,
1497 ) -> Pose {
1498 let branch = self.kinematic_branch(link_id);
1499 self.forward_kinematics_single_branch(bodies, &branch, displacement, out_jacobian)
1500 }
1501
1502 #[profiling::function]
1522 pub fn forward_kinematics_single_branch(
1523 &self,
1524 bodies: &RigidBodySet,
1525 branch: &[usize],
1526 displacement: Option<&[Real]>,
1527 mut out_jacobian: Option<&mut Jacobian<Real>>,
1528 ) -> Pose {
1529 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1530 if out_jacobian.ncols() != self.ndofs {
1531 *out_jacobian = Jacobian::zeros(self.ndofs);
1532 } else {
1533 out_jacobian.fill(0.0);
1534 }
1535 }
1536
1537 let mut parent_link: Option<MultibodyLink> = None;
1538
1539 for i in branch {
1540 let mut link = self.links[*i];
1541
1542 if let Some(displacement) = displacement {
1543 link.joint
1544 .apply_displacement(&displacement[link.assembly_id..]);
1545 }
1546
1547 let parent_to_world;
1548
1549 if let Some(parent_link) = parent_link {
1550 link.local_to_parent = link.joint.body_to_parent();
1551 link.local_to_world = parent_link.local_to_world * link.local_to_parent;
1552
1553 {
1554 let parent_rb = &bodies[parent_link.rigid_body];
1555 let link_rb = &bodies[link.rigid_body];
1556 let c0 = parent_link.local_to_world * parent_rb.mprops.local_mprops.local_com;
1557 let c2 = link.local_to_world
1558 * Vector::from(link.joint.data.local_frame2.translation);
1559 let c3 = link.local_to_world * link_rb.mprops.local_mprops.local_com;
1560
1561 link.shift02 = c2 - c0;
1562 link.shift23 = c3 - c2;
1563 }
1564
1565 parent_to_world = parent_link.local_to_world;
1566
1567 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1568 let (mut link_j_v, parent_j_w) =
1569 out_jacobian.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
1570 let shift_tr = vect_to_na(link.shift02).gcross_matrix_tr();
1571 link_j_v.gemm(1.0, &shift_tr, &parent_j_w, 1.0);
1572 }
1573 } else {
1574 link.local_to_parent = link.joint.body_to_parent();
1575 link.local_to_world = link.local_to_parent;
1576 parent_to_world = Pose::IDENTITY;
1577 }
1578
1579 if let Some(out_jacobian) = out_jacobian.as_deref_mut() {
1580 let ndofs = link.joint.ndofs();
1581 let mut tmp = SMatrix::<Real, SPATIAL_DIM, SPATIAL_DIM>::zeros();
1582 let mut link_joint_j = tmp.columns_mut(0, ndofs);
1583 let mut link_j_part = out_jacobian.columns_mut(link.assembly_id, ndofs);
1584 link.joint.jacobian(
1585 &(parent_to_world.rotation * link.joint.data.local_frame1.rotation),
1586 &mut link_joint_j,
1587 );
1588 link_j_part += link_joint_j;
1589
1590 {
1591 let (mut link_j_v, link_j_w) =
1592 out_jacobian.rows_range_pair_mut(0..DIM, DIM..DIM + ANG_DIM);
1593 let shift_tr = vect_to_na(link.shift23).gcross_matrix_tr();
1594 link_j_v.gemm(1.0, &shift_tr, &link_j_w, 1.0);
1595 }
1596 }
1597
1598 parent_link = Some(link);
1599 }
1600
1601 parent_link
1602 .map(|link| link.local_to_world)
1603 .unwrap_or(Pose::IDENTITY)
1604 }
1605
1606 #[inline]
1608 pub fn ndofs(&self) -> usize {
1609 self.ndofs
1610 }
1611
1612 pub(crate) fn fill_jacobians(
1613 &self,
1614 link_id: usize,
1615 unit_force: Vector,
1616 unit_torque: AngVector,
1617 j_id: &mut usize,
1618 jacobians: &mut DVector,
1619 ) -> (Real, Real) {
1620 if self.ndofs == 0 {
1621 return (0.0, 0.0);
1622 }
1623
1624 let wj_id = *j_id + self.ndofs;
1625 let force = Force {
1626 linear: unit_force,
1627 #[cfg(feature = "dim2")]
1628 angular: unit_torque,
1629 #[cfg(feature = "dim3")]
1630 angular: unit_torque,
1631 };
1632
1633 let link = &self.links[link_id];
1634 let mut out_j = jacobians.rows_mut(*j_id, self.ndofs);
1635 self.body_jacobians[link.internal_id].tr_mul_to(force.as_vector(), &mut out_j);
1636
1637 for i in 0..self.ndofs {
1639 jacobians[wj_id + i] = jacobians[*j_id + i];
1640 }
1641
1642 {
1643 let mut out_invm_j = jacobians.rows_mut(wj_id, self.ndofs);
1644 self.augmented_mass_indices
1645 .with_rearranged_rows_mut(&mut out_invm_j, |out_invm_j| {
1646 self.inv_augmented_mass.solve_mut(out_invm_j);
1647 });
1648 }
1649
1650 let j = jacobians.rows(*j_id, self.ndofs);
1651 let invm_j = jacobians.rows(wj_id, self.ndofs);
1652 *j_id += self.ndofs * 2;
1653
1654 (j.dot(&invm_j), j.dot(&self.generalized_velocity()))
1655 }
1656
1657 pub(crate) fn fill_relative_jacobians(
1661 &self,
1662 link_id1: usize,
1663 unit_force1: Vector,
1664 unit_torque1: AngVector,
1665 link_id2: usize,
1666 unit_force2: Vector,
1667 unit_torque2: AngVector,
1668 j_id: &mut usize,
1669 jacobians: &mut DVector,
1670 ) {
1671 if self.ndofs == 0 {
1672 return;
1673 }
1674
1675 let wj_id = *j_id + self.ndofs;
1676 let force1 = Force {
1677 linear: unit_force1,
1678 angular: unit_torque1,
1679 };
1680 let force2 = Force {
1681 linear: unit_force2,
1682 angular: unit_torque2,
1683 };
1684
1685 let link1 = &self.links[link_id1];
1686 let link2 = &self.links[link_id2];
1687
1688 {
1689 let jb1 = &self.body_jacobians[link1.internal_id];
1690 let jb2 = &self.body_jacobians[link2.internal_id];
1691
1692 let (mut out_j, mut workspace) =
1694 jacobians.rows_range_pair_mut(*j_id..*j_id + self.ndofs, wj_id..wj_id + self.ndofs);
1695 jb2.tr_mul_to(force2.as_vector(), &mut out_j);
1696 jb1.tr_mul_to(force1.as_vector(), &mut workspace);
1697 out_j.axpy(-1.0, &workspace, 1.0);
1698
1699 let scale_sq = jb1.norm_squared() * force1.as_vector().norm_squared()
1703 + jb2.norm_squared() * force2.as_vector().norm_squared();
1704 let eps = Real::EPSILON * 1.0e3;
1705 if out_j.norm_squared() <= eps * eps * scale_sq {
1706 out_j.fill(0.0);
1707 }
1708 }
1709
1710 for i in 0..self.ndofs {
1712 jacobians[wj_id + i] = jacobians[*j_id + i];
1713 }
1714
1715 {
1716 let mut out_invm_j = jacobians.rows_mut(wj_id, self.ndofs);
1717 self.augmented_mass_indices
1718 .with_rearranged_rows_mut(&mut out_invm_j, |out_invm_j| {
1719 self.inv_augmented_mass.solve_mut(out_invm_j);
1720 });
1721 }
1722
1723 *j_id += self.ndofs * 2;
1724 }
1725}
1726
1727#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1728#[derive(Clone, Debug)]
1729struct IndexSequence {
1730 first_to_remove: u32,
1731 index_map: Vec<usize>,
1732}
1733
1734impl IndexSequence {
1735 const NONE: u32 = u32::MAX;
1736
1737 fn new() -> Self {
1738 Self {
1739 first_to_remove: Self::NONE,
1740 index_map: vec![],
1741 }
1742 }
1743
1744 fn start(&self) -> usize {
1746 self.first_to_remove as usize
1747 }
1748
1749 fn clear(&mut self) {
1750 self.first_to_remove = Self::NONE;
1751 self.index_map.clear();
1752 }
1753
1754 fn keep(&mut self, i: usize) {
1755 if self.first_to_remove == Self::NONE {
1756 return;
1759 }
1760
1761 self.index_map.push(i);
1762 }
1763
1764 fn remove(&mut self, i: usize) {
1765 if self.first_to_remove == Self::NONE {
1766 self.first_to_remove = i as u32;
1767 }
1768 }
1769
1770 fn dim_after_removal(&self, original_dim: usize) -> usize {
1771 if self.first_to_remove == Self::NONE {
1772 original_dim
1773 } else {
1774 self.start() + self.index_map.len()
1775 }
1776 }
1777
1778 fn rearrange_columns<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1779 &self,
1780 mat: &mut na::Matrix<Real, R, C, S>,
1781 clear_removed: bool,
1782 ) {
1783 if self.first_to_remove == Self::NONE {
1784 return;
1786 }
1787
1788 for (target_shift, source) in self.index_map.iter().enumerate() {
1789 let target = self.start() + target_shift;
1790 let (mut target_col, source_col) = mat.columns_range_pair_mut(target, *source);
1791 target_col.copy_from(&source_col);
1792 }
1793
1794 if clear_removed {
1795 mat.columns_range_mut(self.start() + self.index_map.len()..)
1796 .fill(0.0);
1797 }
1798 }
1799
1800 fn rearrange_rows<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1801 &self,
1802 mat: &mut na::Matrix<Real, R, C, S>,
1803 clear_removed: bool,
1804 ) {
1805 if self.first_to_remove == Self::NONE {
1806 return;
1808 }
1809
1810 for mut col in mat.column_iter_mut() {
1811 for (target_shift, source) in self.index_map.iter().enumerate() {
1812 let target = self.start() + target_shift;
1813 col[target] = col[*source];
1814 }
1815
1816 if clear_removed {
1817 col.rows_range_mut(self.start() + self.index_map.len()..)
1818 .fill(0.0);
1819 }
1820 }
1821 }
1822
1823 fn inv_rearrange_rows<R: na::Dim, C: na::Dim, S: StorageMut<Real, R, C>>(
1824 &self,
1825 mat: &mut na::Matrix<Real, R, C, S>,
1826 ) {
1827 if self.first_to_remove == Self::NONE {
1828 return;
1830 }
1831
1832 for mut col in mat.column_iter_mut() {
1833 for (target_shift, source) in self.index_map.iter().enumerate().rev() {
1834 let target = self.start() + target_shift;
1835 col[*source] = col[target];
1836 col[target] = 0.0;
1837 }
1838 }
1839 }
1840
1841 fn with_rearranged_rows_mut<C: na::Dim, S: StorageMut<Real, Dyn, C>>(
1842 &self,
1843 mat: &mut na::Matrix<Real, Dyn, C, S>,
1844 mut f: impl FnMut(&mut na::MatrixViewMut<Real, Dyn, C, S::RStride, S::CStride>),
1845 ) {
1846 self.rearrange_rows(mat, true);
1847 let effective_dim = self.dim_after_removal(mat.nrows());
1848 if effective_dim > 0 {
1849 f(&mut mat.rows_mut(0, effective_dim));
1850 }
1851 self.inv_rearrange_rows(mat);
1852 }
1853}
1854
1855#[cfg(test)]
1856mod test {
1857 use super::IndexSequence;
1858 use crate::alloc_prelude::*;
1859 use crate::dynamics::{ImpulseJointSet, IslandManager};
1860 #[cfg(feature = "dim3")]
1861 use crate::math::Vector;
1862 use crate::math::{Real, SPATIAL_DIM};
1863 use crate::prelude::{
1864 ColliderSet, MultibodyJointHandle, MultibodyJointSet, RevoluteJoint, RigidBodyBuilder,
1865 RigidBodySet, SoftBodySet,
1866 };
1867 use na::{DVector, RowDVector};
1868
1869 #[test]
1870 fn test_multibody_append() {
1871 let mut bodies = RigidBodySet::new();
1872 let mut joints = MultibodyJointSet::new();
1873
1874 let a = bodies.insert(RigidBodyBuilder::dynamic());
1875 let b = bodies.insert(RigidBodyBuilder::dynamic());
1876 let c = bodies.insert(RigidBodyBuilder::dynamic());
1877 let d = bodies.insert(RigidBodyBuilder::dynamic());
1878
1879 #[cfg(feature = "dim2")]
1880 let joint = RevoluteJoint::new();
1881 #[cfg(feature = "dim3")]
1882 let joint = RevoluteJoint::new(Vector::X);
1883
1884 let mb_handle = joints.insert(a, b, joint, true).unwrap();
1885 joints.insert(c, d, joint, true).unwrap();
1886 joints.insert(b, c, joint, true).unwrap();
1887
1888 assert_eq!(joints.get(mb_handle).unwrap().0.ndofs, SPATIAL_DIM + 3);
1889 }
1890
1891 #[test]
1892 fn test_multibody_insert() {
1893 let mut rnd = oorandom::Rand32::new(1234);
1894
1895 for k in 0..10 {
1896 let mut bodies = RigidBodySet::new();
1897 let mut multibody_joints = MultibodyJointSet::new();
1898
1899 let num_links = 100;
1900 let mut handles = vec![];
1901
1902 for _ in 0..num_links {
1903 handles.push(bodies.insert(RigidBodyBuilder::dynamic()));
1904 }
1905
1906 let mut insertion_id: Vec<_> = (0..num_links - 1).collect();
1907
1908 #[cfg(feature = "dim2")]
1909 let joint = RevoluteJoint::new();
1910 #[cfg(feature = "dim3")]
1911 let joint = RevoluteJoint::new(Vector::X);
1912
1913 match k {
1914 0 => {} 1 => {
1916 insertion_id.reverse();
1918 }
1919 _ => {
1920 for l in 0..num_links - 1 {
1923 insertion_id.swap(l, rnd.rand_range(0..num_links as u32 - 1) as usize);
1924 }
1925 }
1926 }
1927
1928 let mut mb_handle = MultibodyJointHandle::invalid();
1929 for i in insertion_id {
1930 mb_handle = multibody_joints
1931 .insert(handles[i], handles[i + 1], joint, true)
1932 .unwrap();
1933 }
1934
1935 assert_eq!(
1936 multibody_joints.get(mb_handle).unwrap().0.ndofs,
1937 SPATIAL_DIM + num_links - 1
1938 );
1939 }
1940 }
1941
1942 #[test]
1943 fn test_multibody_remove() {
1944 let mut rnd = oorandom::Rand32::new(1234);
1945
1946 for k in 0..10 {
1947 let mut bodies = RigidBodySet::new();
1948 let mut multibody_joints = MultibodyJointSet::new();
1949 let mut colliders = ColliderSet::new();
1950 let mut impulse_joints = ImpulseJointSet::new();
1951 let mut islands = IslandManager::new();
1952 let mut soft_bodies = SoftBodySet::new();
1953
1954 let num_links = 100;
1955 let mut handles = vec![];
1956
1957 for _ in 0..num_links {
1958 handles.push(bodies.insert(RigidBodyBuilder::dynamic()));
1959 }
1960
1961 #[cfg(feature = "dim2")]
1962 let joint = RevoluteJoint::new();
1963 #[cfg(feature = "dim3")]
1964 let joint = RevoluteJoint::new(Vector::X);
1965
1966 for i in 0..num_links - 1 {
1967 multibody_joints
1968 .insert(handles[i], handles[i + 1], joint, true)
1969 .unwrap();
1970 }
1971
1972 match k {
1973 0 => {} 1 => {
1975 handles.reverse();
1977 }
1978 _ => {
1979 for l in 0..num_links {
1982 handles.swap(l, rnd.rand_range(0..num_links as u32) as usize);
1983 }
1984 }
1985 }
1986
1987 for handle in handles {
1988 bodies.remove(
1989 handle,
1990 &mut islands,
1991 &mut colliders,
1992 &mut impulse_joints,
1993 &mut multibody_joints,
1994 &mut soft_bodies,
1995 true,
1996 );
1997 }
1998 }
1999 }
2000
2001 fn test_sequence() -> IndexSequence {
2002 let mut seq = IndexSequence::new();
2003 seq.remove(2);
2004 seq.remove(3);
2005 seq.remove(4);
2006 seq.keep(5);
2007 seq.keep(6);
2008 seq.remove(7);
2009 seq.keep(8);
2010 seq
2011 }
2012
2013 #[test]
2014 fn index_sequence_rearrange_columns() {
2015 let seq = test_sequence();
2016 let mut vec = RowDVector::from_fn(10, |_, c| c as Real);
2017 seq.rearrange_columns(&mut vec, true);
2018 assert_eq!(
2019 vec,
2020 RowDVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0])
2021 );
2022 }
2023
2024 #[test]
2025 fn index_sequence_rearrange_rows() {
2026 let seq = test_sequence();
2027 let mut vec = DVector::from_fn(10, |r, _| r as Real);
2028 seq.rearrange_rows(&mut vec, true);
2029 assert_eq!(
2030 vec,
2031 DVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0])
2032 );
2033 seq.inv_rearrange_rows(&mut vec);
2034 assert_eq!(
2035 vec,
2036 DVector::from(vec![0.0, 1.0, 0.0, 0.0, 0.0, 5.0, 6.0, 0.0, 8.0, 0.0])
2037 );
2038 }
2039
2040 #[test]
2041 fn index_sequence_with_rearranged_rows_mut() {
2042 let seq = test_sequence();
2043 let mut vec = DVector::from_fn(10, |r, _| r as Real);
2044 seq.with_rearranged_rows_mut(&mut vec, |v| {
2045 assert_eq!(v.len(), 5);
2046 assert_eq!(*v, DVector::from(vec![0.0, 1.0, 5.0, 6.0, 8.0]));
2047 *v *= 10.0;
2048 });
2049 assert_eq!(
2050 vec,
2051 DVector::from(vec![0.0, 10.0, 0.0, 0.0, 0.0, 50.0, 60.0, 0.0, 80.0, 0.0])
2052 );
2053 }
2054}