Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
6826ebf
feat(wind-farm): add the ids, theme, and day and operating model
veelenga Oct 8, 2026
65ad029
test(wind-farm): cover the ids, theme, day and operating model
veelenga Oct 8, 2026
9a8a265
feat(wind-farm): add the assembly contract and placeholder
veelenga Oct 8, 2026
b860a8e
feat(wind-farm): add the manifest and a placeholder cover
veelenga Oct 8, 2026
7cd15b8
feat(wind-farm): add the social card and a placeholder share image
veelenga Oct 8, 2026
398c1fe
feat(wind-farm): add the part labels and their priority
veelenga Oct 8, 2026
3f4a965
feat(wind-farm): add the camera views and the scene controller
veelenga Oct 8, 2026
0c2bf41
feat(wind-farm): add the turbine power and wake model
veelenga Oct 8, 2026
589d506
feat(wind-farm): add the day timeline
veelenga Oct 8, 2026
1669bae
feat(wind-farm): add the chapter presets and the store
veelenga Oct 8, 2026
559ccc7
feat(wind-farm): derive the live reading and the assembly state
veelenga Oct 8, 2026
2f9a0eb
feat(wind-farm): add the dock controls, chapter actions and styles
veelenga Oct 8, 2026
b534881
feat(wind-farm): add the power curve canvas
veelenga Oct 8, 2026
736588f
feat(wind-farm): add the farm energy and homes model
veelenga Oct 8, 2026
8b46394
feat(wind-farm): bind the scene to the store and mount it
veelenga Oct 8, 2026
3fade33
test(wind-farm): cover the scene bindings, camera views and controller
veelenga Oct 8, 2026
d267db1
feat(wind-farm): add the wind shear and scale comparisons
veelenga Oct 8, 2026
46f5554
feat(wind-farm): add the readout formats and the gauge readouts
veelenga Oct 8, 2026
f2dff89
feat(wind-farm): mount the chapter readouts and wind sliders
veelenga Oct 8, 2026
0af18b6
feat(wind-farm): add the explainer entry
veelenga Oct 8, 2026
5abaa04
fix(wind-farm): open the day at the farm chapter's start time
veelenga Oct 8, 2026
8863664
feat(wind-farm): write the English chapters and locale
veelenga Oct 8, 2026
4e8313d
feat(wind-farm): add the Portuguese locale
veelenga Oct 8, 2026
c858d1f
feat(wind-farm): add the Chinese locale
veelenga Oct 8, 2026
7616211
feat(wind-farm): add the Ukrainian locale
veelenga Oct 8, 2026
24e1827
feat(wind-farm): add the Spanish locale
veelenga Oct 8, 2026
c5d6024
feat(wind-farm): add the French locale
veelenga Oct 8, 2026
aeafa5b
feat(wind-farm): add the Japanese locale
veelenga Oct 8, 2026
54c4d4e
feat(wind-farm): add the German locale
veelenga Oct 8, 2026
a7de202
fix(wind-farm): make the English copy easier to follow
veelenga Oct 8, 2026
1b963e3
fix(wind-farm): make the Japanese copy read naturally
veelenga Oct 8, 2026
95234fa
fix(wind-farm): make the Portuguese copy read naturally
veelenga Oct 8, 2026
26e50db
fix(wind-farm): make the Ukrainian copy read naturally
veelenga Oct 8, 2026
14b305a
fix(wind-farm): make the Spanish copy read naturally
veelenga Oct 8, 2026
f543d81
fix(wind-farm): make the German copy read naturally
veelenga Oct 8, 2026
16f3340
fix(wind-farm): make the Chinese copy read naturally
veelenga Oct 8, 2026
112aec3
fix(wind-farm): make the French copy read naturally
veelenga Oct 8, 2026
9561b67
feat(wind-farm): use the writer's social image alt text
veelenga Oct 8, 2026
36103fb
fix(wind-farm): halve the daily wind veer so wider spacing always los…
veelenga Oct 8, 2026
4a9ddec
fix(wind-farm): open the farm chapter with its own view on first load
veelenga Oct 8, 2026
fedd509
fix(wind-farm): put the power curve dot at the hero's live power
veelenga Oct 8, 2026
1dc70e3
fix(wind-farm): tell the power curve lines apart by dash and label th…
veelenga Oct 8, 2026
530dcb7
fix(wind-farm): scope each wind slider to its own range widget
veelenga Oct 8, 2026
90f799c
refactor(wind-farm): key the chapter actions by their ids and parse s…
veelenga Oct 8, 2026
b608631
refactor(wind-farm): derive the chapter opening times from minutes pe…
veelenga Oct 8, 2026
9efbb64
fix(wind-farm): apply the review's English copy items
veelenga Oct 8, 2026
74b817a
fix(wind-farm): apply the review's Spanish copy items
veelenga Oct 8, 2026
313b6ed
fix(wind-farm): apply the review's Japanese copy items
veelenga Oct 8, 2026
594695d
fix(wind-farm): apply the review's German copy items
veelenga Oct 8, 2026
07273a0
fix(wind-farm): apply the review's French copy items
veelenga Oct 8, 2026
bd845ea
fix(wind-farm): apply the review's Portuguese copy items
veelenga Oct 8, 2026
83f6e91
fix(wind-farm): apply the review's Ukrainian copy items
veelenga Oct 8, 2026
8eb4731
fix(wind-farm): apply the review's Chinese copy items
veelenga Oct 8, 2026
e4422a6
feat(wind-farm): add the scene assembly skeleton, finishes, lighting …
veelenga Oct 8, 2026
d51eed9
feat(wind-farm): add the nacelle interior contract
veelenga Oct 8, 2026
d6bab23
feat(wind-farm): mount the nacelle interior in the hero stub
veelenga Oct 8, 2026
b408210
feat(wind-farm): add glossy shell, matt tower and grille finishes
veelenga Oct 8, 2026
b4f1483
feat(wind-farm): add the actuator disc stream tube profile
veelenga Oct 8, 2026
a712a5f
feat(wind-farm): draw the flowing stream tube streamlines round the r…
veelenga Oct 8, 2026
740eaa1
feat(wind-farm): build the hero tower, foundation, transformer and to…
veelenga Oct 8, 2026
2b51838
feat(wind-farm): add the glowing arrow shader with flowing chevrons
veelenga Oct 8, 2026
30f9625
feat(wind-farm): draw the wind shear profile column
veelenga Oct 8, 2026
51b8a58
feat(wind-farm): add the farm wind arrows and prevailing wind marker
veelenga Oct 8, 2026
89e362a
feat(wind-farm): turn each farm wind arrow about its own centre
veelenga Oct 8, 2026
3a796d5
feat(wind-farm): dim the shear arrows with the rest of the farm
veelenga Oct 8, 2026
1ad6f14
feat(wind-farm): build the hero nacelle shell with the cutaway and th…
veelenga Oct 8, 2026
d3fb17c
feat(wind-farm): add the turbine and farm terrains with a field patch…
veelenga Oct 8, 2026
a07c92c
feat(wind-farm): add the nacelle bedplate, frame, yaw drives and gearbox
veelenga Oct 8, 2026
cc2d836
feat(wind-farm): add the main bearing, shafts, brake and generator
veelenga Oct 8, 2026
a5b51b2
feat(wind-farm): assemble the nacelle interior with converter and ser…
veelenga Oct 8, 2026
ad47afe
feat(wind-farm): add the hero rotor with lofted blades, pitch and spi…
veelenga Oct 8, 2026
9298dd5
feat(wind-farm): build the farm diorama with instanced turbines, wake…
veelenga Oct 8, 2026
d6b6764
test(wind-farm): pin hard-edged lathe normals for the nacelle interior
veelenga Oct 8, 2026
6da25cb
feat(wind-farm): keep farm roads, cables and the spacing marker reada…
veelenga Oct 8, 2026
59fa54f
feat(wind-farm): plant instanced tree clumps along hedgerows and in w…
veelenga Oct 8, 2026
65d2f5f
feat(wind-farm): add a camera-centred sky dome with a warm sun glow
veelenga Oct 8, 2026
f3dfcc9
feat(wind-farm): add the hero swept area, wake cone and ground shadow
veelenga Oct 8, 2026
17065e5
refactor(wind-farm): draw the blade tip band as its own mesh
veelenga Oct 8, 2026
8216d8f
feat(wind-farm): lay the hero shadow and trench on the blended turbin…
veelenga Oct 8, 2026
7f25193
test(wind-farm): cover the scene assembly labels, eases and motion
veelenga Oct 8, 2026
bd53c4b
refactor(wind-farm): drop the scene section stub
veelenga Oct 8, 2026
d0fbec2
feat(wind-farm): paint the generator blue and lighten the wake air
veelenga Oct 8, 2026
b2fca63
fix(wind-farm): keep the streamlines within the rotor disc stream tube
veelenga Oct 8, 2026
8039d5a
refactor(wind-farm): share the distance widening material across farm…
veelenga Oct 8, 2026
b6748cd
feat(wind-farm): keep farm turbines readable from afar with widening,…
veelenga Oct 8, 2026
dd86cb8
feat(wind-farm): draw the wake plumes as light drifting air streaks
veelenga Oct 8, 2026
e343b10
feat(wind-farm): brighten the substation and widen the grid conductors
veelenga Oct 8, 2026
1496664
feat(wind-farm): paint the land into a procedural ground texture over…
veelenga Oct 8, 2026
746e8b8
test(wind-farm): pin the farm blur discs, plume opacity, shadow headi…
veelenga Oct 8, 2026
60b692d
feat(wind-farm): give the wake plumes a stronger body and glowing str…
veelenga Oct 8, 2026
ae8f39b
feat(wind-farm): widen the pylons and set the yard equipment on darke…
veelenga Oct 8, 2026
b9b61d5
feat(wind-farm): give the painted woods a crown canopy
veelenga Oct 8, 2026
565632b
feat(wind-farm): mount the real scene with its options and lighting
veelenga Oct 8, 2026
94705c6
fix(wind-farm): drape the ground arrow with a plain subdivision to ke…
veelenga Oct 8, 2026
25b4e7d
fix(wind-farm): draw the streamlines as a stream tube through the rot…
veelenga Oct 8, 2026
58dbf8c
fix(wind-farm): pull the haze back with the camera distance so framed…
veelenga Oct 8, 2026
1086590
fix(wind-farm): give the farm turbines aerial presence with brighter …
veelenga Oct 8, 2026
05bf4b8
fix(wind-farm): draw the wake plumes as distinct blue cores that fade…
veelenga Oct 8, 2026
67915da
test(wind-farm): pin the plume visibility, plume opacity and contact …
veelenga Oct 8, 2026
de7bfa2
fix(wind-farm): anchor the grid line label on a pylon near the substa…
veelenga Oct 8, 2026
1a2fe73
fix(wind-farm): paint the farm fields as larger, softer, muted patches
veelenga Oct 8, 2026
aa11032
fix(wind-farm): haze the sky's distant ground with the scene fog so t…
veelenga Oct 8, 2026
785094b
fix(wind-farm): fade the streamlines near the camera, end-on and away…
veelenga Oct 8, 2026
97046f3
fix(wind-farm): centre the grid region on the substation and hold the…
veelenga Oct 8, 2026
48cd7c1
fix(wind-farm): lower and tighten the camera views so every chapter's…
veelenga Oct 8, 2026
4b5a3b5
test(wind-farm): pin the farm region's upwind edge moving with the sp…
veelenga Oct 8, 2026
6ee653e
fix(wind-farm): keep the hero wake label within a rotor diameter of t…
veelenga Oct 8, 2026
942ac16
fix(wind-farm): carry faint farmland past the terrain edge and thin t…
veelenga Oct 8, 2026
119ee06
fix(wind-farm): pull the rotor view back so the wake label lands in f…
veelenga Oct 8, 2026
9766903
fix(wind-farm): anchor the turbine and access road labels near the su…
veelenga Oct 8, 2026
0843df2
fix(wind-farm): label the turbine nearest the substation
veelenga Oct 8, 2026
c6c3fd1
fix(wind-farm): open the cooler base and roof over the generator in t…
veelenga Oct 8, 2026
fa8dd11
fix(wind-farm): spread the nacelle label anchors so every part fits t…
veelenga Oct 8, 2026
f94875d
fix(wind-farm): widen the grid view to the nearest turbine so every g…
veelenga Oct 8, 2026
225cb94
fix(wind-farm): give the substation yard weight from the grid view
veelenga Oct 8, 2026
4481acc
fix(wind-farm): keep farm rotors readable when seen edge-on
veelenga Oct 8, 2026
92b007a
fix(wind-farm): anchor the hub and pitch cylinder labels downwind of …
veelenga Oct 8, 2026
611fb30
fix(wind-farm): round wood edges and plant hedges as continuous rows
veelenga Oct 8, 2026
b2bec53
fix(wind-farm): fade the stream tube out over its last third
veelenga Oct 8, 2026
f10414b
fix(wind-farm): tighten the English copy for a reader with no background
veelenga Oct 8, 2026
f75d26e
fix(wind-farm): make the Portuguese copy read naturally for a reader …
veelenga Oct 8, 2026
271d063
fix(wind-farm): make the French copy read naturally for a reader with…
veelenga Oct 8, 2026
6cf0ee0
fix(wind-farm): make the Chinese copy read naturally for a reader wit…
veelenga Oct 8, 2026
d3ac855
fix(wind-farm): make the German copy read naturally for a reader with…
veelenga Oct 8, 2026
f35c054
fix(wind-farm): make the Ukrainian copy read naturally for a reader w…
veelenga Oct 8, 2026
a65235f
fix(wind-farm): make the Spanish copy read naturally for a reader wit…
veelenga Oct 8, 2026
2d4138a
fix(wind-farm): make the Japanese copy read naturally for a reader wi…
veelenga Oct 8, 2026
4a118d6
fix(wind-farm): count the turbines with 座 after a number in Chinese s…
veelenga Oct 8, 2026
b42eb30
fix(wind-farm): keep the turbine, wind and substation labels clear on…
veelenga Oct 8, 2026
6ab7d60
feat(wind-farm): add the cover and social images
veelenga Oct 8, 2026
81d1993
fix(wind-farm): rank the prevailing wind label ahead of the row spaci…
veelenga Oct 8, 2026
5a90c98
fix(wind-farm): open the grid chapter mid-afternoon so a day of energ…
veelenga Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
546 changes: 546 additions & 0 deletions explainers/wind-farm/chapters.html

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions explainers/wind-farm/explainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"slug": "wind-farm",
"tags": ["energy", "mechanics", "weather"],
"cover": "cover.webp",
"entry": "src/index.ts",
"chapters": "chapters.html",
"locales": ["en", "zh", "es", "uk", "pt", "fr", "de", "ja"],
"social": {
"image": "social/og-image.png",
"alt": "A wind farm on rolling land with one turbine close up, beside the title How a wind farm works"
}
}
270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/de.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/en.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/es.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/fr.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/ja.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/pt.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/uk.json

