Skip to content

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 (along, above).

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\}\),

\[ \int q\, dm = \rho_b t \int_\text{body} q\, dA + \sum_k (\rho_k - \rho_b)\, d_k \int_{\text{rectangle } k} q\, dA + m_w \left(n_r q(0, 0) + n_f q(w, 0)\right), \]

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. PINE.

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 degree + 1.

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 (start, end) pairs, over which the profile is held fixed. Control heights whose Greville abscissae (where each has the most influence) fall in a range get equal lower and upper bounds, which fixes them at their current values.

()
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 None for the full body thickness.

Inclusion

Bases: Module, ABC

A region of the body made of other materials, described by rectangles.

depth abstractmethod property

Depth through the car, or None for the full body thickness.

rectangles(bottom) abstractmethod

The rectangles of material, for a body with its bottom at bottom.

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

density of the material, e.g. TUNGSTEN or VOID.

required
depth Scalar | None

depth through the car. None, the default, is the full body thickness.

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.

TUNGSTEN
depth Scalar | None

depth of the pocket through the car. None, the default, is the full body thickness.

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:

  1. 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}\).
  2. Forebody pressure drag, \(t\, (C_\text{face} H_\text{blunt} + C_\text{ramp} H_\text{ramp})\). Forward-facing slopes steeper than separated_angle count as blunt, like the front face, and those gentler than attached_angle as 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.
  3. 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.
  4. 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 friction, forebody, base, and

dict[str, Tensor]

wheels components, and their total, along with the

dict[str, Tensor]

frontal_area and the drag_coefficient.

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:

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

which axle's normal force, REAR or FRONT.

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.

dissipation(config, v, car, env)

Derivatives of the dissipation function that doesn't depend on the normal forces, at speed \(v = \dot{s}\).

frictions(config, car, env)

Friction proportional to the normal forces.

Gravity

Bases: PotentialTerm

Gravitational potential energy, \(V = M g\, y_g\).

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

Bases: KineticTerm

Kinetic energy of the car body pitching, \(T = \tfrac{1}{2} I_b \dot\phi^2\).

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

\[ M(s)\, a + C(s)\, v^2 + V_s + D_s = 0, \]

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 (..., 3, 3).

b Tensor

the right hand side, with shape (..., 3).

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 (..., 2).

EquationsOfMotion

Assembles the enabled physics terms into the equations of motion.

The equation of motion along \(s\) is

\[ M a + C v^2 + V_s + D^0_s + \sum_k c_k N_k = 0, \]

and the normal forces, the constraint forces along the lifts, are

\[ N_j = A_j a + B_j + \sum_k e_{jk} N_k, \]

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" or "forward-euler".

'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 (n,).

required
states Tensor

the states \((s, v)\), with shape (n, *batch, 2).

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. "cg_".

parameter Parameter

the parameter.

lower Tensor

lower bound, with the shape of the parameter.

upper Tensor

upper bound, with the shape of the parameter.

free property

Mask of the elements that are tuned, flattened.

size property

Number of tuned elements.

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 scipy.optimize.minimize.

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 (size of the unit in SI, label), e.g. {"cg_": (INCH, "in")}.

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 optimize.

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 lift_off constraint, or None to leave it out.

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" (the default) or "SLSQP".

'trust-constr'
options Mapping | None

optimizer settings, passed to scipy.optimize.minimize, overriding DEFAULT_OPTIONS.

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 stall_iterations iterations, with the constraints satisfied. None leaves the stopping to the optimizer's own tolerances.

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 SimpleCar its property names ("cg", "mass", ...). Bounds are (lower, upper) in SI units, each a scalar or one value per element of the parameter (e.g. the two components of cg). An element with equal lower and upper bounds is fixed at that value, which tunes only part of a parameter. Variables without bounds, or with None, get default bounds of half to twice their current values.

required
constraints Iterable[Constraint]

constraints on the design, e.g. from pwdsim.constraints.

()
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. None removes the constraint.

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" (the default) or "SLSQP".

'trust-constr'
options Mapping | None

optimizer settings passed to scipy.optimize.minimize, overriding DEFAULT_OPTIONS, e.g. {"maxiter": 20}.

None
stall_tolerance float | None

stop once the objective has improved by less than this (in ms, for the default objective) over the last stall_iterations iterations, with the constraints satisfied. The gradients are only accurate to about 1%, so the optimizer's own tolerances can be out of reach for larger problems, while the finish time has long since stopped improving. None switches this off.

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:

For a GeometricCar there are also constraints on its inclusions (weights and voids):

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 fun.

upper float

upper bound on the values, in the units of fun.

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 fun is linear in the car parameters, so the optimizer doesn't need to approximate its curvature.

__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 LENGTH_UNITS.

'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.

Types

Common types and the floating point precision used throughout pwdsim.

DTYPE = torch.float64 module-attribute

Floating point type for all calculations.

Scalar = float | torch.Tensor module-attribute

A scalar value, as a python float or 0-d tensor.

ArrayLike = Sequence[float] | np.ndarray | torch.Tensor module-attribute

A 1D array of values.