Source code for complex_problems.antenna_radiation.result_dialog

"""Result dialog for antenna radiation simulations."""

from __future__ import annotations

import tkinter as tk
from tkinter import ttk

import numpy as np

from complex_problems.antenna_radiation.solver import AntennaRadiationResult
from config import get_env_from_schema
from frontend.plot_embed import embed_plot_in_tk
from frontend.window_utils import center_window, make_modal
from plotting import create_contour_plot, create_solution_plot


def _create_polar_cut_figure(theta_deg: np.ndarray, cut_db: np.ndarray, *, title: str) -> "object":
    import matplotlib.pyplot as plt

    theta_rad = np.deg2rad(theta_deg)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="polar")
    ax.plot(theta_rad, cut_db, linewidth=2.0)
    ax.set_title(title)
    ax.set_theta_zero_location("N")
    ax.set_theta_direction(-1)
    ax.set_rlabel_position(135)
    ax.grid(True, alpha=0.35)
    fig.tight_layout()
    return fig


def _create_3d_pattern_figure(
    theta_deg: np.ndarray,
    phi_deg: np.ndarray,
    gain_db: np.ndarray,
) -> "object":
    import matplotlib.pyplot as plt

    theta = np.deg2rad(theta_deg)
    phi = np.deg2rad(phi_deg)
    TH, PH = np.meshgrid(theta, phi, indexing="ij")

    g_lin = np.power(10.0, np.maximum(gain_db, -40.0) / 10.0)
    r = g_lin / (float(np.max(g_lin)) + 1e-15)
    x = r * np.sin(TH) * np.cos(PH)
    y = r * np.sin(TH) * np.sin(PH)
    z = r * np.cos(TH)

    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    ax.plot_surface(x, y, z, cmap="viridis", linewidth=0.0, antialiased=True, alpha=0.95)
    ax.set_title("Normalized 3D radiation pattern")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")
    fig.tight_layout()
    return fig


[docs] class AntennaRadiationResultDialog: """Result window for antenna radiation problem.""" def __init__(self, parent: tk.Tk | tk.Toplevel, *, result: AntennaRadiationResult) -> None: self.parent = parent self._result = result self.win = tk.Toplevel(parent) self.win.title("Results - Antenna Radiation") self.win.configure(bg=get_env_from_schema("UI_BACKGROUND")) self._map_canvas = None self._cut_canvas = None self._phi_canvas = None self._surface_canvas = None self._field_canvas = None self._build_ui() self.win.protocol("WM_DELETE_WINDOW", self._on_close) center_window(self.win, width=1380, height=920, max_width_ratio=0.96, resizable=True) self.win.minsize(1080, 720) make_modal(self.win, parent) def _on_close(self) -> None: import matplotlib.pyplot as plt for attr in ( "_map_canvas", "_cut_canvas", "_phi_canvas", "_surface_canvas", "_field_canvas", ): canvas = getattr(self, attr, None) if canvas is not None and hasattr(canvas, "figure"): try: plt.close(canvas.figure) except Exception: pass self.win.destroy() def _build_ui(self) -> None: pad = int(get_env_from_schema("UI_PADDING")) top = ttk.Frame(self.win, padding=pad) top.pack(fill=tk.BOTH, expand=True) mag = self._result.magnitudes info_txt = ( f"Dmax: {mag['directivity_max_db']:+.2f} dBi " f"Gmax: {mag['gain_max_db']:+.2f} dBi " f"BW(-3dB): {mag['beamwidth_deg']:.2f} deg " f"Max E(rms): {mag['max_e_rms_vpm']:.3g} V/m" ) far_field_note = ( "Far field: OK" if self._result.metadata.get("is_far_field", False) else f"Far field: NOT OK (Rmin={mag['far_field_min_m']:.3g} m)" ) ttk.Label(top, text=info_txt, style="Small.TLabel").pack(anchor=tk.W, pady=(0, 2)) ttk.Label(top, text=far_field_note, style="Small.TLabel").pack(anchor=tk.W, pady=(0, pad)) nb = ttk.Notebook(top) nb.pack(fill=tk.BOTH, expand=True) tab_map = ttk.Frame(nb) nb.add(tab_map, text=" Angular Map ") self._build_map_tab(tab_map) tab_cut = ttk.Frame(nb) nb.add(tab_cut, text=" Theta Cut ") self._build_theta_cut_tab(tab_cut) tab_phi = ttk.Frame(nb) nb.add(tab_phi, text=" Phi Cut ") self._build_phi_cut_tab(tab_phi) tab_3d = ttk.Frame(nb) nb.add(tab_3d, text=" 3D Pattern ") self._build_3d_tab(tab_3d) tab_field = ttk.Frame(nb) nb.add(tab_field, text=" Field Map ") self._build_field_tab(tab_field) btn_frame = ttk.Frame(self.win, padding=(pad, 0, pad, pad)) btn_frame.pack(fill=tk.X) ttk.Button(btn_frame, text="Close", style="Cancel.TButton", command=self._on_close).pack( side=tk.RIGHT ) def _build_map_tab(self, parent: ttk.Frame) -> None: fig = create_contour_plot( self._result.phi, self._result.theta, self._result.gain_db, title="Gain pattern (dBi) vs angles", xlabel="φ (deg)", ylabel="θ (deg)", ) self._map_canvas = embed_plot_in_tk(fig, parent) def _build_theta_cut_tab(self, parent: ttk.Frame) -> None: fig = _create_polar_cut_figure( self._result.theta, self._result.theta_cut_db, title="θ cut (φ=0) in dBi", ) self._cut_canvas = embed_plot_in_tk(fig, parent) def _build_phi_cut_tab(self, parent: ttk.Frame) -> None: fig = create_solution_plot( self._result.phi, np.atleast_2d(self._result.phi_cut_db), title="φ cut at θ=90 deg", xlabel="φ (deg)", ylabel="Gain (dBi)", selected_derivatives=[0], labels=["Gain"], ) self._phi_canvas = embed_plot_in_tk(fig, parent) def _build_3d_tab(self, parent: ttk.Frame) -> None: fig = _create_3d_pattern_figure(self._result.theta, self._result.phi, self._result.gain_db) self._surface_canvas = embed_plot_in_tk(fig, parent) def _build_field_tab(self, parent: ttk.Frame) -> None: fig = create_contour_plot( self._result.phi, self._result.theta, self._result.e_rms, title="RMS electric field magnitude", xlabel="φ (deg)", ylabel="θ (deg)", ) self._field_canvas = embed_plot_in_tk(fig, parent)