22
22
from typing import Union
23
23
24
24
import numpy as np
25
+ import scipy .constants
25
26
26
27
from grid .atomgrid import AtomGrid
27
28
from grid .basegrid import Grid , LocalGrid , OneDGrid
29
+ from grid .becke import BeckeWeights
30
+ from grid .onedgrid import UniformInteger
31
+ from grid .rtransform import PowerRTransform
32
+ from grid .utils import _DEFAULT_POWER_RTRANSFORM_PARAMS
28
33
29
34
30
35
class MolGrid (Grid ):
@@ -75,7 +80,7 @@ class MolGrid(Grid):
75
80
specifying the size of each Levedev grid at each radial points.
76
81
77
82
>>> preset = "fine" # Many choices available.
78
- >>> molgrid = MolGrid.from_preset(charges, coords, rgrid, preset, aim_weights=becke)
83
+ >>> molgrid = MolGrid.from_preset(charges,coords,preset,aim_weights=becke,rgrid=rgrid )
79
84
80
85
The general way to integrate is the following.
81
86
@@ -342,10 +347,9 @@ def from_preset(
342
347
cls ,
343
348
atnums : np .ndarray ,
344
349
atcoords : np .ndarray ,
345
- rgrid : Union [OneDGrid , list , dict ],
346
350
preset : Union [str , list , dict ],
347
- aim_weights : Union [callable , np . ndarray ] ,
348
- * _ ,
351
+ rgrid : Union [OneDGrid , list , dict ] = None ,
352
+ aim_weights : Union [ callable , np . ndarray ] = None ,
349
353
rotate : int = 37 ,
350
354
store : bool = False ,
351
355
):
@@ -357,10 +361,6 @@ def from_preset(
357
361
Array of atomic numbers.
358
362
atcoords : np.ndarray(M, 3)
359
363
Atomic coordinates of atoms.
360
- rgrid : (OneDGrid, list[OneDGrid], dict[int: OneDGrid])
361
- One dimensional radial grid. If of type `OneDGrid` then this radial grid is used for
362
- all atoms. If a list is provided,then ith grid correspond to the ith atom. If
363
- dictionary is provided, then the keys correspond to the `atnums[i]`attribute.
364
364
preset : (str, list[str], dict[int: str])
365
365
Preset grid accuracy scheme. If string is provided, then preset is used
366
366
for all atoms, either it is specified by a list, or a dictionary whose keys
@@ -371,8 +371,14 @@ def from_preset(
371
371
'sg_0', 'sg_1', 'sg_2', and 'sg_3', and the Ochsenfeld grids:
372
372
'g1', 'g2', 'g3', 'g4', 'g5', 'g6', and 'g7', with higher number indicating
373
373
greater accuracy but denser grid. See `Notes` for more information.
374
- aim_weights : Callable or np.ndarray(K,)
375
- Atoms in molecule weights.
374
+ rgrid : (OneDGrid, list[OneDGrid], dict[int: OneDGrid]), optional
375
+ One dimensional radial grid. If of type `OneDGrid` then this radial grid is used for
376
+ all atoms. If a list is provided,then ith grid correspond to the ith atom. If
377
+ dictionary is provided, then the keys correspond to the `atnums[i]`attribute.
378
+ If None, then using atomic numbers it will generate a default radial grid
379
+ (PowerRTransform of UniformInteger grid).
380
+ aim_weights : Callable or np.ndarray(K,), optional
381
+ Atoms in molecule weights. If None, then aim_weights is Becke weights with order=3.
376
382
rotate : bool or int, optional
377
383
Flag to set auto rotation for atomic grid, if given int, the number
378
384
will be used as a seed to generate rantom matrix.
@@ -407,6 +413,8 @@ def from_preset(
407
413
"shape of atomic nums does not match with coordinates\n "
408
414
f"atomic numbers: { atnums .shape } , coordinates: { atcoords .shape } "
409
415
)
416
+ if aim_weights is None :
417
+ aim_weights = BeckeWeights (order = 3 )
410
418
total_atm = len (atnums )
411
419
atomic_grids = []
412
420
for i in range (total_atm ):
@@ -417,6 +425,9 @@ def from_preset(
417
425
rad = rgrid [i ]
418
426
elif isinstance (rgrid , dict ):
419
427
rad = rgrid [atnums [i ]]
428
+ elif rgrid is None :
429
+ atnum = atnums [i ]
430
+ rad = _generate_default_rgrid (atnum )
420
431
else :
421
432
raise TypeError (f"not supported radial grid input; got input type: { type (rgrid )} " )
422
433
# get proper grid type
@@ -431,7 +442,7 @@ def from_preset(
431
442
f"Not supported preset type; got preset { preset } with type { type (preset )} "
432
443
)
433
444
at_grid = AtomGrid .from_preset (
434
- rad , atnum = atnums [i ], preset = gd_type , center = atcoords [i ], rotate = rotate
445
+ atnum = atnums [i ], preset = gd_type , rgrid = rad , center = atcoords [i ], rotate = rotate
435
446
)
436
447
atomic_grids .append (at_grid )
437
448
return cls (atnums , atomic_grids , aim_weights , store = store )
@@ -441,9 +452,9 @@ def from_size(
441
452
cls ,
442
453
atnums : np .ndarray ,
443
454
atcoords : np .ndarray ,
444
- rgrid : OneDGrid ,
445
455
size : int ,
446
- aim_weights : Union [callable , np .ndarray ],
456
+ rgrid : OneDGrid = None ,
457
+ aim_weights : Union [callable , np .ndarray ] = None ,
447
458
rotate : int = 37 ,
448
459
store : bool = False ,
449
460
):
@@ -455,20 +466,21 @@ def from_size(
455
466
>>> onedg = UniformInteger(100) # number of points, oned grid before TF.
456
467
>>> rgrid = ExpRTransform(1e-5, 2e1).generate_radial(onedg) # radial grid
457
468
>>> becke = BeckeWeights(order=3)
458
- >>> molgrid = MolGrid.from_size(atnums, atcoords, rgrid, 110, becke)
469
+ >>> molgrid = MolGrid.from_size(atnums, atcoords, 110, becke, rgrid )
459
470
460
471
Parameters
461
472
----------
462
473
atnums : np.ndarray(M, 3)
463
474
Atomic number of :math:`M` atoms in molecule.
464
475
atcoords : np.ndarray(N, 3)
465
476
Cartesian coordinates for each atoms
466
- rgrid : OneDGrid
467
- one dimension grid to construct spherical grid
468
477
size : int
469
478
Num of points on each shell of angular grid
470
- aim_weights : Callable or np.ndarray(K,)
471
- Atoms in molecule weights.
479
+ rgrid : OneDGrid, optional
480
+ One-dimensional grid to construct the atomic grid. If none, then
481
+ default radial grid is generated based on atomic numbers.
482
+ aim_weights : Callable or np.ndarray(K,), optional
483
+ Atoms in molecule weights. If None, then aim_weights is Becke weights with order=3.
472
484
rotate : bool or int , optional
473
485
Flag to set auto rotation for atomic grid, if given int, the number
474
486
will be used as a seed to generate rantom matrix.
@@ -481,20 +493,27 @@ def from_size(
481
493
MolGrid instance with specified grid property
482
494
483
495
"""
496
+ if aim_weights is None :
497
+ aim_weights = BeckeWeights (order = 3 )
484
498
at_grids = []
485
499
for i in range (len (atcoords )):
486
- at_grids .append (AtomGrid (rgrid , sizes = [size ], center = atcoords [i ], rotate = rotate ))
500
+ if rgrid is None :
501
+ atnum = atnums [i ]
502
+ rad_grid = _generate_default_rgrid (atnum )
503
+ else :
504
+ rad_grid = rgrid
505
+ at_grids .append (AtomGrid (rad_grid , sizes = [size ], center = atcoords [i ], rotate = rotate ))
487
506
return cls (atnums , at_grids , aim_weights , store = store )
488
507
489
508
@classmethod
490
509
def from_pruned (
491
510
cls ,
492
511
atnums : np .ndarray ,
493
512
atcoords : np .ndarray ,
494
- rgrid : Union [OneDGrid , list ],
495
513
radius : Union [float , list ],
496
- aim_weights : Union [callable , np .ndarray ],
497
514
sectors_r : np .ndarray ,
515
+ rgrid : Union [OneDGrid , list ] = None ,
516
+ aim_weights : Union [callable , np .ndarray ] = None ,
498
517
sectors_degree : np .ndarray = None ,
499
518
sectors_size : np .ndarray = None ,
500
519
rotate : int = 37 ,
@@ -509,21 +528,23 @@ def from_pruned(
509
528
List of atomic numbers for each atom.
510
529
atcoords: np.ndarray(M, 3)
511
530
Cartesian coordinates for each atoms
512
- rgrid : OneDGrid or List[OneDGrid] or Dict[int: OneDGrid]
513
- One dimensional grid for the radial component. If a list is provided,then ith
514
- grid correspond to the ith atom. If dictionary is provided, then the keys are
515
- correspond to the `atnums[i]` attribute.
516
531
radius: float, List[float]
517
532
The atomic radius to be multiplied with `r_sectors` (to make them atom specific).
518
533
If float, then the same atomic radius is used for all atoms, else a list specifies
519
534
it for each atom.
520
- aim_weights: Callable or np.ndarray(\sum^M_n N_n,)
521
- Atoms in molecule/nuclear weights :math:`{ {w_n(r_k)}_k^{N_i}}_n^{M}`, where
522
- :math:`N_i` is the number of points in the ith atomic grid.
523
535
sectors_r: List[List], keyword-only argument
524
536
Each row is a sequence of boundary points specifying radial sectors of the pruned grid
525
537
for the `m`th atom. The first sector is ``[0, radius*sectors_r[0]]``, then
526
538
``[radius*sectors_r[0], radius*sectors_r[1]]``, and so on.
539
+ rgrid : OneDGrid or List[OneDGrid] or Dict[int: OneDGrid], optional
540
+ One dimensional grid for the radial component. If a list is provided,then ith
541
+ grid correspond to the ith atom. If dictionary is provided, then the keys are
542
+ correspond to the `atnums[i]` attribute. If None, then using atomic numbers it will
543
+ generate a default radial grid (PowerRTransform of UniformInteger grid).
544
+ aim_weights: Callable or np.ndarray(\sum^M_n N_n,), optional
545
+ Atoms in molecule/nuclear weights :math:`{ {w_n(r_k)}_k^{N_i}}_n^{M}`, where
546
+ :math:`N_i` is the number of points in the ith atomic grid. If None, then aim_weights
547
+ is Becke weights with order=3.
527
548
sectors_degree: List[List], keyword-only argument
528
549
Each row is a sequence of Lebedev/angular degrees for each radial sector of the pruned
529
550
grid for the `m`th atom. If both `sectors_degree` and `sectors_size` are given,
@@ -548,6 +569,8 @@ def from_pruned(
548
569
raise ValueError (
549
570
"The dimension of coordinates need to be 2\n " f"got shape: { atcoords .ndim } "
550
571
)
572
+ if aim_weights is None :
573
+ aim_weights = BeckeWeights (order = 3 )
551
574
552
575
at_grids = []
553
576
num_atoms = len (atcoords )
@@ -563,6 +586,9 @@ def from_pruned(
563
586
rad = rgrid [i ]
564
587
elif isinstance (rgrid , dict ):
565
588
rad = rgrid [atnums [i ]]
589
+ elif rgrid is None :
590
+ atnum = atnums [i ]
591
+ rad = _generate_default_rgrid (atnum )
566
592
else :
567
593
raise TypeError (f"not supported radial grid input; got input type: { type (rgrid )} " )
568
594
@@ -628,3 +654,35 @@ def __getitem__(self, index: int):
628
654
self ._atcoords [index ],
629
655
)
630
656
return self ._atgrids [index ]
657
+
658
+
659
+ def _generate_default_rgrid (atnum : int ):
660
+ r"""
661
+ Generate default radial transformation grid from default Horton.
662
+
663
+ See _DEFAULT_POWER_RTRANSFORM_PARAMS inside utils for information on how
664
+ it was determined
665
+
666
+ Parameters
667
+ ----------
668
+ atnum: int
669
+ Atomic Number
670
+
671
+ Returns
672
+ -------
673
+ OneDGrid:
674
+ One-dimensional grid that was transformed using PowerRTransform.
675
+
676
+ """
677
+ if atnum in _DEFAULT_POWER_RTRANSFORM_PARAMS :
678
+ rmin , rmax , npt = _DEFAULT_POWER_RTRANSFORM_PARAMS [int (atnum )]
679
+ # Convert from Angstrom to atomic units
680
+ rmin = rmin * scipy .constants .angstrom / scipy .constants .value ("atomic unit of length" )
681
+ rmax = rmax * scipy .constants .angstrom / scipy .constants .value ("atomic unit of length" )
682
+ onedgrid = UniformInteger (npt )
683
+ rgrid = PowerRTransform (rmin , rmax ).transform_1d_grid (onedgrid )
684
+ return rgrid
685
+ else :
686
+ raise ValueError (
687
+ f"Default rgrid parameter is not included for the" f" atomic number { atnum } ."
688
+ )
0 commit comments