#!/usr/bin/env python3
"""Generate rights-clean SemiAgora seed simulations from open-source study gaps.

The calculations here are lightweight educational surrogates. They do not
execute third-party solver code, do not include proprietary PDK/model decks, and
do not encode fab recipes or equipment operating instructions.
"""

from __future__ import annotations

import csv
import json
import math
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
RESULTS = ROOT / "results"


def logistic(x: float) -> float:
    return 1.0 / (1.0 + math.exp(-x))


def write_csv(path: Path, rows: list[dict[str, object]]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    keys: list[str] = []
    for row in rows:
        for key in row:
            if key not in keys:
                keys.append(key)
    with path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=keys)
        writer.writeheader()
        writer.writerows(rows)


def round_float(value: float, digits: int = 6) -> float:
    return round(float(value), digits)


def run_dram_hbm_bank_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    t_ck_ns = 0.625
    hit_cycles = 14
    miss_cycles = 42
    for banks in (8, 16, 32):
        for row_hit_rate in (0.45, 0.60, 0.75, 0.90):
            bank_conflict = 1.0 + 0.55 / math.sqrt(banks)
            avg_cycles = (row_hit_rate * hit_cycles + (1.0 - row_hit_rate) * miss_cycles) * bank_conflict
            efficiency = min(0.98, 0.50 + row_hit_rate * 0.35 + math.log2(banks / 8) * 0.055)
            bandwidth_index = banks * efficiency / avg_cycles
            rows.append(
                {
                    "simulation_id": "SA-SEED-MEM-DRAM-HBM-BANK-001",
                    "domain": "memory",
                    "banks": banks,
                    "row_hit_rate": row_hit_rate,
                    "avg_latency_ns": round_float(avg_cycles * t_ck_ns, 4),
                    "throughput_index": round_float(bandwidth_index, 5),
                    "teaching_point": "Row-buffer locality and bank-level parallelism matter before raw bandwidth does.",
                }
            )
    write_csv(RESULTS / "dram_hbm_bank_sweep.csv", rows)
    best = max(rows, key=lambda item: float(item["throughput_index"]))
    return {
        "id": "SA-SEED-MEM-DRAM-HBM-BANK-001",
        "title": "DRAM/HBM row-buffer locality and bank parallelism",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "best_throughput_index",
            "value": best["throughput_index"],
            "case": {"banks": best["banks"], "row_hit_rate": best["row_hit_rate"]},
        },
        "source_inspiration": ["https://github.com/CMU-SAFARI/ramulator2", "https://github.com/umd-memsys/DRAMsim3"],
        "limits": [
            "Not a cycle-accurate DRAM standard model.",
            "Use Ramulator2 or DRAMsim3 for research-grade controller and timing studies.",
        ],
    }


def run_mram_switching_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for mechanism in ("STT", "SOT"):
        for thermal_barrier in (40, 60, 80):
            for pulse_ns in (0.5, 1.0, 2.0, 5.0):
                critical_ma = (0.090 if mechanism == "SOT" else 0.140) * (thermal_barrier / 60.0)
                drive_ma = critical_ma * (1.0 + 0.45 / math.sqrt(pulse_ns))
                probability = logistic((drive_ma / critical_ma - 1.0) * (3.4 + 0.25 * pulse_ns))
                voltage = 0.72 if mechanism == "SOT" else 0.86
                energy_fj = voltage * drive_ma * pulse_ns * 1000.0
                rows.append(
                    {
                        "simulation_id": "SA-SEED-MEM-MRAM-SWITCH-001",
                        "domain": "memory",
                        "mechanism": mechanism,
                        "thermal_barrier_kT": thermal_barrier,
                        "pulse_ns": pulse_ns,
                        "drive_current_mA": round_float(drive_ma, 5),
                        "switch_probability": round_float(probability, 5),
                        "write_energy_fJ": round_float(energy_fj, 4),
                        "teaching_point": "Retention, write current, pulse width, and energy pull in different directions.",
                    }
                )
    write_csv(RESULTS / "mram_switching_sweep.csv", rows)
    target_cases = [row for row in rows if float(row["switch_probability"]) >= 0.80]
    best = min(target_cases, key=lambda item: float(item["write_energy_fJ"]))
    return {
        "id": "SA-SEED-MEM-MRAM-SWITCH-001",
        "title": "MRAM switching energy versus retention barrier",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "lowest_energy_case_above_80_percent_switch_probability",
            "value_fJ": best["write_energy_fJ"],
            "case": {
                "mechanism": best["mechanism"],
                "thermal_barrier_kT": best["thermal_barrier_kT"],
                "pulse_ns": best["pulse_ns"],
            },
        },
        "source_inspiration": ["https://github.com/neurosim/DNN_NeuroSim_V1.0"],
        "limits": [
            "Probability curve is illustrative, not calibrated to a published MTJ stack.",
            "No micromagnetic solver or thermal-noise LLG integration is used.",
        ],
    }


