import random import pytest from gausse.components.database import ComponentDatabase from gausse.components.schema import CapacitorBank, CapacitorSpec from gausse.optim.objective import build_detail from gausse.optim.search_space import SearchBounds, decode, sample_genome from gausse.sim.coilgun import run_coilgun BASE = CapacitorSpec( part_number="c-base", capacitance_uf=100.0, voltage_v=400.0, esr_ohm=0.2, max_current_a=20.0, price=150.0, source="test", ) def test_series_multiplies_voltage_and_divides_capacitance(): bank = CapacitorBank.from_spec(BASE, n_series=3, n_parallel=1) assert bank.voltage_v == pytest.approx(1200.0) # 400 * 3 assert bank.capacitance_uf == pytest.approx(100.0 / 3) # C / 3 assert bank.esr_ohm == pytest.approx(0.6) # ESR * 3 assert bank.count == 3 assert bank.price == pytest.approx(450.0) # 150 * 3 def test_parallel_multiplies_capacitance_current_and_divides_esr(): bank = CapacitorBank.from_spec(BASE, n_series=1, n_parallel=4) assert bank.capacitance_uf == pytest.approx(400.0) # C * 4 assert bank.voltage_v == pytest.approx(400.0) # без изменения assert bank.esr_ohm == pytest.approx(0.05) # ESR / 4 assert bank.max_current_a == pytest.approx(80.0) # 20 * 4 -- вот "на силу тока" assert bank.count == 4 def test_series_parallel_combination(): bank = CapacitorBank.from_spec(BASE, n_series=2, n_parallel=3) assert bank.voltage_v == pytest.approx(800.0) assert bank.capacitance_uf == pytest.approx(100.0 * 3 / 2) assert bank.esr_ohm == pytest.approx(0.2 * 2 / 3) assert bank.count == 6 assert bank.price == pytest.approx(150.0 * 6) def test_energy_scales_with_bank(): single = CapacitorBank.from_spec(BASE, 1, 1) bank = CapacitorBank.from_spec(BASE, 2, 2) # V×2, C×1 (2 series, 2 parallel => C*2/2=C) e_single = 0.5 * (single.capacitance_uf * 1e-6) * single.voltage_v**2 e_bank = 0.5 * (bank.capacitance_uf * 1e-6) * bank.voltage_v**2 # тот же C, но вдвое большее напряжение -> вчетверо больше энергии assert e_bank == pytest.approx(4 * e_single) def test_decoded_record_contains_capacitor_bank_composition(): db = ComponentDatabase.load() bounds = SearchBounds() rng = random.Random(1) genome = sample_genome(db, bounds, rng) config, ix, iv = decode(genome, db, bounds) result = run_coilgun(config) detail = build_detail(config, result, db, ix, iv) bank = detail["stages"][0]["capacitor_bank"] assert set(bank) >= {"base_part", "n_series", "n_parallel", "count", "total_voltage_rating_v", "max_current_a"} assert bank["count"] == bank["n_series"] * bank["n_parallel"] # база должна быть реальной деталью assert bank["base_part"] in {c.part_number for c in db.capacitors} def test_genome_carries_series_parallel_and_they_vary(): db = ComponentDatabase.load() bounds = SearchBounds() rng = random.Random(5) series_vals, parallel_vals = set(), set() for _ in range(40): g = sample_genome(db, bounds, rng) for st in g.stages: assert bounds.cap_series_min <= st.cap_series <= bounds.cap_series_max assert bounds.cap_parallel_min <= st.cap_parallel <= bounds.cap_parallel_max series_vals.add(st.cap_series) parallel_vals.add(st.cap_parallel) assert len(series_vals) > 1 and len(parallel_vals) > 1