ts_ids_components.xrd.datacube module#

Two-theta scan datacube components for XRD IDSs.

The scan modes where 2-theta is the varying dimension are called 2-Theta Scans (e.g. coupled reflection, grazing incidence, reflectivity, transmission) – as opposed to modes like rocking curves, pole figures, or reciprocal space maps, whose primary scan variable is a different axis entirely (see below). TwoThetaScanDatacube models one 2-Theta Scan’s raw per-point data: every value recorded at each step, not just a 2-theta/intensity pair:

  • Bruker D8 Advance RawData0.xml: each Datum row is effective time per step, scan point index, two theta, theta, intensity (5 values). Theta moves in lockstep with 2Theta in the D8 Advance’s coupled Theta/Theta scan.

  • Malvern Panalytical Empyrean .xrdml: a coupled scan varies both 2Theta and Omega simultaneously, with Phi fixed.

  • Rigaku Profile*.txt: each row is 2Theta, Intensity, Weight (3 values); Weight is the RAS-format attenuation/correction factor applied when an attenuator is switched in near the direct beam. Rigaku’s wider MesurementConditions*.xml axis table also lists Omega, Chi, and other hardware axes available on the same platform.

Every 2-Theta Scan mode – regardless of vendor or specific application – varies 2-theta as its dimension with intensity as a measure; that dimension/measure shape, not “pharma powder XRD” as such, is the precise criterion TwoThetaScanDatacube supports. Scan modes with a different primary scan variable (rocking curves, pole figures, reciprocal space maps – primary dimension is omega, phi, or a reciprocal-space grid, not 2-theta) aren’t 2-Theta Scans, so they don’t fit this shape and have no datacube component defined here yet. If a customer needs one of those modes, it should get its own datacube class following the same fixed-length/ vendor-subclass pattern as TwoThetaScanDatacube below, not a variant of TwoThetaScanDatacube itself. So TwoThetaScanDatacube’s one Dimension is 2-theta itself (name="two theta", unit="DegreeAngle", real angle values), not a synthetic index. Every other per-point column – Theta/Omega (which covary 1:1 with 2-theta rather than varying independently of it) and ancillary fields like Weight or effective time per step – becomes one named XrdMeasure sharing that one 2-theta dimension: no DataCube subclass anywhere in this repo or the wider platform models a quantity that covaries with the primary axis as a second Dimension – that’s reserved for genuinely independent grid axes (e.g. chromatography’s wavelength x time PDA cube), which this data isn’t.

intensity is a required measure on every TwoThetaScanDatacube, and (as counts) a mandatory, non-optional element in Empyrean’s own XRDML schema regardless of scan mode.

ts_ids_core.schema.DataCube’s measures/dimensions fixed_length is a whole-class constant, not additive across inheritance: a vendor subclass can’t “add more measures” to an inherited count, it must restate its own total. So TwoThetaScanDatacube fixes dimensions at exactly 1 (identical for every vendor, never restated by subclasses) and provides fk_method/fk_run/fk_system/fk_sample/name/ description once for everyone, but each vendor’s own subclass still restates measures with its own total fixed_length (e.g. 4 for Bruker’s remaining Datum columns once 2-theta becomes the dimension) – the two theta dimension name and intensity measure presence checks run on every subclass regardless, since they’re a validator, not a structural constraint:

class VendorTwoThetaScanDatacube(TwoThetaScanDatacube):
    # one XrdMeasure per raw per-point column for this vendor, other
    # than 2-theta itself (the dimension), e.g. for Bruker: theta,
    # intensity, effective time per step, scan point index
    measures: Required[Annotated[List[XrdMeasure], fixed_length(4)]]

Every other axis is still trivially found by looking up measures by name.