def run_reram_pcm_fefet_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for technology, base_margin, variability, drift in (
        ("RRAM", 0.62, 0.18, 0.030),
        ("PCM", 0.54, 0.14, 0.075),
        ("FeFET", 0.58, 0.12, 0.050),
    ):
        for cycles in (1e3, 1e5, 1e7):
            for temp_c in (25, 85, 125):
                cycle_loss = math.log10(cycles) * drift
                temp_loss = max(0.0, temp_c - 25) * 0.0017
                margin = max(0.02, base_margin - cycle_loss - temp_loss)
                read_error_proxy_ppm = 1_000_000 * math.exp(-margin / max(0.02, variability))
                rows.append(
                    {
                        "simulation_id": "SA-SEED-MEM-ENVM-MARGIN-001",
                        "domain": "memory",
                        "technology": technology,
                        "cycles": int(cycles),
                        "temperature_c": temp_c,
                        "normalized_read_margin": round_float(margin, 5),
                        "read_error_proxy_ppm": round_float(read_error_proxy_ppm, 3),
                        "teaching_point": "Emerging-memory read margin is a distribution problem, not only a binary material label.",
                    }
                )
    write_csv(RESULTS / "reram_pcm_fefet_margin_sweep.csv", rows)
    worst = max(rows, key=lambda item: float(item["read_error_proxy_ppm"]))
    return {
        "id": "SA-SEED-MEM-ENVM-MARGIN-001",
        "title": "RRAM, PCM, and FeFET read-margin degradation",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "worst_read_error_proxy_ppm",
            "value_ppm": worst["read_error_proxy_ppm"],
            "case": {
                "technology": worst["technology"],
                "cycles": worst["cycles"],
                "temperature_c": worst["temperature_c"],
            },
        },
        "source_inspiration": [
            "https://github.com/sandialabs/cross-sim",
            "https://github.com/coreylammie/MemTorch",
            "https://github.com/thu-nics/MNSIM-2.0",
        ],
        "limits": [
            "Uses normalized distributions instead of material-calibrated compact models.",
            "Suitable for lesson ranking and UI rehearsal, not device selection.",
        ],
    }