Large diffs are not rendered by default.

270 changes: 270 additions & 0 deletions explainers/wind-farm/locales/zh.json

Large diffs are not rendered by default.

Binary file added explainers/wind-farm/public/cover.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added explainers/wind-farm/public/social/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions explainers/wind-farm/src/ids.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, expect, it } from 'vitest';
import {
FARM_PART_IDS,
FOLLOW_DAY,
PART_IDS,
PRESET_IDS,
PRESET_SCENES,
SCENE_IDS,
SCENE_PARTS,
SPACING_CHOICE_IDS,
SPACING_OPTIONS,
TURBINE_PART_IDS,
WIND_AT_IDS,
WIND_PRESET_IDS,
} from './ids';

describe('ids', () => {
it('names every part once', () => {
expect(PART_IDS).toHaveLength(TURBINE_PART_IDS.length + FARM_PART_IDS.length);
expect(new Set(PART_IDS).size).toBe(PART_IDS.length);
});

it('splits the parts between the two scenes with no overlap', () => {
expect(Object.keys(SCENE_PARTS).sort()).toEqual([...SCENE_IDS].sort());
const scenePartIds = SCENE_IDS.flatMap((scene) => [...SCENE_PARTS[scene]]);
expect(scenePartIds).toHaveLength(PART_IDS.length);
expect(new Set(scenePartIds)).toEqual(new Set(PART_IDS));
});

it('places every preset in a scene', () => {
expect(Object.keys(PRESET_SCENES)).toEqual([...PRESET_IDS]);
PRESET_IDS.forEach((preset) => {
expect(SCENE_IDS).toContain(PRESET_SCENES[preset]);
});
});

it('offers following the day before the wind presets', () => {
expect(WIND_AT_IDS[0]).toBe('day');
expect(WIND_AT_IDS).toEqual([FOLLOW_DAY, ...WIND_PRESET_IDS]);
});

it('labels each spacing choice with its rotor diameters', () => {
expect(SPACING_CHOICE_IDS).toEqual(SPACING_OPTIONS.map(String));
});
});
176 changes: 176 additions & 0 deletions explainers/wind-farm/src/ids.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
export const SCENE_IDS = ['farm', 'turbine'] as const;

