GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/crocoddyl/multibody/contacts/multiple-contacts.hxx Lines: 246 278 88.5 %
Date: 2024-02-13 11:12:33 Branches: 197 661 29.8 %

Line Branch Exec Source
1
///////////////////////////////////////////////////////////////////////////////
2
// BSD 3-Clause License
3
//
4
// Copyright (C) 2019-2023, 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
766
ContactModelMultipleTpl<Scalar>::ContactModelMultipleTpl(
14
    boost::shared_ptr<StateMultibody> state, const std::size_t nu)
15
    : state_(state),
16
      nc_(0),
17
      nc_total_(0),
18
      nu_(nu),
19
766
      compute_all_contacts_(false) {}
20
21
template <typename Scalar>
22
18
ContactModelMultipleTpl<Scalar>::ContactModelMultipleTpl(
23
    boost::shared_ptr<StateMultibody> state)
24
    : state_(state),
25
      nc_(0),
26
      nc_total_(0),
27
18
      nu_(state->get_nv()),
28
18
      compute_all_contacts_(false) {}
29
30
template <typename Scalar>
31
786
ContactModelMultipleTpl<Scalar>::~ContactModelMultipleTpl() {}
32
33
template <typename Scalar>
34
1717
void ContactModelMultipleTpl<Scalar>::addContact(
35
    const std::string& name, boost::shared_ptr<ContactModelAbstract> contact,
36
    const bool active) {
37
1717
  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

1717
  std::pair<typename ContactModelContainer::iterator, bool> ret =
44
      contacts_.insert(std::make_pair(
45
          name, boost::make_shared<ContactItem>(name, contact, active)));
46
1717
  if (ret.second == false) {
47
    std::cerr << "Warning: we couldn't add the " << name
48


1
              << " contact item, it already existed." << std::endl;
49
1716
  } else if (active) {
50
1715
    nc_ += contact->get_nc();
51
1715
    nc_total_ += contact->get_nc();
52
1715
    active_set_.insert(name);
53
1
  } else if (!active) {
54
1
    nc_total_ += contact->get_nc();
55
1
    inactive_set_.insert(name);
56
  }
57
1717
}
58
59
template <typename Scalar>
60
2
void ContactModelMultipleTpl<Scalar>::removeContact(const std::string& name) {
61
2
  typename ContactModelContainer::iterator it = contacts_.find(name);
62
2
  if (it != contacts_.end()) {
63
1
    nc_ -= it->second->contact->get_nc();
64
1
    nc_total_ -= it->second->contact->get_nc();
65
1
    contacts_.erase(it);
66
1
    active_set_.erase(name);
67
1
    inactive_set_.erase(name);
68
  } else {
69
    std::cerr << "Warning: we couldn't remove the " << name
70


1
              << " contact item, it doesn't exist." << std::endl;
71
  }
72
2
}
73
74
template <typename Scalar>
75
17
void ContactModelMultipleTpl<Scalar>::changeContactStatus(
76
    const std::string& name, const bool active) {
77
17
  typename ContactModelContainer::iterator it = contacts_.find(name);
78
17
  if (it != contacts_.end()) {
79

16
    if (active && !it->second->active) {
80
1
      nc_ += it->second->contact->get_nc();
81
1
      active_set_.insert(name);
82
1
      inactive_set_.erase(name);
83

15
    } else if (!active && it->second->active) {
84
15
      nc_ -= it->second->contact->get_nc();
85
15
      inactive_set_.insert(name);
86
15
      active_set_.erase(name);
87
    }
88
    // "else" case: Contact status unchanged - already in desired state
89
16
    it->second->active = active;
90
  } else {
91
    std::cerr << "Warning: we couldn't change the status of the " << name
92


1
              << " contact item, it doesn't exist." << std::endl;
93
  }
94
17
}
95
96
template <typename Scalar>
97
62660
void ContactModelMultipleTpl<Scalar>::calc(
98
    const boost::shared_ptr<ContactDataMultiple>& data,
99
    const Eigen::Ref<const VectorXs>& x) {
100
62660
  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
62660
  std::size_t nc = 0;
106
62660
  const std::size_t nv = state_->get_nv();
107
62660
  typename ContactModelContainer::iterator it_m, end_m;
108
62660
  typename ContactDataContainer::iterator it_d, end_d;
109
62660
  if (compute_all_contacts_) {
110
89300
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
111
25930
        it_d = data->contacts.begin(), end_d = data->contacts.end();
112

152670
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
113
63370
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
114
63370
      const std::size_t nc_i = m_i->contact->get_nc();
115
63370
      if (m_i->active) {
116
63366
        const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
117




63366
        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
63366
        m_i->contact->calc(d_i, x);
122

63366
        data->a0.segment(nc, nc_i) = d_i->a0;
123

63366
        data->Jc.block(nc, 0, nc_i, nv) = d_i->Jc;
124
      } else {
125

4
        data->a0.segment(nc, nc_i).setZero();
126

4
        data->Jc.block(nc, 0, nc_i, nv).setZero();
127
      }
128
63370
      nc += nc_i;
129
    }
130
  } else {
131
116332
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
132
36730
        it_d = data->contacts.begin(), end_d = data->contacts.end();
133

195934
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
134
79602
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
135
79602
      if (m_i->active) {
136
79586
        const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
137




79586
        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
79586
        m_i->contact->calc(d_i, x);
143
79586
        const std::size_t nc_i = m_i->contact->get_nc();
144

79586
        data->a0.segment(nc, nc_i) = d_i->a0;
145

79586
        data->Jc.block(nc, 0, nc_i, nv) = d_i->Jc;
146
79586
        nc += nc_i;
147
      }
148
    }
