API reference¶
Tracks¶
Track geometry.
A track is a curve in the plane parameterized by arc length \(s \in [0, L]\), tracing the path of the wheel contact points. Tracks provide the angle \(\theta(s)\) of the track from the horizontal, the curvature \(\kappa(s) = \theta'(s)\) and its derivative \(\kappa'(s)\), and the position \((x(s), y(s))\), with \(x(0) = 0\) and \(y(L) = 0\).
Outside of \([0, L]\) tracks continue as straight lines at their end angles, so that geometry calculations near the ends of the track stay well defined.
Track
¶
Bases: ABC
Base class for tracks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
Scalar
|
total track length \(L\). |
required |
s_start
|
Scalar
|
location of the start pin, where the fronts of the cars start. |
required |
s_finish
|
Scalar
|
location of the finish line, with \(0 \le s_\mathrm{start} < s_\mathrm{finish} \le L\). |
required |
angle(s)
abstractmethod
¶
Angle of the track from the horizontal, \(\theta(s)\).
curvature(s)
abstractmethod
¶
Curvature of the track, \(\kappa(s) = \theta'(s)\).
curvature_derivative(s)
abstractmethod
¶
Derivative of the curvature with respect to arc length, \(\kappa'(s)\).
curvature_second_derivative(s)
abstractmethod
¶
Second derivative of the curvature with respect to arc length, \(\kappa''(s)\).
position(s)
abstractmethod
¶
Position \((x(s), y(s))\) of the track.
height(s)
¶
Height of the track, \(y(s)\).
SplineTrack
¶
Bases: Track
Track whose angle \(\theta(s)\) is a piecewise septic Hermite spline.
The spline is defined by the angle, the curvature, and the first two curvature derivatives at each knot, making \(\theta\), \(\kappa\), \(\kappa'\), and \(\kappa''\) continuous along the track. This smoothness matters for the simulation: a jump in \(\kappa'\) makes the car's acceleration jump, and a jump in \(\kappa''\) makes its derivative jump, and either one adds errors to the time integration that depend on exactly where the car is at each time step.
Specifying the curvature directly makes it easy to build exact straight sections
(\(\kappa = 0\)) and circular arcs (constant \(\kappa\)), with the curvature
derivatives left at zero. Use
interpolate to instead build a smooth
spline from the knot angles alone.
The position is integrated from the angle with Gauss-Legendre quadrature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
knots
|
ArrayLike
|
increasing arc length locations of the knots, starting at 0. The last knot is the track length. |
required |
angles
|
ArrayLike
|
track angle at each knot. |
required |
curvatures
|
ArrayLike
|
track curvature at each knot. |
required |
s_start
|
Scalar
|
location of the start pin. |
required |
s_finish
|
Scalar
|
location of the finish line. |
required |
curvature_derivatives
|
ArrayLike | None
|
derivative of the curvature at each knot, zero by default. |
None
|
curvature_second_derivatives
|
ArrayLike | None
|
second derivative of the curvature at each knot, zero by default. |
None
|
n_quad
|
int
|
number of Gauss-Legendre points used per spline segment to integrate the position. |
16
|
interpolate(knots, angles, s_start, s_finish, end_curvatures=(0.0, 0.0), n_quad=16)
classmethod
¶
Build the \(C^2\) cubic spline through the given knot angles.
The knot curvatures are chosen so that \(\kappa'\) is also continuous, with the curvatures at the two ends of the track fixed (clamped end conditions). The track takes the cubic's curvature derivatives at the knots. The cubic's second curvature derivative jumps at the knots, so the track uses their average there, which makes it slightly smoother than the cubic. It reproduces a single cubic exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
knots
|
ArrayLike
|
increasing arc length locations of the knots, starting at 0. |
required |
angles
|
ArrayLike
|
track angle at each knot. |
required |
s_start
|
Scalar
|
location of the start pin. |
required |
s_finish
|
Scalar
|
location of the finish line. |
required |
end_curvatures
|
tuple[Scalar, Scalar]
|
curvature at the start and end of the track. |
(0.0, 0.0)
|
n_quad
|
int
|
number of Gauss-Legendre points per spline segment. |
16
|
ramp_track(length, ramp_length, ramp_angle, radius, s_start, s_finish, easement=1 * INCH)
¶
Build the classic derby track: a straight ramp, a curve, and a flat run.
The curve is a circular arc joined to the straight sections by short easement curves. Over each easement the curvature ramps smoothly between zero and \(1/R\), following the quintic \(6u^5 - 15u^4 + 10u^3\) of the fraction \(u\) of the way along it, so that the curvature and its first two derivatives are continuous.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
float
|
total track length. |
required |
ramp_length
|
float
|
length of the straight ramp, from the top of the track to the start of the curve. |
required |
ramp_angle
|
float
|
downhill angle of the ramp from the horizontal (positive). |
required |
radius
|
float
|
radius of the circular arc in the curve. |
required |
s_start
|
float
|
location of the start pin. |
required |
s_finish
|
float
|
location of the finish line. |
required |
easement
|
float
|
length of each easement curve. |
1 * INCH
|
besttrack(length_ft=42)
¶
Approximate geometry of a BestTrack aluminum track.
Based on the manufacturer's published dimensions: a 48 in radius curve section 41 in long, 84 in straight sections and a 40 in stop section after the curve, the start pin 37 ft 9 in from the end of a 42 ft track, and racing distances (start pin to timer) of 358, 442, and 526 in for the 35, 42, and 49 ft tracks. The ramp angle is inferred by treating the whole curve section as a circular arc, \(41/48\) rad or about 49 degrees.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length_ft
|
int
|
nominal track length in feet: 35, 42, or 49. |
42
|
Cars¶
Car models.
A car is a rigid body riding on a rear and a front axle. Car
defines the essential parameters the simulation needs, as read-only properties.
Concrete car classes decide where those values come from:
SimpleCar stores each one directly as a trainable
parameter, while GeometricCar computes its mass, center
of gravity, and moment of inertia from a description of the car's geometry and
materials.
Positions are given in the body frame, with its origin at the rear axle center, its first axis pointing toward the front axle center, and its second axis perpendicular to that, pointing away from the track. All values are in SI units.
Car
¶
Bases: Module, ABC
Base class for cars.
Subclasses implement each of the abstract properties, returning a float64
tensor, except for the wheel counts, which are integers. The tensors may have
leading batch dimensions to describe several car designs at once; see
batch_shape. Because a car is a
torch.nn.Module, its trainable parameters are available
through car.parameters() for optimization.
cg
abstractmethod
property
¶
Position of the center of gravity relative to the rear axle, in the body
frame, \(\mathbf{x}_g\), as a tensor with shape (2,).
wheelbase
abstractmethod
property
¶
Distance between the rear and front axle centers, \(w\).
front_offset
abstractmethod
property
¶
Distance from the rear axle to the front of the car, along the body frame first axis, \(d\).
mass
abstractmethod
property
¶
Total mass of the car, including the wheels, \(M\).
body_inertia
abstractmethod
property
¶
Moment of inertia of the car about its center of gravity for pitching motion, \(I_b\).
n_rear_wheels
abstractmethod
property
¶
Number of rear wheels in contact with the track, \(n_r\).
n_front_wheels
abstractmethod
property
¶
Number of front wheels in contact with the track, \(n_f\).
rear_wheel_inertia
abstractmethod
property
¶
Moment of inertia of each rear wheel about its axle, \(I_r\).
front_wheel_inertia
abstractmethod
property
¶
Moment of inertia of each front wheel about its axle, \(I_f\).
rear_wheel_radius
abstractmethod
property
¶
Radius of the rear wheels, \(r_r\).
front_wheel_radius
abstractmethod
property
¶
Radius of the front wheels, \(r_f\).
rear_axle_radius
abstractmethod
property
¶
Radius of the rear axle, \(a_r\).
front_axle_radius
abstractmethod
property
¶
Radius of the front axle, \(a_f\).
rear_axle_friction
abstractmethod
property
¶
Coefficient of friction between the rear wheels and axle, \(\mu_r\).
front_axle_friction
abstractmethod
property
¶
Coefficient of friction between the front wheels and axle, \(\mu_f\).
frontal_area
abstractmethod
property
¶
Cross-sectional area of the car facing the air flow, \(A\).
drag_coefficient
abstractmethod
property
¶
Aerodynamic drag coefficient, \(C_d\).
rolling_friction
abstractmethod
property
¶
Rolling friction coefficient of the wheels on the track, \(c_r\).
PROPERTIES = ('cg', 'wheelbase', 'front_offset', 'mass', 'body_inertia', 'n_rear_wheels', 'n_front_wheels', 'rear_wheel_inertia', 'front_wheel_inertia', 'rear_wheel_radius', 'front_wheel_radius', 'rear_axle_radius', 'front_axle_radius', 'rear_axle_friction', 'front_axle_friction', 'frontal_area', 'drag_coefficient', 'rolling_friction')
class-attribute
instance-attribute
¶
Names of the essential car properties, in order.
batch_shape
property
¶
Batch shape of the car, broadcast from the shapes of its properties.
Every property may have leading batch dimensions, to describe several car
designs at once, as long as the shapes broadcast. The cg has an extra
trailing dimension of size 2. A single car has batch shape ().
summary()
¶
The current values of the essential car properties, by name, as plain python numbers for display. Use the properties themselves in calculations that need gradients.
check()
¶
Check that the car properties are physically meaningful.
Raises:
| Type | Description |
|---|---|
ValueError
|
if any property is out of range. |
SimpleCar
¶
Bases: Car
A car defined directly by its essential properties.
Each property except the wheel counts is stored as a float64
torch.nn.Parameter, so all of them are trainable by
default. To hold a property fixed during optimization, turn off its gradient,
e.g. car.mass_.requires_grad_(False). The parameters are stored with a
trailing underscore (mass_) and exposed through the read-only properties
defined by Car (mass).
All arguments are in SI units: multiply by the constants in
pwdsim.units to give them in customary units.
To describe a batch of car designs, give any of the arguments a leading batch
dimension, e.g. mass=torch.tensor([4.0, 5.0]) * OUNCE or a cg with shape
(nbatch, 2). Arguments without the batch dimension are shared by every car.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cg
|
tuple[Scalar, Scalar] | Tensor
|
center of gravity relative to the rear axle in the body frame,
\(\mathbf{x}_g\), as |
required |
wheelbase
|
Scalar
|
distance between the axle centers, \(w\). |
required |
front_offset
|
Scalar
|
distance from the rear axle to the front of the car, \(d\). |
required |
mass
|
Scalar
|
total mass including the wheels, \(M\). |
required |
body_inertia
|
Scalar
|
pitching moment of inertia about the center of gravity, \(I_b\). |
required |
rear_wheel_inertia
|
Scalar
|
moment of inertia of each rear wheel, \(I_r\). |
required |
front_wheel_inertia
|
Scalar
|
moment of inertia of each front wheel, \(I_f\). |
required |
rear_wheel_radius
|
Scalar
|
radius of the rear wheels, \(r_r\). |
required |
front_wheel_radius
|
Scalar
|
radius of the front wheels, \(r_f\). |
required |
rear_axle_radius
|
Scalar
|
radius of the rear axle, \(a_r\). |
required |
front_axle_radius
|
Scalar
|
radius of the front axle, \(a_f\). |
required |
rear_axle_friction
|
Scalar
|
rear axle friction coefficient, \(\mu_r\). |
required |
front_axle_friction
|
Scalar
|
front axle friction coefficient, \(\mu_f\). |
required |
frontal_area
|
Scalar
|
cross-sectional area, \(A\). |
required |
drag_coefficient
|
Scalar
|
drag coefficient, \(C_d\). |
required |
rolling_friction
|
Scalar
|
rolling friction coefficient, \(c_r\). |
required |
n_rear_wheels
|
int
|
number of rear wheels on the track, \(n_r\). |
2
|
n_front_wheels
|
int
|
number of front wheels on the track, \(n_f\). |
2
|
GeometricCar
¶
Bases: Car
A car whose mass, center of gravity, and moment of inertia come from its geometry.
The body is a Profile extruded through
thickness and made of a material of density body_density, with
inclusions of other materials replacing parts of
it, such as weights and voids. The wheels are point masses at the axle centers.
For each quantity \(q \in \{1, x, y, x^2 + y^2\}\),
which gives the mass, the center of gravity, and the pitching moment of
inertia about the center of gravity. The front offset is the front of the
profile. Inclusions are assumed to lie inside the body; the constraints in
pwdsim.constraints keep them there during optimization.
The frontal area and drag coefficient come from the profile too, through a
DragModel.
Any of the computed properties (mass, cg, body_inertia, front_offset,
frontal_area, drag_coefficient) can instead be given as a number, which
replaces the geometric value.
The geometry's parameters can be optimized by their dotted names, e.g.
"profile.heights" or "inclusions.0.position". The other properties are
stored as parameters like those of SimpleCar.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile
|
Profile
|
the side profile of the body. |
required |
inclusions
|
Iterable[Inclusion]
|
regions of other materials inside the body, listed from the rear of the car to the front. |
()
|
thickness
|
Scalar
|
thickness of the body, across the car. |
required |
body_density
|
Scalar
|
density of the body material, e.g.
|
required |
wheelbase
|
Scalar
|
distance between the axle centers, \(w\). |
required |
wheel_mass
|
Scalar
|
mass of each wheel, with its axle. |
required |
rear_wheel_inertia
|
Scalar
|
moment of inertia of each rear wheel, \(I_r\). |
required |
front_wheel_inertia
|
Scalar
|
moment of inertia of each front wheel, \(I_f\). |
required |
rear_wheel_radius
|
Scalar
|
radius of the rear wheels, \(r_r\). |
required |
front_wheel_radius
|
Scalar
|
radius of the front wheels, \(r_f\). |
required |
rear_axle_radius
|
Scalar
|
radius of the rear axle, \(a_r\). |
required |
front_axle_radius
|
Scalar
|
radius of the front axle, \(a_f\). |
required |
rear_axle_friction
|
Scalar
|
rear axle friction coefficient, \(\mu_r\). |
required |
front_axle_friction
|
Scalar
|
front axle friction coefficient, \(\mu_f\). |
required |
rolling_friction
|
Scalar
|
rolling friction coefficient, \(c_r\). |
required |
n_rear_wheels
|
int
|
number of rear wheels on the track, \(n_r\). |
2
|
n_front_wheels
|
int
|
number of front wheels on the track, \(n_f\). |
2
|
drag_model
|
DragModel | None
|
the model for the drag of the body and wheels. |
None
|
mass
|
Scalar | None
|
if given, replaces the computed mass. |
None
|
cg
|
tuple[Scalar, Scalar] | Tensor | None
|
if given, replaces the computed center of gravity. |
None
|
body_inertia
|
Scalar | None
|
if given, replaces the computed moment of inertia. |
None
|
front_offset
|
Scalar | None
|
if given, replaces the front of the profile. |
None
|
frontal_area
|
Scalar | None
|
if given, replaces the computed frontal area, \(A\). |
None
|
drag_coefficient
|
Scalar | None
|
if given, replaces the computed drag coefficient, \(C_d\). |
None
|
COMPUTED = ('mass', 'cg', 'body_inertia', 'front_offset', 'frontal_area', 'drag_coefficient')
class-attribute
instance-attribute
¶
The properties computed from the geometry, unless given.
overrides
property
¶
The computed properties that were replaced by given values.
mass_moments()
¶
The mass moments of the car: \(\int dm\), \(\int x\, dm\), \(\int y\, dm\), and \(\int (x^2 + y^2)\, dm\), from its geometry.
drag_breakdown()
¶
The drag of the body and wheels from the
DragModel, broken down by component.
Computed from the geometry even where frontal_area or drag_coefficient
are given as numbers.
check()
¶
Check the car properties and the geometry.
Raises:
| Type | Description |
|---|---|
ValueError
|
if any property is out of range. |
Geometry¶
Car body geometry.
The car body is a side profile extruded through a thickness: the region between a
flat bottom and a B-spline height profile, described by a
Profile. Inside it, rectangular regions of other
materials, such as weights and voids, replace the wood. A
Region is a general rectangle, and a
WeightPocket is a pocket drilled up from the
bottom with a weight in it.
Coordinates are in the car body frame: the origin at the rear axle center, \(x\) along the car toward the front axle, and \(y\) up. All values are in SI units.
PINE = 420.0
module-attribute
¶
Density of pine, the usual derby block wood, in kg/m³ (approximate).
BASSWOOD = 415.0
module-attribute
¶
Density of basswood, in kg/m³ (approximate).
TUNGSTEN = 19300.0
module-attribute
¶
Density of tungsten, in kg/m³. Tungsten alloy weights are a little lighter.
LEAD = 11340.0
module-attribute
¶
Density of lead, in kg/m³.
VOID = 0.0
module-attribute
¶
Density of an empty region.
BSpline
¶
A clamped, uniform B-spline basis on \([x_0, x_1]\).
The curve \(h(x) = \sum_i c_i N_i(x)\) starts at \(c_0\) and ends at \(c_{n-1}\), and lies within the range of its control values \(c_i\) (the convex hull property).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
float
|
start of the interval. |
required |
x1
|
float
|
end of the interval. |
required |
n
|
int
|
number of control values, at least |
required |
degree
|
int
|
polynomial degree. |
3
|
basis(x, derivative=0)
¶
The basis functions \(N_i(x)\), or their first derivatives \(N_i'(x)\), in a
trailing dimension of size n.
Points outside \([x_0, x_1]\) are clamped to the interval. The basis is
differentiable with respect to x.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float | Tensor
|
the points to evaluate at. |
required |
derivative
|
int
|
0 for the basis functions, 1 for their derivatives. |
0
|
greville()
¶
The Greville abscissae: where each control value has the most influence.
quadrature(points=5)
¶
Gauss-Legendre points and weights on each span between knots, exact for
polynomials of degree up to 2 * points - 1 on each span.
Moments
dataclass
¶
Integrals over an area: \(\int dA\), \(\int x\, dA\), \(\int y\, dA\), and \(\int (x^2 + y^2)\, dA\).
Profile
¶
Bases: Module
The side profile of the car body: the region between a flat bottom at \(y = y_b\) and the top \(y = y_b + h(x)\), for \(x\) from the rear to the front of the block.
The height \(h(x)\) is a cubic B-spline with control heights heights, a
parameter. Because the spline lies within the range of its control heights,
keeping the control heights between 0 and the block height keeps the whole body
inside the block; bounds gives those bounds
for optimize.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_rear
|
float
|
position of the rear of the block, relative to the rear axle (negative: behind it). |
required |
x_front
|
float
|
position of the front of the block, relative to the rear axle. |
required |
bottom
|
float
|
height of the bottom of the block relative to the axles, \(y_b\) (negative: below them). |
required |
heights
|
Sequence[float] | Tensor
|
control heights of the profile above the bottom. |
required |
max_height
|
float | None
|
height of the uncut block. Defaults to the largest control height. |
None
|
block(x_rear, x_front, bottom, height, n=8)
classmethod
¶
An uncut rectangular block with n control heights.
height(x)
¶
The height of the profile above the bottom, \(h(x)\).
slope(x)
¶
The slope of the profile, \(h'(x)\).
samples(points=16)
¶
Gauss-Legendre points along the profile, for integrating functions of the height and slope that aren't polynomials.
Returns:
| Type | Description |
|---|---|
tuple[Tensor, ...]
|
The points \(x\), their weights, and the height \(h\) and slope \(h'\) there. |
top(x)
¶
The \(y\) coordinate of the top of the profile, \(y_b + h(x)\).
moments()
¶
Area moments of the profile, exact for the cubic spline.
bounds(fixed=(), minimum=0.0)
¶
Bounds on the control heights for optimization: between minimum and
the block height.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fixed
|
Sequence[tuple[float, float]]
|
ranges of \(x\), as |
()
|
minimum
|
float
|
the smallest allowed control height. |
0.0
|
Rectangle
dataclass
¶
A rectangle of material inside the body.
Attributes:
| Name | Type | Description |
|---|---|---|
x0 |
Tensor
|
rear edge. |
x1 |
Tensor
|
front edge. |
y0 |
Tensor
|
bottom edge. |
y1 |
Tensor
|
top edge. |
density |
float
|
density of the material. |
depth |
Tensor | None
|
depth through the car, or |
Inclusion
¶
Region
¶
Bases: Inclusion
A rectangle of a single material, such as a weight or a void.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
Scalar
|
rear edge. |
required |
x1
|
Scalar
|
front edge. |
required |
y0
|
Scalar
|
bottom edge. |
required |
y1
|
Scalar
|
top edge. |
required |
density
|
float
|
required | |
depth
|
Scalar | None
|
depth through the car. |
None
|
WeightPocket
¶
Bases: Inclusion
A pocket drilled up into the car from its bottom, holding a weight at its top.
The pocket is centered at position along the car and is width long. It is
empty from the bottom of the car up to height - length, and filled with the
weight from there up to height.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
position
|
Scalar
|
position of the center of the pocket along the car. |
required |
width
|
Scalar
|
length of the pocket along the car. |
required |
height
|
Scalar
|
total height of the pocket above the bottom of the car. |
required |
length
|
Scalar
|
length of the weight, from the top of the pocket down. |
required |
material
|
float
|
density of the weight, e.g.
|
TUNGSTEN
|
depth
|
Scalar | None
|
depth of the pocket through the car. |
None
|
Aerodynamics¶
Aerodynamic drag from the car's profile.
A pinewood derby car is a blunt body, a little wider than it is tall, running close to the track at a Reynolds number of about \(5 \times 10^4\). Potential-flow (panel) methods predict no drag at all, and coupling them with a boundary layer calculation breaks down behind the blunt rear of the car, where the flow separates. So pwdsim estimates the drag with a semi-empirical component buildup, in the style of Hoerner's Fluid-Dynamic Drag: the drag area \(C_d A\) is the sum of
- skin friction on the wetted surface, from flat-plate laws;
- forebody pressure drag, from the blunt front face and the forward-facing slopes of the profile;
- base drag behind the blunt rear face and any steep rear-facing slopes, from Hoerner's correlation with the forebody drag;
- wheel drag, for the exposed, rotating wheels.
DragModel holds the empirical coefficients and
computes the drag of a Profile. The model ignores the
weight pockets, the track floor and guide rail, and the variation of the Reynolds
number during a run. It aims for the right trends and plausible magnitudes rather
than precision.
DragModel
dataclass
¶
A semi-empirical model of the drag of a car body from its side profile.
For a profile with height \(h(x)\), thickness \(t\), and length \(L\), with the height of the front face \(h_f\) and the rear face \(h_r\), the drag area is the sum of:
- Skin friction, \(C_f S_\text{wet}\). The wetted area is both sides, the top, and the bottom, \(S_\text{wet} = 2 \int h\, dx + t \int \sqrt{1 + h'^2}\, dx + t L\). The friction coefficient comes from the Reynolds number at a reference speed, \(\mathrm{Re} = V L / \nu\): the laminar Blasius law \(C_f = 1.328 / \sqrt{\mathrm{Re}}\), or above the transition Reynolds number the mixed law \(C_f = 0.074\, \mathrm{Re}^{-1/5} - 1742 / \mathrm{Re}\).
- Forebody pressure drag, \(t\, (C_\text{face} H_\text{blunt} +
C_\text{ramp} H_\text{ramp})\). Forward-facing slopes steeper than
separated_anglecount as blunt, like the front face, and those gentler thanattached_angleas ramps, with a smooth blend in between: \(H_\text{blunt} = h_f + \int s\, (-h')^+ dx\) and \(H_\text{ramp} = \int (1 - s)\, (-h')^+ dx\), where \(s\) is the blend. - Base drag, behind the rear face and any steep rear-facing slopes, with base area \(A_b = t\, (h_r + \int s\, (h')^+ dx)\). Hoerner's correlation gives the base drag coefficient from the forebody drag (friction and pressure) on the base area, \(C_{D,b} = 0.029 / \sqrt{C_{D,f}}\), so a cleaner forebody has a stronger base suction. Gently tapering tails stay attached and add only friction.
- Wheels, each a \(2r \times b\) rectangle facing the flow with drag
coefficient \(C_\text{wheel}\), as for exposed rotating wheels in ground
contact. The rear wheels run in the wake of the front wheels, so their drag
is scaled by
rear_wheel_factor, as for bodies in tandem.
The frontal area is \(A = t H_\text{max}\), with a smooth maximum of the height, and the drag coefficient is \(C_d = C_d A / A\).
Attributes:
| Name | Type | Description |
|---|---|---|
face |
float
|
drag coefficient of a blunt, sharp-edged face. |
ramp |
float
|
pressure drag coefficient of gently sloping forward-facing surfaces. |
base_constant |
float
|
the constant in Hoerner's base drag correlation. |
attached_angle |
float
|
slopes gentler than this keep the flow attached, in radians. |
separated_angle |
float
|
slopes steeper than this separate the flow, in radians. |
wheel |
float
|
drag coefficient of each wheel, on its frontal area. |
wheel_width |
float
|
width of the wheel treads. |
rear_wheel_factor |
float
|
fraction of their drag the rear wheels keep, in the wake of the front wheels (an estimate). |
reference_speed |
float
|
speed for the Reynolds number. |
kinematic_viscosity |
float
|
kinematic viscosity of air. |
transition_reynolds |
float
|
Reynolds number of transition to turbulence. |
sharpness |
float
|
sharpness of the smooth maximum height, in 1/m. |
friction_coefficient(length)
¶
The flat-plate skin friction coefficient for a body of this length.
breakdown(profile, thickness, wheel_radii=(0.0, 0.0), wheels=(0, 0))
¶
The drag of a car body, broken down by component.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile
|
Profile
|
the side profile of the body. |
required |
thickness
|
Tensor
|
the thickness of the body. |
required |
wheel_radii
|
tuple[Tensor, Tensor]
|
the radii of the rear and front wheels. |
(0.0, 0.0)
|
wheels
|
tuple[int, int]
|
the numbers of rear and front wheels. |
(0, 0)
|
Returns:
| Type | Description |
|---|---|
dict[str, Tensor]
|
The drag areas (in m²) of the |
dict[str, Tensor]
|
|
dict[str, Tensor]
|
|
Kinematics¶
Kinematics of a car riding on a track.
The configuration of a car on the track is described by the generalized coordinates \(q = (s, h_r, h_f)\): the arc length location \(s\) of the rear wheel contact point, and the lifts \(h_r\) and \(h_f\) of the rear and front axles off the track, along the track normal. The simulation always keeps the car on the track, \(h_r = h_f = 0\), but the lift directions define the normal forces (as constraint forces), so we need derivatives with respect to them.
This module computes the positions of points on the car along with their analytic derivatives: the first three derivatives with respect to \(s\), the first derivatives with respect to \(h_r\) and \(h_f\), and the mixed derivatives with respect to \(s\) and the lifts.
The track provides the unit tangent \(t = (\cos\theta, \sin\theta)\) and normal \(n = (-\sin\theta, \cos\theta)\), with \(t' = \kappa n\) and \(n' = -\kappa t\). An axle of radius \(r\) (plus lift \(h\)) rolling on the track has its center on the offset curve \(c(\sigma) = p(\sigma) + (r + h)\, n(\sigma)\).
The rear axle center is \(R(s) = c_r(s)\). The front axle contact point \(\sigma(s)\) is found implicitly as the location where the front axle center \(c_f(\sigma)\) is a distance \(w\) (the wheelbase) from the rear axle center. The car body frame then has its origin at \(R\) and first axis \(u = (c_f - R) / w\).
FIELDS = ('value', 'ds', 'dss', 'dsss', 'dhr', 'dhf', 'dhr_ds', 'dhf_ds')
module-attribute
¶
Names of the fields of Derivatives.
Derivatives
dataclass
¶
A quantity and its analytic derivatives with respect to the generalized coordinates, evaluated at a configuration.
For vector quantities (points) each field has a trailing dimension of size 2.
Attributes:
| Name | Type | Description |
|---|---|---|
value |
Tensor
|
the quantity itself. |
ds |
Tensor
|
first derivative with respect to \(s\). |
dss |
Tensor
|
second derivative with respect to \(s\). |
dsss |
Tensor
|
third derivative with respect to \(s\). |
dhr |
Tensor
|
first derivative with respect to the rear lift \(h_r\). |
dhf |
Tensor
|
first derivative with respect to the front lift \(h_f\). |
dhr_ds |
Tensor
|
mixed derivative with respect to \(h_r\) and \(s\). |
dhf_ds |
Tensor
|
mixed derivative with respect to \(h_f\) and \(s\). |
TrackPoint
dataclass
¶
The track at a contact point.
Attributes:
| Name | Type | Description |
|---|---|---|
location |
Tensor
|
arc length location of the point. |
angle |
Tensor
|
track angle \(\theta\). |
curvature |
Tensor
|
curvature \(\kappa\). |
dcurvature |
Tensor
|
curvature derivative \(\kappa'\). |
ddcurvature |
Tensor
|
curvature second derivative \(\kappa''\). |
Configuration
¶
The configuration of a car on the track, with derivatives.
Created by CarKinematics.evaluate.
Points on the car body are available through
point, with shortcuts for the
important ones.
Attributes:
| Name | Type | Description |
|---|---|---|
rear_axle |
center of the rear axle, \(R\). |
|
axis |
unit vector along the body frame first axis, \(u\). |
|
front_contact |
arc length location of the front wheel contact, \(\sigma\). |
|
pitch |
pitch angle of the car body, \(\phi\), the angle of \(u\) from the horizontal. |
|
rear_track |
the track at the rear wheel contact, \(s\). |
|
front_track |
the track at the front wheel contact, \(\sigma\). |
front_axle
property
¶
Center of the front axle.
cg
property
¶
Center of gravity of the car.
front
property
¶
Front of the car, the point used for the start and finish.
point(xi, eta)
¶
A point fixed to the car body, at body frame coordinates \((\xi, \eta)\).
The point is \(P = R + \xi u + \eta\, \mathrm{rot}(u)\), which is linear in \(R\) and \(u\), so its derivatives follow directly from theirs.
CarKinematics
¶
Positions of a car on a track, with analytic derivatives.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
track
|
Track
|
the track. |
required |
car
|
Car
|
the car. Car properties with batch dimensions broadcast against the trailing dimensions of the arc length locations. |
required |
newton_iterations
|
int
|
number of Newton iterations used to solve for the front wheel contact location and the starting position. |
3
|
evaluate(s, h_r=0.0, h_f=0.0)
¶
The configuration of the car with its rear wheel contact at s.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s
|
float | Tensor
|
arc length location of the rear wheel contact. |
required |
h_r
|
float | Tensor
|
lift of the rear axle off the track. |
0.0
|
h_f
|
float | Tensor
|
lift of the front axle off the track. |
0.0
|
start_position()
¶
Rear wheel contact location with the front of the car at the start pin.
The front of the car touches the start line: the line through the track at \(s_\mathrm{start}\) along the track normal.
Physics¶
Physics terms for the equation of motion.
The equation of motion comes from the Lagrangian of the car with non-conservative forces from a Rayleigh dissipation function. Its contributions are split into modular terms of three kinds:
KineticTerm: kinetic energy,PotentialTerm: potential energy,DissipativeTerm: Rayleigh dissipation.
Each term supplies its contribution along with the analytic derivatives the
simulation needs, with respect to the generalized coordinates \(q = (s, h_r, h_f)\)
(see pwdsim.kinematics). Terms can be switched on and off
with their enabled flag.
Kinetic energy is expressed through rates: quantities \(\rho = J(q) \cdot \dot{q}\) that are linear in the generalized velocities, such as the components of the velocity of the center of gravity or the spin rate of a wheel. A rate with weight \(m\) contributes \(T = \tfrac{1}{2} m \rho^2\), and its Euler-Lagrange inertial force along coordinate \(i\) is \(m J_i \dot{\rho}\), with \(\dot\rho = J_s \ddot{s} + J_s' \dot{s}^2\) on the track. So a kinetic term only provides \(J_s\) and its derivatives with respect to \(s\), and the lift components \(J_{h_r}\) and \(J_{h_f}\) and their derivatives with respect to \(s\).
Friction that is proportional to the normal force at an axle has the Rayleigh
dissipation function \(F = c N |\rho|\), for a coefficient \(c\) and a rate \(\rho\).
These terms are expressed as NormalFriction
contributions, since the normal forces are themselves unknowns solved for along
with the acceleration.
REAR = 0
module-attribute
¶
Index of the rear axle.
FRONT = 1
module-attribute
¶
Index of the front axle.
Environment
dataclass
¶
Global parameters of the simulation.
Attributes:
| Name | Type | Description |
|---|---|---|
g |
float
|
gravitational acceleration. |
rho |
float
|
air density. |
Rate
dataclass
¶
A rate \(\rho = J \cdot \dot{q}\) contributing \(T = \tfrac{1}{2} m \rho^2\).
Rates may be vectors, with the components in a trailing dimension, which contribute \(T = \tfrac{1}{2} m |\rho|^2\). Each field has that trailing dimension, even for a scalar rate (size 1).
Attributes:
| Name | Type | Description |
|---|---|---|
weight |
Tensor
|
the weight \(m\), e.g. a mass or a moment of inertia. |
ds |
Tensor
|
\(J_s\), the rate per unit \(\dot{s}\). |
dss |
Tensor
|
\(\partial J_s / \partial s\). |
dsss |
Tensor
|
\(\partial^2 J_s / \partial s^2\). |
dhr |
Tensor
|
\(J_{h_r}\), the rate per unit \(\dot{h}_r\). |
dhf |
Tensor
|
\(J_{h_f}\), the rate per unit \(\dot{h}_f\). |
dhr_ds |
Tensor
|
\(\partial J_{h_r} / \partial s\). |
dhf_ds |
Tensor
|
\(\partial J_{h_f} / \partial s\). |
of(weight, quantity, scalar=False)
classmethod
¶
The rate of change of a quantity with known derivatives, e.g. a point or an angle. Scalar quantities get a trailing dimension of size 1.
Potential
dataclass
¶
A potential energy \(V(q)\) and its derivatives.
Attributes:
| Name | Type | Description |
|---|---|---|
value |
Tensor
|
\(V\). |
ds |
Tensor
|
\(\partial V / \partial s\). |
dss |
Tensor
|
\(\partial^2 V / \partial s^2\). |
dhr |
Tensor
|
\(\partial V / \partial h_r\). |
dhf |
Tensor
|
\(\partial V / \partial h_f\). |
dhr_ds |
Tensor
|
\(\partial^2 V / \partial h_r \partial s\). |
dhf_ds |
Tensor
|
\(\partial^2 V / \partial h_f \partial s\). |
Dissipation
dataclass
¶
Derivatives of a Rayleigh dissipation function \(F(q, \dot{q})\) that doesn't depend on the normal forces, on the track, where \(\dot q = (v, 0, 0)\).
The generalized dissipative force is \(-\partial F / \partial \dot{q}\).
Attributes:
| Name | Type | Description |
|---|---|---|
s |
Tensor
|
\(D_s = \partial F / \partial \dot{s}\). |
s_ds |
Tensor
|
\(\partial D_s / \partial s\). |
s_dv |
Tensor
|
\(\partial D_s / \partial v\). |
hr |
Tensor
|
\(D_{h_r} = \partial F / \partial \dot{h}_r\). |
hr_ds |
Tensor
|
\(\partial D_{h_r} / \partial s\). |
hr_dv |
Tensor
|
\(\partial D_{h_r} / \partial v\). |
hf |
Tensor
|
\(D_{h_f} = \partial F / \partial \dot{h}_f\). |
hf_ds |
Tensor
|
\(\partial D_{h_f} / \partial s\). |
hf_dv |
Tensor
|
\(\partial D_{h_f} / \partial v\). |
NormalFriction
dataclass
¶
Friction proportional to the normal force \(N\) at an axle, with the Rayleigh dissipation function \(F = c N |\rho|\) for a rate \(\rho\).
The sign of the rate is regularized as \(\mathrm{sgn}(\rho) \approx \mathrm{sgn}(J_s) \tanh(v / v_\mathrm{reg})\) on the track, which keeps the equation of motion smooth as the car starts from rest.
Attributes:
| Name | Type | Description |
|---|---|---|
axle |
int
|
|
coefficient |
Tensor
|
the coefficient \(c\). |
rate |
Derivatives
|
the quantity whose rate of change is \(\rho\), with its derivatives. |
regularization |
float
|
the velocity \(v_\mathrm{reg}\). |
PhysicsTerm
¶
Bases: Module, ABC
Base class for the terms of the equation of motion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
whether the term contributes to the equation of motion. |
True
|
name = 'term'
class-attribute
instance-attribute
¶
Name of the term, used to switch it on and off in a simulation.
KineticTerm
¶
Bases: PhysicsTerm
A kinetic energy contribution, made up of rates.
rates(config, car, env)
abstractmethod
¶
The rates making up this kinetic energy.
PotentialTerm
¶
Bases: PhysicsTerm
A potential energy contribution.
potential(config, car, env)
abstractmethod
¶
The potential energy and its derivatives.
DissipativeTerm
¶
Bases: PhysicsTerm
A Rayleigh dissipation function contribution.
A term provides the part of its dissipation that doesn't depend on the normal
forces through dissipation, and
friction proportional to the normal forces through
frictions.
Gravity
¶
Translation
¶
Bases: KineticTerm
Kinetic energy of the car moving with its center of gravity, \(T = \tfrac{1}{2} M |\dot{\mathbf{x}}_g|^2\).
BodyRotation
¶
WheelSpin
¶
Bases: KineticTerm
Kinetic energy of the spinning wheels,
\(T = \tfrac{1}{2} n_r I_r \dot\Psi_r^2 + \tfrac{1}{2} n_f I_f \dot\Psi_f^2\),
for wheels rolling without slip (see
wheel_angles).
Drag
¶
Bases: DissipativeTerm
Aerodynamic drag at the center of gravity, a force of \(\tfrac{1}{2} \rho C_d A |\dot{\mathbf{x}}_g|^2\) opposing its motion.
The Rayleigh dissipation function is \(F = \tfrac{1}{6} \rho C_d A |\dot{\mathbf{x}}_g|^3\).
AxleFriction
¶
Bases: DissipativeTerm
Friction between the wheels and their axles.
Friction at an axle of radius \(a\) with normal load \(N\) exerts a torque \(\mu N a\) opposing the spin of the wheels relative to the car body, so \(F = \mu N a |\dot\Psi - \dot\phi|\) at each axle. The axle load is taken to be the normal force at the wheel contact, neglecting the weight of the wheels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
regularization
|
float
|
velocity scale \(v_\mathrm{reg}\) of the regularized sign of the friction force. |
0.001
|
enabled
|
bool
|
whether the term contributes to the equation of motion. |
True
|
RollingFriction
¶
Bases: DissipativeTerm
Rolling resistance of the wheels on the track.
A force \(c_r N\) opposes the motion of each contact point along the track, so \(F = c_r N |\dot c|\) for the contact location \(c\) (\(s\) at the rear, \(\sigma\) at the front).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
regularization
|
float
|
velocity scale \(v_\mathrm{reg}\) of the regularized sign of the friction force. |
0.001
|
enabled
|
bool
|
whether the term contributes to the equation of motion. |
True
|
rear_contact(config)
¶
Location of the rear wheel contact, \(s\), with its derivatives.
wheel_angles(config, car)
¶
Rotation angles of the rear and front wheels, with their derivatives.
A wheel of radius \(r\) rolling without slip on the track, with its contact at arc length \(c\), turns through the angle \(\Psi(c) = -(c - r\theta(c)) / r\): its center moves \(1 - r\kappa\) times as fast as the contact point, along the offset curve. The rear contact is at \(s\) and the front contact at \(\sigma(q)\), which gives the derivatives by the chain rule. The angles are absolute: they include the pitching of the car.
simple_physics()
¶
Gravity and the translational kinetic energy of the car only: a car rolling without friction or drag, with no rotational inertia.
full_physics()
¶
All of the physics terms, enabled.
default_physics()
¶
The physics terms a simulation uses by default: all of them.
Simulation¶
Forward simulation of a car running down a track.
The state of the car is \(x = (s, v)\), the location of the rear wheel contact along the track and its rate \(v = \dot{s}\). The enabled physics terms are assembled into the equation of motion
which gives the acceleration \(a = \ddot{s}\). pyzag integrates the resulting ODE \(\dot{x} = (v, a)\) in time, with gradients of the results with respect to the car parameters from the adjoint method.
LiftOffWarning
¶
Bases: UserWarning
A wheel lifted off the track: the normal force at an axle went negative.
DidNotFinishWarning
¶
Bases: UserWarning
The car did not reach the finish line in the simulated time.
LinearSystem
dataclass
¶
The equations of motion on the track as a linear system \(K x = b\) for \(x = (a, N_r, N_f)\), along with the derivatives of \(K\) and \(b\) with respect to \(s\) and \(v\).
The first row is the equation of motion along \(s\) and the others give the normal forces, the constraint forces along the lifts. Friction proportional to the normal forces couples them.
Attributes:
| Name | Type | Description |
|---|---|---|
K |
Tensor
|
the matrix, with shape |
b |
Tensor
|
the right hand side, with shape |
K_ds |
Tensor
|
\(\partial K / \partial s\). |
b_ds |
Tensor
|
\(\partial b / \partial s\). |
K_dv |
Tensor
|
\(\partial K / \partial v\). |
b_dv |
Tensor
|
\(\partial b / \partial v\). |
mass |
Tensor
|
the effective mass \(M(s) = \sum m J_s \cdot J_s\). |
potential |
Tensor
|
the potential energy. |
dissipation |
Tensor
|
the dissipative force along \(s\) that doesn't depend on the normal forces, \(D^0_s\). |
friction |
Tensor
|
the friction coefficients along \(s\), \(c_k\), so the total
dissipative force along \(s\) is \(D^0_s + \sum_k c_k N_k\), with shape
|
EquationsOfMotion
¶
Assembles the enabled physics terms into the equations of motion.
The equation of motion along \(s\) is
and the normal forces, the constraint forces along the lifts, are
where the \(N_k\) terms come from friction proportional to the normal forces.
Together these are a linear system for \((a, N_r, N_f)\) at each state; see
system.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kinematics
|
CarKinematics
|
the car on the track. |
required |
terms
|
Iterable[PhysicsTerm]
|
the physics terms. Disabled terms are skipped. |
required |
env
|
Environment
|
the global parameters. |
required |
system(s, v)
¶
Assemble the linear system for \((a, N_r, N_f)\) at the states \((s, v)\).
solve(s, v)
¶
The acceleration \(a\) and the normal forces \((N_r, N_f)\), in a trailing dimension.
acceleration(s, v)
¶
The acceleration \(a = \ddot{s}\).
acceleration_and_jacobian(s, v)
¶
The acceleration \(a\) and its derivatives \(\partial a / \partial s\) and \(\partial a / \partial v\).
Differentiating \(K x = b\) gives \(x_{,s} = K^{-1}(b_{,s} - K_{,s} x)\), and likewise for \(v\).
normal_forces(s, v)
¶
Normal forces at the rear and front axles, in a trailing dimension.
The normal forces are the constraint forces holding the axle lifts at zero, from the Euler-Lagrange equations along \(h_r\) and \(h_f\). A positive force pushes the car away from the track.
energy(s, v)
¶
Kinetic and potential energy.
power(s, v)
¶
Power dissipated by the dissipative terms, \(v D_s\).
by_term(s, v)
¶
The contribution of each enabled term, by name: the kinetic energy of a kinetic term, the potential energy of a potential term, and the power dissipated by a dissipative term.
The friction terms use the normal forces from the full equations of motion,
so the dissipated powers add up to
power.
CarODE
¶
Bases: Module
The equation of motion as a pyzag ODE, \(\dot{x} = (v, a)\) for \(x = (s, v)\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
car
|
Car
|
the car, registered as a submodule so pyzag finds its parameters. |
required |
equations
|
EquationsOfMotion
|
the equations of motion. |
required |
Simulation
¶
Bases: Module
A car running down a track.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
track
|
Track
|
the track. |
required |
car
|
Car
|
the car. Cars with batch dimensions simulate several designs at once. |
required |
physics
|
Iterable[PhysicsTerm] | None
|
the physics terms, by default all of the implemented terms. |
None
|
env
|
Environment | None
|
the global parameters. |
None
|
dt
|
float
|
time step. |
0.0001
|
duration
|
float
|
total simulated time, which must be long enough for the car to finish. |
4.0
|
integrator
|
str
|
time integration scheme, |
'backward-euler'
|
block_size
|
int
|
number of time steps pyzag solves together. |
1000
|
batch_shape
property
¶
Batch shape of the simulation, (1,) for a single car.
enable(*names)
¶
Switch on the named physics terms.
disable(*names)
¶
Switch off the named physics terms.
forward(adjoint=True)
¶
Simulate the run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adjoint
|
bool
|
use the adjoint method for gradients. Otherwise gradients come from automatic differentiation through the time steps, which takes much more memory. |
True
|
Returns:
| Type | Description |
|---|---|
Run
|
The results of the run. |
Run
¶
The results of a simulated run.
Quantities are per car in the batch, with the time steps in the leading dimension for time series. Scalar results like the finish time are differentiable with respect to the car parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulation
|
Simulation
|
the simulation that produced the run. |
required |
equations
|
EquationsOfMotion
|
the equations of motion the run was simulated with. Results are computed with these, so switching physics terms on or off after the run doesn't change them. |
required |
times
|
Tensor
|
the times, with shape |
required |
states
|
Tensor
|
the states \((s, v)\), with shape |
required |
equations
property
¶
The equations of motion the run was simulated with.
The results of a run are computed from its states and the car's parameters, so they can be differentiated with respect to those parameters. If the parameters change after the run, for example in an optimization, the results would be wrong, so this raises an error instead.
s
property
¶
Location of the rear wheel contact along the track.
v
property
¶
Rate of the rear wheel contact along the track, \(\dot{s}\).
finished
property
¶
Whether each car crossed the finish line.
finish_time
property
¶
Time when the front of the car crosses the finish line, NaN for cars that don't finish.
finish_speed
property
¶
Speed of the center of gravity as the car crosses the finish line.
max_speed
property
¶
Maximum speed of the center of gravity before the finish.
min_normal_force
property
¶
Minimum normal force at the rear and front axles before the finish, in a trailing dimension. Negative values mean a wheel lifted off the track.
configuration()
¶
The car configuration at every time step.
speed()
¶
Speed of the car's center of gravity.
normal_forces()
¶
Normal forces at the rear and front axles, in a trailing dimension.
smooth_min_normal_force(sharpness=500.0)
¶
A smooth version of the minimum normal force over both axles before the finish, for use as an optimization constraint.
Uses the Kreisselmeier-Steinhauser aggregate \(-\frac{1}{\rho} \log \sum_i e^{-\rho N_i}\) over the time steps and axles. It is never larger than the exact minimum, and at most \(\ln(n) / \rho\) smaller for \(n\) values, so it errs on the safe side. Unlike the exact minimum, it's differentiable everywhere.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sharpness
|
float
|
the parameter \(\rho\), in 1/N. Larger values follow the exact minimum more closely but make the aggregate less smooth. |
500.0
|
energy()
¶
The energy budget at each time step.
Returns the kinetic and potential energy, their sum (the mechanical energy), the energy dissipated so far by drag and friction, and the total of the mechanical and dissipated energy, which stays constant up to the error of the time integration.
energy_by_term()
¶
The energy of each physics term at each time step, by name.
Kinetic and potential terms give their energy. Dissipative terms give the
energy they have dissipated so far. The kinetic terms add up to the kinetic
energy of energy, and likewise for the
potential and dissipated energy.
check()
¶
Warn if a car didn't finish or lifted a wheel off the track.
Optimization¶
Design optimization.
optimize tunes chosen car parameters to minimize
the finish time, subject to bounds and constraints. It uses SciPy's trust-constr
method, a trust region interior point method that builds a BFGS approximation to the
Hessian from the gradients, which come from the adjoint method.
The optimizer works with the design variables scaled to \([0, 1]\) between their bounds, \(p = l + x (u - l)\), which puts parameters of very different sizes on an equal footing.
BoundValue = float | Sequence[float] | torch.Tensor
module-attribute
¶
A bound for a design variable: a scalar or one value per element.
Bounds = tuple[BoundValue, BoundValue]
module-attribute
¶
Lower and upper bounds for a design variable.
FEASIBLE = 1e-06
module-attribute
¶
The largest constraint violation, in the scaled units of each constraint, for a design to count as feasible when checking whether the optimization has stalled.
DEFAULT_OPTIONS = {'trust-constr': {'maxiter': 50, 'gtol': 0.001, 'xtol': 0.0001, 'initial_barrier_parameter': 0.001}, 'SLSQP': {'maxiter': 50, 'ftol': 1e-06}}
module-attribute
¶
Default optimizer settings for each method.
Variable
dataclass
¶
A design variable: a car parameter with bounds.
Elements whose lower and upper bounds are equal are fixed at that value, so
only some elements of a parameter can be tuned, e.g. one component of cg.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
name of the parameter on the car, e.g. |
parameter |
Parameter
|
the parameter. |
lower |
Tensor
|
lower bound, with the shape of the parameter. |
upper |
Tensor
|
upper bound, with the shape of the parameter. |
OptimizationResult
dataclass
¶
The results of an optimization.
Attributes:
| Name | Type | Description |
|---|---|---|
success |
bool
|
whether the optimizer converged. |
message |
str
|
the optimizer's description of how it finished. |
car |
Car
|
the optimized car, the same object that was optimized. |
initial_car |
Car
|
a copy of the car before the optimization. |
initial |
dict[str, Tensor]
|
the design variables before, in SI units, by parameter name. |
final |
dict[str, Tensor]
|
the design variables after, in SI units, by parameter name. |
initial_objective |
float
|
the objective before (by default the finish time in ms). |
final_objective |
float
|
the objective after. |
constraints |
dict[str, Tensor]
|
the values of the constraints at the optimum, by name, in the units of each constraint. |
history |
list[dict]
|
the objective, design variables, and constraint values at each iteration. |
scipy_result |
OptimizeResult | None
|
the full result from |
summary(units=None)
¶
A table of the design variables before and after, and the objective.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
units
|
Mapping[str, tuple[float, str]] | None
|
optional units to report each variable in, by parameter name, as
|
None
|
DesignProblem
¶
The problem of minimizing an objective over some car parameters, subject to bounds and constraints.
Most users will call optimize instead, which
sets up the problem and solves it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulation
|
Simulation
|
the simulation of the car on the track, with its physics. |
required |
variables
|
Iterable[str] | Mapping[str, Bounds | None]
|
the car parameters to tune, as a list of names (with default
bounds) or a dict of names and bounds. See
|
required |
constraints
|
Iterable[Constraint]
|
constraints on the design. |
()
|
lift_off_tolerance
|
float | None
|
the smallest allowed normal force on the wheels, in
newtons, for the |
0.0
|
objective
|
Callable[[Car, Run], Tensor] | None
|
the function of the car and its run to minimize. By default the finish time, in milliseconds. |
None
|
size
property
¶
Number of scalar design variables.
to_x()
¶
The current design variables, scaled to \([0, 1]\) between their bounds.
set_x(x)
¶
Set the car parameters from the scaled design variables.
evaluate(x)
¶
Simulate the design at x and compute the objective, the constraints,
and their gradients. The results are cached, so asking for the objective
and constraints at the same x runs one simulation.
violation(x)
¶
The largest violation of any constraint at x, in the scaled units of
each constraint.
solve(method='trust-constr', options=None, stall_tolerance=0.01, stall_iterations=5)
¶
Run the optimizer from the current design.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'trust-constr'
|
options
|
Mapping | None
|
optimizer settings, passed to |
None
|
stall_tolerance
|
float | None
|
stop once the objective (the finish time in ms, by
default) has improved by less than this over the last
|
0.01
|
stall_iterations
|
int
|
the number of iterations to look back over. |
5
|
Returns:
| Type | Description |
|---|---|
OptimizationResult
|
The results. The optimum is also written into the car. |
optimize(simulation, variables, constraints=(), lift_off_tolerance=0.0, objective=None, method='trust-constr', options=None, stall_tolerance=0.01, stall_iterations=5)
¶
Tune car parameters to minimize the finish time.
result = pwdsim.optimize(
pwdsim.Simulation(track, car),
variables={"cg": ((0.0, 0.1 * INCH), (4.0 * INCH, 1.0 * INCH)), "mass": None},
constraints=pwdsim.constraints.bsa_rules(),
)
print(result.summary())
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulation
|
Simulation
|
the simulation of the car on the track, with its physics and time step. The car must be a single car, not a batch. |
required |
variables
|
Iterable[str] | Mapping[str, Bounds | None]
|
the car parameters to tune; every other parameter stays fixed.
Either a list of names, or a dict of names and their bounds. Names are
the car's parameter names, and for a
|
required |
constraints
|
Iterable[Constraint]
|
constraints on the design, e.g. from
|
()
|
lift_off_tolerance
|
float | None
|
the smallest allowed normal force on the wheels, in
newtons, keeping them on the track. Zero by default; a positive value
gives a more robust design. |
0.0
|
objective
|
Callable[[Car, Run], Tensor] | None
|
the function of the car and its run to minimize. By default the finish time, in milliseconds. |
None
|
method
|
str
|
the SciPy method, |
'trust-constr'
|
options
|
Mapping | None
|
optimizer settings passed to |
None
|
stall_tolerance
|
float | None
|
stop once the objective has improved by less than this
(in ms, for the default objective) over the last |
0.01
|
stall_iterations
|
int
|
the number of iterations to look back over. |
5
|
Returns:
| Type | Description |
|---|---|
OptimizationResult
|
The results. The optimum is also written into the car. |
Constraints¶
Constraints for design optimization.
A Constraint bounds a differentiable function of
the car and, optionally, of its run: lower <= fun(car, run) <= upper. This module
provides the standard ones:
lift_off: keep the wheels on the track.optimizeincludes it by default.cg_between_axles: keep the center of gravity between the axles.max_mass: a maximum mass.bsa_rules: the common Scout race rules.
For a GeometricCar there are also constraints on its
inclusions (weights and voids):
regions_inside_body: inside the body.regions_within_thickness: no deeper than the body is thick.regions_dont_overlap: not overlapping each other.regions_avoid_axles: clear of the axle slots.
The bounding box of the body, the uncut block, is handled by bounds on its control
heights from Profile.bounds.
Constraint
dataclass
¶
A constraint lower <= fun(car, run) <= upper on a car design.
Constraints on the car alone, which don't use the run, are cheap: their gradients don't need the adjoint method.
Attributes:
| Name | Type | Description |
|---|---|---|
fun |
Callable[[Car, Run], Tensor]
|
a differentiable function of the car and its run, returning a scalar or a tensor of values, each of which is constrained. |
lower |
float
|
lower bound on the values, in the units of |
upper |
float
|
upper bound on the values, in the units of |
name |
str
|
a name for reports. |
scale |
float
|
a typical size of the values, used to scale the constraint for the optimizer. |
linear |
bool
|
whether |
__call__(car, run)
¶
The constrained values, flattened.
lift_off(tolerance=0.0, sharpness=500.0)
¶
Keep the wheels on the track: the normal forces at both axles must stay at
least tolerance all the way to the finish.
Uses the smooth minimum
Run.smooth_min_normal_force,
which is never larger than the exact minimum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tolerance
|
float
|
the smallest allowed normal force, in newtons. Zero just keeps the wheels on the track; a positive value leaves a margin for a more robust design. |
0.0
|
sharpness
|
float
|
sharpness of the smooth minimum, in 1/N. |
500.0
|
cg_between_axles(margin=0.0)
¶
Keep the center of gravity between the axles, at least margin from each.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
margin
|
float
|
the smallest allowed distance from the center of gravity to either axle, along the car, in meters. |
0.0
|
max_mass(limit=5 * OUNCE)
¶
A maximum total mass for the car.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit
|
float
|
the maximum mass, in kilograms. |
5 * OUNCE
|
bsa_rules()
¶
The common Scout race rules that apply to the car properties: a maximum mass of 5 oz, with the center of gravity between the axles.
Returns a list, so it's easy to add more constraints.
regions_inside_body(margin=0.0, samples=16, sharpness=10000.0)
¶
Keep each inclusion of a GeometricCar inside the
body, at least margin from its outline.
The top of each inclusion must stay below the top of the profile across its
width. This uses a smooth minimum of the clearance at samples points, which
is never larger than the exact minimum over those points. The inclusions must
also stay within the length of the block, regions above its bottom, and the
weight of a WeightPocket inside its pocket.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
margin
|
float
|
the smallest allowed wall of body material around an inclusion, in meters. |
0.0
|
samples
|
int
|
number of points across each inclusion at which to check the top. |
16
|
sharpness
|
float
|
sharpness of the smooth minimum, in 1/m. |
10000.0
|
regions_within_thickness()
¶
Keep each inclusion of a GeometricCar with a
given depth no deeper than the body is thick.
regions_dont_overlap()
¶
Keep the inclusions of a GeometricCar from
overlapping along the car.
The inclusions must be listed from the rear of the car to the front, and keep that order: each one must end before the next one starts.
regions_avoid_axles(car, clearance=0.125 * INCH)
¶
Keep the inclusions of a GeometricCar clear of
the axle slots.
Each inclusion stays in the section of the car where it starts: behind the rear
axle, between the axles, or ahead of the front axle, at least clearance from
the axle centers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
car
|
GeometricCar
|
the car, to find which section each inclusion starts in. |
required |
clearance
|
float
|
the smallest allowed distance from an inclusion to an axle center, in meters. |
0.125 * INCH
|
Raises:
| Type | Description |
|---|---|
ValueError
|
if an inclusion starts over an axle slot. |
Plotting¶
Plotting routines.
plot_track(track, unit='m', n=2000, equal_aspect=False, axes=None)
¶
Plot the geometry of a track.
The top panel shows the track profile \(y(x)\) with the start pin and finish line marked. The bottom panel shows the track angle \(\theta(s)\) and curvature \(\kappa(s)\) along the track.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
track
|
Track
|
the track to plot. |
required |
unit
|
str
|
length unit for the axes, one of the keys of
|
'm'
|
n
|
int
|
number of points to evaluate along the track. |
2000
|
equal_aspect
|
bool
|
plot the profile with equal axis scales. Tracks are much longer than they are tall, so by default the height is exaggerated. |
False
|
axes
|
Sequence[Axes] | None
|
optional pair of matplotlib axes (profile, angle and curvature) to plot into. If not provided, a new figure is created. |
None
|
Returns:
| Type | Description |
|---|---|
Figure
|
The matplotlib figure. |
plot_runs(runs, labels=None, unit='m', axes=None)
¶
Compare the performance of several runs.
The top panel shows the speed of the center of gravity over time, with the finish marked. The bottom panel shows the normal forces at the rear (solid) and front (dashed) axles along the track up to the finish. A negative normal force means a wheel lifted off the track.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
Sequence[Run]
|
the runs to plot. Each car in each run's batch gets its own line. |
required |
labels
|
Sequence[str] | None
|
optional labels, one for each car across all the runs. |
None
|
unit
|
str
|
length unit for the distance along the track. |
'm'
|
axes
|
Sequence[Axes] | None
|
optional pair of matplotlib axes to plot into. |
None
|
Returns:
| Type | Description |
|---|---|
Figure
|
The matplotlib figure. |
plot_run(run, labels=None, unit='m', axes=None)
¶
Plot the performance of a run: see plot_runs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
Run
|
the run to plot. Each car in a batch gets its own line. |
required |
labels
|
Sequence[str] | None
|
optional labels for the cars in the batch. |
None
|
unit
|
str
|
length unit for the distance along the track. |
'm'
|
axes
|
Sequence[Axes] | None
|
optional pair of matplotlib axes to plot into. |
None
|
Returns:
| Type | Description |
|---|---|
Figure
|
The matplotlib figure. |
plot_energy(run, index=0, ax=None)
¶
Plot the energy budget over a run.
Shows the kinetic and potential energy, the energy dissipated by drag and friction so far, and the total, which stays constant up to the error of the time integration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
Run
|
the run to plot. |
required |
index
|
int
|
which car in a batch to plot. |
0
|
ax
|
Axes | None
|
optional matplotlib axes to plot into. |
None
|
Returns:
| Type | Description |
|---|---|
Figure
|
The matplotlib figure. |
plot_car(simulation, s, index=0, unit='in', margin=0.1, ax=None)
¶
Draw the car on the track at one or more locations.
The car is drawn schematically, at true scale: its wheels, the line from the rear axle to the front of the car, and its center of gravity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulation
|
Simulation
|
the simulation holding the track and car. |
required |
s
|
Sequence[float] | Tensor
|
rear wheel contact locations to draw the car at. |
required |
index
|
int
|
which car in a batch to draw. |
0
|
unit
|
str
|
length unit for the axes. |
'in'
|
margin
|
float
|
length of track to show beyond the car, in meters. |
0.1
|
ax
|
Axes | None
|
optional matplotlib axes to plot into. |
None
|
Returns:
| Type | Description |
|---|---|
Figure
|
The matplotlib figure. |
plot_geometry(car, unit='in', ax=None)
¶
Draw the side profile of a GeometricCar, at
true scale.
Shows the body, its inclusions filled by material, the wheels and axles, the track under the wheels, and the center of gravity. Inclusions shallower than the body are drawn lighter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
car
|
GeometricCar
|
the car to draw. |
required |
unit
|
str
|
length unit for the axes. |
'in'
|
ax
|
Axes | None
|
optional matplotlib axes to plot into. |
None
|
Returns:
| Type | Description |
|---|---|
Figure
|
The matplotlib figure. |
Units¶
Unit conversion factors.
pwdsim works in SI units internally. Multiply by these constants to convert a
quantity into SI, and divide to convert back out, e.g. 7 * INCH is 7 inches in
meters and length / FOOT is length in feet.
LENGTH_UNITS = {'m': METER, 'cm': CENTIMETER, 'mm': MILLIMETER, 'in': INCH, 'ft': FOOT}
module-attribute
¶
Length units by abbreviation, as used by the plotting routines.