Next Day Actual Generation

I had to think about what “Next Day Actual Generation” meant. How do you report on power generated on the next day? Answer…. the data represents the actual generation of a full day’s production (or storage) but is published… the next day. That’s it.

The National Energy Market (NEM) “Next Day Actual Generation” dataset lists the power generated (Mwh) and the associated production sites (duid). In this report for non-scheduled generators (i.e. biomass, backup diesel, powerwalls, rooftop solar etc)

Datasets are imported into my PostgeSQL server and sourced from here:

Below are some queries used to explore the data and derive some insights.

Quick view of data in “actual_generation” tables

  • Have a quick look at the data and how it can be displayed
select  interval_datetime,duid,mwh_reading
from actual_generation
limit 20
;
Output…
   interval_datetime    |   duid    | mwh_reading
------------------------+-----------+-------------
 2025-05-01 04:05:00+10 | BARCSF1   |         0.0
 2025-05-01 04:05:00+10 | BUTLERSG  |    8.399999
 2025-05-01 04:05:00+10 | BWTR1     |    -21.1492
 2025-05-01 04:05:00+10 | CAPTL_WF  |   13.959197
 2025-05-01 04:05:00+10 | CHALLHWF  |        14.3
 2025-05-01 04:05:00+10 | CLOVER    |         0.0
 2025-05-01 04:05:00+10 | CLUNY     |     2.07599
 2025-05-01 04:05:00+10 | CONDONG1  |       -0.86
 2025-05-01 04:05:00+10 | CULLRGWF  |         2.8
 2025-05-01 04:05:00+10 | DIAPURWF1 |         6.8
 2025-05-01 04:05:00+10 | ERGT01    |         0.0
 2025-05-01 04:05:00+10 | GB01      |    10.11902
 2025-05-01 04:05:00+10 | GERMCRK   |      -0.415
 2025-05-01 04:05:00+10 | GRIFSF1   |    0.001526
 2025-05-01 04:05:00+10 | HUGSF1    |        -0.2
 2025-05-01 04:05:00+10 | KEPBG1    |         0.0
 2025-05-01 04:05:00+10 | LRSF1     |         0.0
 2025-05-01 04:05:00+10 | MBAHNTH   |        27.1
 2025-05-01 04:05:00+10 | MLSP1     |       0.023
 2025-05-01 04:05:00+10 | MLWF1     |       2.325
(20 rows)

Total NEM generation per 5-minute interval

  • Measure ramp rates. i.e. how much does each plant’s generation change every interval.
  • Call out fast ramping generators
SELECT
  interval_datetime,
  SUM(mwh_reading) AS total_mwh
FROM actual_generation
GROUP BY interval_datetime
ORDER BY interval_datetime
;
Output…
   interval_datetime    | total_mwh
------------------------+------------
 2024-05-01 04:05:00+10 | 545.497502
 2024-05-01 04:10:00+10 | 539.038527
 2024-05-01 04:15:00+10 | 531.715357
 2024-05-01 04:20:00+10 | 530.386384
 2024-05-01 04:25:00+10 | 534.226486
 2024-05-01 04:30:00+10 | 526.845372
 2024-05-01 04:35:00+10 | 516.330730
 2024-05-01 04:40:00+10 | 510.359193
 2024-05-01 04:45:00+10 | 509.214776
 2024-05-01 04:50:00+10 | 505.824655
 2024-05-01 04:55:00+10 | 503.555583
 2024-05-01 05:00:00+10 | 500.254706
 2024-05-01 05:05:00+10 | 497.356030
 2024-05-01 05:10:00+10 | 497.771763
 2024-05-01 05:15:00+10 | 496.404566
 2024-05-01 05:20:00+10 | 500.029548
 2024-05-01 05:25:00+10 | 496.353926
 2024-05-01 05:30:00+10 | 502.279562
 2024-05-01 05:35:00+10 | 511.691882
 2024-05-01 05:40:00+10 | 519.135316
 2024-05-01 05:45:00+10 | 544.446013
 2024-05-01 05:50:00+10 | 547.439224
 2024-05-01 05:55:00+10 | 550.809626
 2024-05-01 06:00:00+10 | 549.886148
 2024-05-01 06:05:00+10 | 560.390427
 2024-05-01 06:10:00+10 | 558.757519
 2024-05-01 06:15:00+10 | 571.775836
 2024-05-01 06:20:00+10 | 569.472035
 2024-05-01 06:25:00+10 | 570.351214
 2024-05-01 06:30:00+10 | 558.260983
 ...
 ...

