Actual source code: ex1.c
1: static char help[] = "Tests basic creation and destruction of PetscDA objects, and a simple LETKF (NONE-localization) analysis step.\n\n";
2: #include <petscda.h>
4: int main(int argc, char **argv)
5: {
6: PetscDA da;
7: Mat H;
8: Vec x_true, y_obs, obs_error_var;
9: Vec x_mean_forecast, x_mean_analysis;
10: PetscInt state_size = 10, obs_size = 10, ensemble_size = 20;
11: PetscRandom rng;
12: PetscReal norm;
14: PetscFunctionBeginUser;
15: PetscCall(PetscInitialize(&argc, &argv, (char *)0, help));
17: /* Create the DA object */
18: PetscCall(PetscDACreate(PETSC_COMM_WORLD, &da));
19: PetscCall(PetscDALETKFSetLocalizationType(da, PETSCDA_LETKF_LOC_NONE));
20: PetscCall(PetscDASetSizes(da, state_size, obs_size));
21: PetscCall(PetscDAEnsembleSetSize(da, ensemble_size));
22: PetscCall(PetscDASetFromOptions(da));
23: PetscCall(PetscDASetUp(da));
25: /* Initialize random number generator */
26: PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &rng));
27: PetscCall(PetscRandomSetFromOptions(rng));
29: /* Create identity observation matrix H (obs_size x state_size) */
30: PetscCall(MatCreateAIJ(PETSC_COMM_WORLD, PETSC_DECIDE, PETSC_DECIDE, obs_size, state_size, 1, NULL, 0, NULL, &H));
31: PetscCall(MatSetFromOptions(H));
32: PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY));
33: PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY));
34: PetscCall(MatShift(H, 1.0));
36: /* Create vectors using MatCreateVecs from H */
37: PetscCall(MatCreateVecs(H, &x_true, &y_obs));
38: PetscCall(VecSet(x_true, 1.0)); /* True state is all 1s */
40: PetscCall(VecDuplicate(y_obs, &obs_error_var));
41: PetscCall(VecSet(obs_error_var, 0.1)); /* Observation error variance */
42: PetscCall(PetscDASetObsErrorVariance(da, obs_error_var));
44: /* Create synthetic observation: y = x_true (identity observation, no noise for this simple test) */
45: PetscCall(VecCopy(x_true, y_obs));
47: /* Initialize ensemble with some spread around 0 (far from truth 1.0) */
48: for (PetscInt i = 0; i < ensemble_size; i++) {
49: Vec member;
50: PetscCall(VecDuplicate(x_true, &member));
51: PetscCall(VecSetRandom(member, rng)); /* Uniform random [0, 1] */
52: PetscCall(PetscDAEnsembleSetMember(da, i, member));
53: PetscCall(VecDestroy(&member));
54: }
56: /* Compute forecast mean before analysis */
57: PetscCall(VecDuplicate(x_true, &x_mean_forecast));
58: PetscCall(PetscDAEnsembleComputeMean(da, x_mean_forecast));
60: /* Check forecast error */
61: PetscCall(VecAXPY(x_mean_forecast, -1.0, x_true));
62: PetscCall(VecNorm(x_mean_forecast, NORM_2, &norm));
63: PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Forecast error norm: %g\n", (double)norm));
65: /* Perform Analysis Step */
66: PetscCall(PetscDAEnsembleAnalysis(da, y_obs, H));
68: /* Compute analysis mean */
69: PetscCall(VecDuplicate(x_true, &x_mean_analysis));
70: PetscCall(PetscDAEnsembleComputeMean(da, x_mean_analysis));
72: /* Check analysis error */
73: PetscCall(VecAXPY(x_mean_analysis, -1.0, x_true));
74: PetscCall(VecNorm(x_mean_analysis, NORM_2, &norm));
75: PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Analysis error norm: %g\n", (double)norm));
77: /* The analysis should move the ensemble closer to the observation (truth) */
78: /* Since observation error is small (0.1) and prior spread is ~0.08, it should pull towards observation */
80: PetscCall(PetscDAView(da, PETSC_VIEWER_STDOUT_WORLD));
82: /* Cleanup */
83: PetscCall(MatDestroy(&H));
84: PetscCall(VecDestroy(&x_true));
85: PetscCall(VecDestroy(&y_obs));
86: PetscCall(VecDestroy(&obs_error_var));
87: PetscCall(VecDestroy(&x_mean_forecast));
88: PetscCall(VecDestroy(&x_mean_analysis));
89: PetscCall(PetscRandomDestroy(&rng));
90: PetscCall(PetscDADestroy(&da));
92: PetscCall(PetscFinalize());
93: return 0;
94: }
96: /*TEST
98: test:
99: suffix: 1
100: requires: !complex
102: TEST*/