Source code for simulator.design
from __future__ import annotations
from dataclasses import dataclass
from typing import List
from .core import EngineSimulator
from . import io
[docs]
@dataclass
class SweepResult:
label: str
outfile: str
[docs]
def sweep_intake_pressure(
base_config_path: str, pressures_Pa: List[float], out_prefix: str
) -> List[SweepResult]:
"""Simple design sweep over intake pressure.
For each intake pressure value, we run one cycle and write out a result JSON.
"""
cfg = io.load_json(base_config_path)
results: List[SweepResult] = []
for p_intake in pressures_Pa:
cfg_mod = dict(cfg)
cfg_mod.setdefault("operating", {})["intake_pressure_Pa"] = p_intake
sim = EngineSimulator.from_dict(cfg_mod)
sim_result = sim.run(cycles=1)
outfile = f"{out_prefix}_pint_{int(p_intake/100.0):04d}.json"
io.save_json(outfile, sim_result.to_dict())
results.append(SweepResult(label=f"{p_intake/1000.0:.1f} kPa", outfile=outfile))
return results
[docs]
def sweep_speed_full_load(
base_config_path: str, speeds_rpm: List[float], out_prefix: str
) -> List[SweepResult]:
"""Full-load style speed sweep (virtual dyno).
We keep the thermodynamic setup fixed (AFR, pressure-rise factor, etc.)
and vary engine speed. Because the core model is 0-D, "full load" here
simply means WOT-style fixed inputs; brake power scales with speed.
Each point writes a JSON result and returns a simple label/outfile pair.
"""
cfg = io.load_json(base_config_path)
results: List[SweepResult] = []
for N in speeds_rpm:
cfg_mod = dict(cfg)
cfg_mod.setdefault("operating", {})["engine_speed_rpm"] = N
sim = EngineSimulator.from_dict(cfg_mod)
sim_result = sim.run(cycles=1)
outfile = f"{out_prefix}_N_{int(N):05d}.json"
io.save_json(outfile, sim_result.to_dict())
results.append(SweepResult(label=f"{N:.0f} rpm", outfile=outfile))
return results
[docs]
def sweep_speed_motored(
base_config_path: str, speeds_rpm: List[float], out_prefix: str
) -> List[SweepResult]:
"""Motored sweep vs speed (virtual motoring test).
We set pressure_rise_factor=0 and combustion_efficiency=0 so the
cycle is pure compression/expansion + pumping. That mimics dyno
motoring tests used to derive friction / pumping maps.
"""
cfg = io.load_json(base_config_path)
results: List[SweepResult] = []
for N in speeds_rpm:
cfg_mod = dict(cfg)
op = cfg_mod.setdefault("operating", {})
op["engine_speed_rpm"] = N
op["pressure_rise_factor"] = 0.0
op["combustion_efficiency"] = 0.0
sim = EngineSimulator.from_dict(cfg_mod)
sim_result = sim.run(cycles=1)
outfile = f"{out_prefix}_motored_N_{int(N):05d}.json"
io.save_json(outfile, sim_result.to_dict())
results.append(SweepResult(label=f"motored {N:.0f} rpm", outfile=outfile))
return results