oscillating dipole and arrangement
This commit is contained in:
parent
ca8269b538
commit
69d8befe68
3
pathfinder/model/oscillating/__init__.py
Normal file
3
pathfinder/model/oscillating/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from pathfinder.model.oscillating.oscillating_dipole import OscillatingDipole, OscillatingDipoleArrangement
|
||||
|
||||
__all__ = ['OscillatingDipole', 'OscillatingDipoleArrangement']
|
72
pathfinder/model/oscillating/dot.py
Normal file
72
pathfinder/model/oscillating/dot.py
Normal file
@ -0,0 +1,72 @@
|
||||
from dataclasses import dataclass
|
||||
import numpy
|
||||
import numpy.typing
|
||||
|
||||
|
||||
@dataclass
|
||||
class DotMeasurement():
|
||||
'''
|
||||
Representation of a dot measuring oscillating dipoles.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
v : float
|
||||
The voltage measured at the dot.
|
||||
r : numpy.ndarray
|
||||
The position of the dot.
|
||||
f : float
|
||||
The measurement frequency.
|
||||
'''
|
||||
v: float
|
||||
r: numpy.ndarray
|
||||
f: float
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.r = numpy.array(self.r)
|
||||
|
||||
# def v_for_point(self, pt: numpy.ndarray) -> float:
|
||||
# '''
|
||||
# Returns the voltage for a static dipole represented as a phase space point.
|
||||
#
|
||||
# Parameters
|
||||
# ----------
|
||||
# pt: A length 6 numpy array representing a dipole, as (px, py, pz, sx, sy, sz).
|
||||
#
|
||||
# Returns
|
||||
# ----------
|
||||
# Returns the voltage from that dipole at the point.
|
||||
# '''
|
||||
# p = pt[0:3] # hardcoded here because chances
|
||||
# s = pt[3:6] # are we'll only ever work in 3d.
|
||||
#
|
||||
# diff = self.r - s
|
||||
# return p.dot(diff) / (numpy.linalg.norm(diff)**3)
|
||||
#
|
||||
# def cost(self, pts: numpy.ndarray) -> float:
|
||||
# # 6 because dipole in 3d has 6 degrees of freedom.
|
||||
# pt_length = 6
|
||||
# # creates numpy.ndarrays in groups of pt_length.
|
||||
# # Will throw problems for irregular points, but that's okay for now.
|
||||
# chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
|
||||
# return sum(self.v_for_point(pt) for pt in chunked_pts) - self.v
|
||||
#
|
||||
# def jac_pt(self, pt: numpy.ndarray) -> numpy.ndarray:
|
||||
# p = pt[0:3] # hardcoded here because chances
|
||||
# s = pt[3:6] # are we'll only ever work in 3d.
|
||||
#
|
||||
# diff = self.r - s
|
||||
#
|
||||
# p_divs = diff / (numpy.linalg.norm(diff)**3)
|
||||
#
|
||||
# r_divs = -p / (numpy.linalg.norm(diff)**3) + 3 * p.dot(diff) * diff / (numpy.linalg.norm(diff)**5)
|
||||
#
|
||||
# return numpy.append(p_divs, r_divs)
|
||||
#
|
||||
# def jac(self, pts: numpy.ndarray) -> numpy.ndarray:
|
||||
# # 6 because dipole in 3d has 6 degrees of freedom.
|
||||
# pt_length = 6
|
||||
# # creates numpy.ndarrays in groups of pt_length.
|
||||
# # Will throw problems for irregular points, but that's okay for now.
|
||||
# chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)]
|
||||
#
|
||||
# return numpy.append([], [self.jac_pt(pt) for pt in chunked_pts])
|
77
pathfinder/model/oscillating/oscillating_dipole.py
Normal file
77
pathfinder/model/oscillating/oscillating_dipole.py
Normal file
@ -0,0 +1,77 @@
|
||||
from dataclasses import dataclass
|
||||
import numpy
|
||||
import numpy.typing
|
||||
from typing import Sequence, List, Tuple
|
||||
from pathfinder.model.oscillating.dot import DotMeasurement
|
||||
|
||||
|
||||
DotInput = Tuple[numpy.typing.ArrayLike, float]
|
||||
|
||||
|
||||
@dataclass
|
||||
class OscillatingDipole():
|
||||
'''
|
||||
Representation of an oscilltaing dipole, either known or guessed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p : numpy.ndarray
|
||||
The oscillating dipole moment, with overall sign arbitrary.
|
||||
s : numpy.ndarray
|
||||
The position of the dipole.
|
||||
w : float
|
||||
The oscillation frequency.
|
||||
'''
|
||||
p: numpy.ndarray
|
||||
s: numpy.ndarray
|
||||
w: float
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
'''
|
||||
Coerce the inputs into numpy arrays.
|
||||
'''
|
||||
self.p = numpy.array(self.p)
|
||||
self.s = numpy.array(self.s)
|
||||
|
||||
def s_at_position(self, r: numpy.ndarray, f: float) -> float:
|
||||
'''
|
||||
Returns the noise potential at a point r, at some frequency f.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
r : numpy.ndarray
|
||||
The position of the dot.
|
||||
f : float
|
||||
The dot frequency to sample.
|
||||
'''
|
||||
return (self._alpha(r))**2 * self._b(f)
|
||||
|
||||
def _alpha(self, r: numpy.ndarray) -> float:
|
||||
diff = r - self.s
|
||||
return self.p.dot(diff) / (numpy.linalg.norm(diff)**3)
|
||||
|
||||
def _b(self, f: float) -> float:
|
||||
return (1 / numpy.pi) * (self.w / (f**2 + self.w**2))
|
||||
|
||||
|
||||
class OscillatingDipoleArrangement():
|
||||
'''
|
||||
A collection of oscillating dipoles, which we are interested in being able to characterise.
|
||||
|
||||
Parameters
|
||||
--------
|
||||
dipoles : Sequence[OscillatingDipole]
|
||||
'''
|
||||
def __init__(self, dipoles: Sequence[OscillatingDipole]):
|
||||
self.dipoles = dipoles
|
||||
|
||||
def get_dot_measurement(self, dot_input: DotInput) -> DotMeasurement:
|
||||
r = numpy.array(dot_input[0])
|
||||
f = dot_input[1]
|
||||
return DotMeasurement(sum([dipole.s_at_position(r, f) for dipole in self.dipoles]), r, f)
|
||||
|
||||
def get_dot_measurements(self, dot_inputs: Sequence[DotInput]) -> List[DotMeasurement]:
|
||||
'''
|
||||
For a series of points, each with three coordinates and a frequency, return a list of the corresponding DotMeasurements.
|
||||
'''
|
||||
return [self.get_dot_measurement(dot_input) for dot_input in dot_inputs]
|
22
tests/model/oscillating/test_oscillatingdipole.py
Normal file
22
tests/model/oscillating/test_oscillatingdipole.py
Normal file
@ -0,0 +1,22 @@
|
||||
import numpy
|
||||
import pathfinder.model.oscillating as model
|
||||
|
||||
|
||||
def test_static_dipole():
|
||||
d1 = model.OscillatingDipole((1, 2, 3), (4, 5, 6), 7)
|
||||
d2 = model.OscillatingDipole((2, 5, 3), (4, -5, -6), 2)
|
||||
dipoles = model.OscillatingDipoleArrangement([d1, d2])
|
||||
|
||||
dot_position1 = (-1, -1, -1)
|
||||
dot_frequency1 = 11
|
||||
expected_v1 = 0.00001421963287022476
|
||||
expected_v2 = 0.00001107180225755457
|
||||
|
||||
numpy.testing.assert_allclose(d1.s_at_position(dot_position1, dot_frequency1), expected_v1, err_msg="Voltage at dot isn't as expected.")
|
||||
|
||||
dot_measurements = dipoles.get_dot_measurements([(dot_position1, dot_frequency1)])
|
||||
assert len(dot_measurements) == 1, "Should have only had one dot measurement."
|
||||
measurement = dot_measurements[0]
|
||||
numpy.testing.assert_allclose(measurement.r, dot_position1, err_msg="Dot position should have been passed over")
|
||||
numpy.testing.assert_allclose(measurement.f, dot_frequency1, err_msg="Dot frequency should have been passed over")
|
||||
numpy.testing.assert_allclose(measurement.v, expected_v1 + expected_v2, err_msg="Voltage at dot isn't as expected.")
|
Reference in New Issue
Block a user