export type SceneId = (typeof SCENE_IDS)[number];

export const TURBINE_PART_IDS = [
'land',
'foundation',
'tower',
'transformer',
'towerCable',
'nacelle',
'cooler',
'bedplate',
'hub',
'blades',
'pitchCylinders',
'mainBearing',
'mainShaft',
'gearbox',
'brakeDisc',
'generator',
'converter',
'yawDrives',
'sweptArea',
'streamlinesGroup',
'heroWake',
] as const;

export const FARM_PART_IDS = [
'farmLand',
'farmTurbines',
'accessRoads',
'spacingMarker',
'prevailingWind',
'windArrows',
'shearProfile',
'wakePlumes',
'collectorCables',
'substation',
'gridLine',
] as const;

export const PART_IDS = [...TURBINE_PART_IDS, ...FARM_PART_IDS] as const;

export type PartId = (typeof PART_IDS)[number];

export const SCENE_PARTS: Readonly<Record<SceneId, readonly PartId[]>> = {
turbine: TURBINE_PART_IDS,
farm: FARM_PART_IDS,
};

export const PHASE_IDS = ['night', 'morning', 'afternoon', 'storm', 'evening'] as const;

export type PhaseId = (typeof PHASE_IDS)[number];

export const PRESET_IDS = ['farm', 'tower', 'nacelle', 'curve', 'wakes', 'grid'] as const;

