Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions bindings/generated_docstrings/systems_analysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -4340,6 +4340,11 @@ See also:
Initialize(), AdvancePendingEvents(), SimulatorStatus,
set_monitor())""";
} AdvanceTo;
// Symbol: drake::systems::Simulator::EmplaceWithSharedContext
struct /* EmplaceWithSharedContext */ {
// Source: drake/systems/analysis/simulator.h
const char* doc = R"""()""";
} EmplaceWithSharedContext;
// Symbol: drake::systems::Simulator::GetCurrentWitnessTimeIsolation
struct /* GetCurrentWitnessTimeIsolation */ {
// Source: drake/systems/analysis/simulator.h
Expand Down Expand Up @@ -4445,11 +4450,6 @@ Returns ``status``:
See also:
AdvanceTo(), AdvancePendingEvents(), SimulatorStatus)""";
} Initialize;
// Symbol: drake::systems::Simulator::MakeWithSharedContext
struct /* MakeWithSharedContext */ {
// Source: drake/systems/analysis/simulator.h
const char* doc = R"""()""";
} MakeWithSharedContext;
// Symbol: drake::systems::Simulator::ResetStatistics
struct /* ResetStatistics */ {
// Source: drake/systems/analysis/simulator.h
Expand Down
21 changes: 12 additions & 9 deletions bindings/pydrake/multibody/inverse_kinematics_py_differential.cc
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,18 @@ void DefineDifferentialIkSystem(py::module_ m) {

using Class = DifferentialInverseKinematicsSystem;
cls // BR
.def(py::init([](py::object recipe, std::string_view task_frame,
py::object collision_checker, const DofMask& active_dof,
double time_step, double K_VX,
const SpatialVelocity<double>& Vd_TG_limit) {
return std::make_unique<Class>(
make_shared_ptr_from_py_object<Recipe>(recipe), task_frame,
make_shared_ptr_from_py_object<CollisionChecker>(collision_checker),
active_dof, time_step, K_VX, Vd_TG_limit);
}),
.def(
"__init__",
[](Class* self, py::object recipe, std::string_view task_frame,
py::object collision_checker, const DofMask& active_dof,
double time_step, double K_VX,
const SpatialVelocity<double>& Vd_TG_limit) {
new (self) Class(make_shared_ptr_from_py_object<Recipe>(recipe),
task_frame,
make_shared_ptr_from_py_object<CollisionChecker>(
collision_checker),
active_dof, time_step, K_VX, Vd_TG_limit);
},
py::arg("recipe"), py::arg("task_frame"),
py::arg("collision_checker"), py::arg("active_dof"),
py::arg("time_step"), py::arg("K_VX"), py::arg("Vd_TG_limit"),
Expand Down
40 changes: 22 additions & 18 deletions bindings/pydrake/systems/analysis_py.cc
Original file line number Diff line number Diff line change
Expand Up @@ -291,24 +291,28 @@ PYDRAKE_MODULE(analysis, m) {
auto cls = DefineTemplateClassWithDefault<Simulator<T>>(
m, "Simulator", GetPyParam<T>(), doc.Simulator.doc);
cls // BR
.def(py::init([](const System<T>& system, py::object py_context) {
// Handle the two cases for context ownership explicitly:
// 1. If py_context is None, create a new context and take ownership.
// 2. If py_context is provided, use the existing Python wrapper
// directly (it already owns the C++ object).
if (py_context.is_none()) {
std::unique_ptr<Context<T>> context_ptr =
system.CreateDefaultContext();
// Use take_ownership because we just created this context and need
// Python to own it. The unique_ptr is released, leaving the raw
// pointer with no owner until take_ownership establishes Python
// ownership.
py_context =
py::cast(context_ptr.release(), py_rvp::take_ownership);
}
return Simulator<T>::MakeWithSharedContext(
system, make_shared_ptr_from_py_object<Context<T>>(py_context));
}),
.def(
"__init__",
[](Simulator<T>* self, const System<T>& system,
py::object py_context) {
// Handle the two cases for context ownership explicitly:
// 1. If py_context is None, create a new context and take
// ownership.
// 2. If py_context is provided, use the existing Python wrapper
// directly (it already owns the C++ object).
if (py_context.is_none()) {
std::unique_ptr<Context<T>> context_ptr =
system.CreateDefaultContext();
// Use take_ownership because we just created this context and
// need Python to own it. The unique_ptr is released, leaving
// the raw pointer with no owner until take_ownership
// establishes Python ownership.
py_context =
py::cast(context_ptr.release(), py_rvp::take_ownership);
}
Simulator<T>::EmplaceWithSharedContext(self, system,
make_shared_ptr_from_py_object<Context<T>>(py_context));
},
py::arg("system"), py::arg("context") = py::none(),
// Keep alive, reference: `self` keeps `system` alive.
py::keep_alive<1, 2>(),
Expand Down
1 change: 1 addition & 0 deletions systems/analysis/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ drake_cc_googletest(
":implicit_euler_integrator",
":runge_kutta3_integrator",
":simulator",
"//common:scope_exit",
"//common/test_utilities:expect_throws_message",
"//common/test_utilities:is_dynamic_castable",
"//systems/analysis/test_utilities:controlled_spring_mass_system",
Expand Down
9 changes: 4 additions & 5 deletions systems/analysis/simulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ Simulator<T>::Simulator(std::unique_ptr<const System<T>> owned_system,
: Simulator(nullptr, std::move(owned_system), std::move(context), true) {}

template <typename T>
std::unique_ptr<Simulator<T>> Simulator<T>::MakeWithSharedContext(
const System<T>& system, std::shared_ptr<Context<T>> context) {
// This slightly odd spelling allows access to the private constructor.
return std::unique_ptr<Simulator<T>>(
new Simulator(&system, nullptr, std::move(context), false));
void Simulator<T>::EmplaceWithSharedContext(
Simulator<T>* self, const System<T>& system,
std::shared_ptr<Context<T>> context) {
new (self) Simulator(&system, nullptr, std::move(context), false);
}

template <typename T>
Expand Down
14 changes: 10 additions & 4 deletions systems/analysis/simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,14 +275,20 @@ class Simulator {
std::unique_ptr<Context<T>> context = nullptr);

#ifndef DRAKE_DOXYGEN_CXX
// (Internal use only) Makes a Simulator which accepts a context via shared
// pointer.
// (Internal use only) Emplaces a Simulator which accepts a context via
// shared pointer.
//
// The `self` parameter should point to uninitialized storage of at least
// sizeof(Simulator<T>) bytes. It is the calling code's responsibility to
// eventually destroy the Simulator<T> instance, using (for example)
// std::destroy_at.
//
// The shared pointer signature is useful for implementing pydrake memory
// management, because it permits supplying a custom deleter. The context is
// not *actually* shared. The simulator will modify it at will.
static std::unique_ptr<Simulator<T>> MakeWithSharedContext(
const System<T>& system, std::shared_ptr<Context<T>> context);
static void EmplaceWithSharedContext(Simulator<T>* self,
const System<T>& system,
std::shared_ptr<Context<T>> context);
#endif

// TODO(sherm1) Make Initialize() attempt to satisfy constraints.
Expand Down
19 changes: 13 additions & 6 deletions systems/analysis/test/simulator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "drake/common/autodiff.h"
#include "drake/common/drake_assert.h"
#include "drake/common/drake_copyable.h"
#include "drake/common/scope_exit.h"
#include "drake/common/test_utilities/expect_throws_message.h"
#include "drake/common/test_utilities/is_dynamic_castable.h"
#include "drake/common/text_logging.h"
Expand Down Expand Up @@ -814,9 +815,9 @@ GTEST_TEST(SimulatorTest, SecondConstructor) {
EXPECT_EQ(simulator.get_context().get_time(), 3.0);
}

// Tests the internal use (for Python) factory method that takes the context via
// shared pointer.
GTEST_TEST(SimulatorTest, SharedContextFactoryMethod) {
// Tests in-place construction factory method that takes the context via shared
// pointer. (This method is internal use only for Python.)
GTEST_TEST(SimulatorTest, SharedContextEmplacementFactoryMethod) {
// Create the spring-mass system and context.
analysis_test::MySpringMassSystem<double> spring_mass(1.0, 1.0, 0.0);
auto context = spring_mass.CreateDefaultContext();
Expand All @@ -825,9 +826,15 @@ GTEST_TEST(SimulatorTest, SharedContextFactoryMethod) {
context->SetTime(7.0);
std::shared_ptr<Context<double>> shared_context(std::move(context));

// Construct the simulator with the created context.
auto simulator = Simulator<double>::MakeWithSharedContext(
spring_mass, std::move(shared_context));
// Construct the simulator via placement new with the created context.
std::array<char, sizeof(Simulator<double>)> storage;
Simulator<double>* simulator =
reinterpret_cast<Simulator<double>*>(storage.data());
ScopeExit guard([&]() {
std::destroy_at(simulator);
});
Simulator<double>::EmplaceWithSharedContext(simulator, spring_mass,
std::move(shared_context));

// Verify that context values are equivalent.
EXPECT_EQ(simulator->get_context().get_time(), 7.0);
Expand Down