Coverage for openxps/bounds/periodic.py: 83%
18 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-13 22:08 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-13 22:08 +0000
1"""
2Periodic boundary condition.
4.. module:: openxps.bounds.periodic
5 :platform: Linux, MacOS, Windows
6 :synopsis: Periodic boundary condition
8.. classauthor:: Charlles Abreu <craabreu@gmail.com>
10"""
12from .base import Bounds
15class PeriodicBounds(Bounds):
16 """
17 A periodic boundary condition. The dynamical variable is allowed to wrap
18 around the upper and lower bounds.
20 Parameters
21 ----------
22 lower
23 The lower bound for the dynamical variable.
24 upper
25 The upper bound for the dynamical variable.
26 unit
27 The unity of measurement of the bounds. If the bounds do not have a unit, use
28 ``dimensionless``.
30 Example
31 -------
32 >>> import openxps as xps
33 >>> import yaml
34 >>> from openmm import unit
35 >>> bounds = xps.PeriodicBounds(-180, 180, unit.degree)
36 >>> print(bounds)
37 PeriodicBounds(lower=-180, upper=180, unit=deg)
38 >>> assert yaml.safe_load(yaml.safe_dump(bounds)) == bounds
39 """
41 def __post_init__(self) -> None:
42 super().__post_init__()
43 self.period = self.upper - self.lower
45 def leptonExpression(self, variable: str) -> str:
46 scaled = f"scaled_{variable}"
47 if self.lower == 0:
48 shift = deshift = ""
49 elif self.lower > 0:
50 shift = f"-{self.lower}"
51 deshift = f"+{self.lower}"
52 else:
53 shift = f"+{-self.lower}"
54 deshift = f"-{-self.lower}"
55 return (
56 f"({scaled}-floor({scaled}))*{self.period}{deshift}"
57 f";\n{scaled}=({variable}{shift})/{self.period}"
58 )
60 def wrap(self, value: float, rate: float) -> tuple[float, float]:
61 return (value - self.lower) % self.period + self.lower, rate
64PeriodicBounds.registerTag("!openxps.bounds.PeriodicBounds")