Files
gausse/tests/test_inductance.py
jze9 2046dcba10 Add component database layer and validated physics core
- components/schema.py + database.py: typed loader for real retail parts
  (wire, capacitors, switches, sensors, projectile materials)
- physics/inductance.py: multilayer solenoid inductance (Wheeler) with
  ferromagnetic-slug coupling via smooth overlap model and analytic dL/dx
- physics/force.py: F = 0.5*I^2*dL/dx with a saturation clamp
- physics/circuit.py: coupled [Q,I,x,v] discharge ODE
- Validated against textbook RLC analytical solutions (under/over/critically
  damped) and energy conservation, not just spot-checked by eye
- PLAN.md updated with animation (per-run field visualization) and
  GPU-accelerated batch sweep as explicit later stages

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 19:45:32 +05:00

83 lines
3.1 KiB
Python

import math
import numpy as np
import pytest
from gausse.physics.inductance import (
CoilInductanceModel,
air_core_inductance_wheeler,
demagnetizing_factor_prolate,
effective_permeability,
winding_geometry,
)
def test_winding_geometry_scales_with_turns_and_layers():
geo = winding_geometry(tube_od_m=0.02, wire_od_m=0.001, turns_per_layer=50, layers=3)
assert geo.total_turns == 150
assert geo.coil_length_m == pytest.approx(0.05)
assert geo.radial_depth_m == pytest.approx(0.003)
assert geo.total_wire_length_m > 0
# больше слоёв -> больше провода при тех же витках на слой
geo_more_layers = winding_geometry(0.02, 0.001, 50, 5)
assert geo_more_layers.total_wire_length_m > geo.total_wire_length_m
def test_wheeler_inductance_is_positive_and_grows_with_turns():
l_small = air_core_inductance_wheeler(mean_radius_m=0.015, length_m=0.05, depth_m=0.003, turns=50)
l_large = air_core_inductance_wheeler(mean_radius_m=0.015, length_m=0.05, depth_m=0.003, turns=200)
assert l_small > 0
assert l_large > l_small
def test_demagnetizing_factor_bounds():
# почти сфера (aspect_ratio -> 1): классическое значение 1/3
assert demagnetizing_factor_prolate(1.001) == pytest.approx(1 / 3, abs=0.02)
# очень вытянутый стержень: коэффициент размагничивания стремится к 0
assert demagnetizing_factor_prolate(200.0) < 0.01
def test_effective_permeability_limits():
# без размагничивания (Nd=0) эффективная проницаемость равна мю материала
assert effective_permeability(mu_r=500.0, demagnetizing_factor=0.0) == pytest.approx(500.0)
# полное размагничивание (Nd=1) гасит эффект проницаемости до 1
assert effective_permeability(mu_r=500.0, demagnetizing_factor=1.0) == pytest.approx(1.0)
@pytest.fixture
def model() -> CoilInductanceModel:
return CoilInductanceModel(
l_air_h=100e-6,
coil_length_m=0.05,
slug_length_m=0.03,
mu_eff=50.0,
smoothing_width_m=0.001,
)
def test_inductance_peaks_when_slug_centered(model):
l_center = model.l_of_x(np.array(0.0))
l_far = model.l_of_x(np.array(0.2))
assert l_center > l_far
assert l_far == pytest.approx(model.l_air_h, rel=1e-3)
def test_dl_dx_vanishes_at_symmetric_center(model):
assert model.dl_dx(np.array(0.0)) == pytest.approx(0.0, abs=1e-9)
def test_dl_dx_sign_flips_across_center(model):
# слева от центра индуктивность растёт (dL/dx > 0), справа убывает (< 0)
assert model.dl_dx(np.array(-0.01)) > 0
assert model.dl_dx(np.array(0.01)) < 0
def test_analytic_dl_dx_matches_finite_difference(model):
xs = np.linspace(-0.06, 0.06, 25)
h = 1e-7
analytic = np.array([model.dl_dx(x) for x in xs])
finite_diff = np.array(
[(model.l_of_x(x + h) - model.l_of_x(x - h)) / (2 * h) for x in xs]
)
assert np.allclose(analytic, finite_diff, rtol=1e-3, atol=1e-9)