export type PresetId = (typeof PRESET_IDS)[number];

export const PRESET_SCENES: Readonly<Record<PresetId, SceneId>> = {
farm: 'farm',
tower: 'turbine',
nacelle: 'turbine',
curve: 'turbine',
wakes: 'farm',
grid: 'farm',
};

export const SITE_WIND_IDS = ['calm', 'typical', 'windy'] as const;

export type SiteWindId = (typeof SITE_WIND_IDS)[number];

export const SPACING_OPTIONS = [5, 7, 9] as const;

export type SpacingD = (typeof SPACING_OPTIONS)[number];

export const SPACING_CHOICE_IDS = ['5', '7', '9'] as const;

export type SpacingChoiceId = (typeof SPACING_CHOICE_IDS)[number];

export const OPERATING_STATE_IDS = [
'idle',
'partial',
'full',
'rampDown',
'stopping',
'parked',
'starting',
] as const;

export type OperatingStateId = (typeof OPERATING_STATE_IDS)[number];

export const NACELLE_CHOICE_IDS = ['closed', 'open'] as const;

export type NacelleChoiceId = (typeof NACELLE_CHOICE_IDS)[number];

export const WIND_PRESET_IDS = ['cutIn', 'peak', 'rated', 'rampDown', 'cutOut'] as const;