149
  }
150
62660
}
151
152
template <typename Scalar>
153
8845
void ContactModelMultipleTpl<Scalar>::calcDiff(
154
    const boost::shared_ptr<ContactDataMultiple>& data,
155
    const Eigen::Ref<const VectorXs>& x) {
156
8845
  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
8845
  std::size_t nc = 0;
162
8845
  const std::size_t ndx = state_->get_ndx();
163
8845
  typename ContactModelContainer::iterator it_m, end_m;
164
8845
  typename ContactDataContainer::iterator it_d, end_d;
165
8845
  if (compute_all_contacts_) {
166
12540
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
167
3762
        it_d = data->contacts.begin(), end_d = data->contacts.end();
168

21318
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
169
8778
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
170
8778
      const std::size_t nc_i = m_i->contact->get_nc();
171
8778
      if (m_i->active) {
172




8778
        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
8778
        const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
177
178
8778
        m_i->contact->calcDiff(d_i, x);
179

8778
        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
8778
      nc += nc_i;
184
    }
185
  } else {
186
15303
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
187
5083
        it_d = data->contacts.begin(), end_d = data->contacts.end();
188

25523
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
189
10220
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
190
10220
      if (m_i->active) {
191
10216
        const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
192




10216
        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
10216
        m_i->contact->calcDiff(d_i, x);
198
10216
        const std::size_t nc_i = m_i->contact->get_nc();
199

10216
        data->da0_dx.block(nc, 0, nc_i, ndx) = d_i->da0_dx;
200
10216
        nc += nc_i;
201
      }
202
    }
203
  }
