-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnuclear_reactor_database_explorer.sql
More file actions
563 lines (499 loc) · 24.3 KB
/
Copy pathnuclear_reactor_database_explorer.sql
File metadata and controls
563 lines (499 loc) · 24.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
BEGIN;
-- Drop and recreate schema for a clean build during development.
DROP SCHEMA IF EXISTS nuclear CASCADE;
CREATE SCHEMA nuclear;
SET search_path = nuclear, public;
-- Optional, but nice for name consistency.
SET client_min_messages = WARNING;
-- 1. Lookup tables and domains
-- Keep enumerations in tables for transparency and easy updates.
-- 1.1 Countries
CREATE TABLE countries (
country_id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
iso2 CHAR(2) NOT NULL,
iso3 CHAR(3) NOT NULL,
region TEXT NULL,
CONSTRAINT uq_countries_iso2 UNIQUE (iso2),
CONSTRAINT uq_countries_iso3 UNIQUE (iso3),
CONSTRAINT uq_countries_name UNIQUE (name),
CONSTRAINT ck_countries_iso2 CHECK (iso2 ~ '^[A-Z]{2}$'),
CONSTRAINT ck_countries_iso3 CHECK (iso3 ~ '^[A-Z]{3}$')
);
-- 1.2 Organization types, for operators, owners, developers, etc.
CREATE TABLE organization_types (
organization_type_id BIGSERIAL PRIMARY KEY,
code TEXT NOT NULL UNIQUE, -- operator, owner, developer, regulator, vendor
description TEXT NOT NULL
);
-- 1.3 Organizations, such as operators and owners
CREATE TABLE organizations (
organization_id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
organization_type_id BIGINT NOT NULL REFERENCES organization_types(organization_type_id),
country_id BIGINT NOT NULL REFERENCES countries(country_id),
website TEXT NULL,
CONSTRAINT uq_organizations_name UNIQUE (name)
);
-- 1.4 Reactor type families, such as PWR, BWR, PHWR, etc.
CREATE TABLE reactor_families (
reactor_family_id BIGSERIAL PRIMARY KEY,
code TEXT NOT NULL UNIQUE, -- PWR, BWR, PHWR, GCR, AGR, FBR, HTGR, MSR, Other
description TEXT NOT NULL
);
-- 1.5 Detailed reactor designs, linked to a family
-- You can add attributes that are useful for analytics.
CREATE TABLE reactor_designs (
reactor_design_id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE, -- e.g., AP1000, EPR, CANDU 6, VVER-1200
reactor_family_id BIGINT NOT NULL REFERENCES reactor_families(reactor_family_id),
neutron_spectrum TEXT NOT NULL CHECK (neutron_spectrum IN ('thermal', 'fast', 'epithermal')),
primary_coolant TEXT NULL, -- e.g., light water, heavy water, helium, liquid sodium
moderator TEXT NULL, -- e.g., light water, heavy water, graphite, none
typical_thermal_mw NUMERIC(8,2) NULL CHECK (typical_thermal_mw IS NULL OR typical_thermal_mw > 0),
typical_electric_mw NUMERIC(8,2) NULL CHECK (typical_electric_mw IS NULL OR typical_electric_mw > 0),
fuel_form TEXT NULL, -- e.g., UO2, MOX, TRISO, metallic U
typical_enrichment_pct NUMERIC(4,2) NULL CHECK (typical_enrichment_pct IS NULL OR typical_enrichment_pct >= 0)
);
-- 1.6 Unit status lookup
CREATE TABLE unit_status_lu (
unit_status_id BIGSERIAL PRIMARY KEY,
code TEXT NOT NULL UNIQUE, -- PLANNED, UNDER_CONSTRUCTION, OPERATIONAL, SHUTDOWN, DECOMMISSIONED
description TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT FALSE
);
-- 2. Physical sites and units
-- 2.1 Sites, that can host one or more reactor units
CREATE TABLE sites (
site_id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
country_id BIGINT NOT NULL REFERENCES countries(country_id),
location_name TEXT NULL, -- province, state, or locality
latitude_deg NUMERIC(9,6) NULL CHECK (latitude_deg BETWEEN -90 AND 90),
longitude_deg NUMERIC(9,6) NULL CHECK (longitude_deg BETWEEN -180 AND 180),
CONSTRAINT uq_site_name_country UNIQUE (name, country_id)
);
-- 2.2 Reactor units
CREATE TABLE units (
unit_id BIGSERIAL PRIMARY KEY,
site_id BIGINT NOT NULL REFERENCES sites(site_id),
unit_name TEXT NOT NULL, -- local name or number, e.g., Unit 1
reactor_design_id BIGINT NOT NULL REFERENCES reactor_designs(reactor_design_id),
operator_id BIGINT NOT NULL REFERENCES organizations(organization_id),
owner_id BIGINT NULL REFERENCES organizations(organization_id),
thermal_power_mw NUMERIC(8,2) NULL CHECK (thermal_power_mw IS NULL OR thermal_power_mw > 0),
gross_electric_mw NUMERIC(8,2) NULL CHECK (gross_electric_mw IS NULL OR gross_electric_mw > 0),
net_electric_mw NUMERIC(8,2) NULL CHECK (net_electric_mw IS NULL OR net_electric_mw > 0),
load_factor_design_pct NUMERIC(5,2) NULL CHECK (load_factor_design_pct IS NULL OR (load_factor_design_pct >= 0 AND load_factor_design_pct <= 100)),
design_life_years INTEGER NULL CHECK (design_life_years IS NULL OR design_life_years > 0),
construction_start_date DATE NULL,
first_criticality_date DATE NULL,
grid_connection_date DATE NULL,
commercial_operation_date DATE NULL,
permanent_shutdown_date DATE NULL,
unit_status_id BIGINT NOT NULL REFERENCES unit_status_lu(unit_status_id),
-- Simple quality checks on chronology
CONSTRAINT uq_unit_name_per_site UNIQUE (site_id, unit_name),
CONSTRAINT ck_units_dates_order CHECK (
(construction_start_date IS NULL OR first_criticality_date IS NULL OR construction_start_date <= first_criticality_date)
AND (first_criticality_date IS NULL OR grid_connection_date IS NULL OR first_criticality_date <= grid_connection_date)
AND (grid_connection_date IS NULL OR commercial_operation_date IS NULL OR grid_connection_date <= commercial_operation_date)
AND (commercial_operation_date IS NULL OR permanent_shutdown_date IS NULL OR commercial_operation_date <= permanent_shutdown_date)
)
);
-- 2.3 Unit status history to preserve changes over time
CREATE TABLE unit_status_history (
unit_status_history_id BIGSERIAL PRIMARY KEY,
unit_id BIGINT NOT NULL REFERENCES units(unit_id) ON DELETE CASCADE,
unit_status_id BIGINT NOT NULL REFERENCES unit_status_lu(unit_status_id),
valid_from TIMESTAMPTZ NOT NULL DEFAULT now(),
valid_to TIMESTAMPTZ NULL,
note TEXT NULL
);
-- 3. Generation statistics
-- Store annual generation to compute capacity factors and trends.
CREATE TABLE generation_stats (
generation_id BIGSERIAL PRIMARY KEY,
unit_id BIGINT NOT NULL REFERENCES units(unit_id) ON DELETE CASCADE,
calendar_year INTEGER NOT NULL CHECK (calendar_year BETWEEN 1950 AND 2100),
net_generation_mwh NUMERIC(14,2) NOT NULL CHECK (net_generation_mwh >= 0),
-- Optional override for average net capacity this year if uprates or derates occurred.
avg_net_capacity_mw NUMERIC(8,2) NULL CHECK (avg_net_capacity_mw IS NULL OR avg_net_capacity_mw > 0),
CONSTRAINT uq_generation_unit_year UNIQUE (unit_id, calendar_year)
);
-- 4. Indexing strategy
CREATE INDEX idx_sites_country ON sites(country_id);
CREATE INDEX idx_units_status ON units(unit_status_id);
CREATE INDEX idx_units_comop ON units(commercial_operation_date);
CREATE INDEX idx_units_design ON units(reactor_design_id);
CREATE INDEX idx_gen_unit_year ON generation_stats(unit_id, calendar_year);
-- 5. Triggers and helper functions
-- 5.1 When a unit status changes, close the open history row and insert a new one.
CREATE OR REPLACE FUNCTION trg_units_status_audit()
RETURNS TRIGGER LANGUAGE plpgsql AS
$$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO unit_status_history(unit_id, unit_status_id, valid_from, note)
VALUES (NEW.unit_id, NEW.unit_status_id, now(), 'Initial status on insert');
RETURN NEW;
ELSIF TG_OP = 'UPDATE' AND NEW.unit_status_id <> OLD.unit_status_id THEN
-- Close previous open interval
UPDATE unit_status_history
SET valid_to = now()
WHERE unit_id = OLD.unit_id
AND valid_to IS NULL;
-- Insert new status interval
INSERT INTO unit_status_history(unit_id, unit_status_id, valid_from, note)
VALUES (NEW.unit_id, NEW.unit_status_id, now(), 'Status changed via UPDATE on units');
RETURN NEW;
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER trg_units_status_insert
AFTER INSERT ON units
FOR EACH ROW EXECUTE FUNCTION trg_units_status_audit();
CREATE TRIGGER trg_units_status_update
AFTER UPDATE OF unit_status_id ON units
FOR EACH ROW EXECUTE FUNCTION trg_units_status_audit();
-- 6. Views for common explorations
-- 6.1 Current operational units
CREATE OR REPLACE VIEW vw_operational_units AS
SELECT
u.unit_id,
s.name AS site_name,
c.name AS country,
u.unit_name,
rd.name AS reactor_design,
rf.code AS family,
u.net_electric_mw,
u.commercial_operation_date
FROM units u
JOIN sites s ON s.site_id = u.site_id
JOIN countries c ON c.country_id = s.country_id
JOIN reactor_designs rd ON rd.reactor_design_id = u.reactor_design_id
JOIN reactor_families rf ON rf.reactor_family_id = rd.reactor_family_id
JOIN unit_status_lu us ON us.unit_status_id = u.unit_status_id
WHERE us.code = 'OPERATIONAL';
-- 6.2 Country level summary, counts and capacity
CREATE OR REPLACE VIEW vw_country_reactor_summary AS
SELECT
c.country_id,
c.name AS country,
COUNT(*) FILTER (WHERE us.code = 'OPERATIONAL') AS units_operational,
COUNT(*) FILTER (WHERE us.code = 'UNDER_CONSTRUCTION') AS units_under_construction,
COUNT(*) FILTER (WHERE us.code = 'PLANNED') AS units_planned,
COUNT(*) FILTER (WHERE us.code IN ('SHUTDOWN','DECOMMISSIONED')) AS units_retired,
SUM(u.net_electric_mw) FILTER (WHERE us.code = 'OPERATIONAL') AS net_mw_operational
FROM countries c
LEFT JOIN sites s ON s.country_id = c.country_id
LEFT JOIN units u ON u.site_id = s.site_id
LEFT JOIN unit_status_lu us ON us.unit_status_id = u.unit_status_id
GROUP BY c.country_id, c.name;
-- 6.3 Annual capacity factor per unit
-- CF = net_generation_mwh / (hours_in_year * reference_capacity_mw) * 100
-- Uses avg_net_capacity_mw if provided, otherwise unit.net_electric_mw.
CREATE OR REPLACE VIEW vw_unit_capacity_factor AS
SELECT
g.unit_id,
g.calendar_year,
u.unit_name,
s.name AS site_name,
COALESCE(g.avg_net_capacity_mw, u.net_electric_mw) AS reference_capacity_mw,
g.net_generation_mwh,
(g.net_generation_mwh
/ (COALESCE(g.avg_net_capacity_mw, u.net_electric_mw) * 24 * (CASE
WHEN EXTRACT(YEAR FROM make_date(g.calendar_year,12,31))::INT % 4 = 0
AND (g.calendar_year % 100 <> 0 OR g.calendar_year % 400 = 0)
THEN 366 ELSE 365 END))) * 100
AS capacity_factor_pct
FROM generation_stats g
JOIN units u ON u.unit_id = g.unit_id
JOIN sites s ON s.site_id = u.site_id;
-- 7. Seed data
-- Small, illustrative dataset. Values are approximate and only for demo.
-- You should replace or extend with authoritative data later.
-- 7.1 Countries
INSERT INTO countries(name, iso2, iso3, region) VALUES
('United States', 'US', 'USA', 'North America'),
('Canada', 'CA', 'CAN', 'North America'),
('United Kingdom', 'GB', 'GBR', 'Europe'),
('Finland', 'FI', 'FIN', 'Europe'),
('Romania', 'RO', 'ROU', 'Europe');
-- 7.2 Organization types
INSERT INTO organization_types(code, description) VALUES
('operator', 'Entity that operates one or more nuclear units'),
('owner', 'Entity that owns one or more nuclear units'),
('developer', 'Entity developing new reactors'),
('regulator', 'Government regulator');
-- 7.3 Organizations
INSERT INTO organizations(name, organization_type_id, country_id, website)
SELECT 'PG&E', organization_type_id, (SELECT country_id FROM countries WHERE iso2 = 'US'), 'https://www.pge.com'
FROM organization_types WHERE code = 'operator';
INSERT INTO organizations(name, organization_type_id, country_id, website)
SELECT 'Southern Nuclear', organization_type_id, (SELECT country_id FROM countries WHERE iso2 = 'US'), 'https://www.southernnuclear.com'
FROM organization_types WHERE code = 'operator';
INSERT INTO organizations(name, organization_type_id, country_id, website)
SELECT 'EDF Energy', organization_type_id, (SELECT country_id FROM countries WHERE iso2 = 'GB'), 'https://www.edfenergy.com'
FROM organization_types WHERE code = 'operator';
INSERT INTO organizations(name, organization_type_id, country_id, website)
SELECT 'TVO', organization_type_id, (SELECT country_id FROM countries WHERE iso2 = 'FI'), 'https://www.tvo.fi'
FROM organization_types WHERE code = 'operator';
INSERT INTO organizations(name, organization_type_id, country_id, website)
SELECT 'Nuclearelectrica', organization_type_id, (SELECT country_id FROM countries WHERE iso2 = 'RO'), 'https://www.nuclearelectrica.ro'
FROM organization_types WHERE code = 'operator';
INSERT INTO organizations(name, organization_type_id, country_id, website)
SELECT 'Ontario Power Generation', organization_type_id, (SELECT country_id FROM countries WHERE iso2 = 'CA'), 'https://www.opg.com'
FROM organization_types WHERE code = 'operator';
-- 7.4 Reactor families
INSERT INTO reactor_families(code, description) VALUES
('PWR', 'Pressurized Water Reactor'),
('BWR', 'Boiling Water Reactor'),
('PHWR', 'Pressurized Heavy Water Reactor'),
('EPR', 'European Pressurized Reactor family marker'), -- you may keep EPR under PWR family in practice
('VVER', 'Russian PWR lineage'), -- likewise, commonly grouped under PWR
('Other','Other or advanced type');
-- 7.5 Reactor designs, link to the most appropriate family
INSERT INTO reactor_designs(name, reactor_family_id, neutron_spectrum, primary_coolant, moderator, typical_thermal_mw, typical_electric_mw, fuel_form, typical_enrichment_pct)
VALUES
('PWR Generic', (SELECT reactor_family_id FROM reactor_families WHERE code='PWR'),
'thermal','light water','light water', 3000, 1000, 'UO2', 3.50),
('AP1000', (SELECT reactor_family_id FROM reactor_families WHERE code='PWR'),
'thermal','light water','light water', 3400, 1110, 'UO2', 4.50),
('EPR', (SELECT reactor_family_id FROM reactor_families WHERE code='PWR'),
'thermal','light water','light water', 4300, 1600, 'UO2', 4.95),
('CANDU 6', (SELECT reactor_family_id FROM reactor_families WHERE code='PHWR'),
'thermal','heavy water','heavy water', 2065, 700, 'Natural U', 0.71),
('BWR Generic', (SELECT reactor_family_id FROM reactor_families WHERE code='BWR'),
'thermal','light water','light water', 3000, 1000, 'UO2', 3.00);
-- 7.6 Status lookup
INSERT INTO unit_status_lu(code, description, is_active) VALUES
('PLANNED', 'Planned but not under construction', FALSE),
('UNDER_CONSTRUCTION', 'First concrete or similar milestone achieved', TRUE),
('OPERATIONAL', 'In commercial operation', TRUE),
('SHUTDOWN', 'Shutdown, not generating, may be temporary or long term', FALSE),
('DECOMMISSIONED', 'Permanent shutdown, decommissioning state', FALSE);
-- 7.7 Sites
INSERT INTO sites(name, country_id, location_name, latitude_deg, longitude_deg) VALUES
('Diablo Canyon', (SELECT country_id FROM countries WHERE iso2='US'), 'California', 35.211, -120.855),
('Vogtle', (SELECT country_id FROM countries WHERE iso2='US'), 'Georgia', 33.137, -81.765),
('Hinkley Point', (SELECT country_id FROM countries WHERE iso2='GB'), 'Somerset', 51.209, -3.128),
('Olkiluoto', (SELECT country_id FROM countries WHERE iso2='FI'), 'Eurajoki', 61.236, 21.445),
('Cernavoda', (SELECT country_id FROM countries WHERE iso2='RO'), 'Constanța', 44.343, 28.033),
('Darlington', (SELECT country_id FROM countries WHERE iso2='CA'), 'Ontario', 43.875, -78.728);
-- 7.8 Units, simplified attributes for demo
INSERT INTO units(
site_id, unit_name, reactor_design_id, operator_id, owner_id,
thermal_power_mw, gross_electric_mw, net_electric_mw,
load_factor_design_pct, design_life_years,
construction_start_date, first_criticality_date, grid_connection_date,
commercial_operation_date, permanent_shutdown_date, unit_status_id
)
VALUES
-- Diablo Canyon Units 1 and 2, PWRs, operational
((SELECT site_id FROM sites WHERE name='Diablo Canyon'), 'Unit 1',
(SELECT reactor_design_id FROM reactor_designs WHERE name='PWR Generic'),
(SELECT organization_id FROM organizations WHERE name='PG&E'),
NULL,
3000, 1130, 1100, 92, 40,
DATE '1968-01-01', NULL, NULL,
DATE '1985-05-01', NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='OPERATIONAL')),
((SELECT site_id FROM sites WHERE name='Diablo Canyon'), 'Unit 2',
(SELECT reactor_design_id FROM reactor_designs WHERE name='PWR Generic'),
(SELECT organization_id FROM organizations WHERE name='PG&E'),
NULL,
3000, 1130, 1100, 92, 40,
DATE '1970-01-01', NULL, NULL,
DATE '1986-05-01', NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='OPERATIONAL')),
-- Vogtle 3, AP1000, operational
((SELECT site_id FROM sites WHERE name='Vogtle'), 'Unit 3',
(SELECT reactor_design_id FROM reactor_designs WHERE name='AP1000'),
(SELECT organization_id FROM organizations WHERE name='Southern Nuclear'),
NULL,
3400, 1150, 1110, 92, 60,
DATE '2013-03-01', NULL, NULL,
DATE '2023-07-31', NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='OPERATIONAL')),
-- Vogtle 4, AP1000, under construction or recent operation depending on data. Mark as UNDER_CONSTRUCTION for demo.
((SELECT site_id FROM sites WHERE name='Vogtle'), 'Unit 4',
(SELECT reactor_design_id FROM reactor_designs WHERE name='AP1000'),
(SELECT organization_id FROM organizations WHERE name='Southern Nuclear'),
NULL,
3400, 1150, 1110, 92, 60,
DATE '2015-11-01', NULL, NULL,
NULL, NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='UNDER_CONSTRUCTION')),
-- Hinkley Point C Unit 1 and 2, EPR design, under construction
((SELECT site_id FROM sites WHERE name='Hinkley Point'), 'Unit C1',
(SELECT reactor_design_id FROM reactor_designs WHERE name='EPR'),
(SELECT organization_id FROM organizations WHERE name='EDF Energy'),
NULL,
4300, 1630, 1600, 92, 60,
DATE '2018-12-01', NULL, NULL,
NULL, NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='UNDER_CONSTRUCTION')),
((SELECT site_id FROM sites WHERE name='Hinkley Point'), 'Unit C2',
(SELECT reactor_design_id FROM reactor_designs WHERE name='EPR'),
(SELECT organization_id FROM organizations WHERE name='EDF Energy'),
NULL,
4300, 1630, 1600, 92, 60,
DATE '2020-09-01', NULL, NULL,
NULL, NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='UNDER_CONSTRUCTION')),
-- Olkiluoto 3, EPR, operational
((SELECT site_id FROM sites WHERE name='Olkiluoto'), 'Unit 3',
(SELECT reactor_design_id FROM reactor_designs WHERE name='EPR'),
(SELECT organization_id FROM organizations WHERE name='TVO'),
NULL,
4300, 1630, 1600, 92, 60,
DATE '2005-08-01', NULL, NULL,
DATE '2023-04-16', NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='OPERATIONAL')),
-- Cernavoda 1, CANDU 6, operational
((SELECT site_id FROM sites WHERE name='Cernavoda'), 'Unit 1',
(SELECT reactor_design_id FROM reactor_designs WHERE name='CANDU 6'),
(SELECT organization_id FROM organizations WHERE name='Nuclearelectrica'),
NULL,
2065, 705, 700, 90, 40,
DATE '1982-07-01', NULL, NULL,
DATE '1996-12-02', NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='OPERATIONAL')),
-- Darlington 1, CANDU 6 style in this demo model
((SELECT site_id FROM sites WHERE name='Darlington'), 'Unit 1',
(SELECT reactor_design_id FROM reactor_designs WHERE name='CANDU 6'),
(SELECT organization_id FROM organizations WHERE name='Ontario Power Generation'),
NULL,
2065, 705, 700, 90, 40,
DATE '1985-01-01', NULL, NULL,
DATE '1990-12-31', NULL,
(SELECT unit_status_id FROM unit_status_lu WHERE code='OPERATIONAL'));
-- 7.9 Generation stats, a few demo rows to enable capacity factor analytics
INSERT INTO generation_stats(unit_id, calendar_year, net_generation_mwh, avg_net_capacity_mw)
SELECT u.unit_id, 2023, 8_100_000, NULL FROM units u
WHERE (SELECT code FROM unit_status_lu WHERE unit_status_id = u.unit_status_id) = 'OPERATIONAL'
AND u.unit_name IN ('Unit 1','Unit 2','Unit 3');
INSERT INTO generation_stats(unit_id, calendar_year, net_generation_mwh, avg_net_capacity_mw)
SELECT u.unit_id, 2024, 8_300_000, NULL FROM units u
WHERE (SELECT code FROM unit_status_lu WHERE unit_status_id = u.unit_status_id) = 'OPERATIONAL'
AND u.unit_name IN ('Unit 1','Unit 2','Unit 3');
COMMIT;
-- 8. Example analytic queries
-- You can run these as needed.
-- 8.1 Average net capacity by reactor family
/* Returns one row per family, with the average net capacity across units */
SELECT
rf.code AS reactor_family,
ROUND(AVG(u.net_electric_mw)::numeric, 2) AS avg_net_mw,
COUNT(*) AS unit_count
FROM units u
JOIN reactor_designs rd ON rd.reactor_design_id = u.reactor_design_id
JOIN reactor_families rf ON rf.reactor_family_id = rd.reactor_family_id
GROUP BY rf.code
ORDER BY avg_net_mw DESC;
-- 8.2 Reactors commissioned after a given year
/* Replace :year_threshold with your value */
WITH params AS (SELECT 2000::INT AS year_threshold)
SELECT
s.name AS site,
u.unit_name,
c.name AS country,
rd.name AS design,
u.commercial_operation_date
FROM units u
JOIN sites s ON s.site_id = u.site_id
JOIN countries c ON c.country_id = s.country_id
JOIN reactor_designs rd ON rd.reactor_design_id = u.reactor_design_id
JOIN params p ON TRUE
WHERE u.commercial_operation_date >= make_date(p.year_threshold, 1, 1)
ORDER BY u.commercial_operation_date;
-- 8.3 Countries with the most operational reactors
SELECT
country,
units_operational,
net_mw_operational
FROM vw_country_reactor_summary
ORDER BY units_operational DESC, net_mw_operational DESC;
-- 8.4 Capacity factor rankings for a given year
WITH params AS (SELECT 2023::INT AS y)
SELECT
v.site_name,
u.unit_name,
c.name AS country,
ROUND(v.capacity_factor_pct::numeric, 2) AS capacity_factor_pct
FROM vw_unit_capacity_factor v
JOIN units u ON u.unit_id = v.unit_id
JOIN sites s ON s.site_id = u.site_id
JOIN countries c ON c.country_id = s.country_id
JOIN params p ON v.calendar_year = p.y
ORDER BY capacity_factor_pct DESC NULLS LAST;
-- 8.5 Units under construction for more than N years
WITH params AS (SELECT 5::INT AS years_threshold)
SELECT
s.name AS site,
u.unit_name,
c.name AS country,
u.construction_start_date,
EXTRACT(YEAR FROM age(now(), u.construction_start_date)) AS years_since_start
FROM units u
JOIN sites s ON s.site_id = u.site_id
JOIN countries c ON c.country_id = s.country_id
JOIN unit_status_lu us ON us.unit_status_id = u.unit_status_id
JOIN params p ON TRUE
WHERE us.code = 'UNDER_CONSTRUCTION'
AND u.construction_start_date IS NOT NULL
AND age(now(), u.construction_start_date) > make_interval(years => p.years_threshold)
ORDER BY years_since_start DESC;
-- 8.6 Data completeness checks
-- Find units missing basic capacity attributes
SELECT site_id, unit_name
FROM units
WHERE net_electric_mw IS NULL OR thermal_power_mw IS NULL;
-- 8.7 Status history for a specific unit
/* Replace :unit_name as needed */
SELECT
u.unit_name,
us.code AS status,
h.valid_from,
h.valid_to,
h.note
FROM units u
JOIN unit_status_history h ON h.unit_id = u.unit_id
JOIN unit_status_lu us ON us.unit_status_id = h.unit_status_id
WHERE u.unit_name = 'Unit 3'
ORDER BY h.valid_from;
-- 9. Helper query templates you can adapt
-- 9.1 Search by free text across sites, designs, and organizations
-- For full text search, consider adding pg_trgm extension and trigram indexes.
SELECT
s.name AS site,
u.unit_name,
rd.name AS design,
o.name AS operator
FROM units u
JOIN sites s ON s.site_id = u.site_id
JOIN reactor_designs rd ON rd.reactor_design_id = u.reactor_design_id
JOIN organizations o ON o.organization_id = u.operator_id
WHERE
s.name ILIKE '%' || $1 || '%'
OR u.unit_name ILIKE '%' || $1 || '%'
OR rd.name ILIKE '%' || $1 || '%'
OR o.name ILIKE '%' || $1 || '%';
-- 9.2 Year by year generation trend for a unit
SELECT
g.calendar_year,
g.net_generation_mwh,
ROUND(
(g.net_generation_mwh
/ (COALESCE(g.avg_net_capacity_mw, u.net_electric_mw) * 24
* (CASE WHEN (g.calendar_year % 400 = 0) OR (g.calendar_year % 4 = 0 AND g.calendar_year % 100 <> 0) THEN 366 ELSE 365 END)
) * 100
)::numeric, 2
) AS capacity_factor_pct
FROM generation_stats g
JOIN units u ON u.unit_id = g.unit_id
WHERE u.unit_name = $1
ORDER BY g.calendar_year;
-- End of script