export type WindPresetId = (typeof WIND_PRESET_IDS)[number];

export const FOLLOW_DAY = 'day';

export const WIND_AT_IDS = [FOLLOW_DAY, ...WIND_PRESET_IDS] as const;

export type WindAtId = (typeof WIND_AT_IDS)[number];

export const CHOICE_IDS = ['siteWind'] as const;

export type ChoiceId = (typeof CHOICE_IDS)[number];

export const CHAPTER_ACTION_IDS = ['siteWind', 'spacing', 'nacelle', 'windAt'] as const;

export type ChapterActionId = (typeof CHAPTER_ACTION_IDS)[number];

export const VIEW_IDS = ['streamlines', 'wakes', 'cables', 'labels', 'cutaway'] as const;

export type ViewId = (typeof VIEW_IDS)[number];

export type ViewOptions = Record<ViewId, boolean>;

export type CameraView =
| 'farmAerial'
| 'turbineTall'
| 'nacelleCutaway'
| 'rotorQuarter'
| 'wakeStreaks'
| 'gridSubstation';

export type RegionId = 'farm' | 'wakes' | 'grid' | 'turbine' | 'nacelle' | 'rotor';

export type AnchorId = 'hub' | 'yawPivot' | 'towerBase' | 'farmCentre' | 'heroSite' | 'substation';