The datacube’s own name and its dimension and measure names are a cross-vendor standard, defined once below as module-level constants (TWO_THETA_DATACUBE_NAME, TWO_THETA_SCAN_DIMENSION_NAME, INTENSITY_MEASURE_NAME, THETA_MEASURE_NAME, OMEGA_MEASURE_NAME, PHI_MEASURE_NAME, WEIGHT_MEASURE_NAME, DIVERGENCE_CORRECTION_MEASURE_NAME, EFFECTIVE_TIME_PER_STEP_MEASURE_NAME, SCAN_POINT_INDEX_MEASURE_NAME). Task scripts should import these rather than hand-typing the strings, so every XRD IDS emits identical names for the same quantity. Following the platform-wide convention, none of these names appears in the exported schema.json – not as a const/enum and not in a field description – so a future naming change is a task-script update rather than an IDS version bump. Using the constants is a documented convention, not a validation rule.

See ts_ids_components.xrd.datacube_raw for RawTwoThetaScanDatacube, which preserves each datacube’s untouched raw text alongside it.

TWO_THETA_DATACUBE_NAME = 'intensity vs two theta'#

Standard name of every TwoThetaScanDatacube (and its raw mirror, RawTwoThetaScanDatacube <ts_ids_components.xrd.datacube_raw.RawTwoThetaScanDatacube>): what the datacube holds, intensity as a function of 2-theta. Task scripts should pass this constant as name rather than inventing their own. Multiple datacubes in one file (one per scan) share this name and are told apart by their fk_method.

TWO_THETA_SCAN_DIMENSION_NAME = 'two theta'#

Standard name for a TwoThetaScanDatacube’s one required dimension. See the module docstring above for why 2-theta – not a synthetic index – is the dimension every vendor’s datacube shares.

INTENSITY_MEASURE_NAME = 'intensity'#

Standard name for the diffracted X-ray intensity measure, required on every TwoThetaScanDatacube.

THETA_MEASURE_NAME = 'theta'#

Standard name for the theta (sample/tube) angle measure, e.g. Bruker D8 Advance’s Theta column, which moves in lockstep with 2-theta.

OMEGA_MEASURE_NAME = 'omega'#

Standard name for the omega (incidence) angle measure, e.g. Malvern Panalytical Empyrean’s Omega axis.

PHI_MEASURE_NAME = 'phi'#

Standard name for the phi (in-plane sample rotation) angle measure, e.g. Malvern Panalytical Empyrean’s Phi axis.

WEIGHT_MEASURE_NAME = 'weight'#

Standard name for the per-point attenuation/correction factor measure, e.g. Rigaku’s Weight column or Malvern Panalytical Empyrean’s beamAttenuationFactors.

DIVERGENCE_CORRECTION_MEASURE_NAME = 'divergence correction'#

Standard name for the per-point divergence slit correction factor measure, e.g. Malvern Panalytical Empyrean’s divergenceCorrection (XRDML v2.1+).

EFFECTIVE_TIME_PER_STEP_MEASURE_NAME = 'effective time per step'#

Standard name for the per-point effective counting time measure, e.g. Bruker D8 Advance’s first Datum column or Malvern Panalytical Empyrean’s per-point countingTimes. Distinct from XrdScan.time_per_step, which is the scan’s single nominal counting time.

SCAN_POINT_INDEX_MEASURE_NAME = 'scan point index'#

Standard name for the per-point scan point index measure, e.g. Bruker D8 Advance’s second Datum column.

Model XrdMeasure[source]#

Bases: MeasureBase

A single named per-point quantity in a TwoThetaScanDatacube (e.g. "theta", "omega", "intensity", "weight", "effective time per step"), sharing the datacube’s one TWO_THETA_SCAN_DIMENSION_NAME dimension. See the module docstring above for why any axis that covaries with 2-theta – rather than varying independently of it – is modeled as a measure rather than a second dimension.

