"""
Method components for particle sizer IDSs.
:py:class:`Method` holds the measurement conditions common to every particle
sizer technique (temperature, dispersant, particle optics, plate) alongside
two technique-specific settings blocks:
* :py:class:`DLSMethodSettings` -- Dynamic Light Scattering (also the parent
technique for ELS and SLS, which share the same optical configuration).
* :py:class:`LaserDiffractionMethodSettings` -- Laser Diffraction.
A consuming IDS includes only the settings blocks relevant to the techniques
it supports.
"""
from enum import Enum
from typing import List
from ts_ids_core.annotations import (
NullableNumber,
NullableString,
UUIDForeignKey,
UUIDPrimaryKey,
)
from ts_ids_core.base.ids_element import IdsElement
from ts_ids_core.base.ids_field import IdsField
from ts_ids_core.schema import RawValueUnit
from ts_ids_components.particle_sizer.shared import (
Particle,
Plate,
Solvent,
Temperature,
)
[docs]
class ParticleSizerTechnique(str, Enum):
"""
Standardized measurement technique for a particle sizer method. Each
vendor reports the technique using a different raw value, which the task
script should translate to one of these values rather than passing
through verbatim.
* :py:attr:`DYNAMIC_LIGHT_SCATTERING` -- measures Brownian-motion-driven
intensity fluctuations to size sub-micron particles.
* :py:attr:`ELECTROPHORETIC_LIGHT_SCATTERING` -- measures particle
electrophoretic mobility to determine zeta potential.
* :py:attr:`STATIC_LIGHT_SCATTERING` -- measures time-averaged scattered
intensity to determine molar mass (DynaPro, simultaneous with DLS).
* :py:attr:`LASER_DIFFRACTION` -- measures the angular scattering pattern
of a laser to size particles from sub-micron to millimetre.
"""
DYNAMIC_LIGHT_SCATTERING = "dynamic light scattering"
ELECTROPHORETIC_LIGHT_SCATTERING = "electrophoretic light scattering"
STATIC_LIGHT_SCATTERING = "static light scattering"
LASER_DIFFRACTION = "laser diffraction"
# ---------------------------------------------------------------------------
# DLS method settings
# ---------------------------------------------------------------------------
[docs]
class InstrumentState(IdsElement):
"""Per-measurement DLS instrument state, one entry per acquisition."""
measurement_index: NullableNumber = IdsField(
description="Zero- or one-based index of the measurement within the run."
)
measurement_id: NullableString = IdsField(
description="Vendor identifier for the measurement."
)
laser_power: RawValueUnit = IdsField(
description="Laser power used for the measurement."
)
attenuation_level: RawValueUnit = IdsField(
description="Attenuation level applied to the incident laser, set automatically to keep the count rate in range."
)
acquisition_period: RawValueUnit = IdsField(
description="Duration of a single acquisition."
)
[docs]
class AcquisitionQualityGate(IdsElement):
"""
A data quality gate/filter applied during DLS acquisition, used to accept
or reject individual acquisitions before averaging.
"""
file_key: NullableNumber = IdsField(
description="Vendor key identifying the gate in the raw file."
)
label: NullableString = IdsField(
description="Human-readable name of the quality gate."
)
threshold: NullableString = IdsField(
description="Threshold/condition applied by the gate, as reported by the instrument."
)
enabled: NullableNumber = IdsField(
description="Whether the gate is enabled (1) or disabled (0)."
)
[docs]
class SizeLimits(IdsElement):
"""Configured lower and upper size limits for DLS analysis."""
low: RawValueUnit = IdsField(description="Lower size limit for the analysis.")
high: RawValueUnit = IdsField(description="Upper size limit for the analysis.")
[docs]
class DLSMethodSettings(IdsElement):
"""
Dynamic Light Scattering method parameters. Also carries the optical
configuration shared by the ELS and SLS techniques, which are measured on
the same optical bench.
"""
cell_name: NullableString = IdsField(
description="Name/type of the measurement cell or cuvette used."
)
angle: RawValueUnit = IdsField(
description="Scattering detection angle, e.g. 173 degrees for backscatter."
)
wavelength: RawValueUnit = IdsField(description="Wavelength of the incident laser.")
attenuator: RawValueUnit = IdsField(
description="Attenuator setting applied to the incident laser."
)
size_limits: SizeLimits = IdsField(
description="Configured lower/upper size limits for the analysis."
)
instrument_states: List[InstrumentState] = IdsField(
description="Per-acquisition instrument state (laser power, attenuation, etc.)."
)
acquisition_quality_gates: List[AcquisitionQualityGate] = IdsField(
description="Data quality gates applied during acquisition filtering."
)
# ---------------------------------------------------------------------------
# Laser Diffraction method settings
# ---------------------------------------------------------------------------
[docs]
class ObscurationLimits(IdsElement):
"""Configured obscuration (light attenuation) high/low limits for a laser diffraction method."""
high_limit: RawValueUnit = IdsField(description="Upper obscuration limit.")
low_limit: RawValueUnit = IdsField(description="Lower obscuration limit.")
[docs]
class FeedRate(IdsElement):
"""Sample feed rate demand (configured) and achieved (measured) values."""
demand: RawValueUnit = IdsField(description="Configured/demanded feed rate.")
achieved: RawValueUnit = IdsField(description="Actual/achieved feed rate.")
[docs]
class SizeRange(IdsElement):
"""Configured lower and upper size range for laser diffraction analysis."""
low: RawValueUnit = IdsField(description="Lower bound of the analysis size range.")
high: RawValueUnit = IdsField(description="Upper bound of the analysis size range.")
[docs]
class MeasurementTime(IdsElement):
"""Background and sample measurement durations for a laser diffraction method."""
background: RawValueUnit = IdsField(
description="Duration of the background (blank) measurement."
)
sample: RawValueUnit = IdsField(description="Duration of the sample measurement.")
[docs]
class DispersionUnit(IdsElement):
"""
Configuration of the dispersion unit (wet or dry) that suspends and
delivers the sample to the laser diffraction measurement zone.
"""
name: NullableString = IdsField(description="Name/type of the dispersion unit.")
stirrer_speed: RawValueUnit = IdsField(
description="Stirrer speed of the wet dispersion unit."
)
ultrasound_power: RawValueUnit = IdsField(
description="Ultrasound power applied to disperse/de-agglomerate the sample."
)
ultrasound_duration: RawValueUnit = IdsField(
description="Duration for which ultrasound is applied."
)
ultrasound_mode: NullableString = IdsField(
description="Ultrasound application mode, e.g. 'continuous' or 'pre-measurement'."
)
venturi_type: NullableString = IdsField(
description="Venturi type for a dry dispersion unit."
)
[docs]
class LaserDiffractionMethodSettings(IdsElement):
"""Laser Diffraction method parameters (e.g. Mastersizer)."""
sop_file: NullableString = IdsField(
description="Name/path of the Standard Operating Procedure (SOP) file used."
)
scattering_model: NullableString = IdsField(
description="Scattering model used to invert the diffraction pattern, e.g. 'Mie' or 'Fraunhofer'."
)
analysis_model: NullableString = IdsField(
description="Analysis model applied, e.g. 'general purpose' or 'single narrow mode'."
)
sensitivity: NullableString = IdsField(
description="Analysis sensitivity setting, e.g. 'normal' or 'enhanced'."
)
result_emulation: NullableString = IdsField(
description="Result emulation setting used to reproduce results from an older instrument model."
)
fine_powder_mode: NullableString = IdsField(
description="Fine powder mode setting for dry dispersion of cohesive powders."
)
size_range: SizeRange = IdsField(
description="Configured lower/upper size range for the analysis."
)
obscuration: ObscurationLimits = IdsField(
description="Configured obscuration high/low limits."
)
feed_rate: FeedRate = IdsField(
description="Sample feed rate demand and achieved values."
)
gauge_air_pressure_demand: RawValueUnit = IdsField(
description="Configured gauge air pressure for a dry dispersion unit."
)
hopper_gap: RawValueUnit = IdsField(
description="Hopper gap setting controlling dry sample feed."
)
dispersion_unit: DispersionUnit = IdsField(
description="Configuration of the wet/dry dispersion unit."
)
measurement_time: MeasurementTime = IdsField(
description="Background and sample measurement durations."
)
# ---------------------------------------------------------------------------
# Top-level Method
# ---------------------------------------------------------------------------
[docs]
class Method(IdsElement):
"""
Measurement method and conditions common to every particle sizer
technique.
The technique-specific settings blocks are *not* included here. Following
the same pattern as the :py:class:`System <ts_ids_core.schema.system.System>`
and :py:class:`plate reader MeasurementSetting
<ts_ids_components.plate_reader.methods.MeasurementSetting>` components, a
vendor-specific IDS composes only the blocks its instrument supports by
inheriting from this base plus the relevant nested mixin classes -- e.g.
a DLS-only instrument uses ``class MyMethod(Method, Method.DynamicLightScattering)``,
while an instrument supporting several techniques inherits from all the
corresponding nested classes.
"""
[docs]
class DynamicLightScattering(IdsElement):
"""
Adds the Dynamic Light Scattering method settings (which also carry
the ELS/SLS optical configuration). Inherit from this to support DLS,
ELS, or SLS.
"""
dynamic_light_scattering: DLSMethodSettings = IdsField(
description="DLS-specific method parameters (also carries the ELS/SLS optical configuration)."
)
[docs]
class LaserDiffraction(IdsElement):
"""Adds the Laser Diffraction method settings. Inherit from this to support LD."""
laser_diffraction: LaserDiffractionMethodSettings = IdsField(
description="Laser diffraction-specific method parameters."
)
pk: UUIDPrimaryKey = IdsField(
description="Primary key for this method, referenced by results via ``fk_method``."
)
technique: NullableString = IdsField(
description="Standardized measurement technique used.",
json_schema_extra={
"example_values": [technique.value for technique in ParticleSizerTechnique]
},
)
temperature: Temperature = IdsField(
description="Target and actual temperature of the measurement."
)
plate: Plate = IdsField(
description="Plate information, for plate-reader DLS instruments."
)
solvents: List[Solvent] = IdsField(
description="Dispersant(s) the particles are suspended in."
)
particle: Particle = IdsField(
description="Optical properties of the analyte particle material."
)
fk_run: UUIDForeignKey = IdsField(
primary_key="/properties/runs/items/properties/pk",
description="Foreign key to the run this method was used in.",
)