thermoelasticsim.elastic.wave.analytical 源代码

#!/usr/bin/env python3
"""
弹性波解析计算模块

本模块实现立方晶系材料中,基于弹性常数 ``C11, C12, C44`` 与
材料密度 ``ρ`` 的弹性波速解析计算(阶段A)。支持任意传播方向
的Christoffel方程求解,自动区分纵波与横波,并给出相应偏振向量。

单位与约定
----------
- 弹性常数输入单位:GPa
- 密度输入单位:g/cm^3
- 输出速度单位:km/s

在上述单位下,波速满足 v[km/s] = sqrt(C[GPa] / ρ[g/cm^3])。

示例
----
>>> ana = ElasticWaveAnalyzer(C11=110.0, C12=61.0, C44=33.0, density=2.70)
>>> ana.calculate_wave_velocities([1, 0, 0])["longitudinal"]
6.38...
>>> report = ana.generate_report()
>>> sorted(report.keys())
['[100]', '[110]', '[111]']
"""

from __future__ import annotations

from collections.abc import Iterable
from dataclasses import dataclass

import numpy as np


[文档] @dataclass(slots=True) class ElasticWaveAnalyzer: """ 基于弹性常数的波速解析计算器(立方晶系)。 Parameters ---------- C11, C12, C44 : float 立方晶系弹性常数,单位 GPa。 density : float 材料密度,单位 g/cm^3。 Notes ----- - 使用Christoffel方程 Γ·u = v^2 u 求解本征值 v^2 与本征向量 u。 - 在当前单位系统下,Γ 以 (km/s)^2 表示,故速度可直接取平方根。 - 纵波的判定依据:偏振方向与传播方向夹角最小(|n·u| 最大)。 """ C11: float C12: float C44: float density: float # ---------------------------- 公共接口 ----------------------------
[文档] def calculate_wave_velocities(self, direction: Iterable[float]) -> dict: """ 计算指定方向的纵横波速度与偏振方向。 Parameters ---------- direction : Iterable[float] 传播方向向量或米勒指数,例如 [1,0,0], [1,1,0], [1,1,1]。 Returns ------- dict 包含下列键: - 'longitudinal': float,纵波速度 (km/s) - 'transverse1': float,横波速度1 (km/s) - 'transverse2': float,横波速度2 (km/s) - 'polarizations': dict,包含三个模式的单位偏振向量 """ n = _normalize_direction(direction) gamma = self._christoffel_matrix(n) # 对称矩阵,使用 eigh(保证特征值升序、向量正交) vals, vecs = np.linalg.eigh(gamma) # 根据与传播方向的对齐程度区分纵波:|n·u| 最大者为L dots = np.abs(vecs.T @ n) idx_L = int(np.argmax(dots)) # 纵波 vL = float(np.sqrt(max(vals[idx_L], 0.0))) eL = _unit(vecs[:, idx_L]) # 横波(其余两个) idx_T = [i for i in range(3) if i != idx_L] # 将横波按速度升序稳定排序,便于测试与复现 t_modes = sorted( [(float(np.sqrt(max(vals[i], 0.0))), _unit(vecs[:, i])) for i in idx_T], key=lambda x: x[0], ) (vT1, eT1), (vT2, eT2) = t_modes return { "longitudinal": vL, "transverse1": vT1, "transverse2": vT2, "polarizations": { "longitudinal": eL.tolist(), "transverse1": eT1.tolist(), "transverse2": eT2.tolist(), }, }
[文档] def generate_report(self) -> dict[str, dict[str, float | dict]]: """ 生成标准方向 [100]、[110]、[111] 的波速报告。 Returns ------- dict 形如 ``{"[100]": {...}, "[110]": {...}, "[111]": {...}}`` 的字典。 """ report: dict[str, dict[str, float | dict]] = {} for label, d in ( ("[100]", (1.0, 0.0, 0.0)), ("[110]", (1.0, 1.0, 0.0)), ("[111]", (1.0, 1.0, 1.0)), ): report[label] = self.calculate_wave_velocities(d) return report
# ---------------------------- 内部实现 ---------------------------- def _christoffel_matrix(self, n: np.ndarray) -> np.ndarray: """ 构建Christoffel矩阵 Γ(单位:(km/s)^2)。 Parameters ---------- n : ndarray, shape (3,) 单位传播方向向量。 Returns ------- ndarray, shape (3, 3) Christoffel矩阵。 Notes ----- 对立方晶系,Γ 可写为(省略 1/ρ 因子已并入到数值中): Γ_11 = C11 n1^2 + C44 (n2^2 + n3^2) Γ_22 = C11 n2^2 + C44 (n1^2 + n3^2) Γ_33 = C11 n3^2 + C44 (n1^2 + n2^2) Γ_12 = (C12 + C44) n1 n2 (其余对称) 其中 C 以 GPa,ρ 以 g/cm^3 计,Γ 的元素单位为 (km/s)^2。 """ C11, C12, C44 = self.C11, self.C12, self.C44 rho = self.density n1, n2, n3 = float(n[0]), float(n[1]), float(n[2]) factor = 1.0 / rho # 将 1/ρ 吸收入系数,GPa→(km/s)^2 的一致性见模块说明 g11 = (C11 * n1 * n1 + C44 * (n2 * n2 + n3 * n3)) * factor g22 = (C11 * n2 * n2 + C44 * (n1 * n1 + n3 * n3)) * factor g33 = (C11 * n3 * n3 + C44 * (n1 * n1 + n2 * n2)) * factor c12p = (C12 + C44) * factor g12 = c12p * n1 * n2 g13 = c12p * n1 * n3 g23 = c12p * n2 * n3 gamma = np.array( [ [g11, g12, g13], [g12, g22, g23], [g13, g23, g33], ], dtype=float, ) return gamma
# ---------------------------- 工具函数 ---------------------------- def _normalize_direction(direction: Iterable[float]) -> np.ndarray: """ 归一化传播方向向量。 Parameters ---------- direction : Iterable[float] 任意长度可迭代的三个分量。 Returns ------- ndarray, shape (3,) 单位向量。 """ n = np.asarray(list(direction), dtype=float).reshape(3) norm = float(np.linalg.norm(n)) if not np.isfinite(norm) or norm <= 0: raise ValueError("传播方向向量非法:必须为非零有限向量,形如[1,0,0]") return n / norm def _unit(v: np.ndarray) -> np.ndarray: """返回单位向量(数值稳健)。""" n = float(np.linalg.norm(v)) if n == 0.0: return np.array([1.0, 0.0, 0.0]) # 不应出现,兜底 return v / n