Show JSON schema
{
   "description": "A single named per-point quantity in a :py:class:`TwoThetaScanDatacube`\n(e.g. ``\"theta\"``, ``\"omega\"``, ``\"intensity\"``, ``\"weight\"``,\n``\"effective time per step\"``), sharing the datacube's one\n:py:data:`TWO_THETA_SCAN_DIMENSION_NAME` dimension. See the module\ndocstring above for why any axis that covaries with 2-theta -- rather\nthan varying independently of it -- is modeled as a measure rather than\na second dimension.",
   "type": "object",
   "properties": {
      "name": {
         "type": [
            "string",
            "null"
         ]
      },
      "unit": {
         "type": [
            "string",
            "null"
         ]
      },
      "value": {
         "items": {
            "type": [
               "number",
               "null"
            ]
         },
         "type": "array"
      }
   },
   "additionalProperties": false,
   "required": [
      "name",
      "unit",
      "value"
   ]
}

Validators:
  • validate_value_shape » value

field name: Required[Nullable[str]]#
field unit: Required[Nullable[str]]#
field value: List[float | None]#
num_dimensions: ClassVar[int] = 1#
Model TwoThetaScanDatacube[source]#

Bases: DataCube

Base for 2-Theta Scan datacubes. See the module docstring above for the raw file evidence behind this shape. Carries a pk so its raw counterpart (RawTwoThetaScanDatacube <ts_ids_components.xrd.datacube_raw.RawTwoThetaScanDatacube>) can link directly back to it – see ts_ids_components.xrd.datacube_raw. Also carries four foreign keys, linking each datacube to the scan (in methods, see ts_ids_components.xrd.method) via fk_method, the run (in runs, see ts_ids_components.xrd.run) via fk_run, the system (in systems, see ts_ids_components.xrd.system) via fk_system, and the sample (in samples, see ts_ids_components.xrd.sample) via fk_sample.

Show JSON schema
{
   "description": "Base for 2-Theta Scan datacubes. See the module docstring above for the\nraw file evidence behind this shape. Carries a `pk` so its raw\ncounterpart (`RawTwoThetaScanDatacube\n<ts_ids_components.xrd.datacube_raw.RawTwoThetaScanDatacube>`) can link\ndirectly back to it -- see :py:mod:`ts_ids_components.xrd.datacube_raw`.\nAlso carries four foreign keys, linking each datacube to the scan (in\n`methods`, see :py:mod:`ts_ids_components.xrd.method`) via `fk_method`,\nthe run (in `runs`, see :py:mod:`ts_ids_components.xrd.run`) via\n`fk_run`, the system (in `systems`, see\n:py:mod:`ts_ids_components.xrd.system`) via `fk_system`, and the sample\n(in `samples`, see :py:mod:`ts_ids_components.xrd.sample`) via\n`fk_sample`.",
   "type": "object",
   "properties": {
      "name": {
         "type": "string"
      },
      "measures": {
         "items": {
            "$ref": "#/definitions/XrdMeasure"
         },
         "maxItems": 1,
         "minItems": 1,
         "type": "array"
      },
      "dimensions": {
         "items": {
            "$ref": "#/definitions/Dimension"
         },
         "maxItems": 1,
         "minItems": 1,
         "type": "array"
      },
      "pk": {
         "@primary_key": true,
         "description": "Primary key for this datacube, referenced by the raw datacube (in `datacubes_raw`) preserving its untouched source text.",
         "type": "string"
      },
      "fk_method": {
         "@foreign_key": "/properties/methods/items/properties/pk",
         "description": "Foreign key to the scan (in `methods`) that produced this datacube. A single raw file can contain multiple independent scans, e.g. repeat measurements sharing one autosampler batch submission.",
         "type": "string"
      },
      "fk_run": {
         "@foreign_key": "/properties/runs/items/properties/pk",
         "description": "Foreign key to the run (in `runs`) that produced this datacube.",
         "type": "string"
      },
      "fk_system": {
         "@foreign_key": "/properties/systems/items/properties/pk",
         "description": "Foreign key to the system (in `systems`) that produced this datacube.",
         "type": "string"
      },
      "fk_sample": {
         "@foreign_key": "/properties/samples/items/properties/pk",
         "description": "Foreign key to the sample (in `samples`) this datacube was measured on.",
         "type": "string"
      },
      "description": {
         "type": [
            "string",
            "null"
         ]
      }
   },
   "additionalProperties": false,
   "required": [
      "name",
      "measures",
      "dimensions",
      "pk",
      "fk_method",
      "fk_run",
      "fk_system",
      "fk_sample"
   ],
   "definitions": {
      "Dimension": {
         "additionalProperties": false,
         "description": "A dimension of a DataCube",
         "properties": {
            "name": {
               "type": [
                  "string",
                  "null"
               ]
            },
            "unit": {
               "type": [
                  "string",
                  "null"
               ]
            },
            "scale": {
               "items": {
                  "type": [
                     "number",
                     "null"
                  ]
               },
               "type": "array"
            }
         },
         "required": [
            "name",
            "unit",
            "scale"
         ],
         "type": "object"
      },
      "XrdMeasure": {
         "additionalProperties": false,
         "description": "A single named per-point quantity in a :py:class:`TwoThetaScanDatacube`\n(e.g. ``\"theta\"``, ``\"omega\"``, ``\"intensity\"``, ``\"weight\"``,\n``\"effective time per step\"``), sharing the datacube's one\n:py:data:`TWO_THETA_SCAN_DIMENSION_NAME` dimension. See the module\ndocstring above for why any axis that covaries with 2-theta -- rather\nthan varying independently of it -- is modeled as a measure rather than\na second dimension.",
         "properties": {
            "name": {
               "type": [
                  "string",
                  "null"
               ]
            },
            "unit": {
               "type": [
                  "string",
                  "null"
               ]
            },
            "value": {
               "items": {
                  "type": [
                     "number",
                     "null"
                  ]
               },
               "type": "array"
            }
         },
         "required": [
            "name",
            "unit",
            "value"
         ],
         "type": "object"
      }
   }
}

Validators:
field description: str | None#
field dimensions: List[Dimension]#
Constraints:
  • min_length = 1

  • max_length = 1

field fk_method: str#

Foreign key to the scan (in methods) that produced this datacube. A single raw file can contain multiple independent scans, e.g. repeat measurements sharing one autosampler batch submission.

Constraints:
  • func = <function validate_uuid at 0x7fd7db1881f0>

  • json_schema_input_type = PydanticUndefined

  • ids_field_arg = primary_key

  • pk_reference_field = @foreign_key

field fk_run: str#

Foreign key to the run (in runs) that produced this datacube.

Constraints:
  • func = <function validate_uuid at 0x7fd7db1881f0>

  • json_schema_input_type = PydanticUndefined

  • ids_field_arg = primary_key

  • pk_reference_field = @foreign_key

field fk_sample: str#

Foreign key to the sample (in samples) this datacube was measured on.

Constraints:
  • func = <function validate_uuid at 0x7fd7db1881f0>

  • json_schema_input_type = PydanticUndefined

  • ids_field_arg = primary_key

  • pk_reference_field = @foreign_key

field fk_system: str#

Foreign key to the system (in systems) that produced this datacube.

Constraints:
  • func = <function validate_uuid at 0x7fd7db1881f0>

  • json_schema_input_type = PydanticUndefined

  • ids_field_arg = primary_key

  • pk_reference_field = @foreign_key

field measures: List[XrdMeasure]#
Constraints:
  • min_length = 1

  • max_length = 1

field name: str#
field pk: str#

Primary key for this datacube, referenced by the raw datacube (in datacubes_raw) preserving its untouched source text.

Constraints:
  • func = <function validate_uuid at 0x7fd7db1881f0>

  • json_schema_input_type = PydanticUndefined

validator two_theta_dimension_and_intensity_measure_present  »  all fields[source]#

Every TwoThetaScanDatacube’s one dimension must be two theta, and intensity must be present as a measure. This is the precise criterion for what this component supports: any 2-Theta Scan, i.e. any scan with 2-theta as its dimension and intensity as a measure, not specifically pharma powder XRD. See the module docstring above for the scan modes (rocking curves, pole figures, reciprocal space maps – primary dimension is omega, phi, or a reciprocal-space grid, not 2-theta) this intentionally doesn’t support.

Validates:
  • all fields