Source code for squadds.simulations.drivenmodal.extractors

"""Load driven-modal comparison values from exported solver artifacts."""

from __future__ import annotations

import json
from collections.abc import Mapping
from pathlib import Path
from typing import Any

import numpy as np
import pandas as pd

from squadds.simulations.drivenmodal.capacitance import (
    capacitance_matrix_from_y,
    maxwell_capacitance_dataframe,
)
from squadds.simulations.drivenmodal.hfss_data import parameter_dataframe_to_tensor
from squadds.simulations.drivenmodal.workflows import (
    HAMILTONIAN_KEYS,
    NCAP_CAPACITANCE_KEYS,
    QUBIT_CLAW_CAPACITANCE_KEYS,
)


def _nearest_frequency_index(freqs_hz: np.ndarray, target_hz: float) -> int:
    freqs = np.asarray(freqs_hz, dtype=float)
    if freqs.size == 0:
        raise ValueError("freqs_hz must be non-empty.")
    return int(np.argmin(np.abs(freqs - target_hz)))


def _pair_fF_from_maxwell(maxwell: pd.DataFrame, row: str, col: str) -> float:
    return abs(float(maxwell.loc[row, col])) * 1e15


def _pair_capacitances_from_maxwell(maxwell: pd.DataFrame, *, system_kind: str) -> dict[str, float]:
    gnd = "ground"
    if system_kind == "qubit_claw":
        cross = "cross"
        claw = "claw"
        return {
            "cross_to_ground": _pair_fF_from_maxwell(maxwell, cross, gnd),
            "claw_to_ground": _pair_fF_from_maxwell(maxwell, claw, gnd),
            "cross_to_claw": _pair_fF_from_maxwell(maxwell, cross, claw),
            "cross_to_cross": _pair_fF_from_maxwell(maxwell, cross, cross),
            "claw_to_claw": _pair_fF_from_maxwell(maxwell, claw, claw),
            "ground_to_ground": _pair_fF_from_maxwell(maxwell, gnd, gnd),
        }
    if system_kind == "ncap":
        top = "top"
        bottom = "bottom"
        return {
            "top_to_top": _pair_fF_from_maxwell(maxwell, top, top),
            "top_to_bottom": _pair_fF_from_maxwell(maxwell, top, bottom),
            "top_to_ground": _pair_fF_from_maxwell(maxwell, top, gnd),
            "bottom_to_bottom": _pair_fF_from_maxwell(maxwell, bottom, bottom),
            "bottom_to_ground": _pair_fF_from_maxwell(maxwell, bottom, gnd),
            "ground_to_ground": _pair_fF_from_maxwell(maxwell, gnd, gnd),
        }
    raise ValueError("system_kind must be 'qubit_claw' or 'ncap'.")


[docs] def pair_capacitances_fF_from_y_frame( y_frame: pd.DataFrame, *, system_kind: str, extraction_freq_ghz: float, ) -> dict[str, float]: """Convert a Y-parameter sweep table into the same six pair labels used by Q3D rows.""" if extraction_freq_ghz <= 0: raise ValueError("extraction_freq_ghz must be positive.") expected = QUBIT_CLAW_CAPACITANCE_KEYS if system_kind == "qubit_claw" else NCAP_CAPACITANCE_KEYS if system_kind not in ("qubit_claw", "ncap"): raise ValueError("system_kind must be 'qubit_claw' or 'ncap'.") freqs_hz, y_matrices = parameter_dataframe_to_tensor( y_frame, matrix_size=2, parameter_prefix="Y", ) target_hz = extraction_freq_ghz * 1e9 idx = _nearest_frequency_index(freqs_hz, target_hz) f_hz = float(freqs_hz[idx]) y_matrix = y_matrices[idx] c_active = capacitance_matrix_from_y(f_hz, y_matrix) node_names = ["cross", "claw"] if system_kind == "qubit_claw" else ["top", "bottom"] maxwell = maxwell_capacitance_dataframe(c_active, node_names=node_names) pairs = _pair_capacitances_from_maxwell(maxwell, system_kind=system_kind) for key in expected: if key not in pairs: raise KeyError(f"Missing extracted key {key}.") return pairs
[docs] def pair_capacitances_fF_from_run_dir( run_dir: str | Path, *, system_kind: str, extraction_freq_ghz: float, ) -> dict[str, float]: """Load ``artifacts/y_parameters.pkl`` and return pair capacitances in fF.""" y_path = Path(run_dir) / "artifacts" / "y_parameters.pkl" if not y_path.is_file(): raise FileNotFoundError( f"Missing Y-parameter export at {y_path}. " "Run the Ansys driven-modal export so HFSS writes y_parameters.pkl under artifacts/." ) y_frame = pd.read_pickle(y_path) return pair_capacitances_fF_from_y_frame( y_frame, system_kind=system_kind, extraction_freq_ghz=extraction_freq_ghz, )
[docs] def hamiltonian_from_summary_mapping(extracted: Mapping[str, Any] | None) -> dict[str, float]: """Normalize a post-processing ``extracted`` record to ``HAMILTONIAN_KEYS``.""" if extracted is None: raise ValueError("extracted must not be None.") def as_float(key: str) -> float: raw = extracted.get(key) if raw is None: return float("nan") return float(raw) return {key: as_float(key) for key in HAMILTONIAN_KEYS}
[docs] def hamiltonian_from_summary_json(summary_path: str | Path) -> dict[str, float]: """Read ``artifacts/summary.json`` written by coupled driven-modal post-processing.""" path = Path(summary_path) if not path.is_file(): raise FileNotFoundError(f"Missing summary JSON at {path}.") payload = json.loads(path.read_text(encoding="utf-8")) extracted = payload.get("extracted") return hamiltonian_from_summary_mapping(extracted)
[docs] def coupled_hamiltonian_from_prepared_runs(prepared_runs: Mapping[str, Any]) -> dict[str, float]: """Load extracted Hamiltonian metrics from the first available band ``summary.json``.""" band_preferences = ("resonator_band", "qubit_band", "bridge_band") tried: list[str] = [] for band_name in band_preferences: if band_name not in prepared_runs: continue manifest = prepared_runs[band_name].get("manifest") if manifest is None: raise KeyError(f"prepared_runs[{band_name!r}] is missing 'manifest'.") run_dir = Path(manifest["run_dir"]) summary_path = run_dir / "artifacts" / "summary.json" tried.append(str(summary_path)) if summary_path.is_file(): return hamiltonian_from_summary_json(summary_path) detail = "\n - ".join(tried) if tried else "(no segmented band entries were present)." raise FileNotFoundError( "No coupled summary.json found in expected locations. Searched:\n - " + detail + "\nRun coupled post-processing so it writes summary.json next to y_parameters.pkl " "(see tutorials/Tutorial-11_DrivenModal_Coupled_System_Postprocessing.py)." )