Source code for iwfm.read_obs_smp
# read_obs_smp.py
# Read observations from an smp file (PEST observation file)
# Copyright (C) 2020-2026 University of California
# -----------------------------------------------------------------------------
# This information is free; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This work is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a copy of the GNU General Public License, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# -----------------------------------------------------------------------------
'''Read the contents of an observed values smp file (PEST observation file) and return as a polars DataFrame or dict.'''
[docs]
def read_obs_smp(smp_file):
'''Read the contents of an observed values smp file (PEST observation file) and return as a polars DataFrame or dict.
Parameters
----------
smp_file : str
Data file name (PEST-style smp format)
Format: site_name MM/DD/YYYY HH:MM:SS value
Returns
-------
polars.DataFrame or dict
If polars is available, returns a sorted DataFrame with columns::
site_name : str - observation site identifier
date : datetime - observation date
time : str - observation time string (HH:MM:SS)
obs_value : float - observed value
If polars is not available, returns a dict with the same keys,
each containing a list of values. Sorted by (site_name, date, time).
'''
from datetime import datetime
site_names = []
dates = []
times = []
values = []
with open(smp_file, encoding='utf-8') as f:
for line in f:
line = line.replace("_", " ")
parts = line.split()
if len(parts) < 4:
continue
try:
name = parts[0]
dt = datetime.strptime(parts[1], '%m/%d/%Y')
tm = parts[2]
val = float(parts[3])
except (ValueError, IndexError):
continue
site_names.append(name)
dates.append(dt)
times.append(tm)
values.append(val)
# Try to return polars DataFrame if available, otherwise dict
try:
import polars as pl
df = pl.DataFrame({
'site_name': site_names,
'date': dates,
'time': times,
'obs_value': values,
})
return df.sort(['site_name', 'date', 'time'])
except ImportError:
# Sort using indices for dict fallback
sorted_indices = sorted(
range(len(site_names)),
key=lambda i: (site_names[i], dates[i], times[i])
)
return {
'site_name': [site_names[i] for i in sorted_indices],
'date': [dates[i] for i in sorted_indices],
'time': [times[i] for i in sorted_indices],
'obs_value': [values[i] for i in sorted_indices],
}