python/src/rwsims/motions.py

93 lines
2.5 KiB
Python
Raw Normal View History

2024-06-19 17:10:49 +00:00
from __future__ import annotations
2024-06-20 17:19:55 +00:00
from abc import ABC, abstractmethod
2024-06-19 17:10:49 +00:00
import numpy as np
from numpy.random import Generator
from numpy.typing import ArrayLike
2024-06-20 17:19:55 +00:00
from .helper import xyz_to_spherical, spherical_to_xyz, omega_q, draw_orientation, get_rotation_matrix
2024-06-19 17:10:49 +00:00
2024-06-20 17:19:55 +00:00
class BaseMotion(ABC):
def __init__(self, delta: float, eta: float, rng: Generator):
self._delta = delta
self._eta = eta
2024-06-19 17:10:49 +00:00
2024-06-20 17:19:55 +00:00
self._rng = rng
2024-06-19 17:10:49 +00:00
2024-06-20 17:19:55 +00:00
@abstractmethod
def __repr__(self):
pass
2024-06-19 17:10:49 +00:00
2024-06-20 17:19:55 +00:00
def start(self):
pass
2024-06-19 17:10:49 +00:00
2024-06-20 17:19:55 +00:00
@abstractmethod
def jump(self, size: int = 1) -> ArrayLike:
pass
2024-06-19 17:10:49 +00:00
2024-06-20 17:19:55 +00:00
class RandomJump(BaseMotion):
2024-06-19 17:10:49 +00:00
def __repr__(self):
return 'Random Jump'
def jump(self, size: int = 1) -> ArrayLike:
2024-06-20 17:19:55 +00:00
return omega_q(self._delta, self._eta, *draw_orientation(self._rng, size=size))
class TetrahedralJump(BaseMotion):
def __init__(self, delta: float, eta: float, rng: Generator):
super().__init__(delta, eta, rng)
self._orientation = None
self._start = None
def __repr__(self):
return 'Tetrahedral Jump'
def start(self):
self._orientation = self._make_tetrahedron()
self._start = self._rng.choice([0, 1, 2, 3])
def _make_tetrahedron(self) -> np.ndarray:
beta = np.arccos(-1/3) # tetrahedral angle 109.5 degrees
sin_beta = np.sin(beta)
cos_beta = np.cos(beta)
# corners of a tetrahedron
alpha = 2 * np.pi * self._rng.random()
corners = np.array([
[0, 0, 1],
[sin_beta * np.cos(alpha), sin_beta * np.sin(alpha), cos_beta],
[sin_beta * np.cos(alpha+2*np.pi/3), sin_beta * np.sin(alpha+2*np.pi/3), cos_beta],
[sin_beta * np.cos(alpha+4*np.pi/3), sin_beta * np.sin(alpha+4*np.pi/3), cos_beta]
])
# orientation in lab system
theta0, phi0 = draw_orientation(self._rng)
rot = get_rotation_matrix(
corners[0],
np.array(spherical_to_xyz(1., theta0, phi0)),
)
orientations = np.zeros(4)
for i in range(4):
corner_lab = np.dot(rot, corners[i])
_, theta_i, phi_i = xyz_to_spherical(*corner_lab)
orientations[i] = omega_q(self._delta, self._eta, theta_i, phi_i)
return orientations
def jump(self, size: int = 1) -> ArrayLike:
jumps = self._rng.choice([1, 2, 3], size=size)
jumps = np.cumsum(jumps) + self._start
jumps %= 4
self._start = jumps[-1]
return self._orientation[jumps]