GCC Code Coverage Report


Directory: ./
File: include/crocoddyl/multibody/contacts/multiple-contacts.hxx
Date: 2025-05-13 10:30:51
Exec Total Coverage
Lines: 0 282 0.0%
Branches: 0 723 0.0%

Line Branch Exec Source
1 ///////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (C) 2019-2025, LAAS-CNRS, University of Edinburgh,
5 // Heriot-Watt University
6 // Copyright note valid unless otherwise stated in individual files.
7 // All rights reserved.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 namespace crocoddyl {
11
12 template <typename Scalar>
13 ContactModelMultipleTpl<Scalar>::ContactModelMultipleTpl(
14 std::shared_ptr<StateMultibody> state, const std::size_t nu)
15 : state_(state),
16 nc_(0),
17 nc_total_(0),
18 nu_(nu),
19 compute_all_contacts_(false) {}
20
21 template <typename Scalar>
22 ContactModelMultipleTpl<Scalar>::ContactModelMultipleTpl(
23 std::shared_ptr<StateMultibody> state)
24 : state_(state),
25 nc_(0),
26 nc_total_(0),
27 nu_(state->get_nv()),
28 compute_all_contacts_(false) {}
29
30 template <typename Scalar>
31 ContactModelMultipleTpl<Scalar>::~ContactModelMultipleTpl() {}
32
33 template <typename Scalar>
34 void ContactModelMultipleTpl<Scalar>::addContact(
35 const std::string& name, std::shared_ptr<ContactModelAbstract> contact,
36 const bool active) {
37 if (contact->get_nu() != nu_) {
38 throw_pretty("Invalid argument: "
39 << name
40 << " contact item doesn't have the same control dimension (" +
41 std::to_string(nu_) + ")");
42 }
43 std::pair<typename ContactModelContainer::iterator, bool> ret =
44 contacts_.insert(std::make_pair(
45 name, std::make_shared<ContactItem>(name, contact, active)));
46 if (ret.second == false) {
47 std::cerr << "Warning: we couldn't add the " << name
48 << " contact item, it already existed." << std::endl;
49 } else if (active) {
50 nc_ += contact->get_nc();
51 nc_total_ += contact->get_nc();
52 active_set_.insert(name);
53 } else if (!active) {
54 nc_total_ += contact->get_nc();
55 inactive_set_.insert(name);
56 }
57 }
58
59 template <typename Scalar>
60 void ContactModelMultipleTpl<Scalar>::removeContact(const std::string& name) {
61 typename ContactModelContainer::iterator it = contacts_.find(name);
62 if (it != contacts_.end()) {
63 nc_ -= it->second->contact->get_nc();
64 nc_total_ -= it->second->contact->get_nc();
65 contacts_.erase(it);
66 active_set_.erase(name);
67 inactive_set_.erase(name);
68 } else {
69 std::cerr << "Warning: we couldn't remove the " << name
70 << " contact item, it doesn't exist." << std::endl;
71 }
72 }
73
74 template <typename Scalar>
75 void ContactModelMultipleTpl<Scalar>::changeContactStatus(
76 const std::string& name, const bool active) {
77 typename ContactModelContainer::iterator it = contacts_.find(name);
78 if (it != contacts_.end()) {
79 if (active && !it->second->active) {
80 nc_ += it->second->contact->get_nc();
81 active_set_.insert(name);
82 inactive_set_.erase(name);
83 } else if (!active && it->second->active) {
84 nc_ -= it->second->contact->get_nc();
85 inactive_set_.insert(name);
86 active_set_.erase(name);
87 }
88 // "else" case: Contact status unchanged - already in desired state
89 it->second->active = active;
90 } else {
91 std::cerr << "Warning: we couldn't change the status of the " << name
92 << " contact item, it doesn't exist." << std::endl;
93 }
94 }
95
96 template <typename Scalar>
97 void ContactModelMultipleTpl<Scalar>::calc(
98 const std::shared_ptr<ContactDataMultiple>& data,
99 const Eigen::Ref<const VectorXs>& x) {
100 if (data->contacts.size() != contacts_.size()) {
101 throw_pretty("Invalid argument: "
102 << "it doesn't match the number of contact datas and models");
103 }
104
105 std::size_t nc = 0;
106 const std::size_t nv = state_->get_nv();
107 typename ContactModelContainer::iterator it_m, end_m;
108 typename ContactDataContainer::iterator it_d, end_d;
109 if (compute_all_contacts_) {
110 for (it_m = contacts_.begin(), end_m = contacts_.end(),
111 it_d = data->contacts.begin(), end_d = data->contacts.end();
112 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
113 const std::shared_ptr<ContactItem>& m_i = it_m->second;
114 const std::size_t nc_i = m_i->contact->get_nc();
115 if (m_i->active) {
116 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
117 assert_pretty(
118 it_m->first == it_d->first,
119 "it doesn't match the contact name between model and data ("
120 << it_m->first << " != " << it_d->first << ")");
121 m_i->contact->calc(d_i, x);
122 data->a0.segment(nc, nc_i) = d_i->a0;
123 data->Jc.block(nc, 0, nc_i, nv) = d_i->Jc;
124 } else {
125 data->a0.segment(nc, nc_i).setZero();
126 data->Jc.block(nc, 0, nc_i, nv).setZero();
127 }
128 nc += nc_i;
129 }
130 } else {
131 for (it_m = contacts_.begin(), end_m = contacts_.end(),
132 it_d = data->contacts.begin(), end_d = data->contacts.end();
133 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
134 const std::shared_ptr<ContactItem>& m_i = it_m->second;
135 if (m_i->active) {
136 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
137 assert_pretty(
138 it_m->first == it_d->first,
139 "it doesn't match the contact name between model and data ("
140 << it_m->first << " != " << it_d->first << ")");
141
142 m_i->contact->calc(d_i, x);
143 const std::size_t nc_i = m_i->contact->get_nc();
144 data->a0.segment(nc, nc_i) = d_i->a0;
145 data->Jc.block(nc, 0, nc_i, nv) = d_i->Jc;
146 nc += nc_i;
147 }
148 }
149 }
150 }
151
152 template <typename Scalar>
153 void ContactModelMultipleTpl<Scalar>::calcDiff(
154 const std::shared_ptr<ContactDataMultiple>& data,
155 const Eigen::Ref<const VectorXs>& x) {
156 if (data->contacts.size() != contacts_.size()) {
157 throw_pretty("Invalid argument: "
158 << "it doesn't match the number of contact datas and models");
159 }
160
161 std::size_t nc = 0;
162 const std::size_t ndx = state_->get_ndx();
163 typename ContactModelContainer::iterator it_m, end_m;
164 typename ContactDataContainer::iterator it_d, end_d;
165 if (compute_all_contacts_) {
166 for (it_m = contacts_.begin(), end_m = contacts_.end(),
167 it_d = data->contacts.begin(), end_d = data->contacts.end();
168 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
169 const std::shared_ptr<ContactItem>& m_i = it_m->second;
170 const std::size_t nc_i = m_i->contact->get_nc();
171 if (m_i->active) {
172 assert_pretty(
173 it_m->first == it_d->first,
174 "it doesn't match the contact name between model and data ("
175 << it_m->first << " != " << it_d->first << ")");
176 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
177
178 m_i->contact->calcDiff(d_i, x);
179 data->da0_dx.block(nc, 0, nc_i, ndx) = d_i->da0_dx;
180 } else {
181 data->da0_dx.block(nc, 0, nc_i, ndx).setZero();
182 }
183 nc += nc_i;
184 }
185 } else {
186 for (it_m = contacts_.begin(), end_m = contacts_.end(),
187 it_d = data->contacts.begin(), end_d = data->contacts.end();
188 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
189 const std::shared_ptr<ContactItem>& m_i = it_m->second;
190 if (m_i->active) {
191 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
192 assert_pretty(
193 it_m->first == it_d->first,
194 "it doesn't match the contact name between model and data ("
195 << it_m->first << " != " << it_d->first << ")");
196
197 m_i->contact->calcDiff(d_i, x);
198 const std::size_t nc_i = m_i->contact->get_nc();
199 data->da0_dx.block(nc, 0, nc_i, ndx) = d_i->da0_dx;
200 nc += nc_i;
201 }
202 }
203 }
204 }
205
206 template <typename Scalar>
207 void ContactModelMultipleTpl<Scalar>::updateAcceleration(
208 const std::shared_ptr<ContactDataMultiple>& data,
209 const VectorXs& dv) const {
210 if (static_cast<std::size_t>(dv.size()) != state_->get_nv()) {
211 throw_pretty(
212 "Invalid argument: " << "dv has wrong dimension (it should be " +
213 std::to_string(state_->get_nv()) + ")");
214 }
215 data->dv = dv;
216 }
217
218 template <typename Scalar>
219 void ContactModelMultipleTpl<Scalar>::updateForce(
220 const std::shared_ptr<ContactDataMultiple>& data, const VectorXs& force) {
221 if (static_cast<std::size_t>(force.size()) !=
222 (compute_all_contacts_ ? nc_total_ : nc_)) {
223 throw_pretty(
224 "Invalid argument: "
225 << "force has wrong dimension (it should be " +
226 std::to_string((compute_all_contacts_ ? nc_total_ : nc_)) + ")");
227 }
228 if (static_cast<std::size_t>(data->contacts.size()) != contacts_.size()) {
229 throw_pretty("Invalid argument: "
230 << "it doesn't match the number of contact datas and models");
231 }
232
233 for (ForceIterator it = data->fext.begin(); it != data->fext.end(); ++it) {
234 *it = pinocchio::ForceTpl<Scalar>::Zero();
235 }
236
237 std::size_t nc = 0;
238 typename ContactModelContainer::iterator it_m, end_m;
239 typename ContactDataContainer::iterator it_d, end_d;
240 if (compute_all_contacts_) {
241 for (it_m = contacts_.begin(), end_m = contacts_.end(),
242 it_d = data->contacts.begin(), end_d = data->contacts.end();
243 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
244 const std::shared_ptr<ContactItem>& m_i = it_m->second;
245 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
246 assert_pretty(it_m->first == it_d->first,
247 "it doesn't match the contact name between data and model");
248 const std::size_t nc_i = m_i->contact->get_nc();
249 if (m_i->active) {
250 const Eigen::VectorBlock<const VectorXs, Eigen::Dynamic> force_i =
251 force.segment(nc, nc_i);
252 m_i->contact->updateForce(d_i, force_i);
253 const pinocchio::JointIndex joint =
254 state_->get_pinocchio()->frames[d_i->frame].parentJoint;
255 data->fext[joint] = d_i->fext;
256 } else {
257 m_i->contact->setZeroForce(d_i);
258 }
259 nc += nc_i;
260 }
261 } else {
262 for (it_m = contacts_.begin(), end_m = contacts_.end(),
263 it_d = data->contacts.begin(), end_d = data->contacts.end();
264 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
265 const std::shared_ptr<ContactItem>& m_i = it_m->second;
266 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
267 assert_pretty(it_m->first == it_d->first,
268 "it doesn't match the contact name between data and model");
269 if (m_i->active) {
270 const std::size_t nc_i = m_i->contact->get_nc();
271 const Eigen::VectorBlock<const VectorXs, Eigen::Dynamic> force_i =
272 force.segment(nc, nc_i);
273 m_i->contact->updateForce(d_i, force_i);
274 const pinocchio::JointIndex joint =
275 state_->get_pinocchio()->frames[d_i->frame].parentJoint;
276 data->fext[joint] = d_i->fext;
277 nc += nc_i;
278 } else {
279 m_i->contact->setZeroForce(d_i);
280 }
281 }
282 }
283 }
284
285 template <typename Scalar>
286 void ContactModelMultipleTpl<Scalar>::updateAccelerationDiff(
287 const std::shared_ptr<ContactDataMultiple>& data,
288 const MatrixXs& ddv_dx) const {
289 if (static_cast<std::size_t>(ddv_dx.rows()) != state_->get_nv() ||
290 static_cast<std::size_t>(ddv_dx.cols()) != state_->get_ndx()) {
291 throw_pretty(
292 "Invalid argument: " << "ddv_dx has wrong dimension (it should be " +
293 std::to_string(state_->get_nv()) + "," +
294 std::to_string(state_->get_ndx()) + ")");
295 }
296 data->ddv_dx = ddv_dx;
297 }
298
299 template <typename Scalar>
300 void ContactModelMultipleTpl<Scalar>::updateForceDiff(
301 const std::shared_ptr<ContactDataMultiple>& data, const MatrixXs& df_dx,
302 const MatrixXs& df_du) const {
303 const std::size_t ndx = state_->get_ndx();
304 if (static_cast<std::size_t>(df_dx.rows()) !=
305 (compute_all_contacts_ ? nc_total_ : nc_) ||
306 static_cast<std::size_t>(df_dx.cols()) != ndx) {
307 throw_pretty(
308 "Invalid argument: "
309 << "df_dx has wrong dimension (it should be " +
310 std::to_string((compute_all_contacts_ ? nc_total_ : nc_)) + "," +
311 std::to_string(ndx) + ")");
312 }
313 if (static_cast<std::size_t>(df_du.rows()) !=
314 (compute_all_contacts_ ? nc_total_ : nc_) ||
315 static_cast<std::size_t>(df_du.cols()) != nu_) {
316 throw_pretty(
317 "Invalid argument: "
318 << "df_du has wrong dimension (it should be " +
319 std::to_string((compute_all_contacts_ ? nc_total_ : nc_)) + "," +
320 std::to_string(nu_) + ")");
321 }
322 if (static_cast<std::size_t>(data->contacts.size()) != contacts_.size()) {
323 throw_pretty("Invalid argument: "
324 << "it doesn't match the number of contact datas and models");
325 }
326
327 std::size_t nc = 0;
328 typename ContactModelContainer::const_iterator it_m, end_m;
329 typename ContactDataContainer::const_iterator it_d, end_d;
330 if (compute_all_contacts_) {
331 for (it_m = contacts_.begin(), end_m = contacts_.end(),
332 it_d = data->contacts.begin(), end_d = data->contacts.end();
333 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
334 const std::shared_ptr<ContactItem>& m_i = it_m->second;
335 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
336 assert_pretty(it_m->first == it_d->first,
337 "it doesn't match the contact name between data and model");
338 const std::size_t nc_i = m_i->contact->get_nc();
339 if (m_i->active) {
340 const Eigen::Block<const MatrixXs> df_dx_i =
341 df_dx.block(nc, 0, nc_i, ndx);
342 const Eigen::Block<const MatrixXs> df_du_i =
343 df_du.block(nc, 0, nc_i, nu_);
344 m_i->contact->updateForceDiff(d_i, df_dx_i, df_du_i);
345 } else {
346 m_i->contact->setZeroForceDiff(d_i);
347 }
348 nc += nc_i;
349 }
350 } else {
351 for (it_m = contacts_.begin(), end_m = contacts_.end(),
352 it_d = data->contacts.begin(), end_d = data->contacts.end();
353 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
354 const std::shared_ptr<ContactItem>& m_i = it_m->second;
355 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
356 assert_pretty(it_m->first == it_d->first,
357 "it doesn't match the contact name between data and model");
358 if (m_i->active) {
359 const std::size_t nc_i = m_i->contact->get_nc();
360 const Eigen::Block<const MatrixXs> df_dx_i =
361 df_dx.block(nc, 0, nc_i, ndx);
362 const Eigen::Block<const MatrixXs> df_du_i =
363 df_du.block(nc, 0, nc_i, nu_);
364 m_i->contact->updateForceDiff(d_i, df_dx_i, df_du_i);
365 nc += nc_i;
366 } else {
367 m_i->contact->setZeroForceDiff(d_i);
368 }
369 }
370 }
371 }
372
373 template <typename Scalar>
374 void ContactModelMultipleTpl<Scalar>::updateRneaDiff(
375 const std::shared_ptr<ContactDataMultiple>& data,
376 pinocchio::DataTpl<Scalar>& pinocchio) const {
377 if (static_cast<std::size_t>(data->contacts.size()) !=
378 this->get_contacts().size()) {
379 throw_pretty("Invalid argument: "
380 << "it doesn't match the number of contact datas and models");
381 }
382 typename ContactModelContainer::const_iterator it_m, end_m;
383 typename ContactDataContainer::const_iterator it_d, end_d;
384 for (it_m = contacts_.begin(), end_m = contacts_.end(),
385 it_d = data->contacts.begin(), end_d = data->contacts.end();
386 it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
387 const std::shared_ptr<ContactItem>& m_i = it_m->second;
388 const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
389 assert_pretty(it_m->first == it_d->first,
390 "it doesn't match the contact name between data and model");
391 if (m_i->active) {
392 switch (m_i->contact->get_type()) {
393 case pinocchio::ReferenceFrame::LOCAL:
394 break;
395 case pinocchio::ReferenceFrame::WORLD:
396 case pinocchio::ReferenceFrame::LOCAL_WORLD_ALIGNED:
397 pinocchio.dtau_dq += d_i->dtau_dq;
398 break;
399 }
400 }
401 }
402 }
403
404 template <typename Scalar>
405 std::shared_ptr<ContactDataMultipleTpl<Scalar> >
406 ContactModelMultipleTpl<Scalar>::createData(
407 pinocchio::DataTpl<Scalar>* const data) {
408 return std::allocate_shared<ContactDataMultiple>(
409 Eigen::aligned_allocator<ContactDataMultiple>(), this, data);
410 }
411
412 template <typename Scalar>
413 template <typename NewScalar>
414 ContactModelMultipleTpl<NewScalar> ContactModelMultipleTpl<Scalar>::cast()
415 const {
416 typedef ContactModelMultipleTpl<NewScalar> ReturnType;
417 typedef StateMultibodyTpl<NewScalar> StateType;
418 typedef ContactItemTpl<NewScalar> ContactType;
419 ReturnType ret(
420 std::make_shared<StateType>(state_->template cast<NewScalar>()), nu_);
421 typename ContactModelContainer::const_iterator it_m, end_m;
422 for (it_m = contacts_.begin(), end_m = contacts_.end(); it_m != end_m;
423 ++it_m) {
424 const std::string name = it_m->first;
425 const ContactType& m_i = it_m->second->template cast<NewScalar>();
426 ret.addContact(name, m_i.contact, m_i.active);
427 }
428 return ret;
429 }
430
431 template <typename Scalar>
432 const std::shared_ptr<StateMultibodyTpl<Scalar> >&
433 ContactModelMultipleTpl<Scalar>::get_state() const {
434 return state_;
435 }
436
437 template <typename Scalar>
438 const typename ContactModelMultipleTpl<Scalar>::ContactModelContainer&
439 ContactModelMultipleTpl<Scalar>::get_contacts() const {
440 return contacts_;
441 }
442
443 template <typename Scalar>
444 std::size_t ContactModelMultipleTpl<Scalar>::get_nc() const {
445 return nc_;
446 }
447
448 template <typename Scalar>
449 std::size_t ContactModelMultipleTpl<Scalar>::get_nc_total() const {
450 return nc_total_;
451 }
452
453 template <typename Scalar>
454 std::size_t ContactModelMultipleTpl<Scalar>::get_nu() const {
455 return nu_;
456 }
457
458 template <typename Scalar>
459 const std::set<std::string>& ContactModelMultipleTpl<Scalar>::get_active_set()
460 const {
461 return active_set_;
462 }
463
464 template <typename Scalar>
465 const std::set<std::string>& ContactModelMultipleTpl<Scalar>::get_inactive_set()
466 const {
467 return inactive_set_;
468 }
469
470 template <typename Scalar>
471 bool ContactModelMultipleTpl<Scalar>::getContactStatus(
472 const std::string& name) const {
473 typename ContactModelContainer::const_iterator it = contacts_.find(name);
474 if (it != contacts_.end()) {
475 return it->second->active;
476 } else {
477 std::cerr << "Warning: we couldn't get the status of the " << name
478 << " contact item, it doesn't exist." << std::endl;
479 return false;
480 }
481 }
482
483 template <typename Scalar>
484 bool ContactModelMultipleTpl<Scalar>::getComputeAllContacts() const {
485 return compute_all_contacts_;
486 }
487
488 template <typename Scalar>
489 void ContactModelMultipleTpl<Scalar>::setComputeAllContacts(const bool status) {
490 compute_all_contacts_ = status;
491 }
492
493 template <class Scalar>
494 std::ostream& operator<<(std::ostream& os,
495 const ContactModelMultipleTpl<Scalar>& model) {
496 const auto& active = model.get_active_set();
497 const auto& inactive = model.get_inactive_set();
498 os << "ContactModelMultiple:" << std::endl;
499 os << " Active:" << std::endl;
500 for (std::set<std::string>::const_iterator it = active.begin();
501 it != active.end(); ++it) {
502 const std::shared_ptr<
503 typename ContactModelMultipleTpl<Scalar>::ContactItem>& contact_item =
504 model.get_contacts().find(*it)->second;
505 if (it != --active.end()) {
506 os << " " << *it << ": " << *contact_item << std::endl;
507 } else {
508 os << " " << *it << ": " << *contact_item << std::endl;
509 }
510 }
511 os << " Inactive:" << std::endl;
512 for (std::set<std::string>::const_iterator it = inactive.begin();
513 it != inactive.end(); ++it) {
514 const std::shared_ptr<
515 typename ContactModelMultipleTpl<Scalar>::ContactItem>& contact_item =
516 model.get_contacts().find(*it)->second;
517 if (it != --inactive.end()) {
518 os << " " << *it << ": " << *contact_item << std::endl;
519 } else {
520 os << " " << *it << ": " << *contact_item;
521 }
522 }
523 return os;
524 }
525
526 } // namespace crocoddyl
527