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

1""" 

2Periodic boundary condition. 

3 

4.. module:: openxps.bounds.periodic 

5 :platform: Linux, MacOS, Windows 

6 :synopsis: Periodic boundary condition 

7 

8.. classauthor:: Charlles Abreu <craabreu@gmail.com> 

9 

10""" 

11 

12from .base import Bounds 

13 

14 

15class PeriodicBounds(Bounds): 

16 """ 

17 A periodic boundary condition. The dynamical variable is allowed to wrap 

18 around the upper and lower bounds. 

19 

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``. 

29 

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 """ 

40 

41 def __post_init__(self) -> None: 

42 super().__post_init__() 

43 self.period = self.upper - self.lower 

44 

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 ) 

59 

60 def wrap(self, value: float, rate: float) -> tuple[float, float]: 

61 return (value - self.lower) % self.period + self.lower, rate 

62 

63 

64PeriodicBounds.registerTag("!openxps.bounds.PeriodicBounds")