DEV Community

Csott Károly Péter
Csott Károly Péter

Posted on Fully Autonomous

How to Validate 15-Minute Meter Data Before PV/BESS Modelling

Csott Károly Péter

A meter export can open successfully and still describe the wrong timeline. Before using it in a solar PV or battery-storage model, I want to establish what one row means, how its timestamp is encoded and which quantity the meter supplied.

This tutorial checks those questions with Python. Every number below is synthetic. The purpose is a reproducible data-quality exercise, with no customer dataset or commercial offer.

1. Define the interval before parsing values

Use an interval start and an exclusive end. Record whether energy is already aggregated for that interval or is a cumulative register that needs a separate, documented conversion.

For the example schema, a row contains 15-minute energy in kWh. Average power is:

average_kW = interval_kWh / (interval_minutes / 60)
125 kWh / 0.25 h = 500 kW
Enter fullscreen mode Exit fullscreen mode

That 500 kW is an interval average. It does not describe the maximum instantaneous power. Nor is it a PV nameplate rating in kWp.

Do not repair an ambiguous unit by multiplying a column until the result looks plausible. Ask for the source definition, meter multiplier and export documentation.

2. Reject naive timestamps

An unqualified 2026-10-25T02:00:00 is ambiguous during the autumn clock change in Europe/Budapest. These two timestamps identify different instants:

2026-10-25T02:00:00+02:00  -> 2026-10-25T00:00:00+00:00
2026-10-25T02:00:00+01:00  -> 2026-10-25T01:00:00+00:00
Enter fullscreen mode Exit fullscreen mode

Normalize to UTC before comparing or deduplicating. Keep the named local timezone as metadata so local tariff periods can later be interpreted correctly.

from datetime import datetime, timezone

def as_utc(value):
    if value.endswith("Z"):
        value = value[:-1] + "+00:00"
    result = datetime.fromisoformat(value)
    if result.tzinfo is None or result.utcoffset() is None:
        raise ValueError("An explicit UTC offset is required")
    return result.astimezone(timezone.utc)
Enter fullscreen mode Exit fullscreen mode

The standard-library zoneinfo module supplies IANA timezone rules. Python needs an installed timezone database; a machine without one needs the maintained tzdata package.

Reopen the actual exported CSV during testing. Spreadsheet or import/export tools can reinterpret date-like strings even when the in-memory source data was correct. Compare the exported timestamps with the intended instants and offsets before using the file.

3. Check coverage against the requested period

Checking only gaps between the rows received misses a missing first or last interval. Build the expected UTC grid from independently specified period boundaries.

from datetime import timedelta
from collections import Counter

def coverage(starts, period_start, period_end):
    step = timedelta(minutes=15)
    if period_end <= period_start:
        raise ValueError("Expected period must have positive duration")
    seconds = (period_end - period_start).total_seconds()
    if seconds % 900:
        raise ValueError("Expected period is not a whole number of intervals")
    expected = {
        period_start + i * step for i in range(int(seconds // 900))
    }
    counts = Counter(starts)
    return {
        "missing": expected - set(starts),
        "outside_period": set(starts) - expected,
        "duplicates": {t: n for t, n in counts.items() if n > 1},
    }
Enter fullscreen mode Exit fullscreen mode

All datetime inputs to this function must already be aware UTC timestamps. For a 25-hour local day, a complete quarter-hour series contains 100 intervals. For the 23-hour day it contains 92. A hardcoded expectation of 96 would reject correct data and could hide a faulty correction.

4. Check quantities and the measurement boundary

Reject blank required quantities, NaN, infinity and signs that conflict with the documented convention. A real zero is different from a missing reading.

For a common-AC baseline without storage, the interval balance is:

grid import + PV generation = site load + grid export
Enter fullscreen mode Exit fullscreen mode

This identity requires aligned channels and a consistent boundary. When storage is present, the balance needs charge/discharge and relevant loss terms. A checker for the no-storage schema should report that limitation instead of giving an incorrect pass.

Directional import and export can both be positive within a quarter-hour when power flow changes direction inside that interval. Do not reject that situation merely because a simplified dispatch model assumed a single constant flow.

5. Keep suspicious peaks visible

In a synthetic sequence, 230 kWh of import during one quarter-hour corresponds to 920 kW on average. Flag it for review with the meter data owner. Check demand-window definitions, operating events and meter limits. Do not replace it with a percentile or cap it automatically: a real peak can matter for the subsequent assessment.

Similarly, imported energy is not gross site consumption when PV already supplies some loads. Keep the available physical channels separate.

6. Test the failure cases

The companion Industrial Energy Data Readiness Toolkit includes a read-only checker and a deterministic synthetic generator. Its regression cases cover:

  • 92- and 100-interval DST days and a 676-interval synthetic week;
  • missing first and last intervals;
  • repeated UTC instants and legitimate repeated local clocks;
  • naive timestamps, wrong energy units and negative directional quantities;
  • non-finite values, broken balances and an unsupported storage boundary.

Run the code from its repository with:

python3 -m unittest discover -s tests -v
Enter fullscreen mode Exit fullscreen mode

A successful consistency check is a documented input to the next modelling step. It does not prove that the meter is calibrated, that a week represents a year or that a battery investment will perform as expected.

References and disclosure

This tutorial was prepared with generative AI assistance, including drafting and code preparation. The examples are synthetic and the implementation is accompanied by executable tests. Author: Csott Károly Péter, working in B2B energy sales, business development and commercial project screening.

Top comments (0)