Average daily generation per generator

  • Calculate average daily output
  • Potentially display with clustered bar chart
SELECT
  date_trunc('day', interval_datetime) AS day,
  duid,
  AVG(mwh_reading) AS avg_mwh
FROM actual_generation
GROUP BY day, duid
ORDER BY day, avg_mwh DESC
limit 20;
Output…
          day           |   duid   |       avg_mwh
------------------------+----------+---------------------
 2024-05-01 00:00:00+10 | PORTWF   | 98.6606694979079498
 2024-05-01 00:00:00+10 | WAUBRAWF | 79.8161338033472803
 2024-05-01 00:00:00+10 | MBAHNTH  | 53.5248952594142259
 2024-05-01 00:00:00+10 | WOOLNTH1 | 33.7896644476987448
 2024-05-01 00:00:00+10 | CAPTL_WF | 32.1958773179916318
 2024-05-01 00:00:00+10 | SHOAL1   | 28.7500000000000000
 2024-05-01 00:00:00+10 | CONDONG1 | 23.4248536066945607
 2024-05-01 00:00:00+10 | PIONEER  | 22.2016811008403361
 2024-05-01 00:00:00+10 | CHALLHWF | 19.7861923682008368
 2024-05-01 00:00:00+10 | GERMCRK  | 18.7159451983122363
 2024-05-01 00:00:00+10 | PALOONA  | 15.8649369037656904
 2024-05-01 00:00:00+10 | YAMBUKWF | 14.7263597866108787
 2024-05-01 00:00:00+10 | CULLRGWF | 10.9298326485355649
 2024-05-01 00:00:00+10 | MLWF1    |  9.4992677991631799
 2024-05-01 00:00:00+10 | GRIFSF1  |  7.5182350334728033
 2024-05-01 00:00:00+10 | YSWF1    |  6.6966527196652720
 2024-05-01 00:00:00+10 | ROYALLA1 |  5.6247993933054393
 2024-05-01 00:00:00+10 | REPULSE  |  5.0655075146443515
 2024-05-01 00:00:00+10 | LRSF1    |  4.7598413472803347
 2024-05-01 00:00:00+10 | BWTR1    |  4.3709462761506276
(20 rows)

Top 10 generators between dates in 5-minute output

  • Ranking of generators by average output between two date intervals
  • Interesting in bar chart format
SELECT
  duid,
  ROUND(AVG(mwh_reading), 2) AS five_min_avg_mwh
FROM actual_generation
WHERE interval_datetime BETWEEN '2025-05-01' AND '2025-05-31'
GROUP BY duid
ORDER BY five_min_avg_mwh DESC
LIMIT 10
;
Output…
   duid   | five_min_avg_mwh
----------+------------------
 WAUBRAWF |            64.86
 WOOLNTH1 |            42.64
 SHOAL1   |            40.26
 PORTWF   |            38.98
 CAPTL_WF |            33.34
 MBAHNTH  |            23.46
 GERMCRK  |            14.22
 CHALLHWF |            12.97
 CULLRGWF |            11.34
 CONDONG1 |            11.08
(10 rows)

Identify negative generation events (consumption by storage/pumps)

  • Highlight time intervals when energy is consumed
  • Identify pumping patterns by duid/site.
SELECT
  interval_datetime,
  duid,
  mwh_reading
FROM actual_generation
WHERE mwh_reading < 0
ORDER BY interval_datetime
;
Output…
   interval_datetime    |   duid   | mwh_reading
------------------------+----------+-------------
 2024-05-01 04:05:00+10 | BUTLERSG |   -0.000002
 2024-05-01 04:05:00+10 | HUGSF1   |      -0.039
 2024-05-01 04:05:00+10 | RPCG     |        -0.3
 2024-05-01 04:10:00+10 | BUTLERSG |   -0.000002
 2024-05-01 04:10:00+10 | CLOVER   |       -0.01
 2024-05-01 04:10:00+10 | HUGSF1   |      -0.039
 2024-05-01 04:10:00+10 | RPCG     |        -0.3
 2024-05-01 04:15:00+10 | BUTLERSG |   -0.000002
 2024-05-01 04:15:00+10 | CLOVER   |       -0.01
 2024-05-01 04:15:00+10 | HUGSF1   |      -0.039
 2024-05-01 04:15:00+10 | RPCG     |        -0.3
 2024-05-01 04:20:00+10 | BUTLERSG |   -0.000002
 2024-05-01 04:20:00+10 | CLOVER   |       -0.01
 2024-05-01 04:20:00+10 | HUGSF1   |      -0.039
 2024-05-01 04:20:00+10 | RPCG     |        -0.3
 2024-05-01 04:25:00+10 | BUTLERSG |   -0.000002
 2024-05-01 04:25:00+10 | CLOVER   |       -0.01
 2024-05-01 04:25:00+10 | HUGSF1   |      -0.039
 2024-05-01 04:25:00+10 | RPCG     |        -0.3
 2024-05-01 04:30:00+10 | BUTLERSG |   -0.000002
 ...
 ...

Delta of interval power produced per generator

  • Measure ramp rates how much each plant output changes per interval
  • Highlight fast ramping units
SELECT
  interval_datetime,
  duid,
  mwh_reading,
  mwh_reading -
    LAG(mwh_reading) OVER (PARTITION BY duid ORDER BY interval_datetime)
    AS delta_mwh
FROM actual_generation
;
Output…
   interval_datetime    |   duid    | mwh_reading |  delta_mwh
------------------------+-----------+-------------+-------------
 ...
 ...
 2024-05-01 08:05:00+10 | BARCSF1   |         6.7 |         0.6
 2024-05-01 08:10:00+10 | BARCSF1   |         7.3 |         0.6
 2024-05-01 08:15:00+10 | BARCSF1   |         6.7 |        -0.6
 2024-05-01 08:20:00+10 | BARCSF1   |    7.400001 |    0.700001
 2024-05-01 08:25:00+10 | BARCSF1   |         8.3 |    0.899999
 2024-05-01 08:30:00+10 | BARCSF1   |         5.9 |        -2.4
 2024-05-01 08:35:00+10 | BARCSF1   |         7.2 |         1.3
 2024-05-01 08:40:00+10 | BARCSF1   |         8.0 |         0.8
 2024-05-01 08:45:00+10 | BARCSF1   |    7.400001 |   -0.599999
 2024-05-01 08:50:00+10 | BARCSF1   |         8.1 |    0.699999
 2024-05-01 08:55:00+10 | BARCSF1   |         8.1 |         0.0
 2024-05-01 09:00:00+10 | BARCSF1   |         8.1 |         0.0
 2024-05-01 09:05:00+10 | BARCSF1   |    8.200001 |    0.100001
 2024-05-01 09:10:00+10 | BARCSF1   |    8.200001 |    0.000000
 2024-05-01 09:15:00+10 | BARCSF1   |    8.200001 |    0.000000
 2024-05-01 09:20:00+10 | BARCSF1   |    8.200001 |    0.000000
 2024-05-01 09:25:00+10 | BARCSF1   |    8.200001 |    0.000000
 2024-05-01 09:30:00+10 | BARCSF1   |    8.200001 |    0.000000
 2024-05-01 09:35:00+10 | BARCSF1   |    8.200001 |    0.000000
 ...
 ...

Identify missing intervals per generator(duid)

  • Check for tine intervals gaps.
  • Handy to know what data clearing needs to be performed.
WITH all_intervals AS (
  SELECT DISTINCT interval_datetime FROM actual_generation
),
duid_intervals AS (
  SELECT
    duid,
    interval_datetime
  FROM actual_generation
)
SELECT
  ai.interval_datetime,
  di.duid
FROM all_intervals ai
CROSS JOIN (SELECT DISTINCT duid FROM actual_generation) di
LEFT JOIN duid_intervals ON
  di.duid = duid_intervals.duid
  AND ai.interval_datetime = duid_intervals.interval_datetime
WHERE duid_intervals.duid IS NULL
ORDER BY di.duid, ai.interval_datetime
;
Output…
    interval_datetime   |   duid
------------------------+-----------
 ...
 ...
 2025-03-08 08:15:00+11 | YSWF1
 2025-03-08 14:35:00+11 | YSWF1
 2025-03-08 16:50:00+11 | YSWF1
 2025-03-09 12:10:00+11 | YSWF1
 2025-03-20 06:40:00+11 | YSWF1
 2025-03-21 05:35:00+11 | YSWF1
 2025-03-27 16:25:00+11 | YSWF1
 2025-04-01 13:50:00+11 | YSWF1
 2025-04-01 13:55:00+11 | YSWF1
 2025-04-01 14:00:00+11 | YSWF1
 2025-04-01 14:05:00+11 | YSWF1
 2025-04-01 14:10:00+11 | YSWF1
 2025-04-01 14:15:00+11 | YSWF1
 2025-04-01 14:20:00+11 | YSWF1
 2025-04-01 14:25:00+11 | YSWF1
 2025-04-01 14:30:00+11 | YSWF1
 2025-04-01 14:35:00+11 | YSWF1
 ...
 ...