export type Point = readonly [x: number, y: number, z: number];

export type RowIndex = 0 | 1 | 2;

export interface FarmSite {
row: RowIndex;
column: number;
x: number;
z: number;
}

export interface WindReading {
speed: number;
fromDeg: number;
induction: number;
}

export interface RotorReading {
rpm: number;
pitchDeg: number;
yawDeg: number;
state: OperatingStateId;
braked: boolean;
powerShare: number;
}

export interface FarmReading {
spacing: SpacingD;
sites: readonly FarmSite[];
deficits: readonly number[];
plumeLengthD: number;
plumeStrength: number;
outputShare: number;
}

export interface AssemblyState {
scene: SceneId;
phase: number;
playing: boolean;
wind: WindReading;
rotor: RotorReading;
farm: FarmReading;
view: ViewOptions;
}
28 changes: 28 additions & 0 deletions explainers/wind-farm/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import './style.css';
import { defineExplainer } from '@core/explainer';
import { SCENE_OPTIONS, mountWindFarmScene } from './scene';
import { PART_INFO } from './scene/partInfo';
import { PRESETS, createWindFarmStore } from './state';
import type { WindFarmStoreState } from './state';
import { WIND_FARM_TIMELINE } from './timeline';
import {
CHAPTER_ACTIONS,
VIEW_TOGGLES,
WIND_FARM_CHOICES,
WIND_FARM_READOUTS,
mountWindFarmUi,
} from './ui';

