Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default Grid Assesment #210

Merged
merged 25 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0b0851b
Add default PowerRTransform params to utils
Ali-Tehrani Jan 4, 2024
2e9f063
Add default rgrid to from_preset in atomgrid
Ali-Tehrani Jan 4, 2024
b6524e7
Fix that default rgrid is in angstorm
Ali-Tehrani Jan 4, 2024
d270839
Add helper function to generate default rgrid
Ali-Tehrani Jan 4, 2024
61742b1
Add default rgrid to from_preset in molgrid
Ali-Tehrani Jan 4, 2024
04688cf
Add default rgrid to mol from_size and from_pruned
Ali-Tehrani Jan 4, 2024
9ade1d2
Fix bug in atomgrid with default rgrid
Ali-Tehrani Jan 4, 2024
0319c43
Decrease accuracy of ode test
Ali-Tehrani Jan 4, 2024
e1face4
Fix def rgrid in molgrid and add tests
Ali-Tehrani Jan 4, 2024
cffc3ea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 4, 2024
c4d3459
Fix linters issue
Ali-Tehrani Jan 4, 2024
67470d6
Change random test so that it passes in ode
Ali-Tehrani Jan 4, 2024
530fd8e
Add aim_weights default for Molgrid.from_preset
Ali-Tehrani Jan 4, 2024
837a007
Add default aim_weights to MolGrid.from_size
Ali-Tehrani Jan 4, 2024
c8637c9
Add default aim_weights to MolGrid.from_pruned
Ali-Tehrani Jan 4, 2024
1e7bf91
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 4, 2024
543d455
Fix ruff linter
Ali-Tehrani Jan 4, 2024
c6ff8f4
Clarification on converting A^o to a.u.
Ali-Tehrani Jan 4, 2024
eb813ed
Remove *_ from MolGrid.from_preset
Ali-Tehrani Jan 4, 2024
d3b46e9
Fix improt for scipy.constants
Ali-Tehrani Jan 4, 2024
cc61d59
Remove * from AtomicGrid class
FarnazH Jan 4, 2024
0b405c5
Add angstrom scipy conversion
Ali-Tehrani Jan 5, 2024
fe61a51
Remove sub-test where ode doesn't converge
Ali-Tehrani Jan 5, 2024
3dc9a72
Fix ruff linter
Ali-Tehrani Jan 5, 2024
078a98e
Define ang2bohr conversion factor
FarnazH Jan 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/grid/atomgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

