Export Off-Specular scattering to pseudo-ORSO format #10
No files matched your search
+5
-1
@@ -517,6 +517,7 @@ class OutputFomatOption(StrEnum):
|
||||
orb = "orb"
|
||||
Rqz = "Rqz"
|
||||
Rlt = "Rlt"
|
||||
OS_ort = "OS.ort"
|
||||
|
||||
|
||||
class PlotColormaps(StrEnum):
|
||||
@@ -530,7 +531,7 @@ class PlotColormaps(StrEnum):
|
||||
@dataclass
|
||||
class ReflectivityOutputConfig(ArgParsable):
|
||||
outputFormats: List[OutputFomatOption] = field(
|
||||
default_factory=lambda: ['Rqz.ort'],
|
||||
default_factory=lambda: [],
|
||||
metadata={
|
||||
'short': 'of',
|
||||
'group': 'output',
|
||||
@@ -595,6 +596,9 @@ class ReflectivityOutputConfig(ArgParsable):
|
||||
or OutputFomatOption.Rlt_ort in outputFormat\
|
||||
or OutputFomatOption.Rlt in outputFormat:
|
||||
format_list.append(OutputFomatOption.Rlt_ort)
|
||||
if OutputFomatOption.ort in outputFormat\
|
||||
or OutputFomatOption.OS_ort in outputFormat:
|
||||
format_list.append(OutputFomatOption.OS_ort)
|
||||
if OutputFomatOption.orb in outputFormat\
|
||||
or OutputFomatOption.Rqz_orb in outputFormat\
|
||||
or OutputFomatOption.Rqz in outputFormat:
|
||||
|
||||
+62
-2
@@ -13,6 +13,8 @@ from dataclasses import dataclass
|
||||
from .event_data_types import EventDatasetProtocol
|
||||
from .instrument import Detector, LZGrid
|
||||
from .normalization import LZNormalisation
|
||||
from .options import IncidentAngle
|
||||
|
||||
|
||||
class ProjectionInterface(ABC):
|
||||
@abstractmethod
|
||||
@@ -92,6 +94,39 @@ class ProjectedReflectivity:
|
||||
plt.xlabel('Q / Å$^{-1}$')
|
||||
plt.ylabel('R')
|
||||
|
||||
@dataclass
|
||||
class ProjectedOffSpecular:
|
||||
mki_z: np.ndarray
|
||||
kf_z: np.ndarray
|
||||
I: np.ndarray
|
||||
dI: np.ndarray
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""
|
||||
Return combined data compatible with storing as columns in orso file.
|
||||
Q, R, dR, dQ
|
||||
"""
|
||||
ki_sort = np.argsort(self.mki_z[:,0])
|
||||
kf_sort = np.argsort(self.kf_z[0])
|
||||
|
||||
return np.array([self.mki_z[ki_sort, :][:, kf_sort].flatten(),
|
||||
self.kf_z[ki_sort, :][:, kf_sort].flatten(),
|
||||
self.I[ki_sort, :][:, kf_sort].flatten(),
|
||||
self.dI[ki_sort, :][:, kf_sort].flatten()]).T
|
||||
|
||||
@property
|
||||
def n_ki(self):
|
||||
return self.I.shape[0]
|
||||
|
||||
def plot(self, **kwargs):
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.colors import LogNorm
|
||||
plt.pcolormesh(self.kf_z-self.mki_z, self.mki_z+self.kf_z, self.I, norm=LogNorm(), **kwargs)
|
||||
plt.xlabel('($k_{f,z}+k_{i,z}$) / Å$^{-1}$')
|
||||
plt.ylabel('($k_{f,z}-k_{i,z}$) / Å$^{-1}$')
|
||||
plt.colorbar()
|
||||
|
||||
class LZProjection(ProjectionInterface):
|
||||
grid: LZGrid
|
||||
lamda: np.ndarray
|
||||
@@ -110,6 +145,7 @@ class LZProjection(ProjectionInterface):
|
||||
('qx', np.float64),
|
||||
('norm', np.float64),
|
||||
])
|
||||
alphaI = None
|
||||
|
||||
def __init__(self, tthh: float, grid: LZGrid):
|
||||
self.grid = grid
|
||||
@@ -129,13 +165,16 @@ class LZProjection(ProjectionInterface):
|
||||
self.monitor = 0.
|
||||
|
||||
@classmethod
|
||||
def from_dataset(cls, dataset: EventDatasetProtocol, grid: LZGrid, has_offspecular=False):
|
||||
def from_dataset(cls, dataset: EventDatasetProtocol, grid: LZGrid, alpha_i:IncidentAngle=IncidentAngle.alphaF):
|
||||
tthh = dataset.geometry.nu - dataset.geometry.mu
|
||||
output = cls(tthh, grid)
|
||||
output.correct_gravity(dataset.geometry.detectorDistance)
|
||||
if has_offspecular:
|
||||
if alpha_i==IncidentAngle.mu:
|
||||
alphaI_lz = grid.lz()*(dataset.geometry.mu+dataset.geometry.kap+dataset.geometry.kad)
|
||||
output.calculate_q(alphaI_lz)
|
||||
elif alpha_i==IncidentAngle.nu:
|
||||
alphaI_lz = grid.lz()*((dataset.geometry.nu+dataset.geometry.kap+dataset.geometry.kad)/2.)
|
||||
output.calculate_q(alphaI_lz)
|
||||
else:
|
||||
output.calculate_q()
|
||||
return output
|
||||
@@ -144,6 +183,7 @@ class LZProjection(ProjectionInterface):
|
||||
self.alphaF += np.rad2deg( np.arctan( 3.07e-10 * detector_distance * self.lamda**2 ) )
|
||||
|
||||
def calculate_q(self, alphaI=None):
|
||||
self.alphaI = alphaI
|
||||
if alphaI is None:
|
||||
self.data.qz = 4.0*np.pi*np.sin(np.deg2rad(self.alphaF))/self.lamda
|
||||
self.data.qx = 0.*self.data.qz
|
||||
@@ -280,6 +320,26 @@ class LZProjection(ProjectionInterface):
|
||||
dq_q = np.where(fltr, dq_q/W_q, np.nan)
|
||||
return ProjectedReflectivity(R_q, dR_q, (q_q[1:]+q_q[:-1])/2., dq_q)
|
||||
|
||||
def extract_offspec(self, dalphaF=0.01):
|
||||
if self.alphaI is None:
|
||||
raise ValueError("Off-specular projection requires alphaI to be set")
|
||||
if not self.is_normalized:
|
||||
raise ValueError("Dataset needs to be normalized, first")
|
||||
l = self.grid.lamda()
|
||||
L = (l[1:]+l[:-1])/2.
|
||||
alphaF_center = self.alphaF[0,:].mean()
|
||||
norm_fltr = abs(self.alphaF[0,:]-alphaF_center) < dalphaF
|
||||
lnorm = self.data.norm[:,norm_fltr].sum(axis=1)
|
||||
lnorm = np.where(lnorm>0, lnorm, np.nan)
|
||||
I_OS = self.data.I/lnorm[:,np.newaxis]*self.norm_monitor/self.monitor
|
||||
dI_OS = np.sqrt(self.data.I)/lnorm[:,np.newaxis]*self.norm_monitor/self.monitor
|
||||
|
||||
k = 2.*np.pi/L
|
||||
|
||||
mkiz = k[:,np.newaxis]*np.sin(np.deg2rad(self.alphaI))
|
||||
kfz = k[:,np.newaxis]*np.sin(np.deg2rad(self.alphaF))
|
||||
return ProjectedOffSpecular(mkiz, kfz, I_OS, dI_OS)
|
||||
|
||||
def plot(self, **kwargs):
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.colors import LogNorm
|
||||
|
||||
@@ -84,6 +84,9 @@ class ReflectivityReduction:
|
||||
|
||||
# transfer option to data reader
|
||||
AmorEventData.correctTimeFallback = self.config.reduction.correctTimeFallback
|
||||
# default export format is specular
|
||||
if self.config.output.is_default('outputFormats'):
|
||||
self.config.output.outputFormats = ["Rqz.ort"]
|
||||
|
||||
def reduce(self):
|
||||
if not os.path.exists(f'{self.config.output.outputPath}'):
|
||||
@@ -112,6 +115,7 @@ class ReflectivityReduction:
|
||||
# load measurement data and do the reduction
|
||||
self.datasetsRqz = []
|
||||
self.datasetsRlt = []
|
||||
self.datasetsOS = []
|
||||
for i, short_notation in enumerate(self.config.reduction.fileIdentifier):
|
||||
self.read_file_block(i, short_notation)
|
||||
|
||||
@@ -133,6 +137,9 @@ class ReflectivityReduction:
|
||||
if 'Rqz.ort' in self.config.output.outputFormats:
|
||||
self.save_Rqz()
|
||||
|
||||
if 'OS.ort' in self.config.output.outputFormats:
|
||||
self.save_OS()
|
||||
|
||||
if 'Rlt.ort' in self.config.output.outputFormats:
|
||||
self.save_Rtl()
|
||||
|
||||
@@ -257,6 +264,29 @@ class ReflectivityReduction:
|
||||
# plot all reflectivity results in same graph
|
||||
plt.figure(num=99)
|
||||
result.plot(label=f'{self.config.reduction.fileIdentifier[i]}')
|
||||
if 'OS.ort' in self.config.output.outputFormats:
|
||||
result = proj.extract_offspec()
|
||||
columns = [
|
||||
fileio.Column('mki_z', '1/angstrom', 'negative z-component of incident wave vector'),
|
||||
fileio.Column('kf_z', '1/angstrom', 'z-component of outgoing wave vector'),
|
||||
fileio.Column('I', '', 'Scattering intensity'),
|
||||
fileio.ErrorColumn(error_of='I', error_type='uncertainty', value_is='sigma'),
|
||||
]
|
||||
|
||||
headerOS = self.header.orso_header(columns=columns)
|
||||
headerOS.data_set = f'dataset_{i}'
|
||||
headerOS.data_source.measurement.instrument_settings.incident_angle = fileio.Value(
|
||||
proj.alphaI[0,0], 'degree')
|
||||
headerOS.data_source.measurement.scheme = {'type': 'off-specular 2D',
|
||||
'n_ki': result.n_ki}
|
||||
orso_data = fileio.OrsoDataset(headerOS, result.data)
|
||||
self.datasetsOS.append(orso_data)
|
||||
|
||||
if self.config.output.plot:
|
||||
import matplotlib.pyplot as plt
|
||||
plt.figure(num=i+100)
|
||||
result.plot(label=f'{self.config.reduction.fileIdentifier[i]}')
|
||||
|
||||
if 'Rlt.ort' in self.config.output.outputFormats:
|
||||
columns = [
|
||||
fileio.Column('Qz', '1/angstrom', 'normal momentum transfer'),
|
||||
@@ -392,6 +422,39 @@ class ReflectivityReduction:
|
||||
theSecondLine = f' {self.header.experiment.title} | {self.header.experiment.start_date} | sample {self.header.sample.name} | R(q_z)'
|
||||
fileio.save_orso(self.datasetsRqz, fname, data_separator='\n', comment=theSecondLine)
|
||||
|
||||
def save_OS(self):
|
||||
fname = os.path.join(self.config.output.outputPath, f'{self.config.output.outputName}.OS.ort')
|
||||
logging.warning(f' {fname}')
|
||||
theSecondLine = f' {self.header.experiment.title} | {self.header.experiment.start_date} | sample {self.header.sample.name} | OS'
|
||||
#fileio.save_orso(self.datasetsOS, fname, data_separator='\n', comment=theSecondLine)
|
||||
# while there is not official off-specular 2D scheme support in orsopy, export manually
|
||||
from orsopy.fileio.orso import ORSO_DESIGNATE
|
||||
|
||||
with open(fname, "w") as f:
|
||||
header = f"{ORSO_DESIGNATE}\n"
|
||||
if theSecondLine is not None:
|
||||
header += f"# {theSecondLine}\n"
|
||||
|
||||
ds1:fileio.OrsoDataset = self.datasetsOS[0]
|
||||
header += ds1.header()
|
||||
n_kis = ds1.info.data_source.measurement.scheme['n_ki']
|
||||
n_kfs = ds1.data.shape[0]//n_kis
|
||||
np.savetxt(f, ds1.data[:n_kfs], header=header, fmt="%-22.16e")
|
||||
for j in range(1, n_kis):
|
||||
f.write(f"\n### j_ki = {j}\n")
|
||||
np.savetxt(f, ds1.data[(j)*n_kfs:(j+1)*n_kfs],fmt="%-22.16e")
|
||||
|
||||
for dsi in self.datasetsOS[1:]:
|
||||
# write an optional spacer string between dataset e.g. \n
|
||||
f.write("\n\n")
|
||||
hi = ds1.diff_header(dsi)
|
||||
n_kis = dsi.info.data_source.measurement.scheme['n_ki']
|
||||
n_kfs = dsi.data.shape[0]//n_kis
|
||||
np.savetxt(f, dsi.data[:n_kfs], header=hi, fmt="%-22.16e")
|
||||
for j in range(1, n_kis):
|
||||
f.write(f"\n### j_ki = {j}\n")
|
||||
np.savetxt(f, dsi.data[(j)*n_kfs:(j+1)*n_kfs], fmt="%-22.16e")
|
||||
|
||||
def save_Rtl(self):
|
||||
fname = os.path.join(self.config.output.outputPath, f'{self.config.output.outputName}.Rlt.ort')
|
||||
logging.warning(f' {fname}')
|
||||
@@ -446,8 +509,7 @@ class ReflectivityReduction:
|
||||
def project_on_lz(self, dataset=None):
|
||||
if dataset is None:
|
||||
dataset=self.dataset
|
||||
proj = LZProjection.from_dataset(dataset, self.grid,
|
||||
has_offspecular=(self.config.experiment.incidentAngle!=IncidentAngle.alphaF))
|
||||
proj = LZProjection.from_dataset(dataset, self.grid, alpha_i=self.config.experiment.incidentAngle)
|
||||
|
||||
t0 = dataset.geometry.nu-dataset.geometry.mu
|
||||
if not self.config.reduction.is_default('thetaRangeR'):
|
||||
|
||||
Reference in new issue
Block a user