System scalar conversion refers to cloning a System templatized by one scalar type into an identical System that is templatized by a different scalar type.
For example, a MySystem<double>
could be cloned to create a MySystem<AutoDiffXd>
in order to compute the partial numerical derivatives.
See the default scalars for list of supported types.
This is a small example to illustrate the concept.
For a more thorough example, refer to the implementation of drake::systems::Linearize.
System::ToAutoDiffXd
. The scalar-converting copy constructor (described below) is intended for internal use only. For example: In the typical case, Drake users' Systems should be marked with the C++ keyword final
to prevent them from being subclassed. Whether or not the System is final
determines the best way to support scalar conversion.
In the examples below, we use MySystem
as the name of a System class being implemented by a Drake user.
final
For system marked with final
, the two examples below show how to enable system scalar conversion.
Example using drake::systems::LeafSystem as the base class:
Example using drake::systems::VectorSystem as the base class:
The relevant details of the examples are:
MySystem
is templated on a scalar type T
;MySystem
is marked final
;DRAKE_NO_COPY_...
disables the built-in copy and move constructors;MySystem
has whatever usual constructors make sense;MySystem
constructors must pass a SystemTypeTag
to their superclass constructor; if all MySystem
constructors delegate to a single one, this is easy because only that one needs to provide the SystemTypeTag
value;MySystem
has a public scalar-converting copy constructor;MySystem<U>
and delegates to another constructor;final
For a class hierarchy of Systems that support scalar conversion, a slightly different pattern is required.
The relevant details of the examples are:
final
classes like MySystemBase
must offer a protected constructor that takes a SystemScalarConverter as the first argument.MySystemDerived
must delegate to a base class protected constructor that takes a SystemScalarConverter, never to a public constructor without one.MySystemBase
and MySystemDerived
both have a public scalar-converting copy constructor;The framework's default implementation System scalar-type conversion only converts between a limited set of scalar types, as enumerated by the drake::systems::SystemScalarConverter::SystemScalarConverter(SystemTypeTag<S>) constructor documentation.
Systems may specialize their drake::systems::scalar_conversion::Traits to govern the supported scalar types. The recommended mechanism is to use drake::systems::scalar_conversion::NonSymbolicTraits or drake::systems::scalar_conversion::FromDoubleTraits.
The scalar-converting copy constructor uses the following form:
Here, U
is the source scalar type (to convert from), and T
the resulting scalar type (to convert into). For example, in the second line of
we are calling the constructor Foo<AutoDiffXd>::Foo(const Foo<double>&)
so we have U = double
and T = AutoDiffXd
. In other words, we start with a double
-valued object and use it to create an AutoDiffXd
-valued object.
In almost all cases, the implementation of the scalar-converting copy constructor should delegate to another regular constructor, rather than re-implementing its logic. For example, in the VectorSystem
-based example above we have:
The default constructor configures the System to have a input_size == 1
and output_size == 1
. The scalar-converting copy constructor delegates to the default constructor to re-use that logic by stating : MySystem<T>()
.
Without delegation, we would have to duplicate those arguments:
If the object being copied from (usually named other
) has any instance data or customizations, the scalar-converting copy constructor should propagate them from other
to this
. For example, in the LeafSystem
-based example above, we have:
In the typical case, no special effort is needed to create a Diagram that support scalar-type conversion. The Diagram does not even need to be templated on a scalar type T
.
Example using DiagramBuilder::BuildInto:
In this example, MyDiagram
will support the same scalar types as the Integrator. If any sub-system had been added that did not support, e.g., symbolic form, then the Diagram would also not support symbolic form.
By default, even subclasses of a Diagram<U>
will convert to a Diagram<T>
, discarding the diagram subclass details. For example, in the above sample code, MyDiagram::ToAutoDiffXd()
will return an object of runtime type Diagram<AutoDiffXd>
, not type MyDiagram<AutoDiffXd>
. (There is no such class as MyDiagram<AutoDiffXd>
anyway, because MyDiagram
is not templated.)
In the unusual case that the Diagram's subclass must be preserved during conversion, a drake::systems::SystemTypeTag should be used:
Example using DiagramBuilder::BuildInto along with a SystemTypeTag
: