Titration - Acid-Base Analysis
Acid-base potentiometric titration is used to measure freebase/limestone content in pilot plant liquids. The OrionStarT940 Auto Titrator generates pH vs. volume curves and calculates endpoint values.
Method Overview
| Property | Value |
|---|---|
| Full Name | Acid-Base Potentiometric Titration |
| Purpose | Measures freebase/limestone content in pilot plant liquids |
| Instrument | OrionStarT940 Auto Titrator |
| Output | pH vs. volume titration curves and endpoint values |
| SharePoint Location | Analytical Data > OrionStarT940 Auto Titrator > Data Exports |
| File Format | CSV (paired files) |
Instrument
OrionStarT940 Auto Titrator
The OrionStarT940 is an automated potentiometric titrator that measures pH as a function of titrant volume added. It automatically detects endpoints based on inflection points in the pH curve.
Output Files:
*_Tsum.csv- Summary/metadata file (sample ID, method, endpoint values)*_Tdata1.csv- Data file (pH vs. volume curve points)
Sample Types
| Type | Description |
|---|---|
| BBE Pilot Liquids | Liquid samples from the BBE pilot plant |
Sample ID Format
Titrator sample IDs follow pilot plant naming conventions:
| Input Format | Canonical Sample ID |
|---|---|
| R204121620 | PIL13-441-121620-R204-LIQ-RUSH |
| D101121620 | PIL13-441-121620-D101-LIQ-RUSH |
| 121700D101 | PIL13-441-121700-D101-LIQ-RUSH |
The format is: PIL13-441-{MMDDYY}-{Location}-LIQ-RUSH
Data Pipeline
SharePoint Source
- Site: Analytical Data
- Folder: OrionStarT940 Auto Titrator/Data Exports/
- File Type: CSV (paired files)
File Pairing
Files are generated in pairs with matching prefixes:
T40277_20251216_1711_506_Tsum.csv <- Metadata
T40277_20251216_1711_506_Tdata1.csv <- Curve data
The pipeline automatically:
- Separates files by type (Tsum vs Tdata1)
- Matches pairs by prefix
- Fetches missing pairs from SharePoint when only one file arrives
Dagster Assets
The titration data pipeline is defined in apps/datasmart/src/datasmart/assets/analytical/tritation.py:
freebase_titration (sharepoint_multi_asset)
├── freebase_titration_samples (analytical.freebase_titration_samples)
└── freebase_titration_data (analytical.freebase_titration_data)
Asset Description
| Asset | Schema | Description |
|---|---|---|
freebase_titration_samples | analytical | Sample metadata and endpoint values |
freebase_titration_data | analytical | pH vs volume curve data points |
Database Tables
analytical.freebase_titration_samples
Metadata table with one row per sample.
| Column | Type | Description |
|---|---|---|
file_id | string | SharePoint file identifier (primary key) |
original_sample_id | string | Raw sample ID from file |
sample_id | string | Normalized pilot sample ID |
sample_date | datetime | Analysis date/time |
titrator_name | string | Titrator instrument name |
method_name | string | Titration method |
titrant_name | string | Titrant solution name |
titrant_conc | float | Titrant concentration (M) |
sample_amount | float | Sample amount (g or mL) |
sample_conc | float | Sample concentration |
ep_volume | float | Endpoint volume (mL) |
ep_value | float | Endpoint pH value |
initial_ph | float | Initial pH |
temperature | float | Temperature (C) |
duration | string | Analysis duration (min) |
sp_site | string | SharePoint site |
file_path | string | File path |
file_url | string | SharePoint URL |
last_update | datetime | Last modification time |
analytical.freebase_titration_data
Curve data table with multiple rows per sample (one per data point).
| Column | Type | Description |
|---|---|---|
file_id | string | SharePoint file identifier |
volume | float | Titrant volume (mL) |
ph | float | pH value |
File Formats
Tsum File (Metadata)
Comma-separated key-value pairs:
Titrator Name, OrionStarT940
Date, 12/16/2024
Time, 05:11 PM
Method Name, Freebase
Sample ID, R204121620
Titrant Name, 0.1M HCl
Titrant Conc., 0.1 M
Sample Amount, 10.000 gram
EP Volume 1, 15.234 mL
EP Value 1, 4.50
Initial pH, 12.34
Temperature, 25.0
Duration (min), 5:30
Tdata1 File (Curve Data)
CSV with header row followed by data:
Point,Volume (mL),E(pH),dE/dV,d2E/dV2
1,0.000,12.34,0.00,0.00
2,0.100,12.30,-0.40,0.10
3,0.200,12.25,-0.50,0.10
...
Usage Examples
Query Sample Metadata
from shared.db.sql import SQL
# Get all samples with endpoints
samples = SQL.read("""
SELECT sample_id, sample_date, ep_volume, ep_value, initial_ph
FROM analytical.freebase_titration_samples
WHERE sample_id IS NOT NULL
ORDER BY sample_date DESC
""")
Get Titration Curve
# Get curve for a specific sample
curve = SQL.read("""
SELECT d.volume, d.ph
FROM analytical.freebase_titration_data d
JOIN analytical.freebase_titration_samples s ON d.file_id = s.file_id
WHERE s.sample_id = 'PIL13-441-121620-R204-LIQ-RUSH'
ORDER BY d.volume
""")
Filter by Location
# Get all samples from R204 location
r204_samples = SQL.read("""
SELECT *
FROM analytical.freebase_titration_samples
WHERE sample_id LIKE 'PIL13-441-%-R204-%'
""")
Plot Titration Curve
import matplotlib.pyplot as plt
from shared.db.sql import SQL
# Get curve data
curve = SQL.read("""
SELECT d.volume, d.ph
FROM analytical.freebase_titration_data d
JOIN analytical.freebase_titration_samples s ON d.file_id = s.file_id
WHERE s.sample_id = 'PIL13-441-121620-R204-LIQ-RUSH'
ORDER BY d.volume
""")
# Get endpoint values
sample = SQL.read("""
SELECT ep_volume, ep_value
FROM analytical.freebase_titration_samples
WHERE sample_id = 'PIL13-441-121620-R204-LIQ-RUSH'
""").iloc[0]
plt.figure(figsize=(10, 6))
plt.plot(curve['volume'], curve['ph'], 'b-', linewidth=1.5)
plt.axhline(y=sample['ep_value'], color='r', linestyle='--', label=f"Endpoint: pH {sample['ep_value']}")
plt.axvline(x=sample['ep_volume'], color='g', linestyle='--', label=f"EP Volume: {sample['ep_volume']} mL")
plt.xlabel('Volume (mL)')
plt.ylabel('pH')
plt.title('Titration Curve: PIL13-441-121620-R204-LIQ-RUSH')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
Sample ID Parsing
The pipeline automatically converts raw titrator sample IDs to canonical pilot plant format:
import re
SAMPLE_ID_LOCATION_DATE_REGEX = re.compile(r"^([A-Z]\d{3})(\d{6})$")
SAMPLE_ID_DATE_LOCATION_REGEX = re.compile(r"^(\d{6})([A-Z]\d{3})$")
def parse_pilot_sample_id(raw_sample_id: str) -> str | None:
"""
Parse a titrator sample ID into canonical pilot sample ID format.
Examples:
R204121620 -> PIL13-441-121620-R204-LIQ-RUSH
D101121620 -> PIL13-441-121620-D101-LIQ-RUSH
121700D101 -> PIL13-441-121700-D101-LIQ-RUSH
"""
raw_sample_id = raw_sample_id.strip().upper()
# Try location + date format (e.g., R204121620)
match = SAMPLE_ID_LOCATION_DATE_REGEX.match(raw_sample_id)
if match:
location = match.group(1)
date = match.group(2)
return f"PIL13-441-{date}-{location}-LIQ-RUSH"
# Try date + location format (e.g., 121700D101)
match = SAMPLE_ID_DATE_LOCATION_REGEX.match(raw_sample_id)
if match:
date = match.group(1)
location = match.group(2)
return f"PIL13-441-{date}-{location}-LIQ-RUSH"
return None
Related Constants
TITRATOR_FOLDER = "OrionStarT940 Auto Titrator/Data Exports"
TITRATOR_SITE = "Analytical Data"
Troubleshooting
Missing Sample ID
If sample_id is NULL:
- The raw sample ID didn't match expected pilot plant formats
- Check
original_sample_idcolumn for the raw value - Verify the sample ID follows
LNNN + MMDDYYorMMDDYY + LNNNformat
Missing Curve Data
If curve data is empty:
- Check if the paired Tdata1 file exists in SharePoint
- Verify the file prefix matches between Tsum and Tdata1 files
- Check Dagster logs for parsing errors
Unpaired Files
The pipeline attempts to fetch missing pairs from SharePoint. If files remain unpaired:
- Verify both files exist in the SharePoint folder
- Check that filenames have matching prefixes (before
_Tsum.csv/_Tdata1.csv)
Source Files
apps/datasmart/src/datasmart/assets/analytical/tritation.py- Dagster asset definitions