from grid.angular import AngularGrid
from grid.basegrid import Grid, OneDGrid
from grid.onedgrid import UniformInteger
from grid.rtransform import PowerRTransform
from grid.utils import (
_DEFAULT_POWER_RTRANSFORM_PARAMS,
convert_cart_to_sph,
convert_derivative_from_spherical_to_cartesian,
generate_derivative_real_spherical_harmonics,
Expand Down Expand Up @@ -148,6 +151,8 @@ def from_preset(
----------
rgrid : OneDGrid, optional
The (1-dimensional) radial grid representing the radius of spherical grids.
If None, then using the atomic number it will generate a default radial grid
(PowerRTransform of UniformInteger grid).
atnum : int, keyword-only argument
The atomic number specifying the predefined grid.
preset : str, keyword-only argument
Expand Down Expand Up @@ -189,8 +194,16 @@ def from_preset(
if not isinstance(use_spherical, bool):
raise TypeError(f"use_spherical {use_spherical} should be of type bool.")
if rgrid is None:
# TODO: generate a default rgrid, currently raise an error instead
raise ValueError("A default OneDGrid will be generated")
# If the atomic number is found in the default RTransform
if atnum in _DEFAULT_POWER_RTRANSFORM_PARAMS:
rmin, rmax, npt = _DEFAULT_POWER_RTRANSFORM_PARAMS[int(atnum)]
rmin, rmax = rmin * 1.8897259885789, rmax * 1.8897259885789
onedgrid = UniformInteger(npt)
rgrid = PowerRTransform(rmin, rmax).transform_1d_grid(onedgrid)
else:
raise ValueError(
f"Default rgrid parameter is not included for the" f" atomic number {atnum}."
)
center = np.zeros(3, dtype=float) if center is None else np.asarray(center, dtype=float)
cls._input_type_check(rgrid, center)
# load radial points and
Expand Down
108 changes: 82 additions & 26 deletions src/grid/molgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

from grid.atomgrid import AtomGrid
from grid.basegrid import Grid, LocalGrid, OneDGrid
from grid.becke import BeckeWeights
from grid.onedgrid import UniformInteger
from grid.rtransform import PowerRTransform
from grid.utils import _DEFAULT_POWER_RTRANSFORM_PARAMS


class MolGrid(Grid):
Expand Down Expand Up @@ -75,7 +79,7 @@ class MolGrid(Grid):
specifying the size of each Levedev grid at each radial points.

>>> preset = "fine" # Many choices available.
>>> molgrid = MolGrid.from_preset(charges, coords, rgrid, preset, aim_weights=becke)
>>> molgrid = MolGrid.from_preset(charges,coords,preset,aim_weights=becke,rgrid=rgrid)

The general way to integrate is the following.

Expand Down Expand Up @@ -342,9 +346,9 @@ def from_preset(
cls,
atnums: np.ndarray,
atcoords: np.ndarray,
rgrid: Union[OneDGrid, list, dict],
preset: Union[str, list, dict],
aim_weights: Union[callable, np.ndarray],
rgrid: Union[OneDGrid, list, dict] = None,
aim_weights: Union[callable, np.ndarray] = None,
*_,
rotate: int = 37,
store: bool = False,
Expand All @@ -357,10 +361,6 @@ def from_preset(
Array of atomic numbers.
atcoords : np.ndarray(M, 3)
Atomic coordinates of atoms.
rgrid : (OneDGrid, list[OneDGrid], dict[int: OneDGrid])
One dimensional radial grid. If of type `OneDGrid` then this radial grid is used for
all atoms. If a list is provided,then ith grid correspond to the ith atom. If
dictionary is provided, then the keys correspond to the `atnums[i]`attribute.
preset : (str, list[str], dict[int: str])
Preset grid accuracy scheme. If string is provided, then preset is used
for all atoms, either it is specified by a list, or a dictionary whose keys
Expand All @@ -371,8 +371,14 @@ def from_preset(
'sg_0', 'sg_1', 'sg_2', and 'sg_3', and the Ochsenfeld grids:
'g1', 'g2', 'g3', 'g4', 'g5', 'g6', and 'g7', with higher number indicating
greater accuracy but denser grid. See `Notes` for more information.
aim_weights : Callable or np.ndarray(K,)
Atoms in molecule weights.
rgrid : (OneDGrid, list[OneDGrid], dict[int: OneDGrid]), optional
One dimensional radial grid. If of type `OneDGrid` then this radial grid is used for
all atoms. If a list is provided,then ith grid correspond to the ith atom. If
dictionary is provided, then the keys correspond to the `atnums[i]`attribute.
If None, then using atomic numbers it will generate a default radial grid
(PowerRTransform of UniformInteger grid).
aim_weights : Callable or np.ndarray(K,), optional
Atoms in molecule weights. If None, then aim_weights is Becke weights with order=3.
rotate : bool or int, optional
Flag to set auto rotation for atomic grid, if given int, the number
will be used as a seed to generate rantom matrix.
Expand Down Expand Up @@ -407,6 +413,8 @@ def from_preset(
"shape of atomic nums does not match with coordinates\n"
f"atomic numbers: {atnums.shape}, coordinates: {atcoords.shape}"
)
if aim_weights is None:
aim_weights = BeckeWeights(order=3)
total_atm = len(atnums)
atomic_grids = []
for i in range(total_atm):
Expand All @@ -417,6 +425,9 @@ def from_preset(
rad = rgrid[i]
elif isinstance(rgrid, dict):
rad = rgrid[atnums[i]]
elif rgrid is None:
atnum = atnums[i]
rad = _generate_default_rgrid(atnum)
else:
raise TypeError(f"not supported radial grid input; got input type: {type(rgrid)}")
# get proper grid type
Expand All @@ -441,9 +452,9 @@ def from_size(
cls,
atnums: np.ndarray,
atcoords: np.ndarray,
rgrid: OneDGrid,
size: int,
aim_weights: Union[callable, np.ndarray],
rgrid: OneDGrid = None,
aim_weights: Union[callable, np.ndarray] = None,
rotate: int = 37,
store: bool = False,
):
Expand All @@ -455,20 +466,21 @@ def from_size(
>>> onedg = UniformInteger(100) # number of points, oned grid before TF.
>>> rgrid = ExpRTransform(1e-5, 2e1).generate_radial(onedg) # radial grid
>>> becke = BeckeWeights(order=3)
>>> molgrid = MolGrid.from_size(atnums, atcoords, rgrid, 110, becke)
>>> molgrid = MolGrid.from_size(atnums, atcoords, 110, becke, rgrid)

Parameters
----------
atnums : np.ndarray(M, 3)
Atomic number of :math:`M` atoms in molecule.
atcoords : np.ndarray(N, 3)
Cartesian coordinates for each atoms
rgrid : OneDGrid
one dimension grid to construct spherical grid
size : int
Num of points on each shell of angular grid
aim_weights : Callable or np.ndarray(K,)
Atoms in molecule weights.
rgrid : OneDGrid, optional
One-dimensional grid to construct the atomic grid. If none, then
default radial grid is generated based on atomic numbers.
aim_weights : Callable or np.ndarray(K,), optional
Atoms in molecule weights. If None, then aim_weights is Becke weights with order=3.
rotate : bool or int , optional
Flag to set auto rotation for atomic grid, if given int, the number
will be used as a seed to generate rantom matrix.
Expand All @@ -481,20 +493,27 @@ def from_size(
MolGrid instance with specified grid property

"""
if aim_weights is None:
aim_weights = BeckeWeights(order=3)
at_grids = []
for i in range(len(atcoords)):
at_grids.append(AtomGrid(rgrid, sizes=[size], center=atcoords[i], rotate=rotate))
if rgrid is None:
atnum = atnums[i]
rad_grid = _generate_default_rgrid(atnum)
else:
rad_grid = rgrid
at_grids.append(AtomGrid(rad_grid, sizes=[size], center=atcoords[i], rotate=rotate))
return cls(atnums, at_grids, aim_weights, store=store)

@classmethod
def from_pruned(
cls,
atnums: np.ndarray,
atcoords: np.ndarray,
rgrid: Union[OneDGrid, list],
radius: Union[float, list],
aim_weights: Union[callable, np.ndarray],
sectors_r: np.ndarray,
rgrid: Union[OneDGrid, list] = None,
aim_weights: Union[callable, np.ndarray] = None,
sectors_degree: np.ndarray = None,
sectors_size: np.ndarray = None,
rotate: int = 37,
Expand All @@ -509,21 +528,23 @@ def from_pruned(
List of atomic numbers for each atom.
atcoords: np.ndarray(M, 3)
Cartesian coordinates for each atoms
rgrid : OneDGrid or List[OneDGrid] or Dict[int: OneDGrid]
One dimensional grid for the radial component. If a list is provided,then ith
grid correspond to the ith atom. If dictionary is provided, then the keys are
correspond to the `atnums[i]` attribute.
radius: float, List[float]
The atomic radius to be multiplied with `r_sectors` (to make them atom specific).
If float, then the same atomic radius is used for all atoms, else a list specifies
it for each atom.
aim_weights: Callable or np.ndarray(\sum^M_n N_n,)
Atoms in molecule/nuclear weights :math:`{ {w_n(r_k)}_k^{N_i}}_n^{M}`, where
:math:`N_i` is the number of points in the ith atomic grid.
sectors_r: List[List], keyword-only argument
Each row is a sequence of boundary points specifying radial sectors of the pruned grid
for the `m`th atom. The first sector is ``[0, radius*sectors_r[0]]``, then
``[radius*sectors_r[0], radius*sectors_r[1]]``, and so on.
rgrid : OneDGrid or List[OneDGrid] or Dict[int: OneDGrid], optional
One dimensional grid for the radial component. If a list is provided,then ith
grid correspond to the ith atom. If dictionary is provided, then the keys are
correspond to the `atnums[i]` attribute. If None, then using atomic numbers it will
generate a default radial grid (PowerRTransform of UniformInteger grid).
aim_weights: Callable or np.ndarray(\sum^M_n N_n,), optional
Atoms in molecule/nuclear weights :math:`{ {w_n(r_k)}_k^{N_i}}_n^{M}`, where
:math:`N_i` is the number of points in the ith atomic grid. If None, then aim_weights
is Becke weights with order=3.
sectors_degree: List[List], keyword-only argument
Each row is a sequence of Lebedev/angular degrees for each radial sector of the pruned
grid for the `m`th atom. If both `sectors_degree` and `sectors_size` are given,
Expand All @@ -548,6 +569,8 @@ def from_pruned(
raise ValueError(
"The dimension of coordinates need to be 2\n" f"got shape: {atcoords.ndim}"
)
if aim_weights is None:
aim_weights = BeckeWeights(order=3)

at_grids = []
num_atoms = len(atcoords)
Expand All @@ -563,6 +586,9 @@ def from_pruned(
rad = rgrid[i]
elif isinstance(rgrid, dict):
rad = rgrid[atnums[i]]
elif rgrid is None:
atnum = atnums[i]
rad = _generate_default_rgrid(atnum)
else:
raise TypeError(f"not supported radial grid input; got input type: {type(rgrid)}")

Expand Down Expand Up @@ -628,3 +654,33 @@ def __getitem__(self, index: int):
self._atcoords[index],
)
return self._atgrids[index]


def _generate_default_rgrid(atnum: int):
r"""
Generate default radial transformation grid from default Horton.

See _DEFAULT_POWER_RTRANSFORM_PARAMS inside utils for information on how
it was determined

Parameters
----------
atnum: int
Atomic Number

Returns
-------
OneDGrid:
One-dimensional grid that was transformed using PowerRTransform.

"""
if atnum in _DEFAULT_POWER_RTRANSFORM_PARAMS:
rmin, rmax, npt = _DEFAULT_POWER_RTRANSFORM_PARAMS[int(atnum)]
rmin, rmax = rmin * 1.8897259885789, rmax * 1.8897259885789
FarnazH marked this conversation as resolved.
Show resolved Hide resolved
onedgrid = UniformInteger(npt)
rgrid = PowerRTransform(rmin, rmax).transform_1d_grid(onedgrid)
return rgrid
else:
raise ValueError(
f"Default rgrid parameter is not included for the" f" atomic number {atnum}."
)
8 changes: 6 additions & 2 deletions src/grid/tests/test_atomgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ def test_from_predefined(self):
np.sum(np.exp(-np.sum(atgrid.points**2, axis=1)) * atgrid.weights),
5.56832800,
)
# test fine grid but without default radial grid
atgrid = AtomGrid.from_preset(rgrid=None, atnum=1, preset="fine")
assert_almost_equal(
np.sum(np.exp(-np.sum(atgrid.points**2, axis=1)) * atgrid.weights),
5.56832800,
)
# test veryfine grid
pts = UniformInteger(41)
tf = PowerRTransform(1.1774580743206259e-07, 20.140888089596444)
Expand Down Expand Up @@ -964,8 +970,6 @@ def test_error_raises(self):
)

# test preset
with pytest.raises(ValueError):
AtomGrid.from_preset(atnum=1, preset="fine")
with pytest.raises(TypeError):
AtomGrid(OneDGrid(np.arange(3), np.arange(3)), sizes=110)
with pytest.raises(TypeError):
Expand Down
Loading