mirosh111000

НПтаМ_ПР№5_Мірошниченко

Apr 2nd, 2026
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.63 KB | None | 0 0
  1. from __future__ import annotations
  2.  
  3. import argparse
  4. from dataclasses import dataclass
  5. from pathlib import Path
  6. from typing import Iterable
  7.  
  8. import matplotlib.pyplot as plt
  9. import numpy as np
  10.  
  11.  
  12. @dataclass(frozen=True)
  13. class SecondOrderParams:
  14.     g: float = 0.5
  15.  
  16.  
  17. @dataclass(frozen=True)
  18. class FirstOrderParams:
  19.     g_theta: float = 0.6
  20.     theta: float = 0.1
  21.     alpha: float = 0.75
  22.  
  23.  
  24. def epsilon_of_sigma(sigma: np.ndarray, T_e: float) -> np.ndarray:
  25.     return sigma * (T_e - 1.0 + sigma**2) / (1.0 + sigma**2)
  26.  
  27.  
  28. def temperature_of_sigma(sigma: np.ndarray, T_e: float) -> np.ndarray:
  29.     return (T_e + 2.0 * sigma**2) / (1.0 + sigma**2)
  30.  
  31.  
  32. def potential_second_order(sigma: np.ndarray, g: float, T_e: float) -> np.ndarray:
  33.     return 0.5 * (1.0 - g) * sigma**2 + g * (1.0 - T_e / 2.0) * np.log(1.0 + sigma**2)
  34.  
  35.  
  36. def critical_temperature_second_order(g: float) -> float:
  37.     return 1.0 + 1.0 / g
  38.  
  39.  
  40. def stationary_sigma_second_order(T_e: np.ndarray, g: float) -> np.ndarray:
  41.     T_e = np.asarray(T_e, dtype=float)
  42.     sigma0 = np.zeros_like(T_e)
  43.     mask = T_e > critical_temperature_second_order(g)
  44.     val = (g * T_e[mask] - (g + 1.0)) / (1.0 - g)
  45.     sigma0[mask] = np.sqrt(np.clip(val, 0.0, None))
  46.     return sigma0
  47.  
  48.  
  49. def stationary_epsilon_second_order(sigma0: np.ndarray, g: float) -> np.ndarray:
  50.     return sigma0 / g
  51.  
  52.  
  53. def stationary_temperature_second_order(sigma0: np.ndarray, T_e: np.ndarray, g: float) -> np.ndarray:
  54.     T_e = np.asarray(T_e, dtype=float)
  55.     T0 = np.array(T_e, copy=True)
  56.     mask = sigma0 > 0
  57.     T0[mask] = critical_temperature_second_order(g)
  58.     return T0
  59.  
  60.  
  61. def g_of_sigma_first_order(sigma: np.ndarray, p: FirstOrderParams) -> np.ndarray:
  62.     return p.g_theta * (1.0 + (p.theta**-1 - 1.0) / (1.0 + (sigma / p.alpha) ** 2))
  63.  
  64.  
  65. def potential_first_order(sigma: np.ndarray, T_e: float, p: FirstOrderParams) -> np.ndarray:
  66.     term1 = 0.5 * (1.0 - p.g_theta) * sigma**2
  67.     term2 = p.g_theta * (1.0 - T_e / 2.0) * np.log(1.0 + sigma**2)
  68.     pref = -0.5 * p.g_theta * p.alpha**2 * (p.theta**-1 - 1.0) / (p.alpha**2 - 1.0)
  69.     term3 = pref * (
  70.         (T_e - 2.0) * np.log(1.0 + sigma**2)
  71.         + (p.alpha**2 - T_e + 1.0) * np.log(1.0 + sigma**2 / p.alpha**2)
  72.     )
  73.     return term1 + term2 + term3
  74.  
  75.  
  76. def D_plateau_term(p: FirstOrderParams) -> float:
  77.     return (
  78.         16.0
  79.         * p.g_theta**2
  80.         * p.alpha**-4
  81.         * (p.g_theta - 1.0)
  82.         * (p.theta**-1 - 1.0)
  83.         * (p.alpha**-2 - p.theta**-1)
  84.     )
  85.  
  86.  
  87. def plateau_temperature_first_order(p: FirstOrderParams) -> float:
  88.     D = D_plateau_term(p)
  89.     return p.g_theta**-2 * (
  90.         p.g_theta * (p.alpha**2 + p.g_theta + 1.0 - p.alpha**2 * p.g_theta * p.theta**-1)
  91.         + 2.0 * p.alpha**2 * p.g_theta * p.theta**-1 * (p.g_theta - 1.0)
  92.         + 0.5 * p.alpha**4 * np.sqrt(max(D, 0.0))
  93.     )
  94.  
  95.  
  96. def barrier_zero_temperature_first_order(p: FirstOrderParams) -> float:
  97.     return 1.0 + p.theta / p.g_theta
  98.  
  99.  
  100. def D0_stationary(T_e: np.ndarray, p: FirstOrderParams) -> np.ndarray:
  101.     T_e = np.asarray(T_e, dtype=float)
  102.     return (
  103.         (p.g_theta * (T_e - 1.0) / p.alpha**2 + p.g_theta / p.theta - 1.0 - p.alpha**-2) ** 2
  104.         - 4.0 * p.alpha**-2 * (p.g_theta - 1.0) * (p.g_theta * (T_e - 1.0) / p.theta - 1.0)
  105.     )
  106.  
  107.  
  108. def stationary_branches_first_order(T_e: np.ndarray, p: FirstOrderParams) -> tuple[np.ndarray, np.ndarray]:
  109.     T_e = np.asarray(T_e, dtype=float)
  110.     D0 = D0_stationary(T_e, p)
  111.     sqrt_D0 = np.sqrt(np.clip(D0, 0.0, None))
  112.  
  113.     pref = 0.5 * p.alpha**2 / (p.g_theta - 1.0)
  114.     base = 1.0 + p.alpha**-2 - p.g_theta * p.alpha**-2 * (T_e - 1.0) - p.g_theta * p.theta**-1
  115.  
  116.     sigma_max_sq = pref * (base + sqrt_D0)
  117.     sigma_min_sq = pref * (base - sqrt_D0)
  118.  
  119.     sigma_unstable = np.sqrt(np.where(sigma_max_sq >= 0.0, sigma_max_sq, np.nan))
  120.     sigma_stable = np.sqrt(np.where(sigma_min_sq >= 0.0, sigma_min_sq, np.nan))
  121.  
  122.     T_c0_lower = plateau_temperature_first_order(p)
  123.     T_c0_upper = barrier_zero_temperature_first_order(p)
  124.     mask = (T_e >= T_c0_lower) & (T_e <= T_c0_upper)
  125.  
  126.     sigma_stable = np.where(mask, sigma_stable, np.nan)
  127.     sigma_unstable = np.where(mask, sigma_unstable, np.nan)
  128.     return sigma_stable, sigma_unstable
  129.  
  130.  
  131. def _save_figure(fig: plt.Figure, out_path: Path) -> None:
  132.     out_path.parent.mkdir(parents=True, exist_ok=True)
  133.     fig.savefig(out_path, dpi=200, bbox_inches="tight")
  134.     plt.close(fig)
  135.  
  136.  
  137. def plot_part1_epsilon_sigma(out_dir: Path) -> Path:
  138.     sigma = np.linspace(0.0, 4.0, 500)
  139.     T_values = [1.0, 2.0, 4.2]
  140.  
  141.     fig, ax = plt.subplots(figsize=(8, 5))
  142.     for T_e in T_values:
  143.         ax.plot(sigma, epsilon_of_sigma(sigma, T_e), label=rf"$T_e = {T_e}$")
  144.     ax.set_xlabel(r"$\sigma$")
  145.     ax.set_ylabel(r"$\varepsilon$")
  146.     ax.set_title(r"Частина 1. Залежність $\varepsilon(\sigma)$")
  147.     ax.grid(True, alpha=0.3)
  148.     ax.legend()
  149.  
  150.     path = out_dir / "figure_1_epsilon_sigma.png"
  151.     _save_figure(fig, path)
  152.     return path
  153.  
  154.  
  155. def plot_part1_temperature_sigma(out_dir: Path) -> Path:
  156.     sigma = np.linspace(0.0, 4.0, 500)
  157.     T_values = [1.0, 2.0, 4.2]
  158.  
  159.     fig, ax = plt.subplots(figsize=(8, 5))
  160.     for T_e in T_values:
  161.         ax.plot(sigma, temperature_of_sigma(sigma, T_e), label=rf"$T_e = {T_e}$")
  162.     ax.set_xlabel(r"$\sigma$")
  163.     ax.set_ylabel(r"$T$")
  164.     ax.set_title(r"Частина 1. Залежність $T(\sigma)$")
  165.     ax.grid(True, alpha=0.3)
  166.     ax.legend()
  167.  
  168.     path = out_dir / "figure_2_temperature_sigma.png"
  169.     _save_figure(fig, path)
  170.     return path
  171.  
  172.  
  173. def plot_part1_potential(out_dir: Path, g: float = 0.5) -> Path:
  174.     sigma = np.linspace(-3.5, 3.5, 800)
  175.     T_values = [1.0, 4.2]
  176.     T_c0 = critical_temperature_second_order(g)
  177.  
  178.     fig, ax = plt.subplots(figsize=(8, 5))
  179.     for T_e in T_values:
  180.         suffix = r"$< T_{c0}$" if T_e < T_c0 else r"$> T_{c0}$"
  181.         ax.plot(sigma, potential_second_order(sigma, g, T_e), label=rf"$T_e = {T_e}$ {suffix}")
  182.     ax.set_xlabel(r"$\sigma$")
  183.     ax.set_ylabel(r"$V(\sigma)$")
  184.     ax.set_title(rf"Частина 1. Потенціал $V(\sigma)$ для $g = {g}$, $T_{{c0}} = {T_c0:.1f}$")
  185.     ax.grid(True, alpha=0.3)
  186.     ax.legend()
  187.  
  188.     path = out_dir / "figure_3_potential_second_order.png"
  189.     _save_figure(fig, path)
  190.     return path
  191.  
  192.  
  193. def plot_part1_stationary_sigma(out_dir: Path) -> Path:
  194.     T_grid = np.linspace(1.0, 4.0, 500)
  195.     g_values = [0.5, 0.6, 0.7, 0.8, 0.9]
  196.  
  197.     fig, ax = plt.subplots(figsize=(8, 5))
  198.     for g in g_values:
  199.         ax.plot(T_grid, stationary_sigma_second_order(T_grid, g), label=rf"$g = {g}$")
  200.     ax.set_xlabel(r"$T_e$")
  201.     ax.set_ylabel(r"$\sigma_0$")
  202.     ax.set_title(r"Частина 1. Стаціонарні значення $\sigma_0(T_e)$")
  203.     ax.grid(True, alpha=0.3)
  204.     ax.legend()
  205.  
  206.     path = out_dir / "figure_4_stationary_sigma_second_order.png"
  207.     _save_figure(fig, path)
  208.     return path
  209.  
  210.  
  211. def plot_part2_potential(out_dir: Path, p: FirstOrderParams) -> Path:
  212.     sigma = np.linspace(-5.0, 5.0, 1200)
  213.     T_c0_lower = plateau_temperature_first_order(p)
  214.     T_c0_upper = barrier_zero_temperature_first_order(p)
  215.     T_values = [0.50, T_c0_lower, 0.90, 1.30]
  216.  
  217.     fig, ax = plt.subplots(figsize=(8, 5))
  218.     for T_e in T_values:
  219.         if np.isclose(T_e, T_c0_lower, atol=1e-6):
  220.             label = rf"$T_e = T_c^0 \approx {T_e:.3f}$"
  221.         elif T_e < T_c0_lower:
  222.             label = rf"$T_e = {T_e:.2f} < T_c^0$"
  223.         elif T_e < T_c0_upper:
  224.             label = rf"$T_c^0 < T_e = {T_e:.2f} < T_{{c0}}$"
  225.         else:
  226.             label = rf"$T_e = {T_e:.2f} > T_{{c0}}$"
  227.         ax.plot(sigma, potential_first_order(sigma, T_e, p), label=label)
  228.     ax.set_xlabel(r"$\sigma$")
  229.     ax.set_ylabel(r"$V(\sigma)$")
  230.     ax.set_title(r"Частина 2. Потенціал $V(\sigma)$ для переходу першого роду")
  231.     ax.grid(True, alpha=0.3)
  232.     ax.legend()
  233.  
  234.     path = out_dir / "figure_5_potential_first_order.png"
  235.     _save_figure(fig, path)
  236.     return path
  237.  
  238.  
  239. def plot_part2_stationary_branches(out_dir: Path, p: FirstOrderParams) -> Path:
  240.     T_c0_lower = plateau_temperature_first_order(p)
  241.     T_c0_upper = barrier_zero_temperature_first_order(p)
  242.     T_grid = np.linspace(T_c0_lower, T_c0_upper + 0.18, 600)
  243.     sigma_stable, sigma_unstable = stationary_branches_first_order(T_grid, p)
  244.  
  245.     fig, ax = plt.subplots(figsize=(8, 5))
  246.     ax.plot(T_grid, sigma_stable, label=r"стійка гілка $\sigma_0$")
  247.     ax.plot(T_grid, sigma_unstable, linestyle="--", label=r"нестійка гілка $\sigma_m$")
  248.     ax.axvline(T_c0_lower, linestyle=":")
  249.     ax.axvline(T_c0_upper, linestyle=":")
  250.     ax.text(T_c0_lower + 0.01, 2.2, r"$T_c^0$")
  251.     ax.text(T_c0_upper + 0.01, 2.2, r"$T_{c0}$")
  252.     ax.set_xlabel(r"$T_e$")
  253.     ax.set_ylabel(r"$\sigma$")
  254.     ax.set_title(r"Частина 2. Стаціонарні гілки $\sigma(T_e)$")
  255.     ax.grid(True, alpha=0.3)
  256.     ax.legend()
  257.  
  258.     path = out_dir / "figure_6_stationary_branches_first_order.png"
  259.     _save_figure(fig, path)
  260.     return path
  261.  
  262.  
  263. def build_all_figures(out_dir: Path) -> list[Path]:
  264.     p2 = FirstOrderParams()
  265.     saved = [
  266.         plot_part1_epsilon_sigma(out_dir),
  267.         plot_part1_temperature_sigma(out_dir),
  268.         plot_part1_potential(out_dir, g=0.5),
  269.         plot_part1_stationary_sigma(out_dir),
  270.         plot_part2_potential(out_dir, p2),
  271.         plot_part2_stationary_branches(out_dir, p2),
  272.     ]
  273.     return saved
  274.  
  275.  
  276. def print_summary(saved_paths: Iterable[Path]) -> None:
  277.     p2 = FirstOrderParams()
  278.     g = 0.5
  279.     T_c0_2nd = critical_temperature_second_order(g)
  280.     T_c0_1st_lower = plateau_temperature_first_order(p2)
  281.     T_c0_1st_upper = barrier_zero_temperature_first_order(p2)
  282.  
  283.     print("Згенеровано файли:")
  284.     for path in saved_paths:
  285.         print(f" - {path}")
  286.  
  287.     print("\nКлючові параметри:")
  288.     print(f"Частина 1: для g = {g} маємо T_c0 = 1 + 1/g = {T_c0_2nd:.3f}")
  289.     print(
  290.         "Частина 2: "
  291.         f"T_c^0 ≈ {T_c0_1st_lower:.3f}, "
  292.         f"T_c0 = 1 + θ/g_θ ≈ {T_c0_1st_upper:.3f}"
  293.     )
  294.  
  295.  
  296. def parse_args() -> argparse.Namespace:
  297.     parser = argparse.ArgumentParser(
  298.         description="Побудова всіх рисунків для практичної роботи №5."
  299.     )
  300.     parser.add_argument(
  301.         "--out-dir",
  302.         type=Path,
  303.         default=Path("pr5_output"),
  304.         help="Папка, куди буде збережено рисунки.",
  305.     )
  306.     return parser.parse_args()
  307.  
  308.  
  309. def main() -> None:
  310.     args = parse_args()
  311.     saved = build_all_figures(args.out_dir)
  312.     print_summary(saved)
  313.  
  314.  
  315. if __name__ == "__main__":
  316.     main()
  317.  
Advertisement
Add Comment
Please, Sign In to add comment