def run_cim_crossbar_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for array_size in (64, 128, 256):
        for adc_bits in (4, 6, 8):
            for conductance_sigma in (0.03, 0.07, 0.12):
                ir_drop_penalty = 0.014 * (array_size / 64) ** 1.35
                quant_penalty = 0.19 / (2**adc_bits)
                variation_penalty = conductance_sigma * 0.90
                accuracy_proxy = max(0.0, 1.0 - ir_drop_penalty - quant_penalty - variation_penalty)
                energy_index = array_size * array_size * (1.0 + adc_bits * 0.22) / 1000.0
                rows.append(
                    {
                        "simulation_id": "SA-SEED-MEM-CIM-CROSSBAR-001",
                        "domain": "memory",
                        "array_size": array_size,
                        "adc_bits": adc_bits,
                        "conductance_sigma": conductance_sigma,
                        "accuracy_proxy": round_float(accuracy_proxy, 5),
                        "energy_index": round_float(energy_index, 4),
                        "teaching_point": "CIM accuracy comes from array physics, ADC choice, and mapping choices together.",
                    }
                )
    write_csv(RESULTS / "cim_crossbar_sweep.csv", rows)
    best = max(rows, key=lambda item: float(item["accuracy_proxy"]) / float(item["energy_index"]))
    return {
        "id": "SA-SEED-MEM-CIM-CROSSBAR-001",
        "title": "Compute-in-memory crossbar accuracy and energy tradeoff",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "best_accuracy_per_energy_case",
            "value": round_float(float(best["accuracy_proxy"]) / float(best["energy_index"]), 6),
            "case": {
                "array_size": best["array_size"],
                "adc_bits": best["adc_bits"],
                "conductance_sigma": best["conductance_sigma"],
            },
        },
        "source_inspiration": ["https://github.com/sandialabs/cross-sim", "https://github.com/neurosim/DNN_NeuroSim_V2.1"],
        "limits": [
            "No neural-network workload or trained weights are executed.",
            "Use CrossSim, NeuroSim, or MNSIM for research-grade studies after license review.",
        ],
    }


def run_lithography_window_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for focus_nm in range(-80, 81, 20):
        for exposure_pct in range(-8, 9, 2):
            cd_error_nm = 0.0018 * focus_nm * focus_nm - 0.42 * exposure_pct
            overlay_nm = 1.8 + 0.012 * abs(focus_nm) + 0.07 * abs(exposure_pct)
            pass_window = abs(cd_error_nm) <= 3.0 and overlay_nm <= 3.0
            rows.append(
                {
                    "simulation_id": "SA-SEED-PROC-LITHO-WINDOW-001",
                    "domain": "process",
                    "focus_nm": focus_nm,
                    "exposure_delta_pct": exposure_pct,
                    "cd_error_nm": round_float(cd_error_nm, 4),
                    "overlay_proxy_nm": round_float(overlay_nm, 4),
                    "pass_window": pass_window,
                    "teaching_point": "A process window is the usable intersection of CD, focus, exposure, and overlay constraints.",
                }
            )
    write_csv(RESULTS / "lithography_process_window.csv", rows)
    pass_count = sum(1 for row in rows if row["pass_window"])
    return {
        "id": "SA-SEED-PROC-LITHO-WINDOW-001",
        "title": "Lithography focus-exposure process window",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "passing_grid_points",
            "value": pass_count,
            "total": len(rows),
        },
        "source_inspiration": ["SemiAgora simulation atlas process-window backlog"],
        "limits": [
            "No mask, resist, OPC, scanner, or recipe data.",
            "A teaching model only; not a wafer-level process model.",
        ],
    }


def run_anneal_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    k_b = 8.617333262e-5
    for temp_c in (850, 900, 950, 1000, 1050):
        temp_k = temp_c + 273.15
        for time_s in (1, 5, 10, 30, 60):
            activation_rate = 8.0e6 * math.exp(-1.45 / (k_b * temp_k))
            activation = 1.0 - math.exp(-activation_rate * time_s)
            diffusivity = 1.0e-14 * math.exp((temp_c - 900) / 115.0)
            diffusion_nm = math.sqrt(2.0 * diffusivity * time_s) * 1.0e9
            score = activation - 0.018 * diffusion_nm
            rows.append(
                {
                    "simulation_id": "SA-SEED-PROC-ANNEAL-ACTIVATION-001",
                    "domain": "process",
                    "temperature_c": temp_c,
                    "time_s": time_s,
                    "activation_fraction": round_float(activation, 5),
                    "diffusion_length_nm": round_float(diffusion_nm, 4),
                    "junction_tradeoff_score": round_float(score, 5),
                    "teaching_point": "Annealing improves activation while also broadening dopant profiles.",
                }
            )
    write_csv(RESULTS / "anneal_activation_diffusion.csv", rows)
    best = max(rows, key=lambda item: float(item["junction_tradeoff_score"]))
    return {
        "id": "SA-SEED-PROC-ANNEAL-ACTIVATION-001",
        "title": "Anneal activation versus dopant diffusion",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "best_tradeoff_score",
            "value": best["junction_tradeoff_score"],
            "case": {"temperature_c": best["temperature_c"], "time_s": best["time_s"]},
        },
        "source_inspiration": ["SemiAgora process expansion backlog"],
        "limits": [
            "Not calibrated to a specific dopant, substrate, spike/RTA tool, or thermal budget.",
            "Does not provide operational settings.",
        ],
    }


