51 lines
1.7 KiB
Python
51 lines
1.7 KiB
Python
import math
|
|
from typing import Annotated, Tuple
|
|
|
|
from pydantic import Field, BaseModel
|
|
|
|
class DiffractionGeometry(BaseModel):
|
|
energy_keV: Annotated[float, Field(gt=1.0, lt=100.0)]
|
|
dtz_mm: Annotated[float, Field(gt=10.0, lt=5000.0)]
|
|
pixel_size_mm: Annotated[float, Field(ge=0.05, le=0.5)]
|
|
beam_center_pxl: Tuple[float, float]
|
|
detector_size_pxl: Tuple[int, int]
|
|
detector_description: str
|
|
detector_serial_number: str
|
|
poni_rot1_rad: float
|
|
poni_rot2_rad: float
|
|
|
|
@property
|
|
def detector_max_radius_pxl(self):
|
|
x0 = self.detector_size_pxl[0] - self.beam_center_pxl[0]
|
|
x1 = self.beam_center_pxl[0]
|
|
y0 = self.detector_size_pxl[1] - self.beam_center_pxl[1]
|
|
y1 = self.beam_center_pxl[1]
|
|
return max(abs(x0), abs(x1), abs(y0), abs(y1))
|
|
|
|
@property
|
|
def detector_radius_mm(self):
|
|
return self.detector_max_radius_pxl * self.pixel_size_mm
|
|
|
|
@property
|
|
def wavelength_angstrom(self):
|
|
return 12.398 / self.energy_keV
|
|
|
|
@property
|
|
def max_resolution_angstrom(self):
|
|
return self.resolution_angstrom(self.dtz_mm)
|
|
|
|
def resolution_angstrom(self, exp_dtz_mm: float) -> float:
|
|
if exp_dtz_mm <= 0:
|
|
raise ValueError("Detector distance must be positive")
|
|
|
|
theta = math.atan(self.detector_radius_mm / exp_dtz_mm)*0.5
|
|
return self.wavelength_angstrom / (2 * math.sin(theta))
|
|
|
|
def calc_dtz_mm(self, exp_resolution_angstrom: float) -> float:
|
|
if exp_resolution_angstrom <= 0:
|
|
raise ValueError("Resolution must be positive")
|
|
x = self.wavelength_angstrom / (2 * exp_resolution_angstrom)
|
|
if x >= 1.0 or x <= -1.0:
|
|
return 0.0
|
|
theta = math.asin(x)
|
|
return self.detector_radius_mm / math.tan(2*theta) |