Source code for atompy.physics._physics
import numpy as np
import numpy.typing as npt
import time
from typing import overload, Union
from .._vector import Vector
[docs]
def subtract_binding_energy(
pin: Vector, Ebind: Union[float, npt.NDArray[np.float64]]
) -> Vector:
"""Substracts binding energy from p, conserves direction of p
Parameters
----------
pin : :class:`.Vector`
ingoing momenta
Ebind : float or `np.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
binding energy in a.u.
Returns
-------
vectors : :class:`.Vector`
shortened momentum vector
"""
if isinstance(Ebind, np.ndarray) and len(Ebind) != len(pin):
m = f"Length mismatch of Ebind ({len(Ebind)}) and pin ({len(pin)})"
raise ValueError(m)
radicands = 2 * (pin.mag**2 / 2.0 - Ebind)
pmag_new = np.array(
[np.sqrt(radicand) if radicand > 0 else -1.0 for radicand in radicands]
)
thetas = pin.theta
phis = pin.phi
pout = np.array(
[
[
p * np.sin(theta) * np.cos(phi),
p * np.sin(theta) * np.sin(phi),
p * np.cos(theta),
]
for p, theta, phi in zip(pmag_new, thetas, phis)
if p > 0.0
]
)
return Vector(pout)
@overload
def rho_p_microcanonical(
pmag: float, E_bind: float, normalize: bool = True
) -> float: ...
@overload
def rho_p_microcanonical(
pmag: npt.NDArray[np.float64], E_bind: float, normalize: bool = True
) -> npt.NDArray[np.float64]: ...
[docs]
def rho_p_microcanonical(
pmag: Union[float, npt.NDArray[np.float64]], E_bind: float, normalize: bool = True
) -> Union[float, npt.NDArray[np.float64]]:
"""
Momentum distribution of one component in hydrogen-like system
from Abrines Proc Phys. Soc 88 861 (1966)
Parameters
----------
pmag : float or `np.narray`
momentum magnitude in a.u.
E_bind : float
binding energy in a.u.
normalize : bool, default True
if True, normalize resulting distribution to maximum
Returns
-------
float or `np.ndarray`
"""
p0 = np.sqrt(2.0 * E_bind)
out = 8.0 * p0**5 / np.pi**2 / (pmag**2 + p0**2) ** 4
return out / np.amax(out) if normalize else out
[docs]
def mom_init_distr_elec(size: int, E_bind_au: float, scale: float = 3.0) -> Vector:
"""
Dice-throw three momentum components following a microcanonical
distribution
Parameters
----------
size : int
Number of throws
E_bind : float
Binding energy in a.u.
scale : float
scale factor for maximum momentum magnitude (max_mom = p0 * scale)
Returns
-------
`atompy.vector.Vector`
Momentum vectors of the distribution
"""
t0 = time.time()
line0 = "Generating random microcanonical momentum distribution " f"(size {size}). "
succesful_throws = 0
p = np.zeros((size, 3))
while succesful_throws < size:
line = "\r" + line0 + "%.0lf percent done." % (100.0 * succesful_throws / size)
print(line, end="")
buffer = size - succesful_throws
p0 = np.sqrt(2.0 * E_bind_au)
pmag = np.random.uniform(0, p0 * scale, buffer)
density = 8.0 * p0**5 / np.pi**2 / (pmag**2 + p0**2) ** 4 * pmag**2
density /= np.max(density)
second = np.random.random(buffer)
pmag = np.ma.compressed(np.ma.masked_array(pmag, mask=second >= density))
theta = np.arccos(2.0 * np.random.random(pmag.size) - 1.0)
phi = 2.0 * np.pi * np.random.random(pmag.size)
p[succesful_throws : succesful_throws + pmag.size, 0] = (
pmag * np.sin(theta) * np.cos(phi)
)
p[succesful_throws : succesful_throws + pmag.size, 1] = (
pmag * np.sin(theta) * np.sin(phi)
)
p[succesful_throws : succesful_throws + pmag.size, 2] = pmag * np.cos(theta)
succesful_throws += pmag.size
t1 = time.time()
print(f"\r{line0}Total runtime: {t1-t0:.2f}s")
return Vector(p)
[docs]
def mom_init_distr_elec_mol(
distr_atomic: Vector, stretch_factor: float
) -> tuple[Vector, Vector]:
"""
Create molecular momentum distribution
Parameters
----------
distr_atomic : `atompy.vector.Vector`
The (atomic) distribution
stretch_factor : float
The factor how much the distribution will be stretched along that axis
Returns
-------
`atompy.vector.Vector`
The new momentum distribution
`atompy.vector.Vector`
The distribution of directions perpendicular to the directions along
which the stretch factor was applied
Notes
-----
Creates a isotropic distribution of molecular orientations
Takes the atomic distribution and stretches y and z component of it. This
would mean that all molecules are aligned along x
Rotate stretched atomic distribution and rotates it such that it
corresponds to the isotropic distribution of molecules
"""
N = distr_atomic.ndarray.shape[0]
print("Creating molecular orbitals... ")
t0 = time.time()
# stretch along x and y, i.e., molecule is aligned along z
distr_molecular = distr_atomic.ndarray.copy()
distr_molecular[:, :2] = distr_molecular[:, :2] * stretch_factor
distr_molecular = Vector(distr_molecular)
# rotate them randomly
theta = np.arccos(2.0 * np.random.random(N) - 1)
phi = 2.0 * np.pi * np.random.random(N)
molecular_orientation = np.zeros(distr_molecular.ndarray.shape)
molecular_orientation[:, 0] = np.sin(theta) * np.cos(phi)
molecular_orientation[:, 1] = np.sin(theta) * np.sin(phi)
molecular_orientation[:, 2] = np.cos(theta)
distr_molecular = distr_molecular.rotated_around_y(theta).rotated_around_z(phi)
t1 = time.time()
print(f"Done. Total runtime: {t1-t0:.2f}s")
return distr_molecular, Vector(molecular_orientation)