def run_surface_treatment_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for treatment in ("O2_plasma", "O3_ozone"):
        for dose in (0.5, 1.0, 2.0, 4.0):
            for substrate in ("SiO2_like", "metal_like", "polymer_like"):
                substrate_factor = {"SiO2_like": 1.00, "metal_like": 0.82, "polymer_like": 0.64}[substrate]
                chemistry_factor = 1.22 if treatment == "O2_plasma" else 0.96
                damage_factor = 0.035 * dose * (1.6 if treatment == "O2_plasma" and substrate == "polymer_like" else 1.0)
                coverage = max(0.0, min(0.99, substrate_factor * (1.0 - math.exp(-chemistry_factor * dose)) - damage_factor))
                nucleation_delay_cycles = max(0.4, 8.0 * (1.0 - coverage))
                rows.append(
                    {
                        "simulation_id": "SA-SEED-PROC-SURFACE-O2-O3-001",
                        "domain": "process",
                        "treatment": treatment,
                        "normalized_dose": dose,
                        "substrate_class": substrate,
                        "surface_activation_proxy": round_float(coverage, 5),
                        "ald_nucleation_delay_cycles": round_float(nucleation_delay_cycles, 4),
                        "teaching_point": "Surface treatment changes nucleation, but more exposure is not always better.",
                    }
                )
    write_csv(RESULTS / "surface_treatment_o2_o3_ald.csv", rows)
    best = min(rows, key=lambda item: float(item["ald_nucleation_delay_cycles"]))
    return {
        "id": "SA-SEED-PROC-SURFACE-O2-O3-001",
        "title": "O2 plasma and O3 ozone surface activation for ALD nucleation",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "lowest_nucleation_delay_cycles",
            "value": best["ald_nucleation_delay_cycles"],
            "case": {
                "treatment": best["treatment"],
                "normalized_dose": best["normalized_dose"],
                "substrate_class": best["substrate_class"],
            },
        },
        "source_inspiration": ["User-requested surface-treatment track", "SemiAgora process expansion backlog"],
        "limits": [
            "Normalized dose only; no equipment settings, chemistry recipe, or hazard instructions.",
            "A conceptual surface-state replay for lessons, not a process recommendation.",
        ],
    }


def run_sputter_etch_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for aspect_ratio in (1, 2, 4, 6):
        for pressure_mtorr in (2, 5, 10):
            bottom_coverage = math.exp(-0.19 * aspect_ratio) * (1.0 + 0.018 * pressure_mtorr)
            sidewall_coverage = min(0.92, 0.20 + 0.055 * pressure_mtorr - 0.018 * aspect_ratio)
            resputter_risk = max(0.0, 0.35 - 0.025 * pressure_mtorr + 0.035 * aspect_ratio)
            rows.append(
                {
                    "simulation_id": "SA-SEED-PROC-SPUTTER-STEP-001",
                    "domain": "process",
                    "aspect_ratio": aspect_ratio,
                    "pressure_mTorr": pressure_mtorr,
                    "bottom_coverage_proxy": round_float(bottom_coverage, 5),
                    "sidewall_coverage_proxy": round_float(sidewall_coverage, 5),
                    "resputter_risk_proxy": round_float(resputter_risk, 5),
                    "teaching_point": "Directional PVD coverage weakens as features get deeper and narrower.",
                }
            )
    write_csv(RESULTS / "sputter_step_coverage.csv", rows)
    worst = min(rows, key=lambda item: float(item["bottom_coverage_proxy"]))
    return {
        "id": "SA-SEED-PROC-SPUTTER-STEP-001",
        "title": "Sputter/PVD step coverage versus aspect ratio",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "lowest_bottom_coverage_proxy",
            "value": worst["bottom_coverage_proxy"],
            "case": {"aspect_ratio": worst["aspect_ratio"], "pressure_mTorr": worst["pressure_mTorr"]},
        },
        "source_inspiration": ["SemiAgora process expansion backlog"],
        "limits": [
            "Does not model chamber geometry, target erosion, materials, collimation, or ionized PVD.",
            "Used for qualitative coverage intuition only.",
        ],
    }