Daily plant(duid) min / max generation

  • Highlight each generator daily minimum and maximum value
  • Utilise min-max chart to visualise

SELECT
  date_trunc('day', interval_datetime) AS day,
  duid,
  MIN(mwh_reading) AS min_mwh,
  MAX(mwh_reading) AS max_mwh
FROM actual_generation
GROUP BY day, duid
ORDER BY day, duid
;
Output…
          day           |   duid    |  min_mwh   |  max_mwh
------------------------+-----------+------------+------------
 2024-05-01 00:00:00+10 | BARCSF1   |        0.0 |        8.3
 2024-05-01 00:00:00+10 | BUTLERSG  |  -0.000002 |  -0.000002
 2024-05-01 00:00:00+10 | BWTR1     |    3.77256 |    4.80144
 2024-05-01 00:00:00+10 | CAPTL_WF  |        0.0 |  68.547928
 2024-05-01 00:00:00+10 | CHALLHWF  |        6.8 |  35.199997
 2024-05-01 00:00:00+10 | CLOVER    |      -0.03 |      13.66
 2024-05-01 00:00:00+10 | CLUNY     |   1.891051 |  14.140078
 2024-05-01 00:00:00+10 | CONDONG1  |      18.75 |  26.950001
 2024-05-01 00:00:00+10 | CULLRGWF  |       3.14 |  23.389999
 2024-05-01 00:00:00+10 | DIAPURWF1 |        0.0 |        0.0
 2024-05-01 00:00:00+10 | ERGT01    |        0.0 |        0.0
 2024-05-01 00:00:00+10 | GB01      |        0.0 |        0.0
 2024-05-01 00:00:00+10 | GERMCRK   |     14.141 |     20.611
 2024-05-01 00:00:00+10 | GRIFSF1   |   0.001526 |  22.700848
 2024-05-01 00:00:00+10 | HUGSF1    |     -0.113 |   8.697001
 2024-05-01 00:00:00+10 | KEPBG1    |        0.0 |       1.93
 2024-05-01 00:00:00+10 | LRSF1     |       0.01 |     10.081
 2024-05-01 00:00:00+10 | MBAHNTH   |  50.268753 |  57.768753
 ...
 ...

Generate distribution histogram

  • Bucket output levels to see occurrences of frequency distribution
  • Histogram chart would be best to display output
SELECT
  CASE
    WHEN mwh_reading < 0 THEN '< 0'
    WHEN mwh_reading >= 0 AND mwh_reading < 5 THEN '0–5'
    WHEN mwh_reading >= 5 AND mwh_reading < 10 THEN '5–10'
    WHEN mwh_reading >= 10 AND mwh_reading < 15 THEN '10–15'
    WHEN mwh_reading >= 15 AND mwh_reading < 20 THEN '15–20'
    WHEN mwh_reading >= 20 AND mwh_reading < 25 THEN '20–25'
    WHEN mwh_reading >= 25 AND mwh_reading < 30 THEN '25–30'
    WHEN mwh_reading >= 30 AND mwh_reading < 35 THEN '30–35'
    WHEN mwh_reading >= 35 AND mwh_reading < 40 THEN '35–40'
    WHEN mwh_reading >= 40 AND mwh_reading < 45 THEN '40–45'
    WHEN mwh_reading >= 45 AND mwh_reading < 50 THEN '45–50'
    WHEN mwh_reading >= 50 AND mwh_reading < 75 THEN '50–75'
    WHEN mwh_reading >= 75 AND mwh_reading < 100 THEN '75–100'
    ELSE '> 100'
  END AS range_mwh,
  COUNT(*) AS count
FROM actual_generation
GROUP BY range_mwh
ORDER BY MIN(mwh_reading)
;
Output…
 range_mwh |  count
-----------+---------
 < 0       |  525512
 0–5       | 1747144
 5–10      |  512283
 10–15     |  369916
 15–20     |  267429
 20–25     |  172469
 25–30     |  146030
 30–35     |   91441
 35–40     |   76989
 40–45     |   89712
 45–50     |   41362
 50–75     |   73583
 75–100    |   53516
 > 100     |   96456
(14 rows)