beach.native.periodic2 のソースコード

"""Validated periodic2 settings for native field-kernel plans."""

from __future__ import annotations

import math
from typing import Mapping, NamedTuple, TypeAlias


[ドキュメント] class Periodic2Config(NamedTuple): """Validated periodic2 settings with legacy tuple compatibility.""" axes: tuple[int, int] lengths: tuple[float, float] origins: tuple[float, float] image_layers: int far_correction: str ewald_alpha: float ewald_layers: int
Periodic2Tuple: TypeAlias = tuple[ tuple[int, int], tuple[float, float], tuple[float, float], int, str, float, int, ] Periodic2Input: TypeAlias = Mapping[str, object] | Periodic2Tuple | Periodic2Config def coerce_periodic2( periodic2: Periodic2Input | None, *, allow_cached_kneq0: bool = False, allow_historical_root_oracle: bool = False, ) -> Periodic2Config | None: """Validate external periodic2 input and return its canonical typed form.""" if periodic2 is None: return None if isinstance(periodic2, tuple) and len(periodic2) == 7: ( axes, lengths, origins, image_layers, far_correction, ewald_alpha, ewald_layers, ) = periodic2 periodic2 = { "axes": axes, "lengths": lengths, "origins": origins, "image_layers": image_layers, "far_correction": far_correction, "ewald_alpha": ewald_alpha, "ewald_layers": ewald_layers, } if not isinstance(periodic2, Mapping): raise ValueError("periodic2 must be a mapping or None.") if "axes" not in periodic2 or "lengths" not in periodic2: raise ValueError('periodic2 requires "axes" and "lengths".') axes_obj = periodic2["axes"] lengths_obj = periodic2["lengths"] if not isinstance(axes_obj, (list, tuple)) or len(axes_obj) != 2: raise ValueError("periodic2.axes must be a length-2 sequence.") if not isinstance(lengths_obj, (list, tuple)) or len(lengths_obj) != 2: raise ValueError("periodic2.lengths must be a length-2 sequence.") axes = (int(axes_obj[0]), int(axes_obj[1])) if axes[0] == axes[1] or any(axis < 0 or axis > 2 for axis in axes): raise ValueError( "periodic2.axes must contain two distinct axis indices in {0,1,2}." ) lengths = (float(lengths_obj[0]), float(lengths_obj[1])) if any((not math.isfinite(length)) or length <= 0.0 for length in lengths): raise ValueError("periodic2.lengths must be finite and positive.") if "origins" in periodic2: origins_obj = periodic2["origins"] if not isinstance(origins_obj, (list, tuple)) or len(origins_obj) != 2: raise ValueError("periodic2.origins must be a length-2 sequence.") origins = (float(origins_obj[0]), float(origins_obj[1])) elif "box_min" in periodic2: box_min = _coerce_vec3(periodic2["box_min"], name="periodic2.box_min") origins = (box_min[axes[0]], box_min[axes[1]]) else: origins = (0.0, 0.0) if any(not math.isfinite(origin) for origin in origins): raise ValueError("periodic2.origins must be finite.") image_layers = int(periodic2.get("image_layers", 1)) if image_layers < 0: raise ValueError("periodic2.image_layers must be >= 0.") ewald_layers = int(periodic2.get("ewald_layers", 4)) if ewald_layers < 0: raise ValueError("periodic2.ewald_layers must be >= 0.") far_correction, ewald_layers = normalize_periodic2_far_correction( periodic2.get("far_correction", "none"), ewald_layers=ewald_layers, allow_cached_kneq0=allow_cached_kneq0, allow_historical_root_oracle=allow_historical_root_oracle, ) ewald_alpha = float(periodic2.get("ewald_alpha", 0.0)) if (not math.isfinite(ewald_alpha)) or ewald_alpha < 0.0: raise ValueError("periodic2.ewald_alpha must be finite and >= 0.") return Periodic2Config( axes=axes, lengths=lengths, origins=origins, image_layers=image_layers, far_correction=far_correction, ewald_alpha=ewald_alpha, ewald_layers=ewald_layers, ) def normalize_periodic2_far_correction( raw_far_correction: object, *, ewald_layers: int, allow_cached_kneq0: bool = False, allow_historical_root_oracle: bool = False, ) -> tuple[str, int]: """Normalize the configured far-correction policy.""" far_correction = str(raw_far_correction).strip().lower() if far_correction == "m2l_root_oracle": if not allow_historical_root_oracle: replacement = "cached_kneq0" if allow_cached_kneq0 else "none" raise ValueError( 'periodic2.far_correction "m2l_root_oracle" was removed; ' f'use "{replacement}".' ) return "none", ewald_layers allowed = {"auto", "none"} if allow_cached_kneq0: allowed.add("cached_kneq0") if far_correction not in allowed: expected = '"auto" or "none"' if allow_cached_kneq0: expected = '"auto", "none", or "cached_kneq0"' raise ValueError(f"periodic2.far_correction must be {expected}.") if far_correction == "auto": return "none", ewald_layers return far_correction, ewald_layers def _coerce_vec3(value: object, *, name: str) -> tuple[float, float, float]: if not isinstance(value, (list, tuple)) or len(value) != 3: raise ValueError(f"{name} must contain exactly 3 values.") try: return float(value[0]), float(value[1]), float(value[2]) except (TypeError, ValueError) as exc: raise ValueError(f"{name} must contain numeric values.") from exc