def run_current_mirror_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    target_ua = 50.0
    for ro_kohm in (80, 160, 320):
        for vout in [round(0.1 * idx, 2) for idx in range(1, 13)]:
            compliance_v = 0.18
            compliance_drop = 0.0 if vout >= compliance_v else (compliance_v - vout) / compliance_v
            current_ua = target_ua * (1.0 + (vout - 0.6) / ro_kohm) * (1.0 - 0.85 * compliance_drop)
            error_pct = (current_ua / target_ua - 1.0) * 100.0
            rows.append(
                {
                    "simulation_id": "SA-SEED-CIR-MIRROR-COMPLIANCE-001",
                    "domain": "circuit",
                    "output_resistance_kohm": ro_kohm,
                    "vout_v": vout,
                    "current_uA": round_float(current_ua, 5),
                    "current_error_pct": round_float(error_pct, 5),
                    "teaching_point": "Current mirrors need output headroom and output resistance, not only matched devices.",
                }
            )
    write_csv(RESULTS / "current_mirror_compliance.csv", rows)
    worst = max(rows, key=lambda item: abs(float(item["current_error_pct"])))
    return {
        "id": "SA-SEED-CIR-MIRROR-COMPLIANCE-001",
        "title": "Analog current mirror compliance and output resistance",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "largest_abs_current_error_pct",
            "value_pct": abs(float(worst["current_error_pct"])),
            "case": {"output_resistance_kohm": worst["output_resistance_kohm"], "vout_v": worst["vout_v"]},
        },
        "source_inspiration": ["SemiAgora analog circuit simulation atlas"],
        "limits": [
            "Not a BSIM/PDK simulation.",
            "Use ngspice/Xyce with a public model deck for publishable circuit evidence.",
        ],
    }


def run_sense_amp_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for input_delta_mv in (2, 5, 10, 20, 40):
        for offset_sigma_mv in (2, 5, 10):
            decision_probability = logistic((input_delta_mv - 1.25 * offset_sigma_mv) / max(1.0, offset_sigma_mv * 0.42))
            regen_time_ps = 42.0 * math.log(120.0 / max(input_delta_mv, 0.5))
            fail_rate_ppm = (1.0 - decision_probability) * 1_000_000
            rows.append(
                {
                    "simulation_id": "SA-SEED-CIR-SENSE-REGEN-001",
                    "domain": "circuit",
                    "input_delta_mV": input_delta_mv,
                    "offset_sigma_mV": offset_sigma_mv,
                    "decision_probability": round_float(decision_probability, 6),
                    "regen_time_ps": round_float(regen_time_ps, 4),
                    "fail_rate_proxy_ppm": round_float(fail_rate_ppm, 3),
                    "teaching_point": "Sense amplifiers convert small voltage differences into timing and yield questions.",
                }
            )
    write_csv(RESULTS / "sense_amp_regeneration.csv", rows)
    safe = min(rows, key=lambda item: float(item["fail_rate_proxy_ppm"]))
    return {
        "id": "SA-SEED-CIR-SENSE-REGEN-001",
        "title": "Sense-amplifier input delta, offset, and regeneration",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "lowest_fail_rate_proxy_ppm",
            "value_ppm": safe["fail_rate_proxy_ppm"],
            "case": {
                "input_delta_mV": safe["input_delta_mV"],
                "offset_sigma_mV": safe["offset_sigma_mV"],
            },
        },
        "source_inspiration": ["Existing SRAM bitline/sense coverage", "SemiAgora analog circuit simulation atlas"],
        "limits": [
            "Does not solve latch differential equations or include transistor mismatch models.",
            "Good for UI/data contract rehearsal before SPICE characterization.",
        ],
    }


def run_digital_timing_sweep() -> dict[str, object]:
    rows: list[dict[str, object]] = []
    for vdd in (0.7, 0.8, 0.9, 1.0):
        for fanout in (1, 2, 4, 8):
            delay_ps = 18.0 * fanout * (1.0 / max(0.08, vdd - 0.28)) ** 1.45
            energy_fj = 0.55 * fanout * vdd * vdd
            edp = delay_ps * energy_fj
            rows.append(
                {
                    "simulation_id": "SA-SEED-CIR-FO4-TIMING-001",
                    "domain": "circuit",
                    "vdd_v": vdd,
                    "fanout": fanout,
                    "delay_ps": round_float(delay_ps, 4),
                    "energy_fJ": round_float(energy_fj, 4),
                    "edp_proxy": round_float(edp, 4),
                    "teaching_point": "Digital timing, voltage, capacitance, and energy are one tradeoff surface.",
                }
            )
    write_csv(RESULTS / "fo4_timing_energy.csv", rows)
    best = min(rows, key=lambda item: float(item["edp_proxy"]))
    return {
        "id": "SA-SEED-CIR-FO4-TIMING-001",
        "title": "FO4-style delay and energy voltage scaling",
        "status": "precomputed_educational_surrogate",
        "rows": rows,
        "headline_metric": {
            "name": "lowest_edp_proxy",
            "value": best["edp_proxy"],
            "case": {"vdd_v": best["vdd_v"], "fanout": best["fanout"]},
        },
        "source_inspiration": ["SemiAgora digital timing simulation atlas"],
        "limits": [
            "Not tied to a cell library or STA corner.",
            "Use OpenROAD/OpenSTA-style artifacts later for real path replay.",
        ],
    }


def main() -> None:
    RESULTS.mkdir(parents=True, exist_ok=True)
    simulations = [
        run_dram_hbm_bank_sweep(),
        run_mram_switching_sweep(),
        run_reram_pcm_fefet_sweep(),
        run_cim_crossbar_sweep(),
        run_lithography_window_sweep(),
        run_anneal_sweep(),
        run_surface_treatment_sweep(),
        run_sputter_etch_sweep(),
        run_current_mirror_sweep(),
        run_sense_amp_sweep(),
        run_digital_timing_sweep(),
    ]
    summary_rows = []
    domain_labels = {"MEM": "memory", "PROC": "process", "CIR": "circuit"}
    for sim in simulations:
        metric = sim["headline_metric"]
        domain_code = sim["id"].split("-")[2]
        summary_rows.append(
            {
                "id": sim["id"],
                "domain": domain_labels.get(domain_code, domain_code.lower()),
                "title": sim["title"],
                "status": sim["status"],
                "headline_metric": metric["name"],
                "source_count": len(sim["source_inspiration"]),
                "row_count": len(sim["rows"]),
            }
        )
    package = {
        "schema": "semiagora.github-open-source-seed-sweep.v1",
        "run_id": "GITHUB_OPEN_SOURCE_SEED_SWEEP_2026-08-14",
        "generated_at_kst": "2026-08-14",
        "purpose": "Rights-clean seed data for simulation-first lesson planning after GitHub/open-source exploration.",
        "simulations": simulations,
        "public_boundary": [
            "Precomputed educational data only.",
            "No live server solver execution.",
            "No PDK/model ingestion.",
            "No fab recipe, equipment-control, or operating instruction.",
            "Third-party repositories are sources of inspiration and external references unless separately licensed and reviewed.",
        ],
    }
    (RESULTS / "all_seed_results.json").write_text(json.dumps(package, indent=2), encoding="utf-8")
    write_csv(RESULTS / "seed_sweep_summary.csv", summary_rows)
    print(json.dumps({"run_id": package["run_id"], "simulation_count": len(simulations), "summary": str(RESULTS / "seed_sweep_summary.csv")}, indent=2))


if __name__ == "__main__":
    main()
