Repository navigation
Expand file tree
/
Copy pathdocs
More file actions
839 lines (587 loc) · 24.3 KB
/
Copy pathdocs
File metadata and controls
839 lines (587 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
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
# DART Data Assimilation — Experiment Documentation
## Overview
This documentation covers data assimilation experiments conducted using
**DART (Data Assimilation Research Testbed)** — an open-source ensemble
DA framework developed by NCAR/UCAR.
Three models were studied in order of increasing complexity:
| Model | Variables | Dimensions | Purpose |
|-------|-----------|------------|---------|
| [Lorenz 63](lorenz63.md) | 3 (x, y, z) | 0D — 3 equations | Learn DA basics |
| [Lorenz 96](lorenz96.md) | 40 (x1–x40) | 1D — latitude circle | Test inflation and localization |
| [B-grid Solo](bgrid.md) | 28,200 (Ps, T, u, v) | 3D — global atmosphere | Real atmospheric DA |
---
## What is Data Assimilation?
Data assimilation (DA) mathematically combines two imperfect sources of
information to produce the best possible estimate of the true atmospheric state:
1. **A computer model** — simulates the atmosphere but drifts from reality
2. **Observations** — real measurements but sparse and noisy
DART uses the **Ensemble Kalman Filter (EnKF)** — running many slightly
different model forecasts (an ensemble) and using their spread to estimate
uncertainty, then correcting them toward observations.
---
## System Setup
All experiments were run on the **FSU Research Computing Center (RCC) HPC**
via the OnDemand web interface.
### Loading Required Modules
```bash
module load precompiled
module load intel/21
module load openmpi/4.1.6
module load netcdf/4.7.0
module load python/3
which mpif90 # verify — should return a path
```
Add to `~/.bashrc` to load automatically:
```bash
module load intel/21
module load openmpi/4.1.6
```
### DART Installation Path
```
/gpfs/home/ea25e/space/DART/
```
---
## Key Concepts
### Ensemble Size vs Model Size
| Term | Parameter | Meaning |
|------|-----------|---------|
| `model_size` | `&model_nml` | Number of variables in ONE model run |
| `ens_size` | `&filter_nml` | Number of parallel model runs |
### RMSE
Root Mean Square Error — measures how far the ensemble mean is from truth.
Lower = better. Computed separately for prior (before DA) and posterior (after DA).
### Spread/RMSE Ratio
- **Target: 1.0** — ensemble spread matches RMSE (well calibrated)
- **< 0.8** — underdispersive (ensemble too confident)
- **> 1.2** — overdispersive (ensemble too uncertain)
### Inflation
Artificially increases ensemble spread to prevent collapse over time.
Controlled by `inf_flavor` (method) and `inf_initial` (starting strength).
### Localization
Limits how far each observation's influence reaches spatially.
Controlled by `cutoff` in `&assim_tools_nml`.
---
## Navigation
- [Lorenz 63 Experiment](lorenz63.md)
- [Lorenz 96 Experiment](lorenz96.md)
- [B-grid Solo Experiment](bgrid.md)# Lorenz 96 Model
**Model directory:** `DART/models/lorenz_96/work/`
**Date:** May 2026
---
# Lorenz 63 Model
**Study:** Sensitivity of Observation Error and Observation Frequency in Chaotic Models
**Model directory:** `DART/models/lorenz_63/work/`
**Date:** May 2026
---
## Objective
To investigate how **observation error variance (R)** and **observation frequency**
affect data assimilation performance in a chaotic system using the Lorenz 63 model and DART.
---
## What is Lorenz 63?
The Lorenz 63 model is a **3-variable nonlinear chaotic system** originally
designed to model atmospheric convection. It is highly sensitive to initial
conditions — small differences in starting state grow rapidly over time,
making it an ideal testbed for data assimilation methods.
### Governing Equations
```
dx/dt = σ(y - x)
dy/dt = x(ρ - z) - y
dz/dt = xy - βz
Default parameters: σ=10, ρ=28, β=8/3
```
### Key difference from Lorenz 96
| Feature | Lorenz 63 | Lorenz 96 |
|---------|-----------|-----------|
| Variables | 3 (x, y, z) | 40 (x1–x40) |
| Dimensions | 0D — 3 equations | 1D — latitude circle |
| Obs file | Created interactively | Pre-built file |
| Purpose | Sensitivity studies | Inflation/localization |
---
## Data Assimilation Framework
DART was used to:
- Generate a synthetic nature run (truth)
- Create observations with Gaussian noise
- Run the Ensemble Kalman Filter (EnKF)
- Compute RMSE and spread diagnostics
---
## Workflow
### Step 1 — Compile
```bash
module load intel/21
module load openmpi/4.1.6
cd /gpfs/home/ea25e/space/DART/models/lorenz_63/work/
./quickbuild.sh
```
### Step 2 — Create Observation Sequence
```bash
./create_obs_sequence
```
Interactive prompts answered:
```
Input upper bound on number of observations : 1000
Input number of copies of data : 0
Input number of quality control values : 0
Input observation type : RAW_STATE_VARIABLE
Input error variance : (varied per experiment)
input a -1 if there are no more obs : -1
Input filename for sequence : set_def.out
```
### Step 3 — Create Fixed Network Sequence
```bash
./create_fixed_network_seq
```
```
Input filename for network definition : (Enter — use set_def.out)
Regular repeating sequence : 1
Number of observation times : 1000
Initial time (days, seconds) : 0 0
Period (days, seconds) : 0 3600
Output filename : obs_seq.in
```
### Step 4 — Generate Truth and Observations
```bash
./perfect_model_obs
```
Runs the true Lorenz 63 trajectory forward and samples synthetic
observations with Gaussian noise. Produces `true_state.nc` and `obs_seq.out`.
### Step 5 — Run Data Assimilation
```bash
./filter
```
Runs the ensemble Kalman filter. Produces `preassim.nc` and `analysis.nc`.
---
## Experiment 1 — Observation Error Variance
### Purpose
Determine how changing observation error variance **R** affects
assimilation performance. Four values of R were tested.
### Key Concept
- **Large R** → DART trusts observations less → weaker corrections
- **Small R** → DART trusts observations more → stronger corrections
### Results
| R | Prior RMSE | Posterior RMSE |
|---|------------|----------------|
| 8.0 | 0.1780 | 0.1730 | 2.8% |
| 2.0 | 0.0981 | 0.0943 | 3.9% |
| 0.5 | 0.0535 | 0.0504 | 5.8% |
| 0.2 | 0.0339 | 0.0310 | **8.6% ✓ Best** |
### Conclusion
- Smaller observation error variance produced lower RMSE across all cases
- Posterior RMSE was always smaller than prior RMSE — assimilation always helped
- As R decreased from 8.0 to 0.2 the prior RMSE dropped by **81%**
- The filter gave more weight to observations when R was small — pulling
the ensemble closer to truth
---
## Experiment 2 — Observation Frequency
### Purpose
Study how the number of observations affects forecast error growth
and assimilation performance.
### Setup
Two observation network sizes were tested:
| Setup | Observations | Description |
|-------|-------------|-------------|
| Sparse | 50 observations | Few measurements |
| Dense | 200 observations | More frequent updates |
### Key Findings
- **Fewer observations** caused larger forecast errors — less information
to constrain the ensemble
- **More frequent observations** reduced error growth — ensemble tracked
truth more closely
- Frequent updates prevented the chaotic model from drifting too far
from the true state between corrections
---
## Evaluation Metrics
### RMSE (Root Mean Square Error)
Measures the difference between the ensemble mean and the true state.
**Lower RMSE = better assimilation performance.**
```
RMSE = sqrt( mean( (ensemble_mean - truth)^2 ) )
```
Computed separately for:
- **Prior RMSE** — before assimilating observations
- **Posterior RMSE** — after assimilating observations
### Ensemble Spread
Measures the uncertainty in the ensemble forecast — how spread out
the 20 members are around their mean. A well calibrated ensemble has:
```
Spread ≈ RMSE (Spread/RMSE ratio close to 1.0)
```
---
## Overall Conclusions
| Finding | Detail |
|---------|--------|
| Observation error effect | Smaller R → better RMSE. R=0.2 gave 81% lower prior RMSE than R=8.0 |
| Observation frequency effect | More observations → better error control and truth tracking |
| Posterior always better | In all experiments posterior RMSE < prior RMSE — DA always helped |
| Chaotic sensitivity | Lorenz 63's chaos makes observation quality critical — small R matters a lot |
> **Key message:** In chaotic systems like Lorenz 63, the quality and
> frequency of observations strongly controls how well data assimilation
> can track the true state. This motivates careful observation network
> design in real atmospheric DA systems.
---
## Next Steps
- [ ] Test additional R values between 0.2 and 0.5
- [ ] Compare with Lorenz 96 sensitivity results
- [ ] Test impact of ensemble size on sensitivity to R
- [ ] Apply findings to guide observation setup in B-grid experiments
## What is Lorenz 96?
The Lorenz 96 model consists of **40 variables** arranged around a
latitude circle, each representing an atmospheric quantity at one site.
The variables influence each other through a single equation, producing
chaotic (weather-like) behavior when forcing F ≥ 8.
It is used as a **toy atmosphere** — simple enough to run in seconds
but realistic enough to test data assimilation methods.
---
## Governing Equation
```
dx(j)/dt = (x(j+1) - x(j-2)) * x(j-1) - x(j) + F
where j = 1...40 (cyclic: x(41) = x(1))
Terms:
(x(j+1) - x(j-2)) * x(j-1) → advection
- x(j) → dissipation
+ F → external forcing
```
---
## Key Parameters (input.nml)
| Parameter | Default | Namelist | Description |
|-----------|---------|----------|-------------|
| `model_size` | 40 | `&model_nml` | Number of variables |
| `forcing` | 8.0 | `&model_nml` | F ≥ 8 = chaotic behavior |
| `delta_t` | 0.05 | `&model_nml` | Internal RK time step |
| `time_step_seconds` | 3600 | `&model_nml` | 1 hour per DA cycle |
| `ens_size` | 20 | `&filter_nml` | Ensemble members |
| `cutoff` | 0.20 | `&assim_tools_nml` | Localization half-width |
---
## Observation Setup
Used pre-built observation file: `obs_seq_identity_1_20.in`
| Property | Value |
|----------|-------|
| Observed variables | x(1) to x(20) — first 20 of 40 |
| Unobserved variables | x(21) to x(40) — second 20 of 40 |
| Observation error variance | 1.0 for all observations |
| Observations per cycle | 20 |
| Total cycles | 1000 |
| Total observations | 20,000 |
---
## Workflow
### Step 1 — Compile
```bash
module load intel/21
module load openmpi/4.1.6
cd /gpfs/home/ea25e/space/DART/models/lorenz_96/work/
./quickbuild.sh
```
**Note:** Intel/21 compiler required. GNU compiler causes
`'-assume buffered_io'` error. OpenMPI/4.1.6 must be loaded after Intel.
### Step 2 — Set Up Observations
## Pre-built Observation Files
Three observation sequence files are available in the work directory.
Copy your preferred file to `obs_seq.in` before running `perfect_model_obs`:
| File | Observed | Error Variance | Description |
|------|----------|----------------|-------------|
| `obs_seq_identity_1_20.in` | x(1)–x(20) | 1.0 | 20 of 40 variables — used in these experiments |
| `obs_seq_identity_1_40_2.in` | x(1)–x(40) | 2.0 | All 40 variables — moderate noise |
| `obs_seq_identity_1_40_4.in` | x(1)–x(40) | 4.0 | All 40 variables — higher noise |
To switch observation files simply run:
```bash
cp obs_seq_identity_1_40_2.in obs_seq.in
```
Then rerun `perfect_model_obs` and `filter`.
Example;
```bash
cp obs_seq_identity_1_20.in obs_seq.in
```
Observes x(1)–x(20) with observation error variance = 1.0.
### Step 3 — Generate Truth and Observations
```bash
./perfect_model_obs
```
**Produces:**
- `true_state.nc` — true atmosphere (shape: 1000 × 1 × 40)
- `obs_seq.out` — synthetic observations with noise
### Step 4 — Run Data Assimilation
```bash
./filter
```
**Produces:**
- `preassim.nc` — prior ensemble (shape: 1000 × 20 × 40)
- `analysis.nc` — posterior ensemble (shape: 1000 × 20 × 40)
- `obs_seq.final` — observations with DA diagnostics
### Step 5 — Save Results
```bash
mkdir /gpfs/home/ea25e/Trials/L_96/exp1_no_inflation
cp preassim.nc analysis.nc obs_seq.final true_state.nc \
/gpfs/home/ea25e/Trials/L_96/exp1_no_inflation/
```
---
## Experiments
### Experiment 1 — Baseline (No Inflation)
**Settings:** `inf_flavor=0` | `cutoff=0.20` | `ens_size=20`
| Variable Group | Prior RMSE | Post RMSE | Improvement |
|----------------|------------|-----------|-------------|
| All 40 variables | 1.819 | 1.795 | 1.3% |
| Observed x(1)–x(20) | 0.404 | 0.325 | **19.6%** |
| Unobserved x(21)–x(40) | 2.532 | 2.511 | 0.8% |
**Spread/RMSE ratio:** 0.653
---
### Experiment 2 — With Inflation
**Settings:** `inf_flavor=2` | `inf_initial=1.0` | `cutoff=0.20`
Change in `input.nml`:
```fortran
inf_flavor = 2, 0, ! was 0, 0
inf_initial = 1.0, 1.0,
```
| Variable Group | Prior RMSE | Post RMSE |
|----------------|------------|-----------|
| All 40 variables | 1.840 | 1.816 |
| Observed x(1)–x(20) | 0.397 | 0.317 |
| Unobserved x(21)–x(40) | 2.564 | 2.544 |
**Spread/RMSE ratio:** 0.750 — improved but still underdispersive ✗
**Key finding:** Inflation increased spread (1.187 → 1.379) as expected.
Slight increase in overall RMSE is normal — members pushed further from truth.
---
### Experiment 3 — Inflation + Wider Localization
**Settings:** `inf_flavor=2` | `inf_initial=1.0` | `cutoff=0.30`
Change in `input.nml`:
```fortran
cutoff = 0.30 ! was 0.20
```
| Variable Group | Prior RMSE | Post RMSE |
|----------------|------------|-----------|
| All 40 variables | 1.799 | 1.777 |
| Observed x(1)–x(20) | 0.394 | 0.313 |
| Unobserved x(21)–x(40) | 2.507 | 2.488 |
**Key finding:** Wider localization (cutoff=0.30 ≈ 12 variables either side)
allowed observations to reach further into the unobserved region.
---
## Key Findings
1. **Observed variables (x1–x20)** showed 19–21% RMSE improvement
across all experiments — direct observation correction works well
2. **Unobserved variables (x21–x40)** showed only 0.8% improvement
in all cases — the root cause is lack of observations, not settings
3. **Inflation** improved Spread/RMSE ratio from 0.653 → 0.750
— moving toward the target of 1.0
4. **Wider localization** (cutoff=0.30) gave best observed RMSE (0.313)
5. **The dominant problem** is observation coverage — the recommended
next step is observing all 40 variables using `obs_seq_identity_1_40_2.in`
---
## Output File Reference
| File | Shape | Contents |
|------|-------|----------|
| `true_state.nc` | (1000, 1, 40) | Truth trajectory |
| `preassim.nc` | (1000, 20, 40) | Prior ensemble + mean + sd |
| `analysis.nc` | (1000, 20, 40) | Posterior ensemble + mean + sd |
| `obs_seq.out` | — | Synthetic observations |
| `obs_seq.final` | — | Obs + model predictions |
---
## Visualization
Python scripts were developed in Jupyter on FSU RCC to visualize results.
Key plots produced:
- **Time series** — truth vs prior vs posterior for observed and unobserved variables
- **RMSE over time** — prior and posterior RMSE across 1000 cycles
- **Spread vs RMSE** — ensemble calibration diagnostic
- **Split RMSE** — separate panels for all 40, observed, and unobserved variables
- **Snapshot** — state space at final cycle with longitude axis (0°–360°)
---
## Next Steps
- [ ] Observe all 40 variables: `cp obs_seq_identity_1_40_2.in obs_seq.in`
- [ ] Test stronger inflation: `inf_initial = 1.05`
- [ ] Ensemble size sensitivity: `ens_size = 10, 20, 40, 80`
- [ ] Compare with Lorenz 63 results
# B-grid Solo Model
**Model directory:** `DART/models/bgrid_solo/work/`
**Date:** May 2026
---
## What is B-grid Solo?
The B-grid Solo model is a **simplified 3D atmospheric model** based on
GFDL's B-grid dynamical core. It is the next step up in complexity from
Lorenz 96 — bridging the gap between toy models and full weather models.
| Feature | Lorenz 96 | B-grid Solo |
|---------|-----------|-------------|
| Dimensions | 1D circle | 3D atmosphere |
| Variables | 40 (one type) | 28,200 (four types) |
| Variable types | state only | Ps, T, u, v |
| Obs types | RAW_STATE | Radiosonde |
| Run time | Seconds | Minutes |
---
## Model Grid
```
Grid structure: 60 lon × 30 lat × 5 lev
↑ ↑ ↑
longitude latitude vertical
Total variables: ~60 × 30 × 5 × 4 = 36,000
Actual model size: 28,200 (winds on VelJ=29 grid)
```
### Why VelJ = 29 (not 30)?
The B-grid uses **Arakawa B-grid staggering** — temperature and pressure
(T, Ps) sit at grid cell centers, while winds (u, v) sit at cell corners.
Corners of a 30-point latitude grid give 29 interior boundaries.
---
## State Variables
| Symbol | Name | Units | Dimensions | Notes |
|--------|------|-------|------------|-------|
| Ps | Surface Pressure | Pa | 2D (lat × lon) | Ground level only — no vertical dim |
| T | Temperature | K | 3D (lev × lat × lon) | Different at each level |
| u | Zonal Wind | m/s | 3D (lev × VelJ × lon) | East-west component |
| v | Meridional Wind | m/s | 3D (lev × VelJ × lon) | North-south component |
### Why is Ps 2D?
Surface pressure is the **total weight of the atmosphere** pressing down
on one point at the ground. It is measured at the surface only — there is
no such thing as "surface pressure at level 3." Hence no vertical dimension.
---
## Held-Suarez Forcing
The model uses **Held-Suarez forcing** — a standard benchmark for
atmospheric model testing (Held & Suarez, 1994).
- Maintains temperature gradient between tropics and poles
- Relaxes atmosphere toward an equilibrium temperature profile
- Creates realistic jet streams and weather-like patterns
- No moisture, clouds, or ocean — simplified but physically meaningful
Key parameters in `input.nml`:
```fortran
&hs_forcing_nml
delh = 60. ! temperature gradient pole-to-equator
t_zero = 315. ! reference temperature (K)
ka = -40. ! atmospheric relaxation timescale
/
```
---
## Key Parameters (input.nml)
| Parameter | Value | Namelist | Description |
|-----------|-------|----------|-------------|
| `nlon` | 60 | `&bgrid_cold_start_nml` | Longitude points |
| `nlat` | 30 | `&bgrid_cold_start_nml` | Latitude points |
| `nlev` | 5 | `&bgrid_cold_start_nml` | Vertical levels |
| `dt_atmos` | 3600 | `&model_nml` | 1 hour time step |
| `days` | 10 | `&model_nml` | Intended run length |
| `ens_size` | 20 | `&filter_nml` | Ensemble members |
| `inf_flavor` | 0 | `&filter_nml` | No inflation (baseline) |
| `cutoff` | 0.20 | `&assim_tools_nml` | ~1145 km localization radius |
| `perturb_from_single_instance` | .true. | `&filter_nml` | Create ensemble by perturbing |
| `perturbation_amplitude` | 0.2 | `&filter_nml` | Ensemble perturbation size |
---
## Observations
The pre-built `obs_seq.in` file (1.54 MB) contains a real
**radiosonde observation network**.
| Observation Type | Variable | Description |
|-----------------|----------|-------------|
| `RADIOSONDE_TEMPERATURE` | T | Temperature from balloon soundings (K) |
| `RADIOSONDE_U_WIND_COMPONENT` | u | Zonal wind from balloons (m/s) |
| `RADIOSONDE_V_WIND_COMPONENT` | v | Meridional wind from balloons (m/s) |
| `RADIOSONDE_SURFACE_PRESSURE` | Ps | Surface pressure (Pa) |
**Statistics:**
- Total observations: 9,600
- Assimilation cycles completed: 2
- Observations per cycle: 4,800
- QC threshold: 3.0
**Key difference from Lorenz 96:** B-grid uses real radiosonde observation
types at 3D locations (lat, lon, pressure level) vs synthetic identity
observations at 1D locations in Lorenz 96.
---
## Workflow
### Step 1 — Compile
```bash
module load intel/21
module load openmpi/4.1.6
cd /gpfs/home/ea25e/space/DART/models/bgrid_solo/work/
./quickbuild.sh
```
Compiles 15 DART executables (more than Lorenz 96's 11).
### Step 2 — Create NetCDF Input File
```bash
ncgen -o perfect_input.nc perfect_input.cdl
```
Converts the CDL text description to NetCDF binary format.
`perfect_input.cdl` ships with DART and contains a valid B-grid
atmospheric state to use as the starting point.
### Step 3 — Generate Truth and Observations
```bash
./perfect_model_obs
```
**Output:**
```
Model size = 28,200
Total obs in sequence = 9,600
2 assimilation windows completed
```
**Produces:**
- `true_state.nc` — truth (shape: 2 × 1 × 30 × 60 for Ps)
- `obs_seq.out` — synthetic observations with noise
### Step 4 — Create Ensemble Starting Point ⚠️ B-grid specific
```bash
cp perfect_input.nc filter_input.nc
```
**This step is unique to B-grid — not needed in Lorenz 96.**
The filter needs `filter_input.nc` to create its initial ensemble.
Because `perturb_from_single_instance = .true.`, it reads this single
state and creates 20 members by adding random noise (amplitude = 0.2).
### Step 5 — Run Data Assimilation
```bash
./filter
```
Only 2 cycles completed — `obs_seq.in` contained observations for
2 assimilation windows only. The `days = 10` setting in the namelist
was overridden by the available observations.
**Produces:**
- `preassim.nc` — prior ensemble (shape: 2 × 20 × 30 × 60 for Ps)
- `analysis.nc` — posterior ensemble
- `obs_seq.final` — observations with DA diagnostics
- `filter_output.nc` — final ensemble state
---
## Why Only 2 Cycles?
The 2 cycles and 5 levels are **completely independent**:
- **2 cycles** = how many times the forecast → observe → correct loop ran in **time**
- **5 levels** = vertical structure of the model at **each moment in time**
Every cycle, all 5 levels were assimilated simultaneously. The 2 just
means it happened twice. To run more cycles, a new `obs_seq.in` file
with more observation times is needed.
---
## Experiment 1 — Default Run Results
**Settings:** `inf_flavor=0` | `cutoff=0.20` | `ens_size=20` | 2 cycles
| Variable | Max Error | Units | Assessment |
|----------|-----------|-------|------------|
| Surface Pressure (Ps) | 1.298 | Pa | ~0.001% of total — excellent |
| Temperature (T) Level 3 | 0.007 | K | 7 thousandths of a degree — excellent |
| U Wind Level 3 | 0.003 | m/s | ~0.01% of typical winds — excellent |
| V Wind Level 3 | 0.003 | m/s | ~0.01% of typical winds — excellent |
> **Note:** Errors appear very small because only 2 cycles ran.
> The ensemble started close to truth (copied from `perfect_input.nc`)
> and had minimal time to diverge. Running 100+ cycles would produce
> more realistic error statistics.
### Error Map Interpretation
- **Red patches** = posterior overestimates truth
- **Blue patches** = posterior underestimates truth
- **White** = near zero error (ideal)
- **Scattered pattern** = random residual errors, no systematic geographic bias
- **Vertical striping in U wind** = B-grid grid structure signature
---
## Output File Reference
| File | Variable shapes | Contents |
|------|----------------|----------|
| `true_state.nc` | ps:(2,1,30,60) t:(2,1,5,30,60) | Truth trajectory |
| `preassim.nc` | ps:(2,20,30,60) t:(2,20,5,30,60) | Prior ensemble + mean + sd |
| `analysis.nc` | ps:(2,20,30,60) t:(2,20,5,30,60) | Posterior ensemble + mean + sd |
| `filter_output.nc` | ps:(1,20,30,60) | Final ensemble state |
| `perfect_output.nc` | ps:(1,1,30,60) | Final truth state |
---
## Key Findings
1. **B-grid successfully compiled** on FSU RCC with Intel/21 + OpenMPI/4.1.6
2. **Extra step required:** `cp perfect_input.nc filter_input.nc` — unique to B-grid
3. **Only 2 cycles ran** — limited by observations in `obs_seq.in`
4. **All 5 vertical levels assimilated** in both cycles — levels and cycles are independent
5. **Surface pressure is 2D** — exists only at ground level, no vertical dimension
6. **Errors very small** at 2 cycles — ensemble had not diverged from truth yet
7. **RMSE near zero at time step 0** — ensemble identical to truth at start (both from same file)
---
## Next Steps
- [ ] Run more cycles — generate new `obs_seq.in` with longer time coverage
- [ ] Experiment 2: Add inflation (`inf_flavor=2`, `inf_initial=1.0`)
- [ ] Experiment 3: Observation impact — remove wind obs or surface pressure
- [ ] Experiment 4: Localization sensitivity (`cutoff=0.10` vs `0.20` vs `0.40`)
- [ ] Experiment 5: Ensemble size (`ens_size=10, 20, 40, 80`)
---
## References
- Held, I. M., and M. J. Suarez, 1994: A proposal for the intercomparison
of the dynamical cores of atmospheric general circulation models.
*Bulletin of the American Meteorological Society*, 75(10), 1825–1830.
- Anderson, J. L., and Coauthors, 2009: The Data Assimilation Research
Testbed: A community facility. *Bulletin of the American Meteorological
Society*, 90(9), 1283–1296.