Oscillations
The Physics.Oscillations namespace provides one-dimensional oscillator models with both analytic and numerical solutions. All oscillators implement IOscillator for a consistent API.
π΅ Simple Harmonic Oscillatorβ
Models the undamped system where .
Integration uses Velocity Verlet (symplectic, O(dtΒ²)) β excellent energy conservation for undamped systems.
using CSharpNumerics.Physics.Oscillations;
var sho = new SimpleHarmonicOscillator(mass: 2, stiffness: 50, initialPosition: 0.5);
double w = sho.AngularFrequency; // Οβ = β(k/m) = 5 rad/s
double f = sho.Frequency; // fβ = Οβ/2Ο β 0.796 Hz
double T = sho.Period; // T = 1/fβ β 1.257 s
double A = sho.Amplitude; // A = β(xβΒ² + (vβ/Οβ)Β²) = 0.5 m
double phi = sho.PhaseOffset; // Ο = atan2(-vβ/Οβ, xβ)
Analytic solution β exact reference for verification:
double x = sho.AnalyticPosition(t: 1.0); // AΒ·cos(Οβt + Ο)
double v = sho.AnalyticVelocity(t: 1.0); // -AΟβΒ·sin(Οβt + Ο)
Simulation & visualisation
sho.Step(dt: 0.001); // advance one time step
sho.Reset(); // restore initial conditions
List<Serie> trajectory = sho.Trajectory(tEnd: sho.Period * 5, dt: 0.001);
List<Serie> phase = sho.PhasePortrait(tEnd: sho.Period * 2, dt: 0.001);
Energy β conserved for undamped SHM:
double KE = sho.KineticEnergy; // Β½mvΒ²
double PE = sho.PotentialEnergy; // Β½kxΒ²
double E = sho.TotalEnergy; // KE + PE (constant)
List<Serie> energy = sho.EnergyOverTime(tEnd: sho.Period * 10, dt: 0.001);
Frequency spectrum β FFT peak at the natural frequency :
List<Serie> spectrum = sho.FrequencySpectrum(tEnd: sho.Period * 20, dt: 0.01);
With initial velocity
var sho2 = new SimpleHarmonicOscillator(mass: 1, stiffness: 25,
initialPosition: 0, initialVelocity: 5);
Console.WriteLine(sho2.Amplitude); // A = β(0Β² + (5/5)Β²) = 1.0 m
π Damped Oscillatorβ
Models where .
Three damping regimes are automatically detected:
- Underdamped () β oscillates with exponential decay
- Critically damped () β fastest non-oscillatory return
- Overdamped () β slow exponential decay
Integration uses RK4 β appropriate for dissipative systems.
var osc = new DampedOscillator(mass: 1, stiffness: 100, damping: 4, initialPosition: 1.0);
double w0 = osc.NaturalFrequency; // Οβ = 10 rad/s
double g = osc.Gamma; // Ξ³ = 2 rad/s
double wd = osc.DampedFrequency; // Ο_d = β96 β 9.80 rad/s
double Td = osc.DampedPeriod; // 2Ο/Ο_d β 0.641 s
DampingRegime regime = osc.Regime; // Underdamped
double Q = osc.QualityFactor; // Οβ/(2Ξ³) = 2.5
double delta = osc.LogarithmicDecrement; // Ξ³Β·T_d β 1.283
Regime detection
var under = new DampedOscillator(1, 100, 4); // Ξ³=2 < Οβ=10 β Underdamped
var crit = new DampedOscillator(1, 100, 20); // Ξ³=10 = Οβ=10 β CriticallyDamped
var over = new DampedOscillator(1, 100, 40); // Ξ³=20 > Οβ=10 β Overdamped
Analytic solution β all three regimes:
// Underdamped: x(t) = AΒ·e^(βΞ³t)Β·cos(Ο_dΒ·t + Ο)
// Critical: x(t) = (Cβ + Cβt)Β·e^(βΞ³t)
// Overdamped: x(t) = CβΒ·e^(rβt) + CβΒ·e^(rβt)
double x = osc.AnalyticPosition(t: 1.0);
double v = osc.AnalyticVelocity(t: 1.0);
Simulation & visualisation
osc.Step(dt: 0.001);
osc.Reset();
List<Serie> trajectory = osc.Trajectory(tEnd: 5.0, dt: 0.001);
List<Serie> phase = osc.PhasePortrait(tEnd: 5.0, dt: 0.001); // spirals to origin
Energy β monotonically decreasing:
double E = osc.TotalEnergy;
List<Serie> energy = osc.EnergyOverTime(tEnd: 5.0, dt: 0.001);
List<Serie> dissipated = osc.EnergyDissipation(tEnd: 5.0, dt: 0.001); // Eβ β E(t)
Envelope & spectrum
double env = osc.Envelope(t: 1.0); // AΒ·e^(βΞ³t)
List<Serie> envCurve = osc.EnvelopeCurve(tEnd: 5.0, dt: 0.01);
List<Serie> spectrum = osc.FrequencySpectrum(tEnd: 20.0, dt: 0.005); // peak at Ο_d
Zero damping reduces to SimpleHarmonicOscillator behaviour.
β‘ Driven Oscillatorβ
Extends the damped oscillator with a harmonic driving force:
var osc = new DrivenOscillator(
mass: 1.0, stiffness: 100.0, damping: 4.0,
driveAmplitude: 10.0, driveFrequency: 7.0,
initialPosition: 0.0, initialVelocity: 0.0);
| Property | Formula | Description |
|---|---|---|
NaturalFrequency | Undamped natural frequency | |
Gamma | Damping rate | |
DampedFrequency | Damped frequency | |
Regime | auto-detected | Underdamped, CriticallyDamped, or Overdamped |
QualityFactor | Sharpness of resonance | |
ResonanceFrequency | Frequency of max amplitude (0 if ) | |
Bandwidth | Width of resonance peak |
Steady-state response
double A = osc.SteadyStateAmplitude(wd); // (Fβ/m) / β((ΟβΒ²βΟdΒ²)Β² + (2Ξ³Οd)Β²)
double Ο = osc.SteadyStatePhase(wd); // βatan2(2Ξ³Οd, ΟβΒ²βΟdΒ²)
double Adrive = osc.SteadyStateAmplitudeAtDrive;
double Οdrive = osc.SteadyStatePhaseAtDrive;
Resonance curve & phase response
List<Serie> resonance = osc.ResonanceCurve(wMin: 0.1, wMax: 20, steps: 500);
List<Serie> phaseResp = osc.PhaseResponse(wMin: 0.1, wMax: 20, steps: 500);
Transfer function
ComplexNumber H = osc.TransferFunction(s); // H(s) = 1/(sΒ² + 2Ξ³s + ΟβΒ²)
ComplexNumber Hiw = osc.FrequencyResponse(w); // H(iΟ)
Simulation
osc.Step(dt: 0.001);
List<Serie> traj = osc.Trajectory(5.0, 0.001);
List<Serie> phase = osc.PhasePortrait(5.0, 0.001);
Steady-state extraction
List<Serie> steady = osc.SteadyStateTrajectory(
steadyDuration: 10.0, dt: 0.001, transientDuration: null); // auto = 5/Ξ³
double Ameas = osc.MeasuredSteadyStateAmplitude(dt: 0.001);
Energy & power
List<Serie> energy = osc.EnergyOverTime(tEnd: 5.0, dt: 0.001);
List<Serie> power = osc.PowerInput(tEnd: 5.0, dt: 0.001); // F(t)Β·v(t)
Frequency spectrum β peak at drive frequency :
List<Serie> spectrum = osc.FrequencySpectrum(tEnd: 50.0, dt: 0.005);
Zero driving force reduces to DampedOscillator behaviour.
π Coupled Oscillatorsβ
Models an N-mass spring chain with fixed walls:
wall βkββ mβ βkββ mβ βkββ β¦ βmβ βkββ wall
General constructor
var osc = new CoupledOscillators(
masses: new[] { 1.0, 2.0, 1.5 },
stiffnesses: new[] { 5.0, 10.0, 8.0, 5.0 }, // N+1 springs
dampings: new[] { 0.1, 0.2, 0.1 }, // optional
initialPositions: new[] { 1.0, 0.0, -0.5 },
initialVelocities: new[] { 0.0, 0.0, 0.0 });
Uniform constructor
var osc = new CoupledOscillators(
count: 5, mass: 1.0, stiffness: 4.0, damping: 0.0,
initialPositions: new[] { 1.0, 0.0, 0.0, 0.0, 0.0 });
| Property | Description |
|---|---|
Count | Number of masses |
Position(i) / Velocity(i) | State of mass |
Positions / Velocities | Copy of all current states |
KineticEnergy | |
PotentialEnergy | |
TotalEnergy | Kinetic + Potential |
Matrices & normal modes
Matrix K = osc.StiffnessMatrix(); // NΓN tridiagonal symmetric
Matrix M = osc.MassMatrix(); // NΓN diagonal
List<double> frequencies = osc.NormalModes(); // Οβ β€ Οβ β€ β¦ β€ Οβ
List<VectorN> shapes = osc.ModeShapes(); // normalised eigenvectors
Normal modes are computed via the Jacobi eigenvalue algorithm on the symmetrised dynamical matrix where .
For a uniform N-mass chain:
Simulation
osc.Step(dt: 0.001);
osc.Reset();
List<List<Serie>> trajectories = osc.Trajectory(tEnd: 5.0, dt: 0.001); // per mass
List<Serie> phase = osc.PhasePortrait(massIndex: 0, tEnd: 5.0, dt: 0.001);
Energy analysis
List<Serie> energy = osc.EnergyOverTime(tEnd: 5.0, dt: 0.001);
List<Serie> modeEnergy = osc.ModalEnergy(modeIndex: 0, tEnd: 5.0, dt: 0.001);
Dispersion relation β theoretical for uniform periodic chain:
List<Serie> dispersion = osc.DispersionRelation(kValues, latticeSpacing: 1.0);
double vp = osc.PhaseVelocity(k: 1.0); // Ο/k
double vg = osc.GroupVelocity(k: 1.0); // dΟ/dk
Frequency spectrum β peaks at excited normal mode frequencies:
List<Serie> spectrum = osc.FrequencySpectrum(massIndex: 0, tEnd: 50.0, dt: 0.01);