diff --git a/libeos/__init__.py b/libeos/__init__.py index 1099066..9d8cfca 100644 --- a/libeos/__init__.py +++ b/libeos/__init__.py @@ -2,5 +2,5 @@ Package to handle data redction at AMOR instrument to be used by eos.py script. """ -__version__ = '2.0' -__date__ = '2024-03-04' +__version__ = '2.1' +__date__ = '2024-08-25' diff --git a/libeos/command_line.py b/libeos/command_line.py index 22152f7..4d51ab8 100644 --- a/libeos/command_line.py +++ b/libeos/command_line.py @@ -1,5 +1,4 @@ import argparse -from datetime import date from .logconfig import update_loglevel from .options import ReaderConfig, EOSConfig, ExperimentConfig, OutputConfig, ReductionConfig, Defaults diff --git a/libeos/file_reader.py b/libeos/file_reader.py index 11e1424..8185bf7 100644 --- a/libeos/file_reader.py +++ b/libeos/file_reader.py @@ -9,6 +9,7 @@ import h5py import numpy as np import scipy as sp from orsopy import fileio +from orsopy.fileio.model_language import SampleModel from . import const from .header import Header @@ -44,6 +45,8 @@ class AmorData: tofCut: float start_date: str + seriesStartTime = None + #------------------------------------------------------------------------------------------------- def __init__(self, header: Header, reader_config: ReaderConfig, config: ExperimentConfig, short_notation:str, norm=False): @@ -178,15 +181,13 @@ class AmorData: self.read_event_stream() totalNumber = np.shape(self.tof_e)[0] - self.sort_pulses() - - self.associate_pulse_with_current() + self.sort_events_by_pulse() self.define_monitor() - self.extract_walltime(norm) + # sort the events into the related pulses - self.monitor_threshold() + self.extract_walltime(norm) self.filter_strange_times() @@ -207,12 +208,10 @@ class AmorData: pulseTime = np.sort(self.dataPacketTime_p) pulseTime = pulseTime[np.abs(pulseTime[:]-np.roll(pulseTime, 1)[:])>5] - try: - self.seriesStartTime - except: - self.seriesStartTime = pulseTime[0] + if self.seriesStartTime is None: + self.seriesStartTime = float(pulseTime[0]) pulseTime -= self.seriesStartTime - stopTime = pulseTime[-1] + self.stopTime = float(pulseTime[-1]) # fill in missing pulse times # TODO: check for real end time @@ -223,6 +222,8 @@ class AmorData: self.pulseTimeS = np.append(self.pulseTimeS, nxt) nxt += chopperPeriod self.pulseTimeS = np.append(self.pulseTimeS, tt) + # remove 'partially filled' pulses + self.pulseTimeS = self.pulseTimeS[1:-1] def associate_pulse_with_current(self): if self.monitorType == 'protonCharge': @@ -350,11 +351,14 @@ class AmorData: self.pixelID_e = np.array(self.hdf['/entry1/Amor/detector/data/event_id'][:], dtype=int) self.dataPacket_p = np.array(self.hdf['/entry1/Amor/detector/data/event_index'][:], dtype=np.uint64) #self.dataPacketTime_p = np.array(self.hdf['/entry1/Amor/detector/data/event_time_zero'][:], dtype=np.uint64)/1e9 - self.dataPacketTime_p = np.array(self.hdf['/entry1/Amor/detector/data/event_time_zero'][:], dtype=int) + self.dataPacketTime_p = np.array(self.hdf['/entry1/Amor/detector/data/event_time_zero'][:], dtype=float) try: self.currentTime = np.array(self.hdf['entry1/Amor/detector/proton_current/time'][:], dtype=int) self.current = np.array(self.hdf['entry1/Amor/detector/proton_current/value'][:,0], dtype=float) - self.monitorType = 'protonCharge' + if len(self.current)>0: + self.monitorType = 'protonCharge' + else: + self.monitorType = 'countingTime' except(KeyError, IndexError): self.monitorType = 'countingTime' diff --git a/libeos/options.py b/libeos/options.py index 3ab191b..5819094 100644 --- a/libeos/options.py +++ b/libeos/options.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, field from typing import Optional, Tuple from datetime import datetime +import logging class Defaults: # fileIdentifier @@ -99,64 +100,64 @@ class EOSConfig: base = 'python eos.py' inpt = '' - if self.reader_config.year: - inpt += f' -Y {self.reader_config.year}' + if self.reader.year: + inpt += f' -Y {self.reader.year}' else: inpt += f' -Y {datetime.now().year}' - if self.reader_config.dataPath != '.': - inpt += f' --dataPath {self.reader_config.dataPath}' - #if self.reader_config.raw != '.': - # inpt = f' --rawd {self.reader_config.raw}' - if self.reduction_config.subtract: - inpt += f' -subtract {self.reduction_config.subtract}' - if self.reduction_config.normalisationFileIdentifier: - inpt += f' -n {" ".join(self.reduction_config.normalisationFileIdentifier)}' - if self.reduction_config.fileIdentifier: - inpt += f' -f {" ".join(self.reduction_config.fileIdentifier)}' + if self.reader.dataPath != '.': + inpt += f' --dataPath {self.reader.dataPath}' + #if self.reader.raw != '.': + # inpt = f' --rawd {self.reader.raw}' + if self.reduction.subtract: + inpt += f' -subtract {self.reduction.subtract}' + if self.reduction.normalisationFileIdentifier: + inpt += f' -n {" ".join(self.reduction.normalisationFileIdentifier)}' + if self.reduction.fileIdentifier: + inpt += f' -f {" ".join(self.reduction.fileIdentifier)}' otpt = '' - if self.reduction_config.qResolution: - otpt += f' -r {self.reduction_config.qResolution}' - if self.output_config.outputName: - otpt += f' -o {self.output_config.outputName}' - if self.output_config.outputFormats != ['Rqz.ort']: - otpt += f' -of {" ".join(self.output_config.outputFormats)}' + if self.reduction.qResolution: + otpt += f' -r {self.reduction.qResolution}' + if self.output.outputName: + otpt += f' -o {self.output.outputName}' + if self.output.outputFormats != ['Rqz.ort']: + otpt += f' -of {" ".join(self.output.outputFormats)}' mask = '' - if self.experiment_config.yRange != Defaults.yRange: - mask += f' -y {" ".join(str(ii) for ii in self.experiment_config.yRange)}' - if self.experiment_config.lambdaRange!= Defaults.lambdaRange: - mask += f' -l {" ".join(str(ff) for ff in self.experiment_config.lambdaRange)}' - if self.reduction_config.thetaRange != Defaults.thetaRange: - mask += f' -T {" ".join(str(ff) for ff in self.reduction_config.thetaRange)}' - elif self.reduction_config.thetaRangeR != Defaults.thetaRangeR: - mask += f' -t {" ".join(str(ff) for ff in self.reduction_config.thetaRangeR)}' - if self.experiment_config.qzRange!= Defaults.qzRange: - mask += f' -q {" ".join(str(ff) for ff in self.experiment_config.qzRange)}' + if self.experiment.yRange != Defaults.yRange: + mask += f' -y {" ".join(str(ii) for ii in self.experiment.yRange)}' + if self.experiment.lambdaRange!= Defaults.lambdaRange: + mask += f' -l {" ".join(str(ff) for ff in self.experiment.lambdaRange)}' + if self.reduction.thetaRange != Defaults.thetaRange: + mask += f' -T {" ".join(str(ff) for ff in self.reduction.thetaRange)}' + elif self.reduction.thetaRangeR != Defaults.thetaRangeR: + mask += f' -t {" ".join(str(ff) for ff in self.reduction.thetaRangeR)}' + if self.experiment.qzRange!= Defaults.qzRange: + mask += f' -q {" ".join(str(ff) for ff in self.experiment.qzRange)}' para = '' - if self.experiment_config.chopperPhase != Defaults.chopperPhase: - para += f' --chopperPhase {self.experiment_config.chopperPhase}' - if self.experiment_config.chopperPhaseOffset != Defaults.chopperPhaseOffset: - para += f' --chopperPhaseOffset {self.experiment_config.chopperPhaseOffset}' - if self.experiment_config.mu: - para += f' --mu {self.experiment_config.mu}' - elif self.experiment_config.muOffset: - para += f' --muOffset {self.experiment_config.muOffset}' - if self.experiment_config.nu: - para += f' --nu {self.experiment_config.nu}' + if self.experiment.chopperPhase != Defaults.chopperPhase: + para += f' --chopperPhase {self.experiment.chopperPhase}' + if self.experiment.chopperPhaseOffset != Defaults.chopperPhaseOffset: + para += f' --chopperPhaseOffset {self.experiment.chopperPhaseOffset}' + if self.experiment.mu: + para += f' --mu {self.experiment.mu}' + elif self.experiment.muOffset: + para += f' --muOffset {self.experiment.muOffset}' + if self.experiment.nu: + para += f' --nu {self.experiment.nu}' modl = '' - if self.experiment_config.sampleModel: - modl += f" --sampleModel '{self.experiment_config.sampleModel}'" + if self.experiment.sampleModel: + modl += f" --sampleModel '{self.experiment.sampleModel}'" acts = '' - if self.reduction_config.autoscale: - acts += f' --autoscale {" ".join(str(ff) for ff in self.reduction_config.autoscale)}' - if self.reduction_config.scale != Defaults.scale: - acts += f' --scale {self.reduction_config.scale}' - if self.reduction_config.timeSlize: - acts += f' --timeSlize {" ".join(str(ff) for ff in self.reduction_config.timeSlize)}' + if self.reduction.autoscale: + acts += f' --autoscale {" ".join(str(ff) for ff in self.reduction.autoscale)}' + if self.reduction.scale != Defaults.scale: + acts += f' --scale {self.reduction.scale}' + if self.reduction.timeSlize: + acts += f' --timeSlize {" ".join(str(ff) for ff in self.reduction.timeSlize)}' mlst = base + inpt + otpt if mask: @@ -179,7 +180,7 @@ class EOSConfig: if modl: mlst += ' ' + modl - print(mlst) + logging.debug(f'Argument list build in EOSConfig.call_string: {mlst}') return mlst diff --git a/libeos/reduction.py b/libeos/reduction.py index a7ff8d5..e1b27a7 100644 --- a/libeos/reduction.py +++ b/libeos/reduction.py @@ -18,9 +18,9 @@ class AmorReduction: self.reduction_config = config.reduction self.output_config = config.output self.grid = Grid(config.reduction.qResolution, config.experiment.qzRange) - self.header = Header() - self.header.reduction.call = EOSConfig.call_string(self) + self.header = Header() + self.header.reduction.call = config.call_string() def reduce(self): if not os.path.exists(f'{self.reader_config.dataPath}'): @@ -80,8 +80,8 @@ class AmorReduction: self.file_reader, self.norm_lz, self.normAngle, lamda_e, detZ_e) monitor = self.file_reader.monitor if monitor>1 : - ref_lz *= 1/monitor - err_lz *= 1/monitor + ref_lz /= monitor + err_lz /= monitor try: ref_lz *= self.reduction_config.scale[i] err_lz *= self.reduction_config.scale[i] @@ -93,7 +93,7 @@ class AmorReduction: headerRqz.data_set = f'Nr {i} : mu = {self.file_reader.mu:6.3f} deg' if qz_lz[0,int(np.shape(qz_lz)[1]/2)] < 0: - # assuming a 'measurement from below' + # assuming a 'measurement from below' when center of detector at negative qz qz_lz *= -1 # projection on qz-grid @@ -221,7 +221,7 @@ class AmorReduction: self.datasetsRqz.append(orso_data) # reset normal logging behavior logging.StreamHandler.terminator = "\n" - logging.warning(f' time slize {ti:4d}, done') + logging.warning(f' time slizing, done') def save_Rqz(self): fname = os.path.join(self.reader_config.dataPath, f'{self.output_config.outputName}.Rqz.ort') diff --git a/tests/test_full_analysis.py b/tests/test_full_analysis.py index 44a6878..9eff6d0 100644 --- a/tests/test_full_analysis.py +++ b/tests/test_full_analysis.py @@ -6,6 +6,8 @@ from libeos import options, reduction, logconfig logconfig.setup_logging() logconfig.update_loglevel(True, False) +# TODO: add test for new features like proton charge normalization + class FullAmorTest(TestCase): @classmethod def setUpClass(cls): @@ -19,7 +21,9 @@ class FullAmorTest(TestCase): self.pr.enable() self.reader_config = options.ReaderConfig( year=2023, - dataPath=os.path.join('..', "test_data")) + dataPath=os.path.join('..', "test_data"), + raw=(os.path.join('..', "test_data"),) + ) def tearDown(self): self.pr.disable() @@ -37,14 +41,16 @@ class FullAmorTest(TestCase): yRange=(11., 41.), lambdaRange=(2., 15.), qzRange=(0.005, 0.30), - offSpecular=False, + incidentAngle=options.Defaults.incidentAngle, mu=0, nu=0, muOffset=0.0, sampleModel='air | 10 H2O | D2O' ) reduction_config = options.ReductionConfig( + normalisationMethod=options.Defaults.normalisationMethod, qResolution=0.01, + qzRange=options.Defaults.qzRange, thetaRange=(-12., 12.), thetaRangeR=(-12., 12.), fileIdentifier=["610"], @@ -72,19 +78,21 @@ class FullAmorTest(TestCase): yRange=(11., 41.), lambdaRange=(2., 15.), qzRange=(0.005, 0.30), - offSpecular=False, + incidentAngle=options.Defaults.incidentAngle, mu=0, nu=0, muOffset=0.0 ) reduction_config = options.ReductionConfig( qResolution=0.01, + qzRange=options.Defaults.qzRange, + normalisationMethod=options.Defaults.normalisationMethod, thetaRange=(-12., 12.), thetaRangeR=(-12., 12.), fileIdentifier=["610", "611", "608,612-613", "609"], scale=[1], normalisationFileIdentifier=["614"], - autoscale=(0.005, 0.008) + autoscale=(True, True) ) output_config = options.OutputConfig( outputFormats=["Rqz.ort"],