eml-stdlib 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eml_stdlib/__init__.py +81 -0
- eml_stdlib/ballistics/air_density.eml +73 -0
- eml_stdlib/ballistics/ballistic_solver.eml +102 -0
- eml_stdlib/ballistics/coriolis.eml +60 -0
- eml_stdlib/ballistics/drag.eml +44 -0
- eml_stdlib/ballistics/gravity.eml +52 -0
- eml_stdlib/ballistics/muzzle_velocity.eml +42 -0
- eml_stdlib/ballistics/spin_drift.eml +60 -0
- eml_stdlib/ballistics/time_of_flight.eml +79 -0
- eml_stdlib/ballistics/wind.eml +58 -0
- eml_stdlib/biology/abilities/ballistic_armor.eml +61 -0
- eml_stdlib/biology/abilities/bite_force.eml +48 -0
- eml_stdlib/biology/abilities/chromatophore.eml +50 -0
- eml_stdlib/biology/abilities/discharge.eml +45 -0
- eml_stdlib/biology/abilities/elastic_energy.eml +42 -0
- eml_stdlib/biology/abilities/locomotion.eml +50 -0
- eml_stdlib/biology/abilities/muscle_power.eml +47 -0
- eml_stdlib/biology/abilities/sprint.eml +36 -0
- eml_stdlib/biology/abilities/texture_match.eml +49 -0
- eml_stdlib/biology/abilities/toxin.eml +44 -0
- eml_stdlib/biology/cone_l.eml +36 -0
- eml_stdlib/biology/cone_s.eml +32 -0
- eml_stdlib/biology/cryptochrome.eml +44 -0
- eml_stdlib/biology/geomagnetic_field.eml +32 -0
- eml_stdlib/biology/goldman_equation.eml +41 -0
- eml_stdlib/biology/hill_equation.eml +65 -0
- eml_stdlib/biology/hodgkin_huxley.eml +108 -0
- eml_stdlib/biology/michaelis_menten.eml +65 -0
- eml_stdlib/biology/plume_diffusion.eml +47 -0
- eml_stdlib/biology/receptor_binding.eml +35 -0
- eml_stdlib/biology/rod_sensitivity.eml +38 -0
- eml_stdlib/biology/tapetum.eml +70 -0
- eml_stdlib/carriers/acoustic_cloak.eml +56 -0
- eml_stdlib/carriers/ferron_emission.eml +55 -0
- eml_stdlib/carriers/ferron_logic.eml +45 -0
- eml_stdlib/carriers/ferron_propagation.eml +69 -0
- eml_stdlib/carriers/mach_zehnder.eml +52 -0
- eml_stdlib/carriers/magnon_dispersion.eml +57 -0
- eml_stdlib/carriers/magnon_logic.eml +45 -0
- eml_stdlib/carriers/optical_neuron.eml +55 -0
- eml_stdlib/carriers/phonon_bandgap.eml +54 -0
- eml_stdlib/carriers/ring_resonator.eml +48 -0
- eml_stdlib/carriers/spin_torque.eml +61 -0
- eml_stdlib/carriers/thermal_rectifier.eml +63 -0
- eml_stdlib/catalog.json +4209 -0
- eml_stdlib/circuits/buck_converter.eml +74 -0
- eml_stdlib/circuits/cmos_inverter.eml +75 -0
- eml_stdlib/circuits/h_bridge.eml +69 -0
- eml_stdlib/circuits/ldo.eml +66 -0
- eml_stdlib/circuits/mosfet_iv.eml +59 -0
- eml_stdlib/circuits/pll_loop.eml +63 -0
- eml_stdlib/circuits/rc_filter.eml +98 -0
- eml_stdlib/circuits/voltage_divider.eml +68 -0
- eml_stdlib/control/bangbang.eml +34 -0
- eml_stdlib/control/deadband.eml +28 -0
- eml_stdlib/control/hysteresis.eml +28 -0
- eml_stdlib/control/kalman_1d.eml +53 -0
- eml_stdlib/control/lqr_1d.eml +47 -0
- eml_stdlib/control/pid.eml +38 -0
- eml_stdlib/gaming/animation/bounce.eml +39 -0
- eml_stdlib/gaming/animation/ease_in.eml +27 -0
- eml_stdlib/gaming/animation/ease_in_out.eml +29 -0
- eml_stdlib/gaming/animation/ease_out.eml +27 -0
- eml_stdlib/gaming/animation/ik_2bone.eml +57 -0
- eml_stdlib/gaming/animation/spring.eml +39 -0
- eml_stdlib/gaming/audio/distance_attenuation.eml +50 -0
- eml_stdlib/gaming/audio/doppler.eml +39 -0
- eml_stdlib/gaming/audio/lowpass_audio.eml +32 -0
- eml_stdlib/gaming/audio/reverb_delay.eml +41 -0
- eml_stdlib/gaming/camera/dof_blur.eml +30 -0
- eml_stdlib/gaming/camera/fov_projection.eml +47 -0
- eml_stdlib/gaming/camera/orbit.eml +48 -0
- eml_stdlib/gaming/camera/shake.eml +20 -0
- eml_stdlib/gaming/lighting/ambient_occlusion.eml +40 -0
- eml_stdlib/gaming/lighting/area_light.eml +50 -0
- eml_stdlib/gaming/lighting/fog_exp.eml +28 -0
- eml_stdlib/gaming/lighting/fog_exp2.eml +30 -0
- eml_stdlib/gaming/lighting/point_light.eml +33 -0
- eml_stdlib/gaming/lighting/spot_light.eml +43 -0
- eml_stdlib/gaming/noise/fbm.eml +65 -0
- eml_stdlib/gaming/noise/perlin_2d.eml +65 -0
- eml_stdlib/gaming/noise/perlin_3d.eml +63 -0
- eml_stdlib/gaming/noise/simplex_2d.eml +67 -0
- eml_stdlib/gaming/noise/turbulence.eml +46 -0
- eml_stdlib/gaming/noise/value_noise.eml +45 -0
- eml_stdlib/gaming/noise/voronoi_2d.eml +83 -0
- eml_stdlib/gaming/noise/white_noise.eml +47 -0
- eml_stdlib/gaming/noise/worley.eml +79 -0
- eml_stdlib/gaming/particles/drag.eml +30 -0
- eml_stdlib/gaming/particles/emitter_radial.eml +35 -0
- eml_stdlib/gaming/particles/fade.eml +36 -0
- eml_stdlib/gaming/particles/gravity_particle.eml +32 -0
- eml_stdlib/gaming/particles/size_over_life.eml +28 -0
- eml_stdlib/gaming/physics/aabb_overlap.eml +38 -0
- eml_stdlib/gaming/physics/buoyancy.eml +43 -0
- eml_stdlib/gaming/physics/circle_collision.eml +46 -0
- eml_stdlib/gaming/physics/impulse_resolve.eml +39 -0
- eml_stdlib/gaming/physics/rigid_body_2d.eml +61 -0
- eml_stdlib/gaming/physics/spring_damper.eml +30 -0
- eml_stdlib/gaming/physics/verlet.eml +29 -0
- eml_stdlib/gaming/shading/fresnel.eml +57 -0
- eml_stdlib/gaming/shading/matcap.eml +34 -0
- eml_stdlib/gaming/shading/pbr_diffuse.eml +41 -0
- eml_stdlib/gaming/shading/pbr_specular.eml +75 -0
- eml_stdlib/gaming/shading/subsurface.eml +52 -0
- eml_stdlib/gaming/shading/toon.eml +30 -0
- eml_stdlib/gaming/terrain/biome_select.eml +41 -0
- eml_stdlib/gaming/terrain/cliff_detect.eml +35 -0
- eml_stdlib/gaming/terrain/erosion_hydraulic.eml +57 -0
- eml_stdlib/gaming/terrain/erosion_thermal.eml +44 -0
- eml_stdlib/gaming/terrain/heightmap.eml +33 -0
- eml_stdlib/gaming/textures/brick.eml +27 -0
- eml_stdlib/gaming/textures/caustics.eml +25 -0
- eml_stdlib/gaming/textures/checkerboard.eml +27 -0
- eml_stdlib/gaming/textures/fire.eml +26 -0
- eml_stdlib/gaming/textures/marble.eml +24 -0
- eml_stdlib/gaming/textures/rust.eml +23 -0
- eml_stdlib/gaming/textures/water_surface.eml +49 -0
- eml_stdlib/gaming/textures/wood.eml +41 -0
- eml_stdlib/math/abs.eml +45 -0
- eml_stdlib/math/atan.eml +32 -0
- eml_stdlib/math/atan2.eml +61 -0
- eml_stdlib/math/bessel_j0.eml +65 -0
- eml_stdlib/math/cos.eml +42 -0
- eml_stdlib/math/cosh.eml +24 -0
- eml_stdlib/math/erf.eml +66 -0
- eml_stdlib/math/exp.eml +49 -0
- eml_stdlib/math/gamma.eml +55 -0
- eml_stdlib/math/gaussian.eml +18 -0
- eml_stdlib/math/log.eml +59 -0
- eml_stdlib/math/pow.eml +51 -0
- eml_stdlib/math/relu.eml +63 -0
- eml_stdlib/math/sigmoid.eml +21 -0
- eml_stdlib/math/sin.eml +37 -0
- eml_stdlib/math/sinh.eml +24 -0
- eml_stdlib/math/softmax.eml +87 -0
- eml_stdlib/math/sqrt.eml +46 -0
- eml_stdlib/math/tan.eml +36 -0
- eml_stdlib/math/tanh.eml +54 -0
- eml_stdlib/ml/attention.eml +70 -0
- eml_stdlib/ml/cross_entropy.eml +85 -0
- eml_stdlib/ml/gelu.eml +51 -0
- eml_stdlib/ml/layernorm.eml +49 -0
- eml_stdlib/ml/leaky_relu.eml +51 -0
- eml_stdlib/ml/relu.eml +63 -0
- eml_stdlib/ml/rotary_embed.eml +64 -0
- eml_stdlib/ml/silu.eml +40 -0
- eml_stdlib/ml/softmax.eml +87 -0
- eml_stdlib/physics/coulomb.eml +48 -0
- eml_stdlib/physics/damped_oscillator.eml +20 -0
- eml_stdlib/physics/diffusion.eml +45 -0
- eml_stdlib/physics/gravity.eml +60 -0
- eml_stdlib/physics/hooke.eml +56 -0
- eml_stdlib/physics/navier_stokes_1d.eml +57 -0
- eml_stdlib/physics/planck_radiation.eml +77 -0
- eml_stdlib/physics/stefan_boltzmann.eml +75 -0
- eml_stdlib/physics/wave_propagation.eml +52 -0
- eml_stdlib/quantum/cnot.eml +72 -0
- eml_stdlib/quantum/grover_oracle.eml +69 -0
- eml_stdlib/quantum/hadamard.eml +73 -0
- eml_stdlib/quantum/pauli_x.eml +29 -0
- eml_stdlib/quantum/pauli_z.eml +29 -0
- eml_stdlib/quantum/phase_gate.eml +76 -0
- eml_stdlib/quantum/rotation_rz.eml +92 -0
- eml_stdlib/sensors/accelerometer.eml +59 -0
- eml_stdlib/sensors/hall_effect.eml +42 -0
- eml_stdlib/sensors/photodetector.eml +58 -0
- eml_stdlib/sensors/rtd.eml +57 -0
- eml_stdlib/sensors/strain_gauge.eml +63 -0
- eml_stdlib/sensors/thermistor.eml +71 -0
- eml_stdlib/signal/bandpass.eml +39 -0
- eml_stdlib/signal/chirp.eml +42 -0
- eml_stdlib/signal/dft_single.eml +30 -0
- eml_stdlib/signal/fft_butterfly.eml +36 -0
- eml_stdlib/signal/highpass.eml +45 -0
- eml_stdlib/signal/lowpass.eml +49 -0
- eml_stdlib/signal/matched_filter.eml +48 -0
- eml_stdlib/signal/notch_filter.eml +47 -0
- eml_stdlib/signal/window_hamming.eml +47 -0
- eml_stdlib/signal/window_hann.eml +50 -0
- eml_stdlib-0.4.0.dist-info/METADATA +453 -0
- eml_stdlib-0.4.0.dist-info/RECORD +185 -0
- eml_stdlib-0.4.0.dist-info/WHEEL +5 -0
- eml_stdlib-0.4.0.dist-info/licenses/LICENSE +21 -0
- eml_stdlib-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// rotary_embed.eml -- Rotary Position Embedding (RoPE) kernel.
|
|
2
|
+
//
|
|
3
|
+
// RoPE applies a 2D rotation to each (x_even, x_odd) pair in the
|
|
4
|
+
// query/key vector at position m. The rotation angle is
|
|
5
|
+
//
|
|
6
|
+
// theta_i = m / base^(2i/d)
|
|
7
|
+
//
|
|
8
|
+
// where base = 10000 (standard), d is the head dimension, i in
|
|
9
|
+
// [0, d/2). The transformed pair is
|
|
10
|
+
//
|
|
11
|
+
// x_even' = x_even * cos(theta) - x_odd * sin(theta)
|
|
12
|
+
// x_odd' = x_even * sin(theta) + x_odd * cos(theta)
|
|
13
|
+
//
|
|
14
|
+
// Chain order 1 -- one sin, one cos per pair. The dot-product
|
|
15
|
+
// q . k after RoPE encodes the relative position (m - n), which is
|
|
16
|
+
// what the attention layer wants.
|
|
17
|
+
|
|
18
|
+
module rotary_embed;
|
|
19
|
+
|
|
20
|
+
const DEFAULT_BASE: Real = 10000.0
|
|
21
|
+
|
|
22
|
+
// Rotation angle for index i at position m.
|
|
23
|
+
@verify(lean, theorem = "rope_theta_at_zero_position_is_zero")
|
|
24
|
+
fn theta(m: Real, i: Real, d: Real, base: Real) -> Real
|
|
25
|
+
where chain_order <= 1,
|
|
26
|
+
domain: d > 0.0 && base > 0.0
|
|
27
|
+
{
|
|
28
|
+
let exponent = 2.0 * i / d;
|
|
29
|
+
m / pow(base, exponent)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Default-base theta.
|
|
33
|
+
@verify(lean, theorem = "rope_default_theta_at_zero_position_is_zero")
|
|
34
|
+
fn theta_default(m: Real, i: Real, d: Real) -> Real
|
|
35
|
+
where chain_order <= 1,
|
|
36
|
+
domain: d > 0.0
|
|
37
|
+
{
|
|
38
|
+
let exponent = 2.0 * i / d;
|
|
39
|
+
m / pow(DEFAULT_BASE, exponent)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Rotate one (even, odd) pair.
|
|
43
|
+
@verify(lean, theorem = "rope_rotation_preserves_norm")
|
|
44
|
+
fn rotate_pair_even(x_even: Real, x_odd: Real, theta: Real) -> Real
|
|
45
|
+
where chain_order <= 1
|
|
46
|
+
{
|
|
47
|
+
x_even * cos(theta) - x_odd * sin(theta)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@verify(lean, theorem = "rope_rotation_pair_norm_preserved")
|
|
51
|
+
fn rotate_pair_odd(x_even: Real, x_odd: Real, theta: Real) -> Real
|
|
52
|
+
where chain_order <= 1
|
|
53
|
+
{
|
|
54
|
+
x_even * sin(theta) + x_odd * cos(theta)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Pythagorean witness: |rotated|^2 = |x_even|^2 + |x_odd|^2.
|
|
58
|
+
@verify(lean, theorem = "rope_pythagorean_witness")
|
|
59
|
+
fn norm_witness(theta: Real) -> Real
|
|
60
|
+
where chain_order <= 1
|
|
61
|
+
ensures (result == 1.0)
|
|
62
|
+
{
|
|
63
|
+
sin(theta) * sin(theta) + cos(theta) * cos(theta)
|
|
64
|
+
}
|
eml_stdlib/ml/silu.eml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// silu.eml -- SiLU / Swish activation.
|
|
2
|
+
//
|
|
3
|
+
// silu(x) = x * sigmoid(x) = x / (1 + exp(-x))
|
|
4
|
+
//
|
|
5
|
+
// Same shape and chain-order class as sigmoid; the multiplication
|
|
6
|
+
// by x makes the activation unbounded above, which matches the
|
|
7
|
+
// behaviour modern transformer architectures want.
|
|
8
|
+
|
|
9
|
+
module silu;
|
|
10
|
+
|
|
11
|
+
const SILU_X_MAX: Real = 30.0
|
|
12
|
+
|
|
13
|
+
@verify(lean, theorem = "silu_at_zero_is_zero")
|
|
14
|
+
fn silu(x: Real) -> Real
|
|
15
|
+
where chain_order <= 1
|
|
16
|
+
requires (abs(x) < SILU_X_MAX)
|
|
17
|
+
ensures (result >= -1.0) // global lower bound is ~-0.278
|
|
18
|
+
{
|
|
19
|
+
x / (1.0 + exp(-x))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Beta-Swish variant: x * sigmoid(beta * x).
|
|
23
|
+
@verify(lean, theorem = "swish_at_zero_is_zero")
|
|
24
|
+
fn swish(x: Real, beta: Real) -> Real
|
|
25
|
+
where chain_order <= 1
|
|
26
|
+
requires (abs(beta * x) < SILU_X_MAX)
|
|
27
|
+
{
|
|
28
|
+
x / (1.0 + exp(-beta * x))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Derivative -- f'(x) = sigmoid(x) + x * sigmoid'(x)
|
|
32
|
+
// = sigmoid(x) * (1 + x * (1 - sigmoid(x))).
|
|
33
|
+
@verify(lean, theorem = "silu_derivative_at_zero_is_one_half")
|
|
34
|
+
fn silu_grad(x: Real) -> Real
|
|
35
|
+
where chain_order <= 1
|
|
36
|
+
requires (abs(x) < SILU_X_MAX)
|
|
37
|
+
{
|
|
38
|
+
let s = 1.0 / (1.0 + exp(-x));
|
|
39
|
+
s + x * s * (1.0 - s)
|
|
40
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// softmax.eml -- numerically-stable softmax kernels.
|
|
2
|
+
//
|
|
3
|
+
// Softmax over an N-vector requires (a) subtracting the max for
|
|
4
|
+
// numerical stability and (b) reducing across the vector. EML's
|
|
5
|
+
// scalar-kernel surface naturally splits this into three pure
|
|
6
|
+
// gates that the host loop composes:
|
|
7
|
+
//
|
|
8
|
+
// softmax_shift_exp(logit, max_logit) per-element exp(x - m)
|
|
9
|
+
// softmax_normalize(numer, denom_sum) per-element / sum
|
|
10
|
+
// softmax_two_logit(a, b) closed form for N=2
|
|
11
|
+
//
|
|
12
|
+
// The FPGA allocator (Patent #14) maps the per-element gates to
|
|
13
|
+
// SIMD lanes and the reduction to a tree adder; cross-target
|
|
14
|
+
// equivalence (Patent #22) then guarantees the C / Rust / Lean
|
|
15
|
+
// reference matches the Verilog bit-for-bit at IEEE-754
|
|
16
|
+
// rounding.
|
|
17
|
+
|
|
18
|
+
module softmax;
|
|
19
|
+
|
|
20
|
+
const SOFTMAX_X_MAX: Real = 30.0
|
|
21
|
+
|
|
22
|
+
type SoftmaxNumerator = Real where chain_order <= 1
|
|
23
|
+
type SoftmaxValue = Real where chain_order <= 1
|
|
24
|
+
|
|
25
|
+
// Per-element exp(x - max) -- the numerically-stable softmax
|
|
26
|
+
// numerator. `max_logit` is the running maximum of the logit
|
|
27
|
+
// vector, supplied by the host (computed in a separate reduce).
|
|
28
|
+
// Subtracting it before exp() guarantees the largest numerator
|
|
29
|
+
// equals 1.0 and the smallest doesn't underflow.
|
|
30
|
+
@target(fpga, clock_mhz = 250, precision = float32)
|
|
31
|
+
@verify(lean, theorem = "softmax_numerator_bounded_by_one")
|
|
32
|
+
fn softmax_shift_exp(logit: Real, max_logit: Real) -> SoftmaxNumerator
|
|
33
|
+
where chain_order <= 1
|
|
34
|
+
requires (abs(logit) < SOFTMAX_X_MAX)
|
|
35
|
+
requires (abs(max_logit) < SOFTMAX_X_MAX)
|
|
36
|
+
requires (logit <= max_logit)
|
|
37
|
+
ensures (result >= 0.0)
|
|
38
|
+
ensures (result <= 1.0)
|
|
39
|
+
{
|
|
40
|
+
exp(logit - max_logit)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Per-element normaliser. `denom_sum` is the sum of the shifted
|
|
44
|
+
// numerators; supplied by the host's reduction tree.
|
|
45
|
+
@target(fpga, clock_mhz = 400, precision = float32)
|
|
46
|
+
@verify(lean, theorem = "softmax_value_in_unit_interval")
|
|
47
|
+
fn softmax_normalize(numer: Real, denom_sum: Real) -> SoftmaxValue
|
|
48
|
+
where chain_order <= 0
|
|
49
|
+
requires (numer >= 0.0)
|
|
50
|
+
requires (denom_sum > 0.0)
|
|
51
|
+
requires (numer <= denom_sum)
|
|
52
|
+
ensures (result >= 0.0)
|
|
53
|
+
ensures (result <= 1.0)
|
|
54
|
+
{
|
|
55
|
+
numer / denom_sum
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Closed-form softmax for N=2 -- the binary-classifier head.
|
|
59
|
+
// Equivalent to sigmoid(a - b); useful as a sanity-check
|
|
60
|
+
// for the sigmoid-vs-softmax-for-binary debate. Chain order 1.
|
|
61
|
+
@target(fpga, clock_mhz = 250, precision = float32)
|
|
62
|
+
@verify(lean, theorem = "softmax_two_equals_sigmoid_diff")
|
|
63
|
+
fn softmax_two_logit(a: Real, b: Real) -> SoftmaxValue
|
|
64
|
+
where chain_order <= 1
|
|
65
|
+
requires (abs(a) < SOFTMAX_X_MAX)
|
|
66
|
+
requires (abs(b) < SOFTMAX_X_MAX)
|
|
67
|
+
ensures (result >= 0.0)
|
|
68
|
+
ensures (result <= 1.0)
|
|
69
|
+
{
|
|
70
|
+
1.0 / (1.0 + exp(b - a))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Log-sum-exp (LSE) -- stable computation of log(sum exp(x_i)).
|
|
74
|
+
// The host loop accumulates `running_lse` and `next_logit`
|
|
75
|
+
// pair-wise via this gate. Chain order 2 (ln composes through
|
|
76
|
+
// exp). Used as the cross-entropy denominator in transformer
|
|
77
|
+
// language-model heads.
|
|
78
|
+
@target(fpga, clock_mhz = 200, precision = float32)
|
|
79
|
+
@verify(lean, theorem = "log_sum_exp_pair_associative")
|
|
80
|
+
fn log_sum_exp_pair(a: Real, b: Real) -> Real
|
|
81
|
+
where chain_order <= 2
|
|
82
|
+
requires (abs(a) < SOFTMAX_X_MAX)
|
|
83
|
+
requires (abs(b) < SOFTMAX_X_MAX)
|
|
84
|
+
{
|
|
85
|
+
let m = clamp(a, b, SOFTMAX_X_MAX);
|
|
86
|
+
m + ln(exp(a - m) + exp(b - m))
|
|
87
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// coulomb.eml -- Coulomb's law for electrostatic force / field.
|
|
2
|
+
//
|
|
3
|
+
// F = k_e * q1 * q2 / r^2
|
|
4
|
+
// E = k_e * q / r^2
|
|
5
|
+
//
|
|
6
|
+
// k_e = 8.9875517923e9 N*m^2/C^2 (Coulomb constant).
|
|
7
|
+
// Chain order 0 -- pure rational arithmetic.
|
|
8
|
+
|
|
9
|
+
module coulomb;
|
|
10
|
+
|
|
11
|
+
const COULOMB_K: Real = 8.9875517923e9
|
|
12
|
+
const EPS_0: Real = 8.8541878128e-12
|
|
13
|
+
|
|
14
|
+
// Force between two point charges separated by r.
|
|
15
|
+
@verify(lean, theorem = "coulomb_force_sign_matches_charge_product")
|
|
16
|
+
fn force(q1: Real, q2: Real, r: Real) -> Real
|
|
17
|
+
where chain_order <= 0,
|
|
18
|
+
domain: r > 0.0
|
|
19
|
+
{
|
|
20
|
+
COULOMB_K * q1 * q2 / (r * r)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Electric field of a single point charge at distance r.
|
|
24
|
+
@verify(lean, theorem = "coulomb_field_sign_matches_charge")
|
|
25
|
+
fn field(q: Real, r: Real) -> Real
|
|
26
|
+
where chain_order <= 0,
|
|
27
|
+
domain: r > 0.0
|
|
28
|
+
{
|
|
29
|
+
COULOMB_K * q / (r * r)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Potential energy of a pair of charges at separation r.
|
|
33
|
+
@verify(lean, theorem = "coulomb_potential_energy_sign_matches_charges")
|
|
34
|
+
fn potential_energy(q1: Real, q2: Real, r: Real) -> Real
|
|
35
|
+
where chain_order <= 0,
|
|
36
|
+
domain: r > 0.0
|
|
37
|
+
{
|
|
38
|
+
COULOMB_K * q1 * q2 / r
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Electrostatic potential of a single point charge at distance r.
|
|
42
|
+
@verify(lean, theorem = "coulomb_potential_sign_matches_charge")
|
|
43
|
+
fn potential(q: Real, r: Real) -> Real
|
|
44
|
+
where chain_order <= 0,
|
|
45
|
+
domain: r > 0.0
|
|
46
|
+
{
|
|
47
|
+
COULOMB_K * q / r
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// damped_wave.eml -- exp * sin, the chain-2 archetype.
|
|
2
|
+
//
|
|
3
|
+
// y(t) = exp(-zeta * t) * sin(omega * t)
|
|
4
|
+
//
|
|
5
|
+
// The bedrock damped oscillation. Two transcendentals nested
|
|
6
|
+
// over polynomial arguments -- chain order 2. This shape
|
|
7
|
+
// describes a settling spring, a reverb tail, a relaxation
|
|
8
|
+
// curve, and the impulse response of a second-order linear
|
|
9
|
+
// system.
|
|
10
|
+
|
|
11
|
+
module damped_wave;
|
|
12
|
+
|
|
13
|
+
fn damped_wave(zeta: Real, omega: Real, t: Real) -> Real
|
|
14
|
+
where chain_order <= 2
|
|
15
|
+
requires (zeta >= 0.0)
|
|
16
|
+
requires (omega >= 0.0)
|
|
17
|
+
requires (t >= 0.0)
|
|
18
|
+
{
|
|
19
|
+
exp(-zeta * t) * sin(omega * t)
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// diffusion.eml -- 1-D diffusion (heat / mass) equation snapshot.
|
|
2
|
+
//
|
|
3
|
+
// c(x, t) = (M / sqrt(4*pi*D*t)) * exp(-x^2 / (4*D*t))
|
|
4
|
+
//
|
|
5
|
+
// Closed-form Gaussian solution to the diffusion equation
|
|
6
|
+
// dc/dt = D * d^2c/dx^2
|
|
7
|
+
// for an initial impulse of mass M at the origin. Chain order 1.
|
|
8
|
+
// At t = 0 the solution is a delta function -- callers that need
|
|
9
|
+
// the impulse should use the Fick steady-state form instead.
|
|
10
|
+
|
|
11
|
+
module diffusion;
|
|
12
|
+
|
|
13
|
+
const PI: Real = 3.141592653589793
|
|
14
|
+
const SQRT_4_PI: Real = 3.5449077018110318
|
|
15
|
+
|
|
16
|
+
@verify(lean, theorem = "diffusion_concentration_positive_for_finite_time")
|
|
17
|
+
fn concentration(mass: Real, diffusivity: Real, x: Real, t: Real) -> Real
|
|
18
|
+
where chain_order <= 1,
|
|
19
|
+
domain: t > 0.0 && diffusivity > 0.0
|
|
20
|
+
requires (mass > 0.0)
|
|
21
|
+
ensures (result > 0.0)
|
|
22
|
+
{
|
|
23
|
+
let four_dt = 4.0 * diffusivity * t;
|
|
24
|
+
let prefactor = mass / sqrt(PI * four_dt);
|
|
25
|
+
prefactor * exp(-x * x / four_dt)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Standard deviation of the diffusion blob at time t: sigma = sqrt(2*D*t).
|
|
29
|
+
@verify(lean, theorem = "diffusion_sigma_positive_for_finite_time")
|
|
30
|
+
fn sigma(diffusivity: Real, t: Real) -> Real
|
|
31
|
+
where chain_order <= 1,
|
|
32
|
+
domain: t > 0.0 && diffusivity > 0.0
|
|
33
|
+
ensures (result > 0.0)
|
|
34
|
+
{
|
|
35
|
+
sqrt(2.0 * diffusivity * t)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fick's first law: flux = -D * dc/dx (steady-state form).
|
|
39
|
+
@verify(lean, theorem = "fick_flux_sign_opposes_gradient")
|
|
40
|
+
fn fick_first_law(diffusivity: Real, gradient: Real) -> Real
|
|
41
|
+
where chain_order <= 0,
|
|
42
|
+
domain: diffusivity > 0.0
|
|
43
|
+
{
|
|
44
|
+
-diffusivity * gradient
|
|
45
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// gravity.eml -- two gravity models for game physics.
|
|
2
|
+
//
|
|
3
|
+
// Newtonian inverse-square gravity is the right model for orbital
|
|
4
|
+
// games (Kerbal-class, space sims, asteroid simulators). Surface
|
|
5
|
+
// (uniform / "constant-g") gravity is the right model for platformers
|
|
6
|
+
// and most ground-based games where the planet is "flat" at game
|
|
7
|
+
// scale. Both kernels are provided so a hybrid game (rocket
|
|
8
|
+
// launch + planet surface) can switch.
|
|
9
|
+
//
|
|
10
|
+
// Newton: a(r) = G * M / r^2 (vector along -r-hat)
|
|
11
|
+
// Surface: a = -g (constant)
|
|
12
|
+
//
|
|
13
|
+
// drift_risk LOW. Sibling to projectile.eml (which uses
|
|
14
|
+
// surface_gravity_acceleration as input) and bounce.eml.
|
|
15
|
+
|
|
16
|
+
module gravity;
|
|
17
|
+
|
|
18
|
+
const ZERO: Real = 0.0
|
|
19
|
+
const ONE: Real = 1.0
|
|
20
|
+
const TINY: Real = 1.0e-9
|
|
21
|
+
|
|
22
|
+
const G_NEWTON: Real = 6.6743e-11 // N m^2 / kg^2
|
|
23
|
+
const M_EARTH: Real = 5.972e24 // kg
|
|
24
|
+
const R_EARTH: Real = 6.371e6 // m
|
|
25
|
+
const R_MIN: Real = 1.0e3 // 1 km floor (avoid singularity)
|
|
26
|
+
const R_MAX: Real = 1.0e12 // 1 Tm cap (outer solar system)
|
|
27
|
+
const M_MAX: Real = 2.0e30 // ~ solar mass cap
|
|
28
|
+
const G_SURFACE_MAX: Real = 100.0 // jovian-grav cap
|
|
29
|
+
|
|
30
|
+
type Accel = Real where chain_order <= 0
|
|
31
|
+
|
|
32
|
+
// Newtonian gravitational acceleration magnitude at distance r
|
|
33
|
+
// from a point mass M. Chain order 0 (rational).
|
|
34
|
+
@target(fpga, clock_mhz = 500, precision = float32)
|
|
35
|
+
@verify(lean, theorem = "newtonian_gravity_inverse_square")
|
|
36
|
+
fn newtonian_acceleration(mass: Real, r: Real) -> Accel
|
|
37
|
+
where chain_order <= 0
|
|
38
|
+
requires (mass >= ZERO)
|
|
39
|
+
requires (mass <= M_MAX)
|
|
40
|
+
requires (r >= R_MIN)
|
|
41
|
+
requires (r <= R_MAX)
|
|
42
|
+
ensures (result >= ZERO)
|
|
43
|
+
{
|
|
44
|
+
G_NEWTON * mass / (r * r + TINY)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Per-frame velocity update under uniform downward gravity. Chain
|
|
48
|
+
// order 0. Most platformer / FPS engines use this -- the planet
|
|
49
|
+
// is treated as flat at game scale and `g` is a tunable.
|
|
50
|
+
@target(fpga, clock_mhz = 500, precision = float32)
|
|
51
|
+
fn surface_gravity_step(v_y: Real, g: Real, dt: Real) -> Real
|
|
52
|
+
where chain_order <= 0
|
|
53
|
+
requires (abs(v_y) <= 1.0e4)
|
|
54
|
+
requires (g >= ZERO)
|
|
55
|
+
requires (g <= G_SURFACE_MAX)
|
|
56
|
+
requires (dt >= 1.0e-5)
|
|
57
|
+
requires (dt <= 0.1)
|
|
58
|
+
{
|
|
59
|
+
v_y - g * dt
|
|
60
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// hooke.eml -- Hooke's law for springs and elastic media.
|
|
2
|
+
//
|
|
3
|
+
// F = -k * x (restoring force)
|
|
4
|
+
// U = (1/2) * k * x^2 (stored energy)
|
|
5
|
+
//
|
|
6
|
+
// Linear elastic regime only; for stress/strain Mises-yield-style
|
|
7
|
+
// extensions see the construction industry kernels.
|
|
8
|
+
|
|
9
|
+
module hooke;
|
|
10
|
+
|
|
11
|
+
@verify(lean, theorem = "hooke_force_sign_opposes_displacement")
|
|
12
|
+
fn force(stiffness: Real, displacement: Real) -> Real
|
|
13
|
+
where chain_order <= 0,
|
|
14
|
+
domain: stiffness > 0.0
|
|
15
|
+
{
|
|
16
|
+
-stiffness * displacement
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@verify(lean, theorem = "hooke_energy_nonneg")
|
|
20
|
+
fn potential_energy(stiffness: Real, displacement: Real) -> Real
|
|
21
|
+
where chain_order <= 0,
|
|
22
|
+
domain: stiffness > 0.0
|
|
23
|
+
ensures (result >= 0.0)
|
|
24
|
+
{
|
|
25
|
+
0.5 * stiffness * displacement * displacement
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Series stiffness of two springs.
|
|
29
|
+
@verify(lean, theorem = "hooke_series_stiffness_positive")
|
|
30
|
+
fn series_stiffness(k1: Real, k2: Real) -> Real
|
|
31
|
+
where chain_order <= 0,
|
|
32
|
+
domain: k1 > 0.0 && k2 > 0.0
|
|
33
|
+
ensures (result > 0.0)
|
|
34
|
+
{
|
|
35
|
+
(k1 * k2) / (k1 + k2)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Parallel stiffness of two springs.
|
|
39
|
+
@verify(lean, theorem = "hooke_parallel_stiffness_positive")
|
|
40
|
+
fn parallel_stiffness(k1: Real, k2: Real) -> Real
|
|
41
|
+
where chain_order <= 0,
|
|
42
|
+
domain: k1 > 0.0 && k2 > 0.0
|
|
43
|
+
ensures (result > 0.0)
|
|
44
|
+
{
|
|
45
|
+
k1 + k2
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Natural angular frequency of a mass on a spring: omega = sqrt(k/m).
|
|
49
|
+
@verify(lean, theorem = "hooke_omega_positive")
|
|
50
|
+
fn natural_frequency(stiffness: Real, mass: Real) -> Real
|
|
51
|
+
where chain_order <= 1,
|
|
52
|
+
domain: stiffness > 0.0 && mass > 0.0
|
|
53
|
+
ensures (result > 0.0)
|
|
54
|
+
{
|
|
55
|
+
sqrt(stiffness / mass)
|
|
56
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// navier_stokes_1d.eml -- 1-D incompressible Navier-Stokes step.
|
|
2
|
+
//
|
|
3
|
+
// Simplified to the momentum-only form on a 1-D pipe:
|
|
4
|
+
//
|
|
5
|
+
// du/dt + u * du/dx = -(1/rho) * dp/dx + nu * d^2u/dx^2
|
|
6
|
+
//
|
|
7
|
+
// This module ships the right-hand-side terms separately so users
|
|
8
|
+
// can compose explicit, implicit, or semi-Lagrangian time steppers
|
|
9
|
+
// downstream. Chain order 0 throughout.
|
|
10
|
+
|
|
11
|
+
module navier_stokes_1d;
|
|
12
|
+
|
|
13
|
+
// Pressure-gradient acceleration term.
|
|
14
|
+
@verify(lean, theorem = "ns1d_pressure_term_sign_opposes_gradient")
|
|
15
|
+
fn pressure_term(density: Real, dp_dx: Real) -> Real
|
|
16
|
+
where chain_order <= 0,
|
|
17
|
+
domain: density > 0.0
|
|
18
|
+
{
|
|
19
|
+
-dp_dx / density
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Viscous (diffusive) term.
|
|
23
|
+
@verify(lean, theorem = "ns1d_viscous_term_proportional_to_d2u_dx2")
|
|
24
|
+
fn viscous_term(viscosity: Real, d2u_dx2: Real) -> Real
|
|
25
|
+
where chain_order <= 0,
|
|
26
|
+
domain: viscosity >= 0.0
|
|
27
|
+
{
|
|
28
|
+
viscosity * d2u_dx2
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Convective (advective) term.
|
|
32
|
+
@verify(lean, theorem = "ns1d_convective_term_signed")
|
|
33
|
+
fn convective_term(u: Real, du_dx: Real) -> Real
|
|
34
|
+
where chain_order <= 0
|
|
35
|
+
{
|
|
36
|
+
-u * du_dx
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Full RHS of the momentum equation.
|
|
40
|
+
fn rhs(u: Real, du_dx: Real, d2u_dx2: Real,
|
|
41
|
+
density: Real, dp_dx: Real, viscosity: Real) -> Real
|
|
42
|
+
where chain_order <= 0,
|
|
43
|
+
domain: density > 0.0 && viscosity >= 0.0
|
|
44
|
+
{
|
|
45
|
+
-u * du_dx - dp_dx / density + viscosity * d2u_dx2
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Reynolds number: Re = u * L / nu. Determines laminar/turbulent
|
|
49
|
+
// regime; useful invariant for caller's adaptive time step.
|
|
50
|
+
@verify(lean, theorem = "reynolds_nonneg")
|
|
51
|
+
fn reynolds(velocity: Real, length: Real, viscosity: Real) -> Real
|
|
52
|
+
where chain_order <= 0,
|
|
53
|
+
domain: viscosity > 0.0
|
|
54
|
+
requires (length > 0.0)
|
|
55
|
+
{
|
|
56
|
+
velocity * length / viscosity
|
|
57
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// planck_radiation.eml -- Planck blackbody spectral radiance.
|
|
2
|
+
//
|
|
3
|
+
// Planck's law for the spectral radiance of a blackbody at
|
|
4
|
+
// temperature T:
|
|
5
|
+
//
|
|
6
|
+
// B(lambda, T) = (2*h*c^2 / lambda^5) * 1 / (exp(h*c/(lambda*k*T)) - 1)
|
|
7
|
+
//
|
|
8
|
+
// where h is Planck's constant, c is the speed of light, k is
|
|
9
|
+
// Boltzmann's constant. Used in radiometry, IR remote sensing,
|
|
10
|
+
// astrophysics, and thermal-imaging calibration.
|
|
11
|
+
//
|
|
12
|
+
// Chain order 1 (single exp). Companion of stefan_boltzmann.eml
|
|
13
|
+
// (which is the integral of B over all wavelengths).
|
|
14
|
+
|
|
15
|
+
module planck_radiation;
|
|
16
|
+
|
|
17
|
+
const HC: Real = 1.986445857e-25 // h*c, J*m
|
|
18
|
+
const HC_OVER_K: Real = 0.014387768775039337 // h*c/k, m*K
|
|
19
|
+
const TWO_HC2: Real = 1.191042869e-16 // 2*h*c^2, W*m^4*sr^-1
|
|
20
|
+
const LAMBDA_MIN: Real = 1.0e-9 // 1 nm UV
|
|
21
|
+
const LAMBDA_MAX: Real = 1.0e-3 // 1 mm far-IR
|
|
22
|
+
const T_MIN: Real = 1.0
|
|
23
|
+
const T_MAX: Real = 1.0e5
|
|
24
|
+
|
|
25
|
+
type SpectralRadiance = Real where chain_order <= 1
|
|
26
|
+
|
|
27
|
+
@target(fpga, clock_mhz = 100, precision = float64)
|
|
28
|
+
@verify(lean, theorem = "planck_radiance_increases_with_temperature")
|
|
29
|
+
fn spectral_radiance(wavelength_m: Real, temperature_k: Real) -> SpectralRadiance
|
|
30
|
+
where chain_order <= 1
|
|
31
|
+
requires (wavelength_m >= LAMBDA_MIN)
|
|
32
|
+
requires (wavelength_m <= LAMBDA_MAX)
|
|
33
|
+
requires (temperature_k >= T_MIN)
|
|
34
|
+
requires (temperature_k <= T_MAX)
|
|
35
|
+
ensures (result >= 0.0)
|
|
36
|
+
{
|
|
37
|
+
let lambda5 = wavelength_m * wavelength_m * wavelength_m
|
|
38
|
+
* wavelength_m * wavelength_m;
|
|
39
|
+
let prefactor = TWO_HC2 / lambda5;
|
|
40
|
+
let exponent = HC_OVER_K / (wavelength_m * temperature_k);
|
|
41
|
+
prefactor / (exp(exponent) - 1.0)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Wien displacement: lambda_max * T = b. Reused from
|
|
45
|
+
// stefan_boltzmann; here for completeness in this physics
|
|
46
|
+
// kernel pair.
|
|
47
|
+
@target(fpga, clock_mhz = 500, precision = float64)
|
|
48
|
+
@verify(lean, theorem = "wien_constant_inversely_proportional_to_T")
|
|
49
|
+
fn wien_lambda_max(temperature_k: Real) -> Real
|
|
50
|
+
where chain_order <= 0
|
|
51
|
+
requires (temperature_k >= T_MIN)
|
|
52
|
+
requires (temperature_k <= T_MAX)
|
|
53
|
+
ensures (result >= 0.0)
|
|
54
|
+
{
|
|
55
|
+
0.002897771955 / temperature_k
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Rayleigh-Jeans long-wavelength approximation:
|
|
59
|
+
// B(lambda, T) ~ 2*c*k*T / lambda^4
|
|
60
|
+
// Valid when h*c << lambda*k*T (radio / mm-wave radiometry).
|
|
61
|
+
// Chain order 0 (no exp).
|
|
62
|
+
@target(fpga, clock_mhz = 500, precision = float64)
|
|
63
|
+
@verify(lean, theorem = "rayleigh_jeans_underestimates_at_short_wavelength")
|
|
64
|
+
fn rayleigh_jeans_radiance(
|
|
65
|
+
wavelength_m: Real,
|
|
66
|
+
temperature_k: Real,
|
|
67
|
+
) -> SpectralRadiance
|
|
68
|
+
where chain_order <= 0
|
|
69
|
+
requires (wavelength_m >= LAMBDA_MIN)
|
|
70
|
+
requires (wavelength_m <= LAMBDA_MAX)
|
|
71
|
+
requires (temperature_k >= T_MIN)
|
|
72
|
+
requires (temperature_k <= T_MAX)
|
|
73
|
+
ensures (result >= 0.0)
|
|
74
|
+
{
|
|
75
|
+
let lambda4 = wavelength_m * wavelength_m * wavelength_m * wavelength_m;
|
|
76
|
+
2.0 * 299792458.0 * 1.380649e-23 * temperature_k / lambda4
|
|
77
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// stefan_boltzmann.eml -- blackbody radiative flux.
|
|
2
|
+
//
|
|
3
|
+
// The Stefan-Boltzmann law:
|
|
4
|
+
//
|
|
5
|
+
// j = epsilon * sigma * T^4
|
|
6
|
+
//
|
|
7
|
+
// where:
|
|
8
|
+
// sigma = 5.670374419e-8 W m^-2 K^-4 (Stefan-Boltzmann constant)
|
|
9
|
+
// epsilon = surface emissivity (0..1)
|
|
10
|
+
// T = absolute temperature (K)
|
|
11
|
+
//
|
|
12
|
+
// Chain order 1 (single pow). Used in radiative-balance climate
|
|
13
|
+
// models, spacecraft thermal control, IR-camera calibration,
|
|
14
|
+
// and as the upper-bound term in atmospheric energy budgets.
|
|
15
|
+
|
|
16
|
+
module stefan_boltzmann;
|
|
17
|
+
|
|
18
|
+
const SIGMA_SB: Real = 5.670374419e-8
|
|
19
|
+
const T_MIN: Real = 1.0
|
|
20
|
+
const T_MAX: Real = 6000.0
|
|
21
|
+
const EPS_MIN: Real = 0.0
|
|
22
|
+
const EPS_MAX: Real = 1.0
|
|
23
|
+
|
|
24
|
+
type RadiativeFlux = Real where chain_order <= 1
|
|
25
|
+
|
|
26
|
+
@target(fpga, clock_mhz = 200, precision = float64)
|
|
27
|
+
@verify(lean, theorem = "stefan_boltzmann_monotone_in_T")
|
|
28
|
+
fn radiative_flux(emissivity: Real, temperature_k: Real) -> RadiativeFlux
|
|
29
|
+
where chain_order <= 1
|
|
30
|
+
requires (emissivity >= EPS_MIN)
|
|
31
|
+
requires (emissivity <= EPS_MAX)
|
|
32
|
+
requires (temperature_k >= T_MIN)
|
|
33
|
+
requires (temperature_k <= T_MAX)
|
|
34
|
+
ensures (result >= 0.0)
|
|
35
|
+
{
|
|
36
|
+
emissivity * SIGMA_SB * pow(temperature_k, 4.0)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Net radiative exchange between a surface at T_s and an
|
|
40
|
+
// environment at T_env, both treated as graybodies. Chain
|
|
41
|
+
// order 1 (two pow calls but the same exponent -- SuperBEST
|
|
42
|
+
// can fuse into a single pow on the difference of fourth
|
|
43
|
+
// powers if it sees the pattern).
|
|
44
|
+
@target(fpga, clock_mhz = 200, precision = float64)
|
|
45
|
+
@verify(lean, theorem = "net_exchange_zero_at_equilibrium")
|
|
46
|
+
fn net_exchange(
|
|
47
|
+
emissivity: Real,
|
|
48
|
+
surface_t: Real,
|
|
49
|
+
ambient_t: Real,
|
|
50
|
+
) -> RadiativeFlux
|
|
51
|
+
where chain_order <= 1
|
|
52
|
+
requires (emissivity >= EPS_MIN)
|
|
53
|
+
requires (emissivity <= EPS_MAX)
|
|
54
|
+
requires (surface_t >= T_MIN)
|
|
55
|
+
requires (surface_t <= T_MAX)
|
|
56
|
+
requires (ambient_t >= T_MIN)
|
|
57
|
+
requires (ambient_t <= T_MAX)
|
|
58
|
+
{
|
|
59
|
+
emissivity * SIGMA_SB * (pow(surface_t, 4.0) - pow(ambient_t, 4.0))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Wien's displacement law -- peak wavelength of a blackbody
|
|
63
|
+
// at temperature T. lambda_max = b / T with b ≈ 2.898e-3 m·K.
|
|
64
|
+
// Chain order 0 (pure division). Companion to Stefan-Boltzmann
|
|
65
|
+
// for diagnosing the spectral peak from surface temperature.
|
|
66
|
+
@target(fpga, clock_mhz = 500, precision = float64)
|
|
67
|
+
@verify(lean, theorem = "wien_inversely_proportional_to_T")
|
|
68
|
+
fn wien_peak_wavelength(temperature_k: Real) -> Real
|
|
69
|
+
where chain_order <= 0
|
|
70
|
+
requires (temperature_k >= T_MIN)
|
|
71
|
+
requires (temperature_k <= T_MAX)
|
|
72
|
+
ensures (result > 0.0)
|
|
73
|
+
{
|
|
74
|
+
0.002897771955 / temperature_k
|
|
75
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// wave_propagation.eml -- 1-D linear wave equation snapshot.
|
|
2
|
+
//
|
|
3
|
+
// u(x, t) = A * sin(k*x - omega*t + phi)
|
|
4
|
+
//
|
|
5
|
+
// k = 2*pi / lambda, omega = 2*pi*f, c = omega / k.
|
|
6
|
+
//
|
|
7
|
+
// Chain order 1 -- one transcendental, linear argument. Closed
|
|
8
|
+
// boundary identities: u(x, 0) at phi=0 equals A*sin(k*x), and the
|
|
9
|
+
// wave speed is the constant ratio omega/k.
|
|
10
|
+
|
|
11
|
+
module wave_propagation;
|
|
12
|
+
|
|
13
|
+
const TWO_PI: Real = 6.283185307179586
|
|
14
|
+
|
|
15
|
+
@verify(lean, theorem = "wave_amplitude_in_unit_interval")
|
|
16
|
+
fn wave(amplitude: Real, k: Real, omega: Real, phi: Real,
|
|
17
|
+
x: Real, t: Real) -> Real
|
|
18
|
+
where chain_order <= 1
|
|
19
|
+
ensures (abs(result) <= abs(amplitude))
|
|
20
|
+
{
|
|
21
|
+
amplitude * sin(k * x - omega * t + phi)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Wavelength from wavenumber k = 2*pi / lambda.
|
|
25
|
+
@verify(lean, theorem = "wavelength_positive")
|
|
26
|
+
fn wavelength(k: Real) -> Real
|
|
27
|
+
where chain_order <= 0,
|
|
28
|
+
domain: k > 0.0
|
|
29
|
+
ensures (result > 0.0)
|
|
30
|
+
{
|
|
31
|
+
TWO_PI / k
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Frequency from angular frequency omega = 2*pi * f.
|
|
35
|
+
@verify(lean, theorem = "frequency_nonneg")
|
|
36
|
+
fn frequency(omega: Real) -> Real
|
|
37
|
+
where chain_order <= 0,
|
|
38
|
+
domain: omega >= 0.0
|
|
39
|
+
ensures (result >= 0.0)
|
|
40
|
+
{
|
|
41
|
+
omega / TWO_PI
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Phase velocity c = omega / k.
|
|
45
|
+
@verify(lean, theorem = "phase_velocity_positive")
|
|
46
|
+
fn phase_velocity(omega: Real, k: Real) -> Real
|
|
47
|
+
where chain_order <= 0,
|
|
48
|
+
domain: k > 0.0 && omega > 0.0
|
|
49
|
+
ensures (result > 0.0)
|
|
50
|
+
{
|
|
51
|
+
omega / k
|
|
52
|
+
}
|