Optional. Add a viscous damper to oscillator A, so that its energy is no longer conserved. Three quantities are easy to confuse here: PhysLib's damping constant γ (kg/s); ISO 80000-3's damping coefficient δ = γ/(2m) (s⁻¹), which PhysLib calls the decay rate; and the angular frequency ω₀ (rad/s), which has the same dimension as δ. The lesson gives each its own kind, so adding δ to ω₀ fails to type-check. decayRateOf computes δ from one oscillator's γ and mass with IndividualQuantity.div, so oscillator B's mass cannot be used for oscillator A's δ.
KindOfProperty (id, scale) — define a kind of property; the lesson defines its own kind for γ.
ScaleType.ratio — the scale type that licenses × and ÷.
dampingCoefficient — ISO 80000-3 damping coefficient δ, dimension T⁻¹.
KindDiv, QuotientKind.ofRatio — a curated quotient law, here δ = γ/(2m).
Quantity.add — kind- and scale-gated addition.
IndividualQuantity.toQuantity — forget the bearer.
angularFrequencyK — the oscillator's ω.
Carried from earlier steps: DimensionedKind, IndividualQuantity and its add/div, DifferenceKind.ofScale, SortOfSystem, Sorted, Designated, massK, springConstantK.
The damped oscillator's energy decreases at the rate stated by PhysLib's energy-dissipation theorem (theorem) HCP.L5_Damping#energy_dissipation.
decayRateOf divides A's γ by A's 2m, and its magnitude equals PhysLib's decayRate; using B's mass instead fails with a type mismatch (refused) HCP.L5_Damping#kinds_and_decay_rate.
δ and ω₀ have the same dimension, T⁻¹, and adding them fails to type-check (refused) HCP.L5_Damping#same_dimension_different_kinds.
PhysLib's IsUnderdamped is equivalent to δ < ω₀. Comparing the two as kinded quantities fails with a type mismatch, because their kinds differ; IsUnderdamped implies that the magnitude of δ is less than the magnitude of ω₀ (theorem) HCP.L5_Damping#underdamped_regime.
Subtracting an earlier record of A's δ from a later one type-checks as an IndividualQuantity about A of the damping-coefficient kind, whose magnitude is the difference of the two decay rates; subtracting B's δ from A's fails with a type mismatch (type-checked) HCP.L5_Damping#same_oscillator_states.
You are a PhysLib contributor: a physicist who writes Lean in PhysLib. You know Lean,
PhysLib, Dimension, and WithDim. L0–L4 modeled an ideal, conservative oscillator;
this optional rung adds the damping that makes its energy decay.
The oscillator loses energy as its motion fades. This optional rung adds a damper to the nominally named whole-system model. Do not confuse the damper's viscous constant γ with the whole oscillator's decay rate δ. One name is one system: records under that name are its states at different times, configurations, or rival estimates; comparing them is the point. Distinct names are distinct systems.
Similar symbols and dimensions. Bare reals accept γ < ω₀ although the damper's viscous constant γ and angular frequency ω₀ are different kinds; dimension-only types accept δ + ω₀ because both have dimension T⁻¹. The kinded checks pin the distinction at HCP.L5_Damping#kinds_and_decay_rate and HCP.L5_Damping#same_dimension_different_kinds.
Cross-system contamination. Oscillator B's mass cannot be used to compute oscillator A's decay rate, and states of A and B cannot be mixed; see HCP.L5_Damping#kinds_and_decay_rate and HCP.L5_Damping#same_oscillator_states.
The oscillator loses energy. What must the model add, and how should it distinguish the damper's measurement from the whole oscillator's decay?
Add a damper to the oscillator and represent the whole as a nominally named DampedRig.
γ is measured on the damper and m on the body; both retain the whole oscillator's bearer. ISO damping coefficient δ and the damping regime belong to the whole.
Bare γ < ω and δ + ω₀ compile despite wrong or distinct kinds; cross-system division and mixing A's and B's states are refused.
DampedHarmonicOscillator.Basic; ISO 80000-3 items 3-18, 3-24, and 3-25.
L0–L4's conservation assumption ends here. The PhysLib dissipation law says that the
energy derivative is negative when the damper is active and the velocity is nonzero.
This rung is optional; the requirements lesson remains L6.
Apply the upstream energy laws to the state stored in DampedRig.
energy_dissipation
The HCP wrapper does not restate the physics: it supplies its own r.osc to PhysLib's energy_dissipation_rate and energy_not_conserved.
2
Read m on the body and γ on the damper; form their typed decay-rate quotient on the whole oscillator.
kinds_and_decay_rate
Both readings retain bearer o; m + m expresses 2m without dropping it. The local KindDiv licenses γ/(m + m), and A's γ cannot be divided by B's mass. Bare γ < ω is accepted but wrong.
3
Compare δ with ω₀ first as reals, then as kinds.
same_dimension_different_kinds
Both are T⁻¹, but damping coefficient and angular frequency are distinct kinds. The bare sum is accepted; kinded addition is refused.
4
State the underdamped criterion and make its erasure to real magnitudes visible.
underdamped_regime
PhysLib proves the decay-rate inequality; the kinded comparison is refused. The accepted magnitude comparison exposes the rad = 1 convention.
5
Compare earlier and later states of oscillator A; try to mix oscillator B.
same_oscillator_states
A typed difference measures A's change in decay rate; distinct nominal names cannot be combined.
1. Energy is no longer conserved. The HCP state wraps PhysLib's damped oscillator,
so the upstream dissipation results apply to r.osc. L0–L4's conservation assumption
ends here:
/-- Dissipation: Proves that under oscillator O's damped equation, the energy derivative is −γ times the squared speed. -/lemmaenergy_dissipation_on_state{o:Osc}(r:DampedRigo)(xₜ:Time→EuclideanSpaceℝ(Fin1))(t:Time)(h1:r.osc.EquationOfMotionxₜ)(hx:ContDiffℝ∞xₜ):∂ₜ(r.osc.energyxₜ)t=-r.osc.γ*innerℝ(∂ₜxₜt)(∂ₜxₜt):=r.osc.energy_dissipation_ratexₜth1hx/-- Dissipation: Proves that positive γ and nonzero velocity make oscillator O's energy derivative strictly negative. -/lemmaenergy_not_conserved_on_state{o:Osc}(r:DampedRigo)(xₜ:Time→EuclideanSpaceℝ(Fin1))(t:Time)(h1:r.osc.EquationOfMotionxₜ)(hx:ContDiffℝ∞xₜ)(hdx:∂ₜxₜt≠0)(hγ:0<r.osc.γ):∂ₜ(r.osc.energyxₜ)t<0:=r.osc.energy_not_conservedxₜth1hxhdxhγ
2. One name, two kinds.γ is the viscous damping constant read on the damper
(kg/s), and m is measured on the body. Both quantities retain the whole oscillator's
bearer o. ISO 3-24's damping coefficient δ (s⁻¹) is instead PhysLib's decayRate,
attached to the whole. The local quotient law makes the typed γ/(m + m) formula possible:
defviscousDampingConstantK:KindOfProperty:={id:="viscous damping constant",scale:=.ratio}defdampingCoefficientK:KindOfProperty:=Iso80000.Part3.dampingCoefficient.kinddefdampingConstantOf{o:Osc}(r:DampedRigo):IndividualQuantityoviscousDampingConstantKℝ:=⟨r.osc.γ⟩instance:KindDivviscousDampingConstantKmassKdampingCoefficientK:=⟨QuotientKind.ofRatio___⟩defdecayRateOf{o:Osc}(r:DampedRigo):IndividualQuantityodampingCoefficientKℝ:=-- Write 2m as m + m to keep the mass on the oscillator bearer.IndividualQuantity.div(KindDiv.law(k₁:=viscousDampingConstantK)(k₂:=massK)(k:=dampingCoefficientK))(dampingConstantOfr)(IndividualQuantity.addDifferenceKind.ofScale(massOfr)(massOfr))/-- Kind vs dimension: Proves that oscillator O's indexed damping-coefficient magnitude equals PhysLib's decayRate γ/(2m). -/lemmadecayRateOf_magnitude{o:Osc}(r:DampedRigo):(decayRateOfr).magnitude=r.osc.decayRate:=o:Oscr:DampedRigo⊢ (decayRateOfr).magnitude=r.osc.decayRateo:Oscr:DampedRigo⊢ r.osc.γ/Carrier.addr.osc.mr.osc.m=r.osc.decayRateo:Oscr:DampedRigo⊢ r.osc.γ/(r.osc.m+r.osc.m)=r.osc.decayRateo:Oscr:DampedRigo⊢ 2*r.osc.m*r.osc.decayRate/(2*r.osc.m)=r.osc.decayRatehavehm:(2*r.osc.m)≠0:=mul_ne_zero(byo:Oscr:DampedRigo⊢ 2≠0norm_numAll goals completed! 🐙)r.osc.m_ne_zeroo:Oscr:DampedRigohm:2*r.osc.m≠0⊢ 2*r.osc.m*r.osc.decayRate/(2*r.osc.m)=r.osc.decayRatefield_simp[hm]All goals completed! 🐙defangularFrequencyOf{o:Osc}(r:DampedRigo):IndividualQuantityoangularFrequencyKℝ:=⟨r.osc.ω⟩/-- Kind vs dimension: Proves that oscillator O's indexed angular-frequency magnitude equals its angular frequency ω. -/lemmaangularFrequencyOf_magnitude{o:Osc}(r:DampedRigo):(angularFrequencyOfr).magnitude=r.osc.ω:=rfl-- Kind vs dimension: Bare reals let Lean compare oscillator A's γ (kg/s) with ω (s⁻¹), despite their different dimensions; this is not the δ < ω regime test.example(r:DampedRig.A):Prop:=r.osc.γ<r.osc.ω-- Kind vs dimension: Proves that oscillator A's indexed decay-rate magnitude equals PhysLib's decayRate.example(r:DampedRig.A):(decayRateOfr).magnitude=r.osc.decayRate:=decayRateOf_magnituder-- Dissipation: Proves that PhysLib's law for oscillator A is γ = 2m·decayRate, relating damper reading, body mass, and decay rate.example(r:DampedRig.A):r.osc.γ=2*r.osc.m*r.osc.decayRate:=r.osc.gamma_eq_two_mul_m_mul_decayRate-- Bearer safety: Refused: type mismatch between oscillator B's mass bearer and oscillator A's quotient bearer; this prevents computing A's decay rate with B's mass./--
error: Application type mismatch: The argument
b
has type
DampedRig Osc.B
but is expected to have type
DampedRig Osc.A
in the application
massOf b
-/#guard_msgsinexample(a:DampedRig.A)(b:DampedRig.B):=IndividualQuantity.div(KindDiv.law(k₁:=viscousDampingConstantK)(k₂:=massK)(k:=dampingCoefficientK))(dampingConstantOfa)(massOfb)
The real-valued γ < ω above is accepted, but wrong: γ is kg/s, not the whole
system's decay rate. massOf and dampingConstantOf both retain the oscillator's
nominal bearer: mass is measured on the designated body and γ on the designated damper.
Writing 2m as m + m keeps the quotient bearer-safe; the pinned refusal below prevents
dividing A's damper reading by B's mass.
3. Same dimension, different kind. Both δ and natural angular frequency ω₀ have
dimension T⁻¹; the bare-real sum is accepted, while the kinded sum is refused:
-- Kind vs dimension: Proves that damping coefficient δ and angular frequency ω both have dimension T⁻¹; matching dimensions alone do not license mixing them.example:Iso80000.Part3.dampingCoefficient.dim=Iso80000.Part3.angularFrequency.dim:=rfl-- Kind vs dimension: Proves that dampingCoefficientK and angularFrequencyK are distinct kinds despite their shared T⁻¹ dimension.example:dampingCoefficientK≠angularFrequencyK:=by⊢ dampingCoefficientK≠angularFrequencyKdecideAll goals completed! 🐙-- Kind vs dimension: Bare reals accept oscillator A's δ + ω as a real although the decay-rate and angular-frequency kinds differ; no sum kind is recorded.example(r:DampedRig.A):ℝ:=r.osc.decayRate+r.osc.ω-- Kind vs dimension: Refused: type mismatch between dampingCoefficientK and angularFrequencyK quantities; this prevents summing δ and ω despite their shared T⁻¹ dimension./--
error: Application type mismatch: The argument
(angularFrequencyOf r).toQuantity
has type
Quantity angularFrequencyK ℝ
but is expected to have type
Quantity dampingCoefficientK ℝ
in the application
Quantity.add ⋯ (decayRateOf r).toQuantity (angularFrequencyOf r).toQuantity
-/#guard_msgsinexample(r:DampedRig.A):=Quantity.addDifferenceKind.ofScale(decayRateOfr).toQuantity(angularFrequencyOfr).toQuantity
4. Regime. PhysLib's isUnderdamped_iff_decayRate_lt reduces the regime to the
numerical condition. Its kinded < is refused; the accepted HCP comparison states
explicitly that magnitudes are being compared:
-- Regime: Proves that oscillator A is underdamped exactly when its numerical decay rate δ is below ω.example(r:DampedRig.A):r.osc.IsUnderdamped↔r.osc.decayRate<r.osc.ω:=r.osc.isUnderdamped_iff_decayRate_lt-- Regime: Refused: type mismatch between angularFrequencyK and dampingCoefficientK; direct comparison erases the magnitude conversion and assumes rad = 1./--
error: Type mismatch
(angularFrequencyOf r).toQuantity
has type
Quantity angularFrequencyK ℝ
but is expected to have type
Quantity dampingCoefficientK ℝ
-/#guard_msgsinexample(r:DampedRig.A):=(decayRateOfr).toQuantity<(angularFrequencyOfr).toQuantity/-- Regime: Proves that an underdamped oscillator O has indexed decay-rate magnitude below its indexed angular-frequency magnitude. -/lemmaunderdamped_magnitude_test{o:Osc}(r:DampedRigo)(h:r.osc.IsUnderdamped):(decayRateOfr).magnitude<(angularFrequencyOfr).magnitude:=byo:Oscr:DampedRigoh:r.osc.IsUnderdamped⊢ (decayRateOfr).magnitude<(angularFrequencyOfr).magnituderw[decayRateOf_magnitude,o:Oscr:DampedRigoh:r.osc.IsUnderdamped⊢ r.osc.decayRate<(angularFrequencyOfr).magnitudeangularFrequencyOf_magnitudeo:Oscr:DampedRigoh:r.osc.IsUnderdamped⊢ r.osc.decayRate<r.osc.ω]o:Oscr:DampedRigoh:r.osc.IsUnderdamped⊢ r.osc.decayRate<r.osc.ωexactr.osc.isUnderdamped_decayRatehAll goals completed! 🐙
The honest criterion is the dimensionless ratio δ/ω₀ < 1. Directly ordering the
kinded quantities erases that conversion and silently assumes rad = 1.
5. States of one oscillator. Two DampedRig .A records can describe the same
oscillator before and after its damper ages. Their typed difference computes the change
from those states; B remains a different system:
defdecayRateChange(earlierlater:DampedRig.A):IndividualQuantityOsc.AdampingCoefficientKℝ:=IndividualQuantity.addDifferenceKind.ofScale(decayRateOflater)(-(decayRateOfearlier))/-- Same-name records: Proves that oscillator A's indexed decay-rate difference has magnitude later decay rate minus earlier decay rate. -/lemmadecayRateChange_magnitude(earlierlater:DampedRig.A):(decayRateChangeearlierlater).magnitude=later.osc.decayRate-earlier.osc.decayRate:=byearlier:DampedRigOsc.Alater:DampedRigOsc.A⊢ (decayRateChangeearlierlater).magnitude=later.osc.decayRate-earlier.osc.decayRatesimp[decayRateChange,IndividualQuantity.add_magnitude,IndividualQuantity.neg_magnitude,decayRateOf_magnitude]earlier:DampedRigOsc.Alater:DampedRigOsc.A⊢ Carrier.addlater.osc.decayRate(-earlier.osc.decayRate)=later.osc.decayRate-earlier.osc.decayRatechangelater.osc.decayRate+-(earlier.osc.decayRate)=later.osc.decayRate-earlier.osc.decayRateearlier:DampedRigOsc.Alater:DampedRigOsc.A⊢ later.osc.decayRate+-earlier.osc.decayRate=later.osc.decayRate-earlier.osc.decayRateringAll goals completed! 🐙-- Bearer safety: Refused: type mismatch between oscillator A's and oscillator B's damping-coefficient bearers; this prevents subtracting one oscillator's decay rate from the other's./--
error: Type mismatch
-decayRateOf b
has type
IndividualQuantity Osc.B dampingCoefficientK ℝ
but is expected to have type
IndividualQuantity Osc.A dampingCoefficientK ℝ
-/#guard_msgsinexample(a:DampedRig.A)(b:DampedRig.B):=IndividualQuantity.addDifferenceKind.ofScale(decayRateOfa)(-(decayRateOfb))
Body, Spring, and Damper are designated particulars of the damped oscillator.
γ is measured on the damper; δ and IsUnderdamped describe the whole. Records
under one name may be states, configurations, or rival estimates; distinct names are
distinct systems. L5 is optional and revisits the no-dissipation assumption left by L0–L4.
Keep the lesson independent and use nominal Osc | A | B identity.
Treat records under one name as states, configurations, or rival estimates of one
system; compare them, and refuse different names.
Keep Body, Spring, and Damper as designated particulars. Measure γ on the
damper and m on the body, while retaining the whole oscillator as the bearer of both;
place δ and the regime on the whole.
Apply PhysLib laws to r.osc; every accepted HCP quantity must compute from the
lesson's own state fields.
Keep accepted formulas at the IndividualQuantity level; do not drop the oscillator
bearer to build the decay-rate quotient.
Keep the γ kind distinct from ISO 3-24 damping coefficient, even though the
upstream formula relates them.
Compare δ and ω₀ by explicit magnitudes when showing the underdamped criterion;
do not present their kinded comparison as licensed.
Do not change ForPhysLib, PKC core, PhysLib, or Tutorial.