Coverage for cosmolayer/store/grid.py: 100%
23 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 00:09 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 00:09 +0000
1"""The symmetric, evenly spaced grid a sigma profile is binned onto.
3A sigma profile is a histogram of surface area over charge density
4(sigma, in e/Ų). Profiles are always binned onto the ``SigmaGrid``
5passed in.
7Two operations are named separately because both are sometimes called
8"shifting":
10- *centering* subtracts a row's mean charge density from its segments
11 **before** binning, so the profile has zero first moment. It changes
12 the data, not the grid (``SigmaProfileTable.centered``).
13- *translating* moves an already-binned row along a grid, used when
14 un-centering atom profiles while summing them to molecule level.
15"""
17from dataclasses import dataclass
18from functools import cached_property
20import numpy as np
21from numpy.typing import NDArray
23DEFAULT_MAX_ABS_SIGMA = 0.025
24DEFAULT_NUM_POINTS = 51
27@dataclass(frozen=True)
28class SigmaGrid:
29 """A symmetric, evenly spaced grid of sigma-profile points.
31 Parameters
32 ----------
33 max_abs_sigma : float, optional
34 Bounded sigma-profile value at each end of the grid, in e/Ų, by
35 default ``DEFAULT_MAX_ABS_SIGMA``.
36 num_points : int, optional
37 Number of grid points, by default ``DEFAULT_NUM_POINTS``. An odd
38 count puts a point at sigma = 0; an even count straddles it.
40 Examples
41 --------
42 >>> grid = SigmaGrid(max_abs_sigma=0.025, num_points=51)
43 >>> round(grid.bin_width, 6)
44 0.001
45 >>> len(grid)
46 51
47 >>> grid.values[0], grid.values[-1]
48 (np.float64(-0.025), np.float64(0.025))
49 """
51 max_abs_sigma: float = DEFAULT_MAX_ABS_SIGMA
52 num_points: int = DEFAULT_NUM_POINTS
54 @property
55 def bin_width(self) -> float:
56 """Width of one bin, in e/Ų.
58 Returns
59 -------
60 float
61 ``2 * max_abs_sigma / (num_points - 1)``.
62 """
63 return (2.0 * self.max_abs_sigma) / (self.num_points - 1)
65 @cached_property
66 def values(self) -> NDArray[np.float64]:
67 """Sigma value at every grid point, in e/Ų.
69 Returns
70 -------
71 np.ndarray, shape (num_points,)
72 Evenly spaced values from ``-max_abs_sigma`` to
73 ``max_abs_sigma``.
74 """
75 return np.linspace(-self.max_abs_sigma, self.max_abs_sigma, self.num_points)
77 @classmethod
78 def from_values(cls, values: NDArray[np.float64]) -> "SigmaGrid":
79 """Reconstruct a :class:`SigmaGrid` from an existing array of grid
80 values.
82 Parameters
83 ----------
84 values : np.ndarray
85 Evenly spaced, symmetric grid values, as returned by
86 ``self.values``.
88 Returns
89 -------
90 SigmaGrid
91 A grid whose ``.values`` reproduce ``values``.
93 Examples
94 --------
95 >>> grid = SigmaGrid(0.025, 51)
96 >>> SigmaGrid.from_values(grid.values) == grid
97 True
98 """
99 return cls(float(values[-1]), len(values))
101 def __len__(self) -> int:
102 return self.num_points
105DEFAULT_SIGMA_GRID = SigmaGrid()
106"""Default 51-point grid from -0.025 to 0.025 e/Ų."""