Toy implementation of a well log database for geoscientists — ingest LAS files, standardize curve mnemonics at load time, and query depth-indexed measurements across wells. Built as a Memorial University group project comparing PostgreSQL and Apache Cassandra backends on real Hibernia B-16 2Z data from Newfoundland.
Engineering write-up: Building a Well Log Database · Author: Kwame Essuman
Team: Dang Tran · J Chris Pickett · Kwame Essuman · Arnob
| Layer | Technology |
|---|---|
| Language | Python 3.11 |
| LAS parsing | lasio |
| Primary store | PostgreSQL 16 (psycopg2) |
| Alternate store | Apache Cassandra 4.1 (wide-column curves) |
| UI | Streamlit |
| Bulk load | PostgreSQL COPY protocol |
| Infra | Docker Compose (pg_local, cassandra_local) |
flowchart LR
subgraph ingestPipeline ["Ingestion"]
LASFile["LAS file"]
ParserPG["las_parser.py"]
ParserCas["las_parser_cas.py"]
PG[("PostgreSQL")]
Cas[("Cassandra")]
LASFile --> ParserPG --> PG
LASFile --> ParserCas
ParserCas --> PG
ParserCas --> Cas
end
subgraph queryLayer ["Query layer"]
WellQuery["well_query.py"]
StreamlitUI["Streamlit UI"]
end
PG --> WellQuery
Cas --> WellQuery
WellQuery --> StreamlitUI
Ingestion flow: parse LAS header curves → resolve aliases via mnemonic / mnemonic_name → upsert well metadata → bulk-load depth-indexed values into well_curve (PostgreSQL) and optionally mirror curves to Cassandra.
Query flow: four search functions expose well metadata, LAS metadata lookup, depth-range filtering, and multi-curve retrieval — via Python API, SQL shell, or Streamlit GUI.
- Mnemonic standardization at ingest —
DEPT,DEPTH, andMDmap to one canonicalmnemonic_idso cross-well queries work without per-file renaming - Seven-table PostgreSQL schema —
well,well_metadata,mnemonic,mnemonic_name,well_param,curve_param,well_curve - Fast bulk loading — ASCII curve data loaded with PostgreSQL
COPY, not row-by-row inserts - Depth-range queries — regex-guarded casts on
VARCHARvalues for safe numeric filtering - Two implementations — Impl 1 (PostgreSQL only) and Impl 2 (PostgreSQL + Cassandra) for side-by-side comparison
- Streamlit GUIs —
app.pyandapp_cas.pyfor interactive exploration - Real well data — Hibernia B-16 2Z (UWI
302B164650048451), Jeanne d'Arc basin
sql/
wellv2.sql PostgreSQL schema (7 tables)
well_mnemonic.sql Seed canonical mnemonics
mnemonic_name.sql Seed alias mappings (DEPT, DEPTH, MD, …)
cassandra_curve.sql Cassandra keyspace + curve table
las_parser.py Impl 1 — ingest LAS → PostgreSQL
las_parser_cas.py Impl 2 — ingest LAS → PostgreSQL + Cassandra
well_query.py Impl 1 — four query functions
well_query_cas.py Impl 2 — Cassandra-backed curve reads
create_well.py Seed Hibernia B-16 2Z well record
db_connect.py PostgreSQL + Cassandra connection helpers
app.py Streamlit UI (PostgreSQL)
app_cas.py Streamlit UI (PostgreSQL + Cassandra)
docker-compose.yml pg_local + cassandra_local containers
The design centres on a mnemonic dictionary that decouples raw LAS field names from storage IDs:
| Table | Purpose |
|---|---|
mnemonic |
Canonical curve concept (unit, description) |
mnemonic_name |
Alias → mnemonic_id mapping |
well / well_metadata |
Well identity and header fields |
well_param |
LAS header parameters per well |
curve_param |
Which curves exist for a well |
well_curve |
Depth-indexed measurements (uwi, mnemonic, row_id, value) |
Values are stored as VARCHAR during ingestion so malformed numerics are handled at query time rather than failing the load.
- Docker Desktop
- Python 3.11 (Cassandra driver requires ≤ 3.11)
pip install lasio psycopg2-binary cassandra-driver streamlit pandas
git clone https://github.com/kaybee77/DB_ToyImp.git
cd DB_ToyImp
docker compose up -dWait for both containers (pg_local, cassandra_local) to be healthy in Docker Desktop.
docker exec -it pg_local psql -U admin -d welllogsIn the psql shell, run the SQL files in order:
sql/wellv2.sql— create tablessql/well_mnemonic.sql— seed mnemonicssql/mnemonic_name.sql— seed aliases
docker exec -it cassandra_local cqlshRun the statements in sql/cassandra_curve.sql.
python create_well.pyPlace a Hibernia B-16 2Z LAS file (e.g. LAS-017963) in the repo root, then ingest:
python las_parser.py # Impl 1 — PostgreSQL
# or
python las_parser_cas.py # Impl 2 — PostgreSQL + Cassandra| Method | Command |
|---|---|
| PostgreSQL shell | docker exec -it pg_local psql -U admin -d welllogs |
| Streamlit (Impl 1) | py -3.11 -m streamlit run app.py |
| Streamlit (Impl 2) | py -3.11 -m streamlit run app_cas.py |
| Python queries | python well_query.py or well_query_cas.py |
Open Streamlit at http://localhost:8501.
Both well_query.py and well_query_cas.py expose four functions:
| Function | Description |
|---|---|
get_well_metadata(uwi) |
Well header and metadata by UWI |
get_las_metadata(uwi, name, lta) |
Resolve wells by UWI, name, or land tenure area |
get_las_range(mnemonic, start, stop, uwi) |
Depth-range filter on a single curve |
get_las(uwi, mnemonics) |
Pull multiple curves for one well |
Example SQL exploration:
SELECT * FROM well_curve LIMIT 20;Configured in docker-compose.yml and db_connect.py:
| Setting | Value |
|---|---|
| PostgreSQL host | localhost:5432 |
| Database | welllogs |
| User / password | admin / admin |
| Cassandra host | localhost:9042 |
| Keyspace | well_logs |
Override in db_connect.py if your Docker ports differ.
- Standardize at ingest, not query time — a
mnemonic_namejoin table means one-off LAS aliases never leak into application logic - PostgreSQL
COPYfor curve data — orders-of-magnitude faster than per-row inserts on deep logs VARCHARvalues inwell_curve— tolerates inconsistent LAS typing; numeric casts happen in queries with regex guards- Two backends side by side — PostgreSQL handles relational metadata cleanly; Cassandra tests wide-column storage for time-series curves without claiming either is universally better at toy scale
- Unknown mnemonics skipped silently — ingestion does not fail on proprietary vendor curve names; they are simply not indexed
- Upsert on re-ingest —
ON CONFLICT DO UPDATEon header params allows refreshing LAS files without duplicates
| Impl 1 | Impl 2 | |
|---|---|---|
| Ingest | las_parser.py |
las_parser_cas.py |
| Queries | well_query.py |
well_query_cas.py |
| UI | app.py |
app_cas.py |
| Storage | PostgreSQL only | PostgreSQL + Cassandra |
Use Impl 1 for the simpler relational path. Use Impl 2 to compare Cassandra curve reads against PostgreSQL for the same well.
Academic / portfolio project. Contact contributors for reuse terms.