diff --git a/src/essreflectometry/amor/load.py b/src/essreflectometry/amor/load.py index 9dda1458..d2183244 100644 --- a/src/essreflectometry/amor/load.py +++ b/src/essreflectometry/amor/load.py @@ -153,32 +153,7 @@ def extract_events( data.coords['position'].fields.y += data.coords['position'].fields.z * sc.tan( 2.0 * data.coords['sample_rotation'] - (0.955 * sc.units.deg) ) - return RawData[Run](data) - - -# TODO -# def populate_orso(orso: Any, data: sc.DataGroup, filename: str) -> Any: -# """ -# Populate the Orso object, by calling the :code:`base_orso` and adding data from the -# file. -# -# Parameters -# ---------- -# orso: -# The orso object to be populated by additional information from the loaded file. -# data: -# Data group to source information from. -# Should mimic the structure of the NeXus file. -# filename: -# Path of the file to load. -# """ -# orso.data_source.experiment.title = data['title'] -# orso.data_source.experiment.instrument = data['name'] -# orso.data_source.experiment.start_date = datetime.strftime( -# datetime.strptime(data['start_time'][:-3], '%Y-%m-%dT%H:%M:%S.%f'), -# '%Y-%m-%d', -# ) -# orso.data_source.measurement.data_files = [filename] + return RawEvents[Run](data) providers = (extract_events, load_raw_nexus) diff --git a/src/essreflectometry/amor/orso.py b/src/essreflectometry/amor/orso.py index 99bb6878..dd9e17e9 100644 --- a/src/essreflectometry/amor/orso.py +++ b/src/essreflectometry/amor/orso.py @@ -1,60 +1,79 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) -import platform -from datetime import datetime - -from orsopy import fileio - -from .. import __version__ - - -def make_orso( - owner: fileio.base.Person, - sample: fileio.data_source.Sample, - creator: fileio.base.Person, - reduction_script: str, -) -> fileio.orso.Orso: - """ - Generate the base Orso object for the Amor instrument. - Populate the Orso object for metadata storage. - - Parameters - ---------- - owner: - The owner of the data set, i.e. the main proposer of the measurement. - sample: - A description of the sample. - creator: - The creator of the reduced data, the person responsible for the - reduction process. - reduction_script: - The script or notebook used for reduction. - - Returns - ------- - : - Orso object with the default parameters for the Amor instrument. - """ - orso = fileio.orso.Orso.empty() - orso.data_source.experiment.probe = 'neutrons' - orso.data_source.experiment.facility = 'Paul Scherrer Institut' - orso.data_source.measurement.scheme = 'angle- and energy-dispersive' - orso.reduction.software = fileio.reduction.Software( - 'scipp-ess', __version__, platform.platform() +"""ORSO utilities for Amor.""" +import os +from typing import Optional + +from dateutil.parser import parse as parse_datetime +from orsopy.fileio import base as orso_base +from orsopy.fileio import data_source + +from ..orso import ( + OrsoExperiment, + OrsoInstrument, + OrsoMeasurement, + OrsoOwner, + OrsoSample, +) +from ..types import Filename, RawData, Reference, Run, Sample + + +def parse_orso_experiment(raw_data: RawData[Run]) -> OrsoExperiment[Run]: + """Parse ORSO experiment data from raw Amor NeXus data.""" + return OrsoExperiment( + data_source.Experiment( + title=raw_data['title'], + instrument=raw_data['name'], + facility=raw_data['facility'], + start_date=parse_datetime(raw_data['start_time']), + probe='neutron', + ) + ) + + +def parse_orso_owner(raw_data: RawData[Run]) -> OrsoOwner[Run]: + """Parse ORSO owner data from raw Amor NeXus data.""" + return OrsoOwner( + orso_base.Person( + name=raw_data['user']['name'], + contact=raw_data['user']['email'], + affiliation=None, + ) ) - orso.reduction.timestep = datetime.now() - orso.reduction.corrections = [] - orso.reduction.computer = platform.node() - orso.columns = [ - fileio.base.Column('Qz', '1/angstrom', 'wavevector transfer'), - fileio.base.Column('R', None, 'reflectivity'), - fileio.base.Column('sR', None, 'standard deivation of reflectivity'), - fileio.base.Column( - 'sQz', '1/angstrom', 'standard deviation of wavevector transfer resolution' - ), - ] - orso.data_source.owner = owner - orso.data_source.sample = sample - orso.reduction.creator = creator - orso.reduction.script = reduction_script - return orso + + +def parse_orso_sample(raw_data: RawData[Run]) -> OrsoSample[Run]: + """Parse ORSO sample data from raw Amor NeXus data.""" + if not raw_data.get('sample'): + return OrsoSample(data_source.Sample.empty()) + raise NotImplementedError('Amor NsXus sample parsing is not implemented') + + +def build_orso_measurement( + sample_filename: Filename[Sample], + reference_filename: Filename[Reference], + instrument: Optional[OrsoInstrument], +) -> OrsoMeasurement: + """Assemble ORSO measurement data.""" + # TODO populate timestamp + # doesn't work with a local file because we need the timestamp of the original, + # SciCat can provide that + return OrsoMeasurement( + data_source.Measurement( + instrument_settings=instrument, + data_files=[orso_base.File(file=os.path.basename(sample_filename))], + additional_files=[ + orso_base.File( + file=os.path.basename(reference_filename), comment='supermirror' + ) + ], + ) + ) + + +providers = ( + parse_orso_experiment, + build_orso_measurement, + parse_orso_owner, + parse_orso_sample, +) diff --git a/src/essreflectometry/orso.py b/src/essreflectometry/orso.py index c824ca09..5550c123 100644 --- a/src/essreflectometry/orso.py +++ b/src/essreflectometry/orso.py @@ -1,15 +1,101 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) -import warnings +"""ORSO utilities for reflectometry.""" +import platform +from datetime import datetime, timezone +from typing import NewType, Optional +import sciline +from orsopy.fileio import base as orso_base +from orsopy.fileio import data_source, reduction -def not_found_warning(): +from . import __version__ +from .types import Reference, Run, Sample + + +class OrsoExperiment( + sciline.Scope[Run, data_source.Experiment], data_source.Experiment +): + """ORSO experiment for a run.""" + + +class OrsoInstrument( + sciline.Scope[Run, data_source.InstrumentSettings], data_source.InstrumentSettings +): + """ORSO instrument settings for a run.""" + + +class OrsoOwner(sciline.Scope[Run, orso_base.Person], orso_base.Person): + """ORSO owner of a file.""" + + +class OrsoReduction(sciline.Scope[Run, reduction.Reduction], reduction.Reduction): + """ORSO measurement for a run.""" + + +class OrsoSample(sciline.Scope[Run, data_source.Sample], data_source.Sample): + """ORSO sample of a run.""" + + +OrsoCreator = NewType('OrsoCreator', orso_base.Person) +"""ORSO creator, that is, the person who processed the data.""" + +OrsoDataSource = NewType('OrsoDataSource', data_source.DataSource) +"""ORSO data source.""" + +OrsoMeasurement = NewType('OrsoMeasurement', data_source.Measurement) +"""ORSO measurement.""" + + +def build_orso_reduction(creator: Optional[OrsoCreator]) -> OrsoReduction: + """Construct ORSO reduction data. + + This assumes that ess.reflectometry is the primary piece of software + used to reduce the data. """ - A function to raise a orso specific error if necessary. + return OrsoReduction( + reduction.Reduction( + software=reduction.Software( + name='ess.reflectometry', + version=str(__version__), + platform=platform.system(), + ), + timestamp=datetime.now(tz=timezone.utc), + creator=creator, + corrections=[], + ) + ) + + +def build_orso_data_source( + owner: Optional[OrsoOwner[Sample]], + sample: Optional[OrsoSample[Sample]], + sample_experiment: Optional[OrsoExperiment[Sample]], + reference_experiment: Optional[OrsoExperiment[Reference]], + measurement: Optional[OrsoMeasurement], +) -> OrsoDataSource: + """Judiciously assemble an ORSO DataSource. + + Makes some assumptions about how sample and reference runs should be merged, + giving precedence to the sample run. """ - warnings.warn( - "For metadata to be logged in the data array, " - "it is necessary to install the orsopy package.", - UserWarning, - stacklevel=2, + # We simply assume that the owner of the reference measurement + # has no claim on this data. + if (sample_experiment.facility != reference_experiment.facility) or ( + sample_experiment.instrument != reference_experiment.instrument + ): + raise ValueError( + 'The sample and reference experiments were done at different instruments' + ) + + return OrsoDataSource( + data_source.DataSource( + owner=owner, + sample=sample, + experiment=sample_experiment, + measurement=measurement, + ) ) + + +providers = (build_orso_reduction, build_orso_data_source)