204
8845
}
205
206
template <typename Scalar>
207
32220
void ContactModelMultipleTpl<Scalar>::updateAcceleration(
208
    const boost::shared_ptr<ContactDataMultiple>& data,
209
    const VectorXs& dv) const {
210
32220
  if (static_cast<std::size_t>(dv.size()) != state_->get_nv()) {
211
    throw_pretty("Invalid argument: "
212
                 << "dv has wrong dimension (it should be " +
213
                        std::to_string(state_->get_nv()) + ")");
214
  }
215
32220
  data->dv = dv;
216
32220
}
217
218
template <typename Scalar>
219
58151
void ContactModelMultipleTpl<Scalar>::updateForce(
220
    const boost::shared_ptr<ContactDataMultiple>& data, const VectorXs& force) {
221

116302
  if (static_cast<std::size_t>(force.size()) !=
222
58151
      (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
58151
  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
1330179
  for (ForceIterator it = data->fext.begin(); it != data->fext.end(); ++it) {
234

1272028
    *it = pinocchio::ForceTpl<Scalar>::Zero();
235
  }
236
237
58151
  std::size_t nc = 0;
238
58151
  typename ContactModelContainer::iterator it_m, end_m;
239
58151
  typename ContactDataContainer::iterator it_d, end_d;
240
58151
  if (compute_all_contacts_) {
241
89300
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
242
25930
        it_d = data->contacts.begin(), end_d = data->contacts.end();
243

152670
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
244
63370
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
245
63370
      const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
246


63370
      assert_pretty(it_m->first == it_d->first,
247
                    "it doesn't match the contact name between data and model");
248
63370
      const std::size_t nc_i = m_i->contact->get_nc();
249
63370
      if (m_i->active) {
250
63366
        const Eigen::VectorBlock<const VectorXs, Eigen::Dynamic> force_i =
251
            force.segment(nc, nc_i);
252

63366
        m_i->contact->updateForce(d_i, force_i);
253
63366
        const pinocchio::JointIndex joint =
254
63366
            state_->get_pinocchio()->frames[d_i->frame].parent;
255
63366
        data->fext[joint] = d_i->fext;
256
      } else {
257
4
        m_i->contact->setZeroForce(d_i);
258
      }
259
63370
      nc += nc_i;
260
    }
261
  } else {
262
102146
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
263
32221
        it_d = data->contacts.begin(), end_d = data->contacts.end();
264

172071
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
265
69925
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
266
69925
      const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
267


69925
      assert_pretty(it_m->first == it_d->first,
268
                    "it doesn't match the contact name between data and model");
269
69925
      if (m_i->active) {
270
69921
        const std::size_t nc_i = m_i->contact->get_nc();
271
69921
        const Eigen::VectorBlock<const VectorXs, Eigen::Dynamic> force_i =
272
            force.segment(nc, nc_i);
273

69921
        m_i->contact->updateForce(d_i, force_i);
274
69921
        const pinocchio::JointIndex joint =
275
69921
            state_->get_pinocchio()->frames[d_i->frame].parent;
276
69921
        data->fext[joint] = d_i->fext;
277
69921
        nc += nc_i;
278
      } else {
279
4
        m_i->contact->setZeroForce(d_i);
280
      }
281
    }
282
  }
283
58151
}
284
285
template <typename Scalar>
286
5078
void ContactModelMultipleTpl<Scalar>::updateAccelerationDiff(
287
    const boost::shared_ptr<ContactDataMultiple>& data,
288
    const MatrixXs& ddv_dx) const {
289

10156
  if (static_cast<std::size_t>(ddv_dx.rows()) != state_->get_nv() ||
290
5078
      static_cast<std::size_t>(ddv_dx.cols()) != state_->get_ndx()) {
291
    throw_pretty("Invalid argument: "
292
                 << "ddv_dx has wrong dimension (it should be " +
293
                        std::to_string(state_->get_nv()) + "," +
294
                        std::to_string(state_->get_ndx()) + ")");
295
  }
296
5078
  data->ddv_dx = ddv_dx;
297
5078
}
298
299
template <typename Scalar>
300
36507
void ContactModelMultipleTpl<Scalar>::updateForceDiff(
301
    const boost::shared_ptr<ContactDataMultiple>& data, const MatrixXs& df_dx,
302
    const MatrixXs& df_du) const {
303
36507
  const std::size_t ndx = state_->get_ndx();
304
73014
  if (static_cast<std::size_t>(df_dx.rows()) !=
305

109521
          (compute_all_contacts_ ? nc_total_ : nc_) ||
306

36507
      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
73014
  if (static_cast<std::size_t>(df_du.rows()) !=
314

109521
          (compute_all_contacts_ ? nc_total_ : nc_) ||
315

36507
      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
36507
  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
36507
  std::size_t nc = 0;
328
36507
  typename ContactModelContainer::const_iterator it_m, end_m;
329
36507
  typename ContactDataContainer::const_iterator it_d, end_d;
330
36507
  if (compute_all_contacts_) {
331
107626
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
332
31430
        it_d = data->contacts.begin(), end_d = data->contacts.end();
333

183822
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
334
76196
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
335
76196
      const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
336


76196
      assert_pretty(it_m->first == it_d->first,
337
                    "it doesn't match the contact name between data and model");
338
76196
      const std::size_t nc_i = m_i->contact->get_nc();
339
76196
      if (m_i->active) {
340
76196
        const Eigen::Block<const MatrixXs> df_dx_i =
341
            df_dx.block(nc, 0, nc_i, ndx);
342
76196
        const Eigen::Block<const MatrixXs> df_du_i =
343
76196
            df_du.block(nc, 0, nc_i, nu_);
344

76196
        m_i->contact->updateForceDiff(d_i, df_dx_i, df_du_i);
345
      } else {
346
        m_i->contact->setZeroForceDiff(d_i);
347
      }
348
76196
      nc += nc_i;
349
    }
350
  } else {
351
15273
    for (it_m = contacts_.begin(), end_m = contacts_.end(),
352
5077
        it_d = data->contacts.begin(), end_d = data->contacts.end();
353

25469
         it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
354
10196
      const boost::shared_ptr<ContactItem>& m_i = it_m->second;
355
10196
      const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
356


10196
      assert_pretty(it_m->first == it_d->first,
357
                    "it doesn't match the contact name between data and model");
358
10196
      if (m_i->active) {
359
10196
        const std::size_t nc_i = m_i->contact->get_nc();
360
10196
        const Eigen::Block<const MatrixXs> df_dx_i =
361
            df_dx.block(nc, 0, nc_i, ndx);
362
10196
        const Eigen::Block<const MatrixXs> df_du_i =
363
10196
            df_du.block(nc, 0, nc_i, nu_);
364

10196
        m_i->contact->updateForceDiff(d_i, df_dx_i, df_du_i);
365
10196
        nc += nc_i;
366
      } else {
367
        m_i->contact->setZeroForceDiff(d_i);
368
      }
369
    }
370
  }
371
36507
}
372
373
template <typename Scalar>
374
8839
void ContactModelMultipleTpl<Scalar>::updateRneaDiff(
375
    const boost::shared_ptr<ContactDataMultiple>& data,
376
    pinocchio::DataTpl<Scalar>& pinocchio) const {
377
17678
  if (static_cast<std::size_t>(data->contacts.size()) !=
378
8839
      this->get_contacts().size()) {
379
    throw_pretty("Invalid argument: "
380
                 << "it doesn't match the number of contact datas and models");
381
  }
382
8839
  typename ContactModelContainer::const_iterator it_m, end_m;
383
8839
  typename ContactDataContainer::const_iterator it_d, end_d;
384
27813
  for (it_m = contacts_.begin(), end_m = contacts_.end(),
385
8839
      it_d = data->contacts.begin(), end_d = data->contacts.end();
386

46787
       it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
387
18974
    const boost::shared_ptr<ContactItem>& m_i = it_m->second;
388
18974
    const boost::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
389


18974
    assert_pretty(it_m->first == it_d->first,
390
                  "it doesn't match the contact name between data and model");
391
18974
    if (m_i->active) {
392
18974
      switch (m_i->contact->get_type()) {
393
11384
        case pinocchio::ReferenceFrame::LOCAL:
394
11384
          break;
395
7590
        case pinocchio::ReferenceFrame::WORLD:
396
        case pinocchio::ReferenceFrame::LOCAL_WORLD_ALIGNED:
397
7590
          pinocchio.dtau_dq += d_i->dtau_dq;
398
7590
          break;
399
      }
400
    }
401
  }
402
8839
}
403
404
template <typename Scalar>
405
boost::shared_ptr<ContactDataMultipleTpl<Scalar> >
406
70999
ContactModelMultipleTpl<Scalar>::createData(
407
    pinocchio::DataTpl<Scalar>* const data) {
408
  return boost::allocate_shared<ContactDataMultiple>(
409
70999
      Eigen::aligned_allocator<ContactDataMultiple>(), this, data);
410
}
411
412
template <typename Scalar>
413
const boost::shared_ptr<StateMultibodyTpl<Scalar> >&
414
426022
ContactModelMultipleTpl<Scalar>::get_state() const {
415
426022
  return state_;
416
}
417
418
template <typename Scalar>
419
const typename ContactModelMultipleTpl<Scalar>::ContactModelContainer&
420
459377
ContactModelMultipleTpl<Scalar>::get_contacts() const {
421
459377
  return contacts_;
422
}
423
424
template <typename Scalar>
425
102819
std::size_t ContactModelMultipleTpl<Scalar>::get_nc() const {
426
102819
  return nc_;
427
}
428
429
template <typename Scalar>
430
514944
std::size_t ContactModelMultipleTpl<Scalar>::get_nc_total() const {
431
514944
  return nc_total_;
432
}
433
434
template <typename Scalar>
435
763
std::size_t ContactModelMultipleTpl<Scalar>::get_nu() const {
436
763
  return nu_;
437
}
438
439
template <typename Scalar>
440
29693
const std::set<std::string>& ContactModelMultipleTpl<Scalar>::get_active_set()
441
    const {
442
29693
  return active_set_;
443
}
444
445
template <typename Scalar>
446
29693
const std::set<std::string>& ContactModelMultipleTpl<Scalar>::get_inactive_set()
447
    const {
448
29693
  return inactive_set_;
449
}
450
451
template <typename Scalar>
452
bool ContactModelMultipleTpl<Scalar>::getContactStatus(
453
    const std::string& name) const {
454
  typename ContactModelContainer::const_iterator it = contacts_.find(name);
455
  if (it != contacts_.end()) {
456
    return it->second->active;
457
  } else {
458
    std::cerr << "Warning: we couldn't get the status of the " << name
459
              << " contact item, it doesn't exist." << std::endl;
460
    return false;
461
  }
462
}
463
464
template <typename Scalar>
465
bool ContactModelMultipleTpl<Scalar>::getComputeAllContacts() const {
466
  return compute_all_contacts_;
467
}
468
469
template <typename Scalar>
470
4161
void ContactModelMultipleTpl<Scalar>::setComputeAllContacts(const bool status) {
471
4161
  compute_all_contacts_ = status;
472
4161
}
473
474
template <class Scalar>
475
1
std::ostream& operator<<(std::ostream& os,
476
                         const ContactModelMultipleTpl<Scalar>& model) {
477
1
  const auto& active = model.get_active_set();
478
1
  const auto& inactive = model.get_inactive_set();
479
1
  os << "ContactModelMultiple:" << std::endl;
480
1
  os << "  Active:" << std::endl;
481
1
  for (std::set<std::string>::const_iterator it = active.begin();
482
1
       it != active.end(); ++it) {
483
    const boost::shared_ptr<
484
        typename ContactModelMultipleTpl<Scalar>::ContactItem>& contact_item =
485
        model.get_contacts().find(*it)->second;
486
    if (it != --active.end()) {
487
      os << "    " << *it << ": " << *contact_item << std::endl;
488
    } else {
489
      os << "    " << *it << ": " << *contact_item << std::endl;
490
    }
491
  }
492
1
  os << "  Inactive:" << std::endl;
493
1
  for (std::set<std::string>::const_iterator it = inactive.begin();
494
1
       it != inactive.end(); ++it) {
495
    const boost::shared_ptr<
496
        typename ContactModelMultipleTpl<Scalar>::ContactItem>& contact_item =
497
        model.get_contacts().find(*it)->second;
498
    if (it != --inactive.end()) {
499
      os << "    " << *it << ": " << *contact_item << std::endl;
500
    } else {
501
      os << "    " << *it << ": " << *contact_item;
502
    }
503
  }
504
1
  return os;
505
}
506
507
}  // namespace crocoddyl