The spin ratio¶
How fast a ball spins against how fast it moves through the air, S = r |w| / |u|, its surface's speed about its centre over its speed through the air (u = v - w_air), the quantity a spinning sphere's lift and drag coefficients are given in.
0 with no motion through the air (no lift for it to scale). Source: Nathan, The effect of spin on the flight of a baseball, Am. J. Phys. 76 (2008) 119. Checked against a reference in numpy, which has its own tests.
- Source
- Nathan, Am. J. Phys. 76 (2008) 119.
- Proof
- reference, limit.
spin_ratio.trait
namespace lusine.laws;
float spin_ratio(vector<float<m/s>, 3> velocity, vector<float<m/s>, 3> wind, vector<float<rad/s>, 3> angular_velocity,
float<m> radius)
{
let u = velocity - wind;
let speed = sqrt(dot(u, u));
if (speed <= 0) return 0.0;
return (radius * sqrt(dot(angular_velocity, angular_velocity))) / speed;
}
A ball's surface speed about its centre over its speed through the air, r |w| / |u|, the quantity a spinning sphere's lift coefficient is given in (lusine.laws.lift_coefficient).
import os, sys
root = os.getcwd()
while not os.path.isfile(os.path.join(root, 'docsite.json')):
root = os.path.dirname(root)
sys.path.insert(0, os.path.join(root, 'tools'))
import numpy as np
import matplotlib.pyplot as plt
from lusine import trait_run
F32 = np.float32
U = float(np.finfo(F32).eps) / 2 # binary32's unit roundoff, 2^-24
rng = np.random.default_rng(2026)
The kernel¶
The law as the library states it.
print(trait_run.source('laws/spin_ratio.trait'))
// lusine.laws.spin_ratio: how fast a ball spins against how fast it moves through the air, S = r |w| / |u|, its
// surface's speed about its centre over its speed through the air (u = v - w_air), the quantity a spinning sphere's
// lift and drag coefficients are given in. 0 with no motion through the air (no lift for it to scale).
// Source: Nathan, The effect of spin on the flight of a baseball, Am. J. Phys. 76 (2008) 119.
// Checked against a reference in numpy, which has its own tests.
namespace lusine.laws;
float spin_ratio(vector<float<m/s>, 3> velocity, vector<float<m/s>, 3> wind, vector<float<rad/s>, 3> angular_velocity,
float<m> radius)
{
let u = velocity - wind;
let speed = sqrt(dot(u, u));
if (speed <= 0) return 0.0;
return (radius * sqrt(dot(angular_velocity, angular_velocity))) / speed;
}
The program¶
Each section below runs the kernel over a lattice of items, one a case: the host sets every parameter, and main() binds the kernel over the items.
print(trait_run.law_program('laws/spin_ratio.trait', 'spin_ratio'))
namespace probe;
lusine.version version = { { number = 1 } };
struct item {
host vector<float<m/s>, 3> velocity;
host vector<float<m/s>, 3> wind;
host vector<float<rad/s>, 3> angular_velocity;
host float<m> radius;
float result_0;
};
lattice<item, 1> items(spacing = 1 m, boundary = wall);
void main() { items.result_0 = lusine.laws.spin_ratio(items.velocity, items.wind, items.angular_velocity, items.radius); }
from lusine.kernels.laws.spin_ratio import spin_ratio as reference
Reference: at random¶
The kernel's ratio within binary32's roundings of the float64 reference's.
v = F32(rng.normal(0.0, 8.0, (500, 3))).astype(float)
w = F32(rng.normal(0.0, 3.0, (500, 3))).astype(float)
s = F32(rng.normal(0.0, 40.0, (500, 3))).astype(float)
got = trait_run.law('laws/spin_ratio.trait', 'spin_ratio', velocity=v, wind=w, angular_velocity=s, radius=0.037)
want = reference(v, w, s, float(F32(0.037)))
err = np.max(np.abs(got - want) / want)
assert err <= 16 * U, err
print(f'{len(v)} cases within {err / U:.1f} unit roundoffs; a boule at 7 m/s spinning at 100 rad/s:',
float(trait_run.law('laws/spin_ratio.trait', 'spin_ratio', velocity=[7.0, 0.0, 0.0], wind=[0.0, 0.0, 0.0],
angular_velocity=[0.0, 0.0, 100.0], radius=0.037)[0]))
500 cases within 2.9 unit roundoffs; a boule at 7 m/s spinning at 100 rad/s: 0.5285714268684387
Limit: no motion through the air¶
A ball moving with the wind has no speed through the air to set its spin against: 0, and no lift.
got = trait_run.law('laws/spin_ratio.trait', 'spin_ratio', velocity=v, wind=v, angular_velocity=s, radius=0.037)
assert np.all(got == 0.0)
print('0 with the wind')
0 with the wind