export default defineExplainer<WindFarmStoreState>({
id: 'wind-farm',
timeline: WIND_FARM_TIMELINE,
presets: PRESETS,
parts: PART_INFO,
createStore: () => createWindFarmStore(),
dock: { choices: WIND_FARM_CHOICES, toggles: VIEW_TOGGLES },
readouts: WIND_FARM_READOUTS,
actions: CHAPTER_ACTIONS,
scene: SCENE_OPTIONS,
mountScene: mountWindFarmScene,
mountUi: mountWindFarmUi,
});
36 changes: 36 additions & 0 deletions explainers/wind-farm/src/model/constants.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest';
import {
BETZ_LIMIT,
CUT_IN_MS,
CUT_OUT_MS,
FARM_RATED_KW,
RAMP_START_MS,
RATED_WIND_MS,
RESTART_MS,
ROTOR_RADIUS_M,
TIP_HEIGHT_M,
TURBINE_COUNT,
} from './constants';

describe('constants', () => {
it('puts the blade tip 180 m above the ground', () => {
expect(ROTOR_RADIUS_M).toBe(75);
expect(TIP_HEIGHT_M).toBe(180);
});

it('rates the 27 turbine farm at 113,400 kW', () => {
expect(TURBINE_COUNT).toBe(27);
expect(FARM_RATED_KW).toBe(113_400);
});

it('caps the share of wind power at the Betz limit of 0.593', () => {
expect(BETZ_LIMIT).toBeCloseTo(0.593, 3);
});

it('orders the wind thresholds from cut-in to cut-out', () => {
expect(CUT_IN_MS).toBeLessThan(RATED_WIND_MS);
expect(RATED_WIND_MS).toBeLessThan(RAMP_START_MS);
expect(RAMP_START_MS).toBeLessThan(RESTART_MS);
expect(RESTART_MS).toBeLessThan(CUT_OUT_MS);
});
});
48 changes: 48 additions & 0 deletions explainers/wind-farm/src/model/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export const ROTOR_DIAMETER_M = 150;
export const ROTOR_RADIUS_M = ROTOR_DIAMETER_M / 2;
export const HUB_HEIGHT_M = 105;
export const TIP_HEIGHT_M = HUB_HEIGHT_M + ROTOR_RADIUS_M;
export const BLADE_LENGTH_M = 73.7;
export const MAX_CHORD_M = 4.2;
export const SWEPT_AREA_M2 = 17_671;
export const AIR_DENSITY = 1.225;

export const RATED_KW = 4200;
export const CUT_IN_MS = 3;
export const RATED_WIND_MS = 12;
export const RAMP_START_MS = 20;
export const CUT_OUT_MS = 24.5;
export const RESTART_MS = 22.5;
export const MAX_RPM = 10.4;
export const GEAR_RATIO = 143;
export const MODEL_TIP_SPEED_RATIO = 8;
export const FEATHER_DEG = 90;
export const STOP_MINUTES = 20;
export const START_MINUTES = 20;
export const BETZ_LIMIT = 16 / 27;

export const TURBINE_COUNT = 27;
export const FARM_RATED_KW = TURBINE_COUNT * RATED_KW;
export const ACROSS_SPACING_D = 4;
export const DEFAULT_SPACING_D = 7;
export const WAKE_DECAY = 0.075;
export const PLUME_VISIBLE_DEFICIT = 0.03;
export const MAX_PLUME_D = 20;
export const SHEAR_EXPONENT = 1 / 7;

export const GENERATOR_VOLTS = 800;
export const COLLECTOR_KV = 36;
export const GRID_KV = 110;

export const HOME_KWH_PER_YEAR = 10_791;
export const HOURS_PER_YEAR = 8760;
export const ANNUAL_MWH_PER_TURBINE = 14_692;
export const CARBON_G_PER_KWH = 7.3;
export const PAYBACK_MONTHS = 7.6;
export const DESIGN_LIFE_YEARS = 20;
export const BUILT_HA_PER_MW = 0.3;
export const PROJECT_HA_PER_MW = 34;
export const FOOTBALL_PITCH_M2 = 7140;

export const WIND_FROM_DEG = 270;
export const VEER_DEG = 3;
Loading
Loading