Xact 625i XRF Analyzer
The Xact 625i is a continuous X-ray fluorescence (XRF) analyzer for real-time elemental analysis of particulate matter.
Instrument Overview
| Specification | Value |
|---|---|
| Manufacturer | Cooper Environmental Services |
| Model | Xact 625i |
| Measurement | Elemental composition (ng/m3) |
| Time Resolution | Hourly |
| Elements | 72 elements (Mg to U) |
Data Format
- File format: CSV
- Time format:
MM/DD/YYYY HH:MM:SS - Data structure: Element concentrations with uncertainties + environmental parameters
Column Structure
The raw data file contains:
- Element concentrations:
Element AtomicNumber (ng/m3)(e.g.,Mg 12 (ng/m3)) - Uncertainties:
Element Uncert (ng/m3)(e.g.,Mg uncert (ng/m3)) - Environmental parameters: Temperature, pressure, flow, RH, etc.
- Alarm codes: Instrument status indicators
Supported Elements
ELEMENTS = [
'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe',
'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo',
'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Cs', 'Ba', 'La', 'Ce',
'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu',
'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Th', 'Pa', 'U'
]
Usage
from datetime import datetime
from pathlib import Path
from AeroViz import RawDataReader
# Read Xact data
data = RawDataReader(
instrument='Xact',
path=Path('/path/to/xact/data'),
start=datetime(2024, 1, 1),
end=datetime(2024, 12, 31),
mean_freq='1h'
)
# View available elements
print(data.columns.tolist())
# ['Mg', 'Al', 'Si', 'S', 'K', 'Ca', 'Fe', 'Pb', ...]
Quality Control
QC Rules Applied
| Rule | Condition | Description |
|---|---|---|
| Instrument Error | ALARM code 100-110 | Instrument malfunction detected |
| Upscale Warning | ALARM code 200-203 | Concentration exceeds measurement range |
| Invalid Value | < 0 or > 100,000 ng/m3 | Concentration outside valid range |
| High Uncertainty | Uncertainty > 50% of value | Measurement reliability concern |
Alarm Codes
Error Codes (100-110) - Invalidate data:
| Code | Description |
|---|---|
| 100 | X-ray Voltage Error |
| 101 | X-ray Current Error |
| 102 | Tube Temperature Error |
| 103 | Enclosure Temperature Error |
| 104 | Tape Error |
| 105 | Pump Error |
| 106 | Filter Wheel Error |
| 107 | Dynamic Rod Error |
| 108 | Nozzle Error |
| 109 | Energy Calibration Error |
| 110 | Software Error |
Warning Codes (200-203) - Upscale warnings:
| Code | Description |
|---|---|
| 200 | Upscale Cr Warning |
| 201 | Upscale Pb Warning |
| 202 | Upscale Cd Warning |
| 203 | Upscale Nb Warning |
Output Data
Element Columns
Each detected element has two columns:
- Element - Concentration in ng/m3 (e.g., Pb, Fe, S)
- Element_uncert - Measurement uncertainty in ng/m3
Environmental Columns
| Column | Description | Unit |
|---|---|---|
AT |
Ambient Temperature | C |
SAMPLE_T |
Sample Temperature | C |
BP |
Barometric Pressure | mmHg |
TAPE |
Tape Pressure | mmHg |
FLOW_25 |
Flow at 25C | slpm |
FLOW_ACT |
Actual Flow | lpm |
FLOW_STD |
Standard Flow | slpm |
VOLUME |
Sample Volume | L |
TUBE_T |
X-ray Tube Temperature | C |
ENCLOSURE_T |
Enclosure Temperature | C |
FILAMENT_V |
Filament Voltage | V |
SDD_T |
SDD Temperature | C |
DPP_T |
DPP Temperature | C |
RH |
Relative Humidity | % |
WIND |
Wind Speed | m/s |
WIND_DIR |
Wind Direction | deg |
SAMPLE_TIME |
Sample Time | min |
ALARM |
Alarm Code | - |
Example Analysis
from AeroViz import RawDataReader
from pathlib import Path
from datetime import datetime
# Read data
xact = RawDataReader(
instrument='Xact',
path=Path('./data/xact'),
start=datetime(2024, 1, 1),
end=datetime(2024, 3, 31)
)
# Calculate crustal elements ratio
soil_elements = ['Al', 'Si', 'Ca', 'Fe', 'Ti']
xact['Soil'] = (
2.20 * xact['Al'] +
2.49 * xact['Si'] +
1.63 * xact['Ca'] +
2.42 * xact['Fe'] +
1.94 * xact['Ti']
) / 1000 # Convert to ug/m3
# Heavy metals analysis
heavy_metals = ['Pb', 'Cd', 'As', 'Cr', 'Ni']
print(xact[heavy_metals].describe())
Notes
- Data is automatically rounded to hourly resolution
- Duplicate timestamps are removed
- Non-numeric values are coerced to NaN
- QC flags are stored in
QC_Flagcolumn during processing - Units are ng/m3 (nanograms per cubic meter)
API Reference
AeroViz.rawDataReader.script.Xact.Reader
Bases: AbstractReader
Xact 625i XRF Analyzer Data Reader
A specialized reader for Xact 625i continuous XRF analyzer data files, which measure elemental composition of particulate matter.
Attributes
ELEMENTS
class-attribute
instance-attribute
ELEMENTS = ['Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Th', 'Pa', 'U']
ENV_COLUMNS
class-attribute
instance-attribute
ENV_COLUMNS = ['AT', 'SAMPLE_T', 'BP', 'TAPE', 'FLOW_25', 'FLOW_ACT', 'FLOW_STD', 'VOLUME', 'TUBE_T', 'ENCLOSURE_T', 'FILAMENT_V', 'SDD_T', 'DPP_T', 'RH', 'WIND', 'WIND_DIR', 'SAMPLE_TIME', 'ALARM', 'SAMPLE_TYPE']
ERROR_CODES
class-attribute
instance-attribute
ERROR_CODES = {100: 'Xray Voltage Error', 101: 'Xray Current Error', 102: 'Tube Temperature Error', 103: 'Enclosure Temperature Error', 104: 'Tape Error', 105: 'Pump Error', 106: 'Filter Wheel Error', 107: 'Dynamic Rod Error', 108: 'Nozzle Error', 109: 'Energy Calibration Error', 110: 'Software Error'}
WARNING_CODES
class-attribute
instance-attribute
WARNING_CODES = {200: 'Upscale Cr Warning', 201: 'Upscale Pb Warning', 202: 'Upscale Cd Warning', 203: 'Upscale Nb Warning'}
MANUAL_MDL
class-attribute
instance-attribute
MANUAL_MDL = {'Al': {15: 840, 30: 290, 60: 100, 120: 35, 180: 19, 240: 12}, 'Si': {15: 150, 30: 51, 60: 17.8, 120: 6.3, 180: 3.4, 240: 2.2}, 'P': {15: 44, 30: 15, 60: 5.2, 120: 1.8, 180: 0.99, 240: 0.64}, 'S': {15: 26, 30: 9.1, 60: 3.16, 120: 1.1, 180: 0.6, 240: 0.39}, 'Cl': {15: 15, 30: 5, 60: 1.73, 120: 0.61, 180: 0.33, 240: 0.21}, 'K': {15: 9.8, 30: 3.4, 60: 1.17, 120: 0.41, 180: 0.22, 240: 0.14}, 'Ca': {15: 2.5, 30: 0.86, 60: 0.3, 120: 0.1, 180: 0.057, 240: 0.037}, 'Ti': {15: 1.3, 30: 0.46, 60: 0.16, 120: 0.056, 180: 0.03, 240: 0.02}, 'V': {15: 1, 30: 0.34, 60: 0.12, 120: 0.042, 180: 0.023, 240: 0.015}, 'Cr': {15: 0.97, 30: 0.33, 60: 0.12, 120: 0.041, 180: 0.022, 240: 0.014}, 'Mn': {15: 1.2, 30: 0.41, 60: 0.14, 120: 0.05, 180: 0.027, 240: 0.018}, 'Fe': {15: 1.4, 30: 0.49, 60: 0.17, 120: 0.061, 180: 0.033, 240: 0.021}, 'Co': {15: 1.1, 30: 0.39, 60: 0.14, 120: 0.049, 180: 0.026, 240: 0.017}, 'Ni': {15: 0.78, 30: 0.27, 60: 0.1, 120: 0.034, 180: 0.018, 240: 0.012}, 'Cu': {15: 0.65, 30: 0.23, 60: 0.079, 120: 0.028, 180: 0.015, 240: 0.01}, 'Zn': {15: 0.55, 30: 0.19, 60: 0.067, 120: 0.023, 180: 0.013, 240: 0.008}, 'As': {15: 0.52, 30: 0.18, 60: 0.063, 120: 0.022, 180: 0.012, 240: 0.008}, 'Se': {15: 0.66, 30: 0.23, 60: 0.081, 120: 0.029, 180: 0.016, 240: 0.01}, 'Br': {15: 0.85, 30: 0.3, 60: 0.1, 120: 0.037, 180: 0.02, 240: 0.013}, 'Ag': {15: 16, 30: 5.5, 60: 1.9, 120: 0.68, 180: 0.37, 240: 0.24}, 'Cd': {15: 21, 30: 7.2, 60: 2.5, 120: 0.89, 180: 0.48, 240: 0.31}, 'In': {15: 26, 30: 8.9, 60: 3.1, 120: 1.1, 180: 0.6, 240: 0.39}, 'Sn': {15: 33, 30: 12, 60: 4.1, 120: 1.4, 180: 0.78, 240: 0.51}, 'Sb': {15: 42, 30: 15, 60: 5.2, 120: 1.8, 180: 0.99, 240: 0.64}, 'Ba': {15: 3.3, 30: 1.1, 60: 0.39, 120: 0.14, 180: 0.074, 240: 0.048}, 'Hg': {15: 0.99, 30: 0.35, 60: 0.12, 120: 0.043, 180: 0.023, 240: 0.015}, 'Tl': {15: 0.95, 30: 0.33, 60: 0.12, 120: 0.041, 180: 0.022, 240: 0.014}, 'Pb': {15: 1, 30: 0.36, 60: 0.13, 120: 0.045, 180: 0.024, 240: 0.016}, 'Bi': {15: 1.1, 30: 0.37, 60: 0.13, 120: 0.046, 180: 0.025, 240: 0.016}}
MANUAL_SAMPLE_TIMES
class-attribute
instance-attribute
MDL
property
Per-element 1σ detection limits (ng/m³) for this run's sample time.
The manual's table wins where it has an entry, because it is the vendor's
own specification and it varies with sample time — a 15-minute sample
has ~8× the detection limit of a 60-minute one, so a single fixed number
is only right for one configuration. config/supported_instruments.py
supplies the rest (elements CES publishes no limit for); those are marked
as unverified by element_reliability.
Used for the per-element below-MDL diagnostic, never as a row-level flag
— see log_below_mdl.
Methods:
manual_mdl
The manual's detection limits at sample_time minutes.
Falls back to the sample time seen in the data (SAMPLE_TIME), then to
60 minutes. A time between two tabulated ones takes the longer column,
which is the conservative direction: a shorter sample cannot have a lower
detection limit than the manual quotes for a longer one.
element_reliability
Classify each element by how well the instrument actually measured it.
The {element}_uncert columns are 1σ uncertainties on the same basis as
the manual's detection limits, so Currie's criteria apply directly:
a value is detected at >= 3σ and quantifiable at >= 10σ. An
element is classed by the fraction of the run's samples reaching each:
quantitative usable as a time series
semi-quantitative detected, but the magnitudes carry large error
below-detection reported, but indistinguishable from noise
Whether the manual specifies the element is reported separately, in
manual_mdl / published_limit. The two axes are independent and
both matter: an element measured at 100 sigma with no published detection
limit (the internal standard, Nb) is empirically solid but has no vendor
accuracy to appeal to, while one that is in the manual and still sits
below detection is simply not present at this site. Collapsing them would
hide one or the other.
This is per element, not per row, and that is the point: on a 45-column XRF frame the interesting question is never "is this row bad" but "which of these columns can I use". A row-level rule over all elements fires on ~100 % of rows (measured), which says nothing.
Returns a DataFrame indexed by element, worst first.
_raw_reader
Read and parse raw Xact 625i XRF data files.
Returns all columns from the raw file. Column selection is deferred to _QC() and _process() stages.
_QC
Perform quality control on Xact XRF data.
QC Rules Applied
- Calibration Mode : SAMPLE_TYPE != 1 indicates zero calibration
- Instrument Error : ALARM code 100-110 indicates instrument error
- Upscale Warning : ALARM code 200-203 indicates upscale warning
- Invalid Value : Element concentration outside valid range (0-100000 ng/m3)
- Internal Std Drift : Nb internal standard deviates ±20% from median
Detection limits (MDL, from the config) are reported per element in the
log rather than flagged: with 45 elements, "any element below its MDL" is
true for practically every row, and a non-Valid flag NaNs the whole row
downstream — flagging it would delete the dataset. See log_below_mdl.
_report_element_reliability
Log the per-element verdicts and write them next to the other outputs.
The sidecar is the actionable artifact: it tells you which of the 45 columns to build a time series from, which to treat as indicative, and which are noise the instrument reports because it was configured to.