feat: adds fixedorientation model
All checks were successful
gitea-physics/pdme/pipeline/head This commit looks good
All checks were successful
gitea-physics/pdme/pipeline/head This commit looks good
This commit is contained in:
parent
eb080fed02
commit
2cdf46afa1
140
pdme/model/log_spaced_random_choice_fixed_orientation_model.py
Normal file
140
pdme/model/log_spaced_random_choice_fixed_orientation_model.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import numpy
|
||||||
|
import numpy.random
|
||||||
|
from pdme.model.model import DipoleModel
|
||||||
|
from pdme.measurement import (
|
||||||
|
OscillatingDipole,
|
||||||
|
OscillatingDipoleArrangement,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LogSpacedRandomCountMultipleDipoleFixedMagnitudeFixedOrientationModel(
|
||||||
|
DipoleModel
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Model of multiple oscillating dipoles with a fixed magnitude and fixed rotation. Spaced log uniformly in relaxation time.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
|
||||||
|
wexp_min: log-10 lower bound for dipole frequency
|
||||||
|
wexp_min: log-10 upper bound for dipole frequency
|
||||||
|
|
||||||
|
pfixed : float
|
||||||
|
The fixed dipole magnitude.
|
||||||
|
|
||||||
|
thetafixed: float
|
||||||
|
The fixed theta (polar angle).
|
||||||
|
Should be between 0 and pi.
|
||||||
|
|
||||||
|
phifixed: float
|
||||||
|
The fixed phi (azimuthal angle).
|
||||||
|
Should be between 0 and 2 pi.
|
||||||
|
|
||||||
|
n_max : int
|
||||||
|
The maximum number of dipoles.
|
||||||
|
|
||||||
|
prob_occupancy : float
|
||||||
|
The probability of dipole occupancy
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
xmin: float,
|
||||||
|
xmax: float,
|
||||||
|
ymin: float,
|
||||||
|
ymax: float,
|
||||||
|
zmin: float,
|
||||||
|
zmax: float,
|
||||||
|
wexp_min: float,
|
||||||
|
wexp_max: float,
|
||||||
|
pfixed: float,
|
||||||
|
thetafixed: float,
|
||||||
|
phifixed: float,
|
||||||
|
n_max: int,
|
||||||
|
prob_occupancy: float,
|
||||||
|
) -> None:
|
||||||
|
self.xmin = xmin
|
||||||
|
self.xmax = xmax
|
||||||
|
self.ymin = ymin
|
||||||
|
self.ymax = ymax
|
||||||
|
self.zmin = zmin
|
||||||
|
self.zmax = zmax
|
||||||
|
self.wexp_min = wexp_min
|
||||||
|
self.wexp_max = wexp_max
|
||||||
|
self.pfixed = pfixed
|
||||||
|
self.thetafixed = thetafixed
|
||||||
|
self.phifixed = phifixed
|
||||||
|
self.rng = numpy.random.default_rng()
|
||||||
|
self.n_max = n_max
|
||||||
|
|
||||||
|
px = self.pfixed * numpy.sin(self.thetafixed) * numpy.cos(self.phifixed)
|
||||||
|
py = self.pfixed * numpy.sin(self.thetafixed) * numpy.sin(self.phifixed)
|
||||||
|
pz = self.pfixed * numpy.cos(self.thetafixed)
|
||||||
|
|
||||||
|
self.moment_fixed = numpy.array([px, py, pz])
|
||||||
|
if prob_occupancy >= 1 or prob_occupancy <= 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"The probability of a dipole site occupancy must be between 0 and 1, got {prob_occupancy}"
|
||||||
|
)
|
||||||
|
self.prob_occupancy = prob_occupancy
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"LogSpacedRandomCountMultipleDipoleFixedMagnitudeFixedOrientationModel({self.xmin}, {self.xmax}, {self.ymin}, {self.ymax}, {self.zmin}, {self.zmax}, {self.wexp_min}, {self.wexp_max}, {self.pfixed}, {self.thetafixed}, {self.phifixed}, {self.n_max}, {self.prob_occupancy})"
|
||||||
|
|
||||||
|
def get_dipoles(
|
||||||
|
self, max_frequency: float, rng_to_use: numpy.random.Generator = None
|
||||||
|
) -> OscillatingDipoleArrangement:
|
||||||
|
rng: numpy.random.Generator
|
||||||
|
if rng_to_use is None:
|
||||||
|
rng = self.rng
|
||||||
|
else:
|
||||||
|
rng = rng_to_use
|
||||||
|
|
||||||
|
dipoles = []
|
||||||
|
|
||||||
|
n = rng.binomial(self.n_max, self.prob_occupancy)
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
s_pts = numpy.array(
|
||||||
|
(
|
||||||
|
rng.uniform(self.xmin, self.xmax),
|
||||||
|
rng.uniform(self.ymin, self.ymax),
|
||||||
|
rng.uniform(self.zmin, self.zmax),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
frequency = 10 ** rng.uniform(self.wexp_min, self.wexp_max)
|
||||||
|
|
||||||
|
dipoles.append(OscillatingDipole(self.moment_fixed, s_pts, frequency))
|
||||||
|
return OscillatingDipoleArrangement(dipoles)
|
||||||
|
|
||||||
|
def get_monte_carlo_dipole_inputs(
|
||||||
|
self,
|
||||||
|
monte_carlo_n: int,
|
||||||
|
_: float,
|
||||||
|
rng_to_use: numpy.random.Generator = None,
|
||||||
|
) -> numpy.ndarray:
|
||||||
|
|
||||||
|
rng: numpy.random.Generator
|
||||||
|
if rng_to_use is None:
|
||||||
|
rng = self.rng
|
||||||
|
else:
|
||||||
|
rng = rng_to_use
|
||||||
|
|
||||||
|
shape = (monte_carlo_n, self.n_max)
|
||||||
|
|
||||||
|
p_mask = rng.binomial(1, self.prob_occupancy, shape)
|
||||||
|
|
||||||
|
dipoles = numpy.einsum("ij,k->ijk", p_mask, self.moment_fixed)
|
||||||
|
# Is there a better way to create the final array? probably! can create a flatter guy then reshape.
|
||||||
|
# this is easier to reason about.
|
||||||
|
px = dipoles[:, :, 0]
|
||||||
|
py = dipoles[:, :, 1]
|
||||||
|
pz = dipoles[:, :, 2]
|
||||||
|
|
||||||
|
sx = rng.uniform(self.xmin, self.xmax, shape)
|
||||||
|
sy = rng.uniform(self.ymin, self.ymax, shape)
|
||||||
|
sz = rng.uniform(self.zmin, self.zmax, shape)
|
||||||
|
|
||||||
|
w = 10 ** rng.uniform(self.wexp_min, self.wexp_max, shape)
|
||||||
|
|
||||||
|
return numpy.stack([px, py, pz, sx, sy, sz, w], axis=-1)
|
Loading…
x
Reference in New Issue
Block a user