omgkit 2.12.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -12
- package/package.json +2 -2
- package/plugin/agents/api-designer.md +5 -0
- package/plugin/agents/architect.md +8 -0
- package/plugin/agents/brainstormer.md +4 -0
- package/plugin/agents/cicd-manager.md +6 -0
- package/plugin/agents/code-reviewer.md +6 -0
- package/plugin/agents/copywriter.md +2 -0
- package/plugin/agents/data-engineer.md +255 -0
- package/plugin/agents/database-admin.md +10 -0
- package/plugin/agents/debugger.md +10 -0
- package/plugin/agents/devsecops.md +314 -0
- package/plugin/agents/docs-manager.md +4 -0
- package/plugin/agents/domain-decomposer.md +181 -0
- package/plugin/agents/embedded-systems.md +397 -0
- package/plugin/agents/fullstack-developer.md +12 -0
- package/plugin/agents/game-systems-designer.md +375 -0
- package/plugin/agents/git-manager.md +10 -0
- package/plugin/agents/journal-writer.md +2 -0
- package/plugin/agents/ml-engineer.md +284 -0
- package/plugin/agents/observability-engineer.md +353 -0
- package/plugin/agents/oracle.md +9 -0
- package/plugin/agents/performance-engineer.md +290 -0
- package/plugin/agents/pipeline-architect.md +6 -0
- package/plugin/agents/planner.md +12 -0
- package/plugin/agents/platform-engineer.md +325 -0
- package/plugin/agents/project-manager.md +3 -0
- package/plugin/agents/researcher.md +5 -0
- package/plugin/agents/scientific-computing.md +426 -0
- package/plugin/agents/scout.md +3 -0
- package/plugin/agents/security-auditor.md +7 -0
- package/plugin/agents/sprint-master.md +17 -0
- package/plugin/agents/tester.md +10 -0
- package/plugin/agents/ui-ux-designer.md +12 -0
- package/plugin/agents/vulnerability-scanner.md +6 -0
- package/plugin/commands/data/pipeline.md +47 -0
- package/plugin/commands/data/quality.md +49 -0
- package/plugin/commands/domain/analyze.md +34 -0
- package/plugin/commands/domain/map.md +41 -0
- package/plugin/commands/game/balance.md +56 -0
- package/plugin/commands/game/optimize.md +62 -0
- package/plugin/commands/iot/provision.md +58 -0
- package/plugin/commands/ml/evaluate.md +47 -0
- package/plugin/commands/ml/train.md +48 -0
- package/plugin/commands/perf/benchmark.md +54 -0
- package/plugin/commands/perf/profile.md +49 -0
- package/plugin/commands/platform/blueprint.md +56 -0
- package/plugin/commands/security/audit.md +54 -0
- package/plugin/commands/security/scan.md +55 -0
- package/plugin/commands/sre/dashboard.md +53 -0
- package/plugin/registry.yaml +711 -0
- package/plugin/skills/ai-ml/experiment-tracking/SKILL.md +338 -0
- package/plugin/skills/ai-ml/feature-stores/SKILL.md +340 -0
- package/plugin/skills/ai-ml/llm-ops/SKILL.md +454 -0
- package/plugin/skills/ai-ml/ml-pipelines/SKILL.md +390 -0
- package/plugin/skills/ai-ml/model-monitoring/SKILL.md +398 -0
- package/plugin/skills/ai-ml/model-serving/SKILL.md +386 -0
- package/plugin/skills/event-driven/cqrs-patterns/SKILL.md +348 -0
- package/plugin/skills/event-driven/event-sourcing/SKILL.md +334 -0
- package/plugin/skills/event-driven/kafka-deep/SKILL.md +252 -0
- package/plugin/skills/event-driven/saga-orchestration/SKILL.md +335 -0
- package/plugin/skills/event-driven/schema-registry/SKILL.md +328 -0
- package/plugin/skills/event-driven/stream-processing/SKILL.md +313 -0
- package/plugin/skills/game/game-audio/SKILL.md +446 -0
- package/plugin/skills/game/game-networking/SKILL.md +490 -0
- package/plugin/skills/game/godot-patterns/SKILL.md +413 -0
- package/plugin/skills/game/shader-programming/SKILL.md +492 -0
- package/plugin/skills/game/unity-patterns/SKILL.md +488 -0
- package/plugin/skills/iot/device-provisioning/SKILL.md +405 -0
- package/plugin/skills/iot/edge-computing/SKILL.md +369 -0
- package/plugin/skills/iot/industrial-protocols/SKILL.md +438 -0
- package/plugin/skills/iot/mqtt-deep/SKILL.md +418 -0
- package/plugin/skills/iot/ota-updates/SKILL.md +426 -0
- package/plugin/skills/microservices/api-gateway-patterns/SKILL.md +201 -0
- package/plugin/skills/microservices/circuit-breaker-patterns/SKILL.md +246 -0
- package/plugin/skills/microservices/contract-testing/SKILL.md +284 -0
- package/plugin/skills/microservices/distributed-tracing/SKILL.md +246 -0
- package/plugin/skills/microservices/service-discovery/SKILL.md +304 -0
- package/plugin/skills/microservices/service-mesh/SKILL.md +181 -0
- package/plugin/skills/mobile-advanced/mobile-ci-cd/SKILL.md +407 -0
- package/plugin/skills/mobile-advanced/mobile-security/SKILL.md +403 -0
- package/plugin/skills/mobile-advanced/offline-first/SKILL.md +473 -0
- package/plugin/skills/mobile-advanced/push-notifications/SKILL.md +494 -0
- package/plugin/skills/mobile-advanced/react-native-deep/SKILL.md +374 -0
- package/plugin/skills/simulation/numerical-methods/SKILL.md +434 -0
- package/plugin/skills/simulation/parallel-computing/SKILL.md +382 -0
- package/plugin/skills/simulation/physics-engines/SKILL.md +377 -0
- package/plugin/skills/simulation/validation-verification/SKILL.md +479 -0
- package/plugin/skills/simulation/visualization-scientific/SKILL.md +365 -0
- package/plugin/templates/autonomous/archetypes/event-driven-app.yaml +460 -0
- package/plugin/templates/autonomous/archetypes/microservices-app.yaml +431 -0
- package/plugin/templates/autonomous/state-schema.yaml +1 -1
- package/plugin/workflows/ai-engineering/agent-development.md +3 -3
- package/plugin/workflows/ai-engineering/fine-tuning.md +3 -3
- package/plugin/workflows/ai-engineering/model-evaluation.md +3 -3
- package/plugin/workflows/ai-engineering/prompt-engineering.md +2 -2
- package/plugin/workflows/ai-engineering/rag-development.md +4 -4
- package/plugin/workflows/ai-ml/data-pipeline.md +188 -0
- package/plugin/workflows/ai-ml/experiment-cycle.md +203 -0
- package/plugin/workflows/ai-ml/feature-engineering.md +208 -0
- package/plugin/workflows/ai-ml/model-deployment.md +199 -0
- package/plugin/workflows/ai-ml/monitoring-setup.md +227 -0
- package/plugin/workflows/api/api-design.md +1 -1
- package/plugin/workflows/api/api-testing.md +2 -2
- package/plugin/workflows/content/technical-docs.md +1 -1
- package/plugin/workflows/database/migration.md +1 -1
- package/plugin/workflows/database/optimization.md +1 -1
- package/plugin/workflows/database/schema-design.md +3 -3
- package/plugin/workflows/development/bug-fix.md +3 -3
- package/plugin/workflows/development/code-review.md +2 -1
- package/plugin/workflows/development/feature.md +3 -3
- package/plugin/workflows/development/refactor.md +2 -2
- package/plugin/workflows/event-driven/consumer-groups.md +190 -0
- package/plugin/workflows/event-driven/event-storming.md +172 -0
- package/plugin/workflows/event-driven/replay-testing.md +186 -0
- package/plugin/workflows/event-driven/saga-implementation.md +206 -0
- package/plugin/workflows/event-driven/schema-evolution.md +173 -0
- package/plugin/workflows/fullstack/authentication.md +4 -4
- package/plugin/workflows/fullstack/full-feature.md +4 -4
- package/plugin/workflows/game-dev/content-pipeline.md +218 -0
- package/plugin/workflows/game-dev/platform-submission.md +263 -0
- package/plugin/workflows/game-dev/playtesting.md +237 -0
- package/plugin/workflows/game-dev/prototype-to-production.md +205 -0
- package/plugin/workflows/microservices/contract-first.md +151 -0
- package/plugin/workflows/microservices/distributed-tracing.md +166 -0
- package/plugin/workflows/microservices/domain-decomposition.md +123 -0
- package/plugin/workflows/microservices/integration-testing.md +149 -0
- package/plugin/workflows/microservices/service-mesh-setup.md +153 -0
- package/plugin/workflows/microservices/service-scaffolding.md +151 -0
- package/plugin/workflows/omega/1000x-innovation.md +2 -2
- package/plugin/workflows/omega/100x-architecture.md +2 -2
- package/plugin/workflows/omega/10x-improvement.md +2 -2
- package/plugin/workflows/quality/performance-optimization.md +2 -2
- package/plugin/workflows/research/best-practices.md +1 -1
- package/plugin/workflows/research/technology-research.md +1 -1
- package/plugin/workflows/security/penetration-testing.md +3 -3
- package/plugin/workflows/security/security-audit.md +3 -3
- package/plugin/workflows/sprint/sprint-execution.md +2 -2
- package/plugin/workflows/sprint/sprint-retrospective.md +1 -1
- package/plugin/workflows/sprint/sprint-setup.md +1 -1
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
# Validation and Verification
|
|
2
|
+
|
|
3
|
+
V&V practices for scientific simulations including code verification, solution validation, uncertainty quantification, and benchmarking.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Validation and Verification (V&V) ensures simulation codes are correctly implemented and accurately represent physical reality.
|
|
8
|
+
|
|
9
|
+
## Core Concepts
|
|
10
|
+
|
|
11
|
+
### Definitions
|
|
12
|
+
- **Verification**: "Solving the equations right" (math correctness)
|
|
13
|
+
- **Validation**: "Solving the right equations" (physical accuracy)
|
|
14
|
+
- **Uncertainty Quantification (UQ)**: Quantifying prediction uncertainty
|
|
15
|
+
|
|
16
|
+
### V&V Hierarchy
|
|
17
|
+
```
|
|
18
|
+
Code Verification → Solution Verification → Model Validation
|
|
19
|
+
↓ ↓ ↓
|
|
20
|
+
Bug-free code Numerical accuracy Physical fidelity
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Code Verification
|
|
24
|
+
|
|
25
|
+
### Unit Testing for Numerical Code
|
|
26
|
+
```python
|
|
27
|
+
import numpy as np
|
|
28
|
+
import pytest
|
|
29
|
+
from typing import Callable
|
|
30
|
+
|
|
31
|
+
class TestNumericalMethods:
|
|
32
|
+
"""Unit tests for numerical methods"""
|
|
33
|
+
|
|
34
|
+
def test_derivative_accuracy(self):
|
|
35
|
+
"""Test derivative approximation order"""
|
|
36
|
+
f = lambda x: np.sin(x)
|
|
37
|
+
df_exact = lambda x: np.cos(x)
|
|
38
|
+
x0 = 1.0
|
|
39
|
+
|
|
40
|
+
def forward_diff(f, x, h):
|
|
41
|
+
return (f(x + h) - f(x)) / h
|
|
42
|
+
|
|
43
|
+
def central_diff(f, x, h):
|
|
44
|
+
return (f(x + h) - f(x - h)) / (2 * h)
|
|
45
|
+
|
|
46
|
+
h_values = [0.1, 0.01, 0.001, 0.0001]
|
|
47
|
+
|
|
48
|
+
for h in h_values:
|
|
49
|
+
forward_error = abs(forward_diff(f, x0, h) - df_exact(x0))
|
|
50
|
+
central_error = abs(central_diff(f, x0, h) - df_exact(x0))
|
|
51
|
+
|
|
52
|
+
# Forward diff should be O(h)
|
|
53
|
+
# Central diff should be O(h^2)
|
|
54
|
+
if h == 0.01:
|
|
55
|
+
assert forward_error < 0.01 # O(h)
|
|
56
|
+
assert central_error < 0.0001 # O(h^2)
|
|
57
|
+
|
|
58
|
+
def test_convergence_order(self):
|
|
59
|
+
"""Verify expected convergence order"""
|
|
60
|
+
errors = []
|
|
61
|
+
h_values = []
|
|
62
|
+
|
|
63
|
+
for n in [10, 20, 40, 80, 160]:
|
|
64
|
+
h = 1.0 / n
|
|
65
|
+
h_values.append(h)
|
|
66
|
+
error = compute_error(n) # Your method
|
|
67
|
+
errors.append(error)
|
|
68
|
+
|
|
69
|
+
# Compute convergence order
|
|
70
|
+
orders = []
|
|
71
|
+
for i in range(len(errors) - 1):
|
|
72
|
+
order = np.log(errors[i] / errors[i+1]) / np.log(h_values[i] / h_values[i+1])
|
|
73
|
+
orders.append(order)
|
|
74
|
+
|
|
75
|
+
# Should converge to expected order (e.g., 2 for second-order method)
|
|
76
|
+
expected_order = 2.0
|
|
77
|
+
assert abs(orders[-1] - expected_order) < 0.1
|
|
78
|
+
|
|
79
|
+
def test_conservation_properties(self):
|
|
80
|
+
"""Test conservation of invariants"""
|
|
81
|
+
# Example: Energy conservation in Hamiltonian system
|
|
82
|
+
initial_energy = compute_energy(initial_state)
|
|
83
|
+
|
|
84
|
+
# Run simulation
|
|
85
|
+
for step in range(1000):
|
|
86
|
+
state = integrate_step(state)
|
|
87
|
+
current_energy = compute_energy(state)
|
|
88
|
+
|
|
89
|
+
# Energy should be conserved (within tolerance)
|
|
90
|
+
assert abs(current_energy - initial_energy) / initial_energy < 1e-6
|
|
91
|
+
|
|
92
|
+
def test_symmetry_preservation(self):
|
|
93
|
+
"""Test that symmetric problems yield symmetric solutions"""
|
|
94
|
+
# Create symmetric initial condition
|
|
95
|
+
n = 100
|
|
96
|
+
x = np.linspace(-1, 1, n)
|
|
97
|
+
u0 = np.exp(-x**2) # Symmetric about x=0
|
|
98
|
+
|
|
99
|
+
# Solve
|
|
100
|
+
u_final = solve(u0)
|
|
101
|
+
|
|
102
|
+
# Check symmetry
|
|
103
|
+
assert np.allclose(u_final, u_final[::-1], rtol=1e-10)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Method of Manufactured Solutions (MMS)
|
|
107
|
+
```python
|
|
108
|
+
import sympy as sp
|
|
109
|
+
import numpy as np
|
|
110
|
+
|
|
111
|
+
class ManufacturedSolution:
|
|
112
|
+
"""
|
|
113
|
+
Method of Manufactured Solutions for code verification
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def __init__(self):
|
|
117
|
+
# Define symbolic variables
|
|
118
|
+
self.x, self.y, self.t = sp.symbols('x y t')
|
|
119
|
+
self.alpha = sp.Symbol('alpha') # Diffusion coefficient
|
|
120
|
+
|
|
121
|
+
def verify_diffusion_equation(self):
|
|
122
|
+
"""
|
|
123
|
+
Verify diffusion equation solver: ∂u/∂t = α∇²u + f
|
|
124
|
+
"""
|
|
125
|
+
# Choose manufactured solution
|
|
126
|
+
u_mms = sp.sin(sp.pi * self.x) * sp.sin(sp.pi * self.y) * sp.exp(-self.t)
|
|
127
|
+
|
|
128
|
+
# Compute required source term
|
|
129
|
+
du_dt = sp.diff(u_mms, self.t)
|
|
130
|
+
laplacian_u = sp.diff(u_mms, self.x, 2) + sp.diff(u_mms, self.y, 2)
|
|
131
|
+
|
|
132
|
+
# Source term: f = ∂u/∂t - α∇²u
|
|
133
|
+
f = du_dt - self.alpha * laplacian_u
|
|
134
|
+
f = sp.simplify(f)
|
|
135
|
+
|
|
136
|
+
# Convert to numerical functions
|
|
137
|
+
u_exact = sp.lambdify((self.x, self.y, self.t), u_mms, 'numpy')
|
|
138
|
+
source = sp.lambdify((self.x, self.y, self.t, self.alpha), f, 'numpy')
|
|
139
|
+
|
|
140
|
+
return u_exact, source
|
|
141
|
+
|
|
142
|
+
def run_convergence_study(self, solver, u_exact, source, alpha=1.0):
|
|
143
|
+
"""Run grid convergence study"""
|
|
144
|
+
results = []
|
|
145
|
+
|
|
146
|
+
for nx in [16, 32, 64, 128, 256]:
|
|
147
|
+
# Set up grid
|
|
148
|
+
x = np.linspace(0, 1, nx)
|
|
149
|
+
y = np.linspace(0, 1, nx)
|
|
150
|
+
dx = x[1] - x[0]
|
|
151
|
+
|
|
152
|
+
# Get manufactured source
|
|
153
|
+
X, Y = np.meshgrid(x, y)
|
|
154
|
+
t_final = 0.1
|
|
155
|
+
f = source(X, Y, t_final, alpha)
|
|
156
|
+
|
|
157
|
+
# Solve with manufactured source
|
|
158
|
+
u_computed = solver(nx, alpha, f, t_final)
|
|
159
|
+
|
|
160
|
+
# Compute error
|
|
161
|
+
u_true = u_exact(X, Y, t_final)
|
|
162
|
+
l2_error = np.sqrt(np.mean((u_computed - u_true)**2))
|
|
163
|
+
linf_error = np.max(np.abs(u_computed - u_true))
|
|
164
|
+
|
|
165
|
+
results.append({
|
|
166
|
+
'nx': nx,
|
|
167
|
+
'dx': dx,
|
|
168
|
+
'l2_error': l2_error,
|
|
169
|
+
'linf_error': linf_error
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
# Compute convergence order
|
|
173
|
+
for i in range(len(results) - 1):
|
|
174
|
+
order = np.log(results[i]['l2_error'] / results[i+1]['l2_error']) / np.log(2)
|
|
175
|
+
results[i+1]['order'] = order
|
|
176
|
+
|
|
177
|
+
return results
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Solution Verification
|
|
181
|
+
|
|
182
|
+
### Grid Convergence Index (GCI)
|
|
183
|
+
```python
|
|
184
|
+
class GridConvergenceIndex:
|
|
185
|
+
"""
|
|
186
|
+
Roache's Grid Convergence Index for solution verification
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
def __init__(self, refinement_ratio: float = 2.0):
|
|
190
|
+
self.r = refinement_ratio
|
|
191
|
+
self.Fs = 1.25 # Safety factor (1.25 for 3+ grids)
|
|
192
|
+
|
|
193
|
+
def compute_gci(
|
|
194
|
+
self,
|
|
195
|
+
f1: float, # Fine grid solution
|
|
196
|
+
f2: float, # Medium grid solution
|
|
197
|
+
f3: float, # Coarse grid solution
|
|
198
|
+
p_formal: float = None # Formal order of accuracy
|
|
199
|
+
) -> dict:
|
|
200
|
+
"""
|
|
201
|
+
Compute GCI for three grid solutions
|
|
202
|
+
|
|
203
|
+
Returns Richardson extrapolation and GCI estimates
|
|
204
|
+
"""
|
|
205
|
+
# Compute observed order of convergence
|
|
206
|
+
epsilon_32 = f3 - f2
|
|
207
|
+
epsilon_21 = f2 - f1
|
|
208
|
+
|
|
209
|
+
if epsilon_32 * epsilon_21 < 0:
|
|
210
|
+
# Oscillatory convergence
|
|
211
|
+
return {'error': 'Oscillatory convergence detected'}
|
|
212
|
+
|
|
213
|
+
# Observed order
|
|
214
|
+
p = np.log(epsilon_32 / epsilon_21) / np.log(self.r)
|
|
215
|
+
|
|
216
|
+
# Richardson extrapolation
|
|
217
|
+
f_exact = f1 + (f1 - f2) / (self.r**p - 1)
|
|
218
|
+
|
|
219
|
+
# GCI
|
|
220
|
+
gci_fine = self.Fs * abs((f1 - f2) / f1) / (self.r**p - 1) * 100
|
|
221
|
+
gci_medium = self.Fs * abs((f2 - f3) / f2) / (self.r**p - 1) * 100
|
|
222
|
+
|
|
223
|
+
# Asymptotic range check
|
|
224
|
+
asymptotic_ratio = gci_medium / (self.r**p * gci_fine)
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
'observed_order': p,
|
|
228
|
+
'richardson_extrapolation': f_exact,
|
|
229
|
+
'gci_fine': gci_fine,
|
|
230
|
+
'gci_medium': gci_medium,
|
|
231
|
+
'asymptotic_ratio': asymptotic_ratio,
|
|
232
|
+
'in_asymptotic_range': abs(asymptotic_ratio - 1.0) < 0.1
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
def compute_with_uncertainty(self, solutions: list, grid_sizes: list) -> dict:
|
|
236
|
+
"""
|
|
237
|
+
Comprehensive grid convergence with uncertainty estimates
|
|
238
|
+
"""
|
|
239
|
+
# Ensure at least 3 solutions
|
|
240
|
+
assert len(solutions) >= 3
|
|
241
|
+
|
|
242
|
+
# Use finest three grids
|
|
243
|
+
f1, f2, f3 = solutions[0], solutions[1], solutions[2]
|
|
244
|
+
gci_result = self.compute_gci(f1, f2, f3)
|
|
245
|
+
|
|
246
|
+
# Uncertainty estimate
|
|
247
|
+
numerical_uncertainty = gci_result['gci_fine'] / 100 * f1
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
**gci_result,
|
|
251
|
+
'best_estimate': gci_result['richardson_extrapolation'],
|
|
252
|
+
'numerical_uncertainty': numerical_uncertainty,
|
|
253
|
+
'confidence_interval': (
|
|
254
|
+
gci_result['richardson_extrapolation'] - numerical_uncertainty,
|
|
255
|
+
gci_result['richardson_extrapolation'] + numerical_uncertainty
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Model Validation
|
|
261
|
+
|
|
262
|
+
### Comparison with Experiments
|
|
263
|
+
```python
|
|
264
|
+
import numpy as np
|
|
265
|
+
from scipy import stats
|
|
266
|
+
from dataclasses import dataclass
|
|
267
|
+
from typing import List
|
|
268
|
+
|
|
269
|
+
@dataclass
|
|
270
|
+
class ValidationMetrics:
|
|
271
|
+
"""Metrics for model validation"""
|
|
272
|
+
rmse: float
|
|
273
|
+
bias: float
|
|
274
|
+
correlation: float
|
|
275
|
+
skill_score: float
|
|
276
|
+
within_uncertainty: float # Fraction within experimental uncertainty
|
|
277
|
+
|
|
278
|
+
def compute_validation_metrics(
|
|
279
|
+
simulation: np.ndarray,
|
|
280
|
+
experiment: np.ndarray,
|
|
281
|
+
exp_uncertainty: np.ndarray = None
|
|
282
|
+
) -> ValidationMetrics:
|
|
283
|
+
"""
|
|
284
|
+
Compute comprehensive validation metrics
|
|
285
|
+
"""
|
|
286
|
+
residuals = simulation - experiment
|
|
287
|
+
|
|
288
|
+
# Root Mean Square Error
|
|
289
|
+
rmse = np.sqrt(np.mean(residuals**2))
|
|
290
|
+
|
|
291
|
+
# Bias (systematic error)
|
|
292
|
+
bias = np.mean(residuals)
|
|
293
|
+
|
|
294
|
+
# Correlation
|
|
295
|
+
correlation = np.corrcoef(simulation, experiment)[0, 1]
|
|
296
|
+
|
|
297
|
+
# Skill score (relative to climatology/baseline)
|
|
298
|
+
mse = np.mean(residuals**2)
|
|
299
|
+
variance = np.var(experiment)
|
|
300
|
+
skill_score = 1 - mse / variance
|
|
301
|
+
|
|
302
|
+
# Fraction within uncertainty bounds
|
|
303
|
+
if exp_uncertainty is not None:
|
|
304
|
+
within = np.abs(residuals) <= exp_uncertainty
|
|
305
|
+
within_uncertainty = np.mean(within)
|
|
306
|
+
else:
|
|
307
|
+
within_uncertainty = np.nan
|
|
308
|
+
|
|
309
|
+
return ValidationMetrics(
|
|
310
|
+
rmse=rmse,
|
|
311
|
+
bias=bias,
|
|
312
|
+
correlation=correlation,
|
|
313
|
+
skill_score=skill_score,
|
|
314
|
+
within_uncertainty=within_uncertainty
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def validation_hypothesis_test(
|
|
318
|
+
simulation: np.ndarray,
|
|
319
|
+
experiment: np.ndarray,
|
|
320
|
+
exp_uncertainty: np.ndarray,
|
|
321
|
+
significance: float = 0.05
|
|
322
|
+
) -> dict:
|
|
323
|
+
"""
|
|
324
|
+
Statistical hypothesis test for model validation
|
|
325
|
+
|
|
326
|
+
H0: Model predictions are within experimental uncertainty
|
|
327
|
+
"""
|
|
328
|
+
# Normalized residuals
|
|
329
|
+
normalized_residuals = (simulation - experiment) / exp_uncertainty
|
|
330
|
+
|
|
331
|
+
# Chi-squared test
|
|
332
|
+
chi2_stat = np.sum(normalized_residuals**2)
|
|
333
|
+
dof = len(simulation) - 1
|
|
334
|
+
p_value = 1 - stats.chi2.cdf(chi2_stat, dof)
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
'chi2_statistic': chi2_stat,
|
|
338
|
+
'degrees_of_freedom': dof,
|
|
339
|
+
'p_value': p_value,
|
|
340
|
+
'reject_null': p_value < significance,
|
|
341
|
+
'validated': p_value >= significance
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Uncertainty Quantification
|
|
346
|
+
|
|
347
|
+
### Monte Carlo UQ
|
|
348
|
+
```python
|
|
349
|
+
import numpy as np
|
|
350
|
+
from typing import Callable, List
|
|
351
|
+
from scipy import stats
|
|
352
|
+
|
|
353
|
+
class MonteCarloUQ:
|
|
354
|
+
"""Monte Carlo uncertainty quantification"""
|
|
355
|
+
|
|
356
|
+
def __init__(self, model: Callable, n_samples: int = 10000):
|
|
357
|
+
self.model = model
|
|
358
|
+
self.n_samples = n_samples
|
|
359
|
+
|
|
360
|
+
def propagate_uncertainty(
|
|
361
|
+
self,
|
|
362
|
+
param_distributions: dict
|
|
363
|
+
) -> dict:
|
|
364
|
+
"""
|
|
365
|
+
Propagate input uncertainty through model
|
|
366
|
+
|
|
367
|
+
param_distributions: {param_name: scipy.stats distribution}
|
|
368
|
+
"""
|
|
369
|
+
# Sample parameters
|
|
370
|
+
samples = {}
|
|
371
|
+
for name, dist in param_distributions.items():
|
|
372
|
+
samples[name] = dist.rvs(size=self.n_samples)
|
|
373
|
+
|
|
374
|
+
# Run model for each sample
|
|
375
|
+
outputs = []
|
|
376
|
+
for i in range(self.n_samples):
|
|
377
|
+
params = {name: samples[name][i] for name in samples}
|
|
378
|
+
output = self.model(**params)
|
|
379
|
+
outputs.append(output)
|
|
380
|
+
|
|
381
|
+
outputs = np.array(outputs)
|
|
382
|
+
|
|
383
|
+
# Compute statistics
|
|
384
|
+
return {
|
|
385
|
+
'mean': np.mean(outputs),
|
|
386
|
+
'std': np.std(outputs),
|
|
387
|
+
'median': np.median(outputs),
|
|
388
|
+
'percentile_5': np.percentile(outputs, 5),
|
|
389
|
+
'percentile_95': np.percentile(outputs, 95),
|
|
390
|
+
'samples': outputs
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
def sensitivity_analysis(
|
|
394
|
+
self,
|
|
395
|
+
param_distributions: dict,
|
|
396
|
+
output_name: str = 'output'
|
|
397
|
+
) -> dict:
|
|
398
|
+
"""
|
|
399
|
+
Compute Sobol sensitivity indices
|
|
400
|
+
"""
|
|
401
|
+
from SALib.sample import saltelli
|
|
402
|
+
from SALib.analyze import sobol
|
|
403
|
+
|
|
404
|
+
# Define problem
|
|
405
|
+
problem = {
|
|
406
|
+
'num_vars': len(param_distributions),
|
|
407
|
+
'names': list(param_distributions.keys()),
|
|
408
|
+
'bounds': [[d.ppf(0.01), d.ppf(0.99)]
|
|
409
|
+
for d in param_distributions.values()]
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
# Generate samples
|
|
413
|
+
param_values = saltelli.sample(problem, self.n_samples)
|
|
414
|
+
|
|
415
|
+
# Evaluate model
|
|
416
|
+
Y = np.array([self.model(**dict(zip(problem['names'], x)))
|
|
417
|
+
for x in param_values])
|
|
418
|
+
|
|
419
|
+
# Analyze
|
|
420
|
+
Si = sobol.analyze(problem, Y)
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
'first_order': dict(zip(problem['names'], Si['S1'])),
|
|
424
|
+
'total_order': dict(zip(problem['names'], Si['ST'])),
|
|
425
|
+
'second_order': Si['S2']
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Best Practices
|
|
430
|
+
|
|
431
|
+
1. **Test Known Solutions**: Analytical solutions, MMS
|
|
432
|
+
2. **Convergence Studies**: Verify expected order
|
|
433
|
+
3. **Document Assumptions**: Model limitations
|
|
434
|
+
4. **Quantify Uncertainty**: Input and numerical
|
|
435
|
+
5. **Peer Review**: Independent verification
|
|
436
|
+
|
|
437
|
+
## V&V Checklist
|
|
438
|
+
|
|
439
|
+
```
|
|
440
|
+
Code Verification:
|
|
441
|
+
□ Unit tests pass
|
|
442
|
+
□ MMS verification complete
|
|
443
|
+
□ Expected convergence order achieved
|
|
444
|
+
□ Conservation properties verified
|
|
445
|
+
|
|
446
|
+
Solution Verification:
|
|
447
|
+
□ Grid convergence study
|
|
448
|
+
□ Time step convergence
|
|
449
|
+
□ GCI computed
|
|
450
|
+
□ Asymptotic range confirmed
|
|
451
|
+
|
|
452
|
+
Validation:
|
|
453
|
+
□ Comparison with experiments
|
|
454
|
+
□ Statistical metrics computed
|
|
455
|
+
□ Uncertainty quantified
|
|
456
|
+
□ Limitations documented
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Anti-Patterns
|
|
460
|
+
|
|
461
|
+
- Only testing with "working" cases
|
|
462
|
+
- Ignoring numerical uncertainty
|
|
463
|
+
- Overfitting to validation data
|
|
464
|
+
- Not documenting model limitations
|
|
465
|
+
- Skipping convergence studies
|
|
466
|
+
|
|
467
|
+
## When to Use
|
|
468
|
+
|
|
469
|
+
- Safety-critical simulations
|
|
470
|
+
- Publication/peer review
|
|
471
|
+
- Regulatory compliance
|
|
472
|
+
- High-consequence predictions
|
|
473
|
+
- Production deployment
|
|
474
|
+
|
|
475
|
+
## When NOT to Use
|
|
476
|
+
|
|
477
|
+
- Early prototyping
|
|
478
|
+
- Quick estimates
|
|
479
|
+
- Educational examples
|