mirosh111000

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

Apr 9th, 2026
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.07 KB | None | 0 0
  1. from __future__ import annotations
  2.  
  3. import math
  4. from pathlib import Path
  5. from typing import Dict, Tuple
  6.  
  7. import matplotlib.pyplot as plt
  8. import numpy as np
  9.  
  10. from numba import njit
  11.  
  12.  
  13. G = 0.8
  14. I_EPS = 0.8
  15. D_NOISE = 0.8
  16. I_SIGMA = 0.1
  17. TAU_SIGMA = 1.0
  18.  
  19. POINTS: Dict[str, Tuple[float, float, str, str]] = {
  20.     "1": (1.0, 3.8, "SF", "Рідинне тертя"),
  21.     "2": (6.0, 2.5, "SS", "Переривчасте тертя"),
  22.     "3": (3.0, 0.5, "DF", "Сухе тертя"),
  23. }
  24.  
  25.  
  26. @njit(cache=True)
  27. def drift(sigma: float, i_t: float, t_e: float, g: float = G) -> float:
  28.     d = 1.0 / (1.0 + sigma * sigma)
  29.     return -sigma + g * sigma * (1.0 - (2.0 - t_e) * d)
  30.  
  31.  
  32. @njit(cache=True)
  33. def intensity(sigma: float, i_t: float, i_sigma: float = I_SIGMA, i_eps: float = I_EPS, g: float = G) -> float:
  34.     d = 1.0 / (1.0 + sigma * sigma)
  35.     return i_sigma + (i_eps + i_t * sigma * sigma) * g * g * d * d
  36.  
  37.  
  38. @njit(cache=True)
  39. def simulate_path(noise: np.ndarray, delta_t: float, i_t: float, t_e: float, sigma_0: float = 0.0) -> np.ndarray:
  40.     n = noise.size
  41.     out = np.empty(n + 1, dtype=np.float64)
  42.     out[0] = sigma_0
  43.     sigma = sigma_0
  44.     noise_scale = math.sqrt(2.0 * D_NOISE * delta_t) / TAU_SIGMA
  45.     drift_scale = delta_t / TAU_SIGMA
  46.     for i in range(n):
  47.         ff = drift(sigma, i_t, t_e)
  48.         ii = intensity(sigma, i_t)
  49.         sigma = sigma + ff * drift_scale + math.sqrt(ii) * noise_scale * noise[i]
  50.         out[i + 1] = sigma
  51.     return out
  52.  
  53.  
  54. @njit(cache=True)
  55. def simulate_prob_samples(noise: np.ndarray, delta_t: float, i_t: float, t_e: float, burn_in: int, thin: int, sigma_0: float = 0.0) -> np.ndarray:
  56.     n = noise.size
  57.     kept = (n - burn_in + thin - 1) // thin
  58.     out = np.empty(kept, dtype=np.float64)
  59.     sigma = sigma_0
  60.     noise_scale = math.sqrt(2.0 * D_NOISE * delta_t) / TAU_SIGMA
  61.     drift_scale = delta_t / TAU_SIGMA
  62.     j = 0
  63.     for i in range(n):
  64.         ff = drift(sigma, i_t, t_e)
  65.         ii = intensity(sigma, i_t)
  66.         sigma = sigma + ff * drift_scale + math.sqrt(ii) * noise_scale * noise[i]
  67.         if i >= burn_in and ((i - burn_in) % thin == 0):
  68.             out[j] = sigma
  69.             j += 1
  70.     return out[:j]
  71.  
  72.  
  73.  
  74. def line_i(i_t: np.ndarray) -> np.ndarray:
  75.     return 1.0 + 1.0 / G + 2.0 * G * D_NOISE * (i_t - 2.0 * I_EPS)
  76.  
  77.  
  78.  
  79. def curve_ii_sigma_search(i_t_values: np.ndarray, sigma_max: float = 10.0, dsigma: float = 0.001) -> tuple[np.ndarray, np.ndarray]:
  80.     sigma = np.arange(0.0, sigma_max + dsigma, dsigma)
  81.     x = 1.0 + sigma * sigma
  82.     i_vals = []
  83.     te_vals = []
  84.     for i_t in i_t_values:
  85.         te = 2.0 + ((1.0 - G) * x**3 - 2.0 * G * G * D_NOISE * i_t * x + 4.0 * G * G * D_NOISE * (i_t - I_EPS)) / (G * x**2)
  86.         mask = (te[1:-1] < te[:-2]) & (te[1:-1] < te[2:])
  87.         if np.any(mask):
  88.             idxs = np.where(mask)[0] + 1
  89.             idx = idxs[0]
  90.             i_vals.append(i_t)
  91.             te_vals.append(te[idx])
  92.     return np.asarray(i_vals), np.asarray(te_vals)
  93.  
  94.  
  95.  
  96. def tricritical_point() -> tuple[float, float]:
  97.     te = (2.0 / 3.0) * (1.0 + 2.0 / G - 2.0 * D_NOISE * G * I_EPS)
  98.     i_t = (1.0 / (6.0 * G * D_NOISE)) * (1.0 / G - 1.0 + 8.0 * D_NOISE * G * I_EPS)
  99.     return i_t, te
  100.  
  101.  
  102.  
  103. def analytical_probability(sigma_grid: np.ndarray, i_t: float, t_e: float) -> np.ndarray:
  104.     i_vals = intensity(sigma_grid, i_t)
  105.     f_vals = drift(sigma_grid, i_t, t_e)
  106.     integrand = f_vals / i_vals
  107.     integral = np.zeros_like(sigma_grid)
  108.     dx = np.diff(sigma_grid)
  109.     integral[1:] = np.cumsum(0.5 * (integrand[:-1] + integrand[1:]) * dx)
  110.     u = np.log(i_vals) - integral / D_NOISE
  111.     u = u - np.min(u)
  112.     p = np.exp(-u)
  113.     z = np.trapezoid(p, sigma_grid)
  114.     return p / z
  115.  
  116.  
  117.  
  118. def numerical_probability(samples: np.ndarray, sigma_grid: np.ndarray, symmetrize: bool = True) -> np.ndarray:
  119.     bins = np.linspace(sigma_grid[0], sigma_grid[-1], sigma_grid.size)
  120.     hist, edges = np.histogram(samples, bins=bins, density=True)
  121.     centers = 0.5 * (edges[:-1] + edges[1:])
  122.     kernel = np.array([1, 2, 3, 2, 1], dtype=np.float64)
  123.     kernel /= kernel.sum()
  124.     smooth = np.convolve(hist, kernel, mode="same")
  125.     if symmetrize:
  126.         smooth = 0.5 * (smooth + smooth[::-1])
  127.     return centers, smooth
  128.  
  129.  
  130.  
  131. def make_phase_diagram(out_path: Path) -> None:
  132.     plt.rcParams.update({
  133.         "font.family": "DejaVu Serif",
  134.         "font.size": 12,
  135.         "mathtext.fontset": "dejavuserif",
  136.     })
  137.     i_t_values = np.arange(0.0, 18.01, 0.01)
  138.     te_i = line_i(i_t_values)
  139.     i_curve, te_curve = curve_ii_sigma_search(i_t_values)
  140.     i_tri, te_tri = tricritical_point()
  141.  
  142.     fig, ax = plt.subplots(figsize=(7.1, 5.3), dpi=180)
  143.     ax.plot(i_t_values, te_i, lw=1.6, label="I")
  144.     ax.plot(i_curve, te_curve, lw=1.6, label="II")
  145.     ax.plot(i_tri, te_tri, marker="o", ms=4)
  146.     ax.text(i_tri + 0.25, te_tri - 0.1, "T", fontsize=11)
  147.  
  148.     for key, (i_t, t_e, region, _) in POINTS.items():
  149.         ax.plot(i_t, t_e, marker="D", ms=5, mfc="white", mec="black", mew=0.9)
  150.         ax.text(i_t + 0.2, t_e + 0.05, key, fontsize=11)
  151.  
  152.     ax.text(1.2, 4.35, "SF", fontsize=12)
  153.     ax.text(10.5, 3.05, "SS", fontsize=12)
  154.     ax.text(5.8, 0.55, "DF", fontsize=12)
  155.     ax.text(2.15, 3.95, "I", fontsize=12)
  156.     ax.text(10.7, 1.1, "II", fontsize=12)
  157.  
  158.     ax.set_xlim(0, 18)
  159.     ax.set_ylim(0, 5)
  160.     ax.set_xlabel(r"$I_T$")
  161.     ax.set_ylabel(r"$T_e$")
  162.     ax.set_title("Фазова діаграма при g = 0.8, Iε = 0.8, D = 0.8", fontsize=12, pad=10)
  163.     ax.spines[["top", "right"]].set_visible(False)
  164.     fig.tight_layout()
  165.     fig.savefig(out_path, bbox_inches="tight")
  166.     plt.close(fig)
  167.  
  168.  
  169.  
  170. def make_time_series(out_path: Path, seed: int = 20260409) -> None:
  171.     plt.rcParams.update({
  172.         "font.family": "DejaVu Serif",
  173.         "font.size": 11,
  174.         "mathtext.fontset": "dejavuserif",
  175.     })
  176.     rng = np.random.default_rng(seed)
  177.     t_all = 5000.0
  178.     n = 200_000
  179.     delta_t = t_all / n
  180.     t = np.linspace(0.0, t_all, n + 1)
  181.  
  182.     fig, axes = plt.subplots(3, 1, figsize=(7.2, 7.6), dpi=180, sharex=True)
  183.     for ax, key in zip(axes, ["1", "2", "3"]):
  184.         i_t, t_e, region, region_full = POINTS[key]
  185.         noise = rng.normal(size=n)
  186.         sigma = simulate_path(noise, delta_t, i_t, t_e)
  187.         ax.plot(t, sigma, lw=0.5)
  188.         ax.set_ylabel("σ")
  189.         ax.text(0.02, 0.87, f"Точка {key}: $I_T$={i_t:g}, $T_e$={t_e:g} ({region})", transform=ax.transAxes, fontsize=10)
  190.         ax.set_ylim(-6.0, 6.0)
  191.         ax.grid(alpha=0.2, linewidth=0.4)
  192.     axes[-1].set_xlabel("t")
  193.     fig.suptitle("Часові ряди напружень σ(t)", fontsize=13, y=0.995)
  194.     fig.tight_layout(rect=(0, 0, 1, 0.985))
  195.     fig.savefig(out_path, bbox_inches="tight")
  196.     plt.close(fig)
  197.  
  198.  
  199.  
  200. def make_probability_figure(out_path: Path, seed: int = 20260410) -> None:
  201.     plt.rcParams.update({
  202.         "font.family": "DejaVu Serif",
  203.         "font.size": 11,
  204.         "mathtext.fontset": "dejavuserif",
  205.     })
  206.     sigma_grid = np.linspace(-6.0, 6.0, 1201)
  207.     rng = np.random.default_rng(seed)
  208.  
  209.     fig, axes = plt.subplots(2, 1, figsize=(7.2, 7.6), dpi=180, sharex=True)
  210.  
  211.     for key in ["1", "2", "3"]:
  212.         i_t, t_e, region, _ = POINTS[key]
  213.         p = analytical_probability(sigma_grid, i_t, t_e)
  214.         axes[0].plot(sigma_grid, p, lw=1.25)
  215.         peak_idx = int(np.argmax(p))
  216.         axes[0].text(sigma_grid[peak_idx] + 0.12, p[peak_idx] + 0.01, key, fontsize=11)
  217.  
  218.         n_steps = 4_000_000 if key == "1" else 2_000_000
  219.         burn_in = 100_000
  220.         thin = 10
  221.         noise = rng.normal(size=n_steps)
  222.         samples = simulate_prob_samples(noise, 0.01, i_t, t_e, burn_in=burn_in, thin=thin)
  223.         centers, p_num = numerical_probability(samples, sigma_grid)
  224.         axes[1].plot(centers, p_num, lw=1.15)
  225.         peak_idx_num = int(np.argmax(p_num))
  226.         axes[1].text(centers[peak_idx_num] + 0.12, p_num[peak_idx_num] + 0.01, key, fontsize=11)
  227.  
  228.     axes[0].text(0.05, 0.88, "a", transform=axes[0].transAxes, fontsize=14, fontstyle="italic")
  229.     axes[1].text(0.05, 0.88, "b", transform=axes[1].transAxes, fontsize=14, fontstyle="italic")
  230.     axes[0].set_ylabel("P(σ)")
  231.     axes[1].set_ylabel("P(σ)")
  232.     axes[1].set_xlabel("σ")
  233.     axes[0].set_title("Аналітичний розподіл")
  234.     axes[1].set_title("Чисельний розподіл за часовими рядами")
  235.     for ax in axes:
  236.         ax.grid(alpha=0.18, linewidth=0.4)
  237.         ax.set_xlim(-6.0, 6.0)
  238.     fig.suptitle("Порівняння аналітичних і чисельних розподілів ймовірності", fontsize=13, y=0.995)
  239.     fig.tight_layout(rect=(0, 0, 1, 0.985))
  240.     fig.savefig(out_path, bbox_inches="tight")
  241.     plt.close(fig)
  242.  
  243.  
  244.  
  245. def main() -> None:
  246.     base_dir = Path(__file__).resolve().parent
  247.     out_dir = base_dir / "pr6_outputs"
  248.     out_dir.mkdir(exist_ok=True)
  249.  
  250.     make_phase_diagram(out_dir / "phase_diagram.png")
  251.     make_time_series(out_dir / "time_series.png")
  252.     make_probability_figure(out_dir / "probability_distributions.png")
  253.  
  254.     print(f"Saved outputs to: {out_dir}")
  255.  
  256.  
  257. if __name__ == "__main__":
  258.     main()
  259.  
Advertisement
Add Comment
Please, Sign In to add comment