diff --git a/_research/cfd-simulations/adaptive_prediction/.gitkeep b/_research/cfd-simulations/adaptive_prediction/.gitkeep
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/_research/cfd-simulations/adaptive_prediction/.gitkeep
@@ -0,0 +1 @@
+
diff --git a/_research/cfd-simulations/adaptive_prediction/adaptive-prediction.md b/_research/cfd-simulations/adaptive_prediction/adaptive-prediction.md
new file mode 100644
index 000000000000..95e4be050103
--- /dev/null
+++ b/_research/cfd-simulations/adaptive_prediction/adaptive-prediction.md
@@ -0,0 +1,263 @@
+---
+layout: page
+category: "Adaptive Methodologies"
+topic: "CFD-Surrogate Prediction"
+thumbnail: "assets/img/Adaptive_Framework.png"
+tldr: "A divergence-aware adaptive CFD-surrogate framework that alternates between fast POD-DL forecasting and targeted OpenFOAM recalls for robust long-horizon prediction of unsteady flows."
+title: Divergence-aware Adaptive Prediction Framework for Accelerating CFD Simulations
+subtitle: Closed-loop OpenFOAM and POD-DL coupling for long-horizon unsteady-flow prediction
+published: true
+---
+
+
+# Divergence-aware Adaptive CFD-Surrogate Prediction
+
+Long-horizon prediction of unsteady flows remains a major challenge in computational fluid dynamics (CFD). Full CFD simulations are accurate, but they are expensive when many time steps, operating conditions, or design iterations are required. Data-driven reduced-order models can strongly accelerate prediction, but their autoregressive errors may gradually accumulate, especially when the forecast horizon is long or when the flow regime changes.
+
+This work develops a **divergence-aware adaptive prediction framework** that couples an OpenFOAM-based CFD solver with a **proper orthogonal decomposition-deep learning (POD-DL)** surrogate model. The central idea is straightforward:
+
+> use the fast surrogate when it is reliable, and recall CFD only when the prediction begins to diverge.
+
+The framework is designed for realistic online prediction scenarios, where future CFD ground truth is not available during deployment. Instead of comparing the prediction with reference data, the method monitors internal reliability indicators, especially ensemble uncertainty in the POD coefficient space.
+
+
+# Research Motivation
+
+CFD simulations play an essential role in fluid mechanics, combustion, meteorology, aerodynamics, and engineering design. However, resolving the relevant spatial and temporal scales of realistic unsteady flows often requires large computational resources.
+
+Reduced-order models provide an efficient alternative by compressing the flow field into a low-dimensional representation and learning its temporal evolution. However, purely offline surrogates face two important limitations:
+
+- **Long-horizon error accumulation:** small prediction errors are propagated and amplified during autoregressive forecasting.
+- **Poor robustness under regime changes:** a surrogate trained under one condition may lose reliability when the flow state or boundary condition changes.
+
+The proposed framework addresses these limitations by introducing an **adaptive CFD-surrogate loop**. The surrogate is not used blindly over the full prediction horizon. Instead, its reliability is monitored online, and CFD is recalled only when new high-fidelity information is required.
+
+
+# Methodology Overview
+
+The adaptive framework contains four main components:
+
+1. **CFD data generation** using OpenFOAM.
+2. **POD compression** of high-dimensional flow snapshots.
+3. **Deep-learning temporal prediction** in the reduced POD space.
+4. **Online divergence monitoring** and selective CFD recall.
+
+## Closed-loop CFD/POD-DL workflow
+
+The workflow starts with a short CFD simulation. The generated snapshots are used to build the initial POD basis and train a neural-network predictor. After training, the surrogate model advances the reduced state autoregressively. During this prediction stage, the monitoring module evaluates whether the forecast remains reliable.
+
+When the scheduled update time is reached, or when prediction divergence is detected, the workflow switches back to CFD. New snapshots are generated, the POD-DL model is retrained or updated, and surrogate prediction then resumes. In this way, the framework alternates between **expensive but accurate CFD simulation** and **fast but monitored POD-DL prediction**.
+
+
+
+
+
+Figure 1. Closed-loop adaptive framework coupling online CFD simulation and POD-DL prediction. CFD snapshots are compressed by POD, the reduced coefficients are predicted by a deep-learning model, and the monitoring module decides whether the forecast can continue or whether CFD should be recalled.
+
+## POD-DL surrogate model
+
+The high-dimensional CFD snapshot matrix is first mean-centered and decomposed using singular value decomposition. The dominant POD modes provide a compact basis for the unsteady flow structures. The corresponding temporal coefficients are standardized and used as the training data for a deep-learning sequence predictor.
+
+In the present implementation, the predictor is based on a stacked **long short-term memory (LSTM)** network followed by fully connected layers. The model takes a rolling window of past POD coefficients and predicts the next coefficient block. During long-horizon forecasting, the predicted coefficients are fed back into the input window, forming an autoregressive prediction loop.
+
+The predicted POD coefficients are finally transformed back to the physical space through inverse POD reconstruction.
+
+## Divergence detection and adaptive control
+
+Two adaptive strategies are considered.
+
+### Scheduled adaptive update
+
+In the scheduled mode, CFD is recalled after a fixed number of predicted snapshots. This is a simple and robust correction strategy. It periodically introduces new high-fidelity data and prevents irreversible growth of the prediction error.
+
+### Event-triggered adaptive update
+
+In the event-triggered mode, the framework recalls CFD only when the surrogate becomes unreliable. Several POD-DL predictors are trained with different random seeds and advanced simultaneously. Their disagreement is used to estimate **ensemble uncertainty** in the reduced POD space.
+
+The uncertainty is weighted by the retained POD singular values, so uncertainty in energetically important modes contributes more strongly to the final indicator. A dynamic threshold is estimated from the recent uncertainty history using a median-MAD rule. If the uncertainty exceeds the threshold for a sustained period, the current forecast is terminated and CFD is recalled.
+
+This event-triggered strategy is important because it does not require future CFD ground truth during prediction. The model decides when to stop based on its own reliability indicator.
+
+
+# CFD Dataset Generation and Validation
+
+The framework is assessed using the canonical problem of **three-dimensional flow past a circular cylinder**. This case is widely used for studying vortex shedding, wake instability, and unsteady-flow prediction.
+
+## Computational setup
+
+The CFD simulations are performed using **OpenFOAM** with the `pimpleFoam` solver for incompressible flow. The Reynolds-number range is approximately **Re = 160-400**. The computational domain has a height of 20D, an upstream length of 10D, a downstream length of 20D, and a spanwise length of 3D, where D is the cylinder diameter.
+
+The mesh contains approximately **741 thousand cells**, with refinement near the cylinder and in the wake region. The spanwise boundaries are periodic. The inlet velocity and kinematic viscosity are adjusted to obtain different Reynolds numbers while keeping the same geometry. Probe points are placed in the wake region to evaluate the temporal evolution of velocity and pressure predicted by the surrogate.
+
+
+
+
+
+Figure 2. Computational geometry and mesh for the circular-cylinder wake. The domain extends 10D upstream and 20D downstream of the cylinder, with a 20D cross-stream height. The mesh is refined around the cylinder and in the wake region where vortex shedding develops.
+
+## Numerical validation
+
+Before training the surrogate model, the CFD setup is validated using standard integral quantities:
+
+- mean drag coefficient,
+- mean base-pressure coefficient,
+- Strouhal number.
+
+The numerical results follow the experimental trends over the investigated Reynolds-number range. The mean drag coefficient decreases gradually with Reynolds number. The base-pressure coefficient remains close to the experimental values and captures the expected tendency. The Strouhal number remains around the characteristic vortex-shedding range for cylinder wakes. This validation confirms that the generated CFD snapshots provide a reliable basis for POD-DL training and adaptive prediction.
+
+
+
+
+
+Figure 3. Validation of the OpenFOAM setup using mean drag coefficient, mean base-pressure coefficient and Strouhal number. The simulated values are consistent with experimental trends over Re = 200-400.
+
+
+# Baseline POD-DL Prediction without Adaptation
+
+The first test evaluates the original POD-DL surrogate without adaptive correction. This baseline is useful because it shows why online adaptation is needed.
+
+## Case Re = 200
+
+At Re = 200, the wake exhibits an organized periodic vortex-shedding pattern. The leading POD modes capture the dominant coherent structures, and the predicted probe signals reproduce the main oscillatory behavior of velocity and pressure. The predicted amplitude and phase remain reasonable over a finite prediction window.
+
+
+
+
+
+Figure 4. Comparison between prediction and CFD ground truth at probe location (0.5D, 0.5D, 0) for the flow past a circular cylinder at Re=200.
+
+However, the RMSE gradually increases as the prediction horizon extends. The error growth is especially visible for the streamwise and cross-stream velocity components. This confirms that even for a relatively regular wake regime, small autoregressive errors accumulate over time.
+
+
+
+
+
+Figure 5. RMSE evolution for the baseline POD-DL prediction at Re = 200. The model captures the main periodic dynamics, but the error gradually increases during long-horizon autoregressive forecasting.
+
+## Case Re = 300
+
+At Re = 300, the wake becomes more complex. The prediction still captures the dominant oscillatory behavior, but the mismatch grows faster than in the Re = 200 case.
+
+
+
+
+
+Figure 6. Comparison between prediction and CFD ground truth at probe location (0.5D, 0.5D, 0) for the flow past a circular cylinder at Re=300.
+
+The RMSE also increases as the prediction horizon extends, and the error increases in comparison to Case Re=200.
+
+
+
+
+
+Figure 7. RMSE evolution for the baseline POD-DL prediction at Re = 300. Compared with Re = 200, the prediction mismatch grows more rapidly because the wake dynamics are less regular and more modal content is required.
+
+These results show that a purely offline POD-DL surrogate is not sufficiently robust for extended forecasts when the wake dynamics become more complex.
+
+
+# Scheduled Adaptive Prediction
+
+The scheduled adaptive strategy recalls CFD after a fixed number of predicted snapshots. In the representative test, the model is trained using an initial interval, predicts the next interval, then CFD is recalled to generate new snapshots and retrain the surrogate.
+
+The updated model significantly reduces the RMSE in the second prediction stage compared with the non-adaptive baseline. In particular, the errors of the main velocity components are effectively reset after retraining and then evolve again from a lower baseline.
+
+
+
+
+
+Figure 8. RMSE evolution for scheduled adaptive prediction at Re = 300. The transparent curves indicate the non-adaptive baseline, while the highlighted curves show the adaptive prediction. After CFD recall and retraining, the prediction error is reduced in the second forecast stage.
+
+
+The method also preserves key wake statistics, including:
+
+- mean and fluctuation level of turbulent kinetic energy,
+- temporal correlation of the streamwise velocity,
+- overall evolution of the wake dynamics.
+
+For a representative **200-snapshot** interval, the CFD computation requires 15906.9 s using 60 CPU cores, while the surrogate workflow requires 2591.7 s using 4 CPU cores. After normalizing by CPU core count, the effective speed-up is approximately **92 times** relative to CFD.
+
+
+# Event-triggered Divergence Detection
+
+Although scheduled updating is simple, it may recall CFD too early or too late because the update time is fixed in advance. The event-triggered strategy is more flexible because it links CFD recall directly to the reliability of the ongoing prediction.
+
+In this mode, three POD-DL predictors are trained with different random seeds. Their prediction spread provides an ensemble uncertainty indicator. A dynamic threshold is estimated from the recent uncertainty history. When the uncertainty exceeds this threshold persistently, the prediction is considered unreliable.
+
+The results show that the uncertainty trigger is consistent with physically meaningful degradation. The raw uncertainty signal is noisy, so a smoothed signal is used to identify robust trends. When the smoothed ensemble uncertainty rises above the dynamic threshold, the forecast is terminated and CFD is recalled. This avoids the need for future CFD ground-truth data during online prediction.
+
+
+
+
+
+Figure 9. Event-triggered divergence detection based on ensemble uncertainty. The dashed green curves show raw uncertainty, the solid green curves show smoothed uncertainty, and the dashed yellow curves show the dynamic threshold. CFD is recalled when the uncertainty exceeds the threshold persistently.
+
+This strategy provides a practical online solution when future CFD reference data are unavailable. It allows the surrogate to continue when the prediction is stable, and automatically switches back to CFD when the reduced-order forecast becomes unreliable.
+
+
+# Adaptive Prediction under Varying Inlet Velocity
+
+A more demanding test is performed under time-varying inlet velocity. The inlet velocity changes during prediction according to:
+
+$$
+U_{in}: 1 \rightarrow 2 \rightarrow 0.8 \rightarrow 1.5 \; \text{m/s}
+$$
+
+This corresponds to a Reynolds-number range of approximately **160-400**. The case represents a realistic scenario where the operating condition changes during simulation.
+
+The ensemble uncertainty increases sharply after each inlet-velocity change. This rise in uncertainty triggers termination of the current surrogate forecast and recall of the CFD solver. The boundary condition is then updated, new CFD snapshots are generated, and the surrogate is retrained.
+
+After retraining, the predicted drag and lift histories recover physically expected trends. This demonstrates that the adaptive framework can track regime changes and maintain predictive reliability under evolving operating conditions.
+
+
+
+
+
+Figure 10. Adaptive prediction under varying inlet velocity. The inlet velocity changes from 1 to 2, then to 0.8 and 1.5 m/s. The uncertainty indicator rises after each regime change, triggering CFD recall and surrogate retraining.
+
+
+# Key Findings
+
+The main findings can be summarized as follows:
+
+- **Baseline POD-DL prediction is accurate over a finite horizon**, but autoregressive errors grow gradually during long-time forecasting.
+- **Scheduled adaptive updating reduces accumulated error** by periodically recalling CFD and retraining the surrogate.
+- **Event-triggered updating provides an online reliability mechanism** based on ensemble uncertainty, without requiring future CFD ground truth.
+- **The adaptive framework can handle changing inlet conditions**, because uncertainty rises after regime changes and activates targeted CFD recall.
+- **The closed-loop CFD-surrogate workflow provides strong acceleration**, with an effective speed-up of approximately 92 times for a representative 200-snapshot interval.
+
+
+# CFD-Surrogate Pipeline
+
+This research can be organized as a reproducible workflow for adaptive prediction:
+
+1. **Generate CFD snapshots**
+ - Run OpenFOAM for an initial short interval.
+ - Save velocity and pressure fields at selected sampling times.
+
+2. **Construct the POD representation**
+ - Mean-center the snapshot matrix.
+ - Apply singular value decomposition.
+ - Retain the dominant POD modes and temporal coefficients.
+
+3. **Train the POD-DL predictor**
+ - Standardize modal coefficients.
+ - Train an LSTM-based temporal predictor.
+ - Use rolling-window autoregressive forecasting.
+
+4. **Monitor prediction reliability**
+ - Run ensemble predictors with different random seeds.
+ - Compute energy-weighted uncertainty in POD space.
+ - Estimate a dynamic threshold using recent uncertainty history.
+
+5. **Recall CFD when needed**
+ - Stop the surrogate forecast when divergence is detected.
+ - Restart OpenFOAM from the accepted predicted state or updated operating condition.
+ - Generate new high-fidelity snapshots and retrain the surrogate.
+
+# Related Publication
+
+Xiangrui Zou, Zhuoqun Zhao, Guillermo Barragán, Soledad Le Clainche.
+Divergence-aware adaptive prediction framework for accelerating CFD simulations of unsteady flows.
+[https://doi.org/10.48550/arXiv.2605.24150](https://doi.org/10.48550/arXiv.2605.24150)
+
+
diff --git a/_research/research_post.md b/_research/research_post.md
new file mode 100644
index 000000000000..7f2300c59dc6
--- /dev/null
+++ b/_research/research_post.md
@@ -0,0 +1,222 @@
+# Adaptive CFD–POD–LSTM Pipeline
+### Validation on Canonical Cylinder Flow
+
+## Abstract
+
+This work develops and validates a generalized adaptive pipeline that couples the OpenFOAM CFD solver with a data-driven surrogate model (POD + LSTM) in a closed loop. The pipeline is first verified on the canonical flow around a circular cylinder at Re = 300 — a benchmark that has been used extensively in the ROM literature —.
+
+---
+
+## 1. Motivation
+
+High-fidelity CFD simulations solve the incompressible Navier–Stokes equations over fine spatial meshes and at small time steps, yielding accurate flow fields at the cost of significant computational resources. Tasks that require many flow evaluations — parameter studies, sensitivity analyses, real-time forecasting — quickly become infeasible if every evaluation must be covered by the full solver.
+
+**Purely offline surrogate models** (trained once on a snapshot database and then used for prediction) accumulate autoregressive error and diverge over long prediction horizons. The adaptive prediction paradigm solves this by periodically restarting the CFD solver, regenerating ground-truth snapshots, and retraining the model from scratch using transfer learning. This way, prediction accuracy remains controlled indefinitely.
+
+---
+
+## 2. Methodology
+
+### 2.1 The adaptive loop
+
+The pipeline alternates between two computational phases:
+
+```
+ CFD block ML block CFD block ML block
+[T₀ ───── T₁] [T₁ ───── T₂] [T₂-δt ─── T₃] [T₃ ───── T₄]
+ └── warm restart
+```
+
+| Step | What happens |
+|------|-------------|
+| **1. CFD phase** | OpenFOAM runs for `TIME_PERIOD_OF` seconds, saving a snapshot every `TIME_STEP_WRITE_OF` seconds → K snapshots |
+| **2. POD compression** | Truncated SVD of the snapshot matrix $\mathbf{X} \in \mathbb{R}^{C N_x \times K}$ retains the $r_0$ most energetic spatial modes |
+| **3. LSTM training** | The network is trained (or fine-tuned via transfer learning from the previous checkpoint) on the $r_0$ temporal coefficient series |
+| **4. Prediction** | Auto-regressive loop produces $K_{\text{pred}}$ future coefficient vectors; full fields reconstructed as $\hat{\mathbf{X}} \approx \mathbf{U}_{r_0} \boldsymbol{\Sigma}_{r_0} \hat{\mathbf{C}}$ |
+| **5. Warm restart** | OpenFOAM restarts from T₂ − TIME_AHEAD (a small overlap), ensuring the next CFD block begins from a physically coherent state |
+
+
+
+### 2.2 Dimensionality reduction: truncated SVD
+
+Given the snapshot matrix $\mathbf{X} \in \mathbb{R}^{C N_x \times K}$:
+
+$$\mathbf{X} = \mathbf{U}\boldsymbol{\Sigma}\mathbf{V}^\top \approx \mathbf{U}_{r_0}\boldsymbol{\Sigma}_{r_0}\mathbf{V}_{r_0}^\top$$
+
+The temporal coefficients $\mathbf{C} = \mathbf{V}_{r_0}^T \in \mathbb{R}^{r_0 \times K}$ encode how each spatial mode contributes at each time step. By the Eckart–Young theorem, the truncated SVD is the optimal rank-$r_0$ approximation in both the Frobenius and spectral norms. For the cylinder flow at Re = 300, the Kármán vortex street is dominated by a single periodic spatial structure: **5 modes capture over 90% of the fluctuating energy**.
+
+### 2.3 Temporal prediction: LSTM
+
+The LSTM ingests a sliding window of `INP_SEQ` past coefficient vectors and predicts the next one:
+
+```
+Input: c(t-L+1), c(t-L+2), …, c(t) ← L = INP_SEQ past steps
+Output: ĉ(t+1) ← 1 step ahead
+```
+
+Predictions are fed back as inputs and the window slides forward, generating an auto-regressive chain of arbitrary length. At the end of each adaptive iteration, the model weights are saved as a checkpoint: the next training phase loads these weights and fine-tunes them on fresh data — **transfer learning** — instead of starting from random initialisation.
+
+| Hyperparameter | Value | Description |
+|---|---|---|
+| `HIDDEN_SIZE` | 128 | LSTM hidden state dimension |
+| `NUM_LAYERS` | 1 | Stacked LSTM layers |
+| `INP_SEQ` | 8 | Sliding window length (time steps) |
+| `EPOCHS` | 1000 | Maximum training epochs |
+| `LOSS_THRESHOLD` | $1 \times 10^{-8}$ | Early stopping criterion (MSE) |
+| `OPTIMIZER` | Adam | lr = $10^{-3}$, weight_decay = $10^{-4}$ |
+
+### 2.4 Pipeline architecture
+
+The orchestration layer was fully refactored from the reference implementation, resolving five limitations that tied the original code to a single test case:
+
+| Limitation | Solution |
+|---|---|
+| L1 — Parameters scattered across `.sh` and `.py` files | Single `config.yaml` with all parameters |
+| L2 — `sed -i` text substitution of OpenFOAM dictionaries | `foamDictionary` (native OF tool, validates syntax) |
+| L3 — No experiment isolation | Auto-named directory `sim_TpOF_TpPY_t0/` per run |
+| L4 — Missing fields on solver restart | `copy_missing_fields` + `fill_missing_fields` functions |
+| L5 — No offline-only mode | `SOLO_PYTHON: true` flag in `config.yaml` |
+
+The result is a single Python orchestrator (`directorpython.py`) that reads `config.yaml` and drives the entire CFD–SVD–LSTM cycle. **Changing the test case requires only editing `config.yaml`.**
+
+
+
+---
+
+## 3. Canonical Cylinder Case
+
+### 3.1 Case description
+
+The benchmark geometry is an incompressible three-dimensional flow around a circular cylinder of diameter D placed inside a rectangular channel. This case is a standard reference in the computational fluid dynamics literature: at moderate Reynolds numbers, the cylinder wake develops the alternating vortex shedding known as the **Kármán vortex street** — a periodic, spatially coherent structure that can be represented with very few POD modes, making it the ideal first test for a POD + LSTM surrogate.
+
+| Parameter | Value |
+|---|---|
+| Reynolds number | Re = 300 (laminar, no turbulence model) |
+| Solver | OpenFOAM `foamRun` with `incompressibleFluid` module |
+| Pressure–velocity coupling | PISO algorithm |
+| Mesh | ~10⁴ cells (structured, boundary-layer refinement around cylinder) |
+| Inlet BC | Uniform velocity |
+| Outlet BC | Zero pressure gradient |
+| Walls | No-slip |
+
+### 3.2 Temporal parameters
+
+The simulation uses an adaptive time step (`adjustTimeStep yes`, `maxCo = 0.5`) to keep the Courant number below 0.5 throughout the domain. Snapshots are written at fixed physical intervals.
+
+| Parameter | Value | Description |
+|---|---|---|
+| `endTime` | 100 s | Physical end time of the reference CFD run |
+| `maxDeltaT` | 1 s | Maximum allowed time step |
+| `writeInterval` | 1 s | Snapshot saving interval |
+| `INITIAL_TIME` | 100 s | Start of the adaptive loop (flow in periodic regime) |
+| `TIME_PERIOD_OF` | 100 s | Duration of each CFD block |
+| `TIME_PERIOD_PY` | 100 s | Duration of each ML prediction block |
+| `TIME_AHEAD` (δt) | 5 s | Warm-restart overlap |
+| `NUM_ITERATIONS` | 2 | Number of CFD → ML cycles |
+
+The adaptive loop is initialised at t = 100 s, the instant at which the Kármán vortex shedding has reached its statistically periodic regime. The experiment is stored in the isolated directory `sim_100_100_100/`.
+
+### 3.3 Physical picture
+
+The Kármán vortex street is clearly visible in both the pressure and velocity fields. At t ≈ 100 s the wake already shows the characteristic lateral modulation; at t ≈ 200 s (end of the first CFD block) the alternating high- and low-velocity structures extend several diameters downstream. Low-pressure regions (blue) alternate on either flank of the cylinder as vortices are shed and convected.
+
+This periodicity — and the compactness of its POD representation — is what makes the cylinder the canonical benchmark for the adaptive framework: **5 modes suffice to capture over 90% of the fluctuating kinetic energy**, so the LSTM operates on a 5-dimensional coefficient space.
+
+
+Pressure fields p (left) and velocity modulus |U| (right) at the beginning (t ≈ 100 s, established periodic regime) and at the end of the first CFD block (t ≈ 200 s). The Kármán vortex street is clearly visible in both variables.
+
+---
+
+## 4. Results
+
+### 4.1 Successful end-to-end execution
+
+The pipeline completed both adaptive iterations without errors, producing the isolated experiment directory `sim_100_100_100/` with CFD time folders and ML predictions correctly separated. The execution confirmed the correct functioning of all five architectural improvements:
+
+- `config.yaml` was read and all parameters were passed coherently to both OpenFOAM and the Python module.
+- `foamDictionary` modified `system/controlDict` robustly at each iteration without syntax errors.
+- The isolated experiment directory was created and populated correctly.
+- Missing OpenFOAM fields (`phi`, internal time-step registers) were copied to the restart directory before each new CFD phase, preventing fatal errors on solver start.
+- The `SOLO_PYTHON` mode allowed rerunning the ML module on existing snapshots without relaunching any simulation.
+
+### 4.2 POD energy spectrum
+
+The cumulative energy curve $\varepsilon(r_0)$ for the cylinder exhibits an elbow at $r_0 = 5$, as it is shown in the following picture:
+
+
+
+
+
+| Modes $r_0$ | Cumulative energy $\varepsilon(r_0)$ |
+|---|---|
+| 1 | ~75% |
+| 2 | ~88% |
+| 5 | **>90%** |
+| 10 | ~99% |
+
+
+
+This fast growth of the cummulative energy ratio — a hallmark of flows dominated by a single coherent structure — confirms that the LSTM needs to predict only a handful of coefficients to reconstruct the full three-dimensional field with high fidelity.
+
+### 4.3 Key validation outcomes
+
+> **The transition from the Zou (2025) reference implementation to the generalised pipeline required zero changes to the prediction module (`main_code.py`). Only `config.yaml` was edited.** This confirms that the separation between the orchestration layer and the mathematical prediction layer produces a geometry-agnostic pipeline.
+
+| Verification objective | Outcome |
+|---|---|
+| Correct `config.yaml` parsing | **PASS** — all 7 parameter blocks read without errors |
+| `foamDictionary` field editing | **PASS** — `controlDict` updated correctly in both iterations |
+| Experiment directory isolation | **PASS** — `sim_100_100_100/` created and self-contained |
+| Missing-fields management | **PASS** — `phi` and internal registers copied before each restart |
+| `SOLO_PYTHON` mode | **PASS** — ML module ran on existing snapshots without launching OF |
+| Warm-restart continuity | **PASS** — iteration 2 started from t = 295 s (CFD-verified state) |
+| End message | **PASS** — `"All iterations completed successfully!"` |
+
+---
+
+## 5. Conclusions
+
+The canonical cylinder case at Re = 300 served as the verification benchmark for the generalised adaptive CFD–POD–LSTM pipeline. All five architectural improvements introduced over the Zou (2025) reference implementation were validated end-to-end. The pipeline:
+
+- Reads a single `config.yaml` configuration file — no `.sh` or `.py` files need to be edited.
+- Manages the full CFD–SVD–LSTM cycle through a single Python orchestrator (`directorpython.py`).
+- Isolates each experiment in its own directory, enabling clean comparison of configurations.
+- Handles OpenFOAM dictionary editing robustly via `foamDictionary`, regardless of file formatting.
+- Copies missing internal fields before each solver restart, ensuring no fatal abort.
+- Provides an offline prediction mode (`SOLO_PYTHON`) for hyperparameter tuning without re-running CFD.
+
+For the cylinder at Re = 300, the Kármán vortex street is well captured by $r_0 = 5$ POD modes (> 90% energy). The LSTM, operating on this compact 5-dimensional coefficient space with a sliding window of $L = 8$ steps, reproduces the temporal dynamics through transfer learning across adaptive iterations.
+
+---
+
+## References
+
+- Zou X. et al. (2025). *Tutorial for adaptive prediction combining OpenFOAM and Python code*. arXiv:2505.01531.
+- Brunton S.L. & Kutz J.N. (2022). *Data-Driven Science and Engineering*. Cambridge University Press.
+- Hochreiter S. & Schmidhuber J. (1997). Long short-term memory. *Neural Computation*, 9(8), 1735–1780.
+- Weller H.G. et al. (1998). A tensorial approach to CFD using object-oriented techniques. *Computers in Physics*, 12(6), 620–631.
+
+---
+
+*ModelFLOWs-UPM · ETSIAE · Universidad Politécnica de Madrid*
+*Adaptive CFD–LSTM — Canonical cylinder validation, Re = 300*
diff --git a/_tutorials/tutorial.md b/_tutorials/tutorial.md
new file mode 100644
index 000000000000..3feeade6d20e
--- /dev/null
+++ b/_tutorials/tutorial.md
@@ -0,0 +1,693 @@
+---
+layout: page
+title: "Adaptive CFD–POD–LSTM Pipeline"
+application: "Urban Flows"
+category: "AI & Data-Driven Models"
+tldr: "Step-by-step tutorial to run the generalized adaptive prediction pipeline combining OpenFOAM (CFD) and a POD+LSTM surrogate model. All parameters controlled from a single config.yaml file."
+author: "Xiangrui Zou, Carlos Sainz García, Mikel Navarro Huarte"
+sphinx_repository: "https://github.com/modelflows/adaptive-cfd"
+tutorial_file: "TUTORIAL.md"
+---
+
+# Overview
+
+This tutorial explains how to use the generalized **adaptive CFD–POD–LSTM pipeline** developed at ETSIAE–UPM. The pipeline couples an OpenFOAM CFD solver with a data-driven surrogate model (Proper Orthogonal Decomposition + Long Short-Term Memory network) in a closed loop. At each iteration, OpenFOAM generates a block of flow snapshots, the LSTM is trained on them and predicts the next block, and the solver restarts — keeping prediction accuracy controlled over arbitrarily long horizons.
+
+The generalization introduced in this work replaces all scattered shell and Python configuration files from the original implementation (Zou 2025) with a single `config.yaml` file and a single Python orchestrator (`directorpython.py`), making the pipeline portable to any OpenFOAM case.
+
+# What This Tutorial Covers
+
+- What CFD adaptive prediction is and why it is needed.
+- How the CFD–POD–LSTM loop works (adaptive framework).
+- How POD via truncated SVD reduces the problem dimensionality.
+- How the LSTM is trained and used for auto-regressive prediction.
+- How to install the required Python environment.
+- How to copy the pipeline into any OpenFOAM case.
+- How to configure every parameter in `config.yaml`.
+- How to run in offline mode (`SOLO_PYTHON=true`) on pre-computed snapshots.
+- How to run the full adaptive loop (`SOLO_PYTHON=false`) with a live OpenFOAM simulation.
+- How to read the output logs and visualise results in ParaView.
+
+# Related Links
+
+- Notebook: *(to be added)*
+- Video: *(to be added)*
+- Dataset: *(to be added)*
+- Application hub:
+
+# Contributors
+- Xiangrui Zou
+- Carlos Sainz García
+- Mikel Navarro Huarte
+
+---
+
+## Full Tutorial Content
+
+### Table of Contents
+
+1. [Introduction](#1-introduction)
+2. [The adaptive framework](#2-the-adaptive-framework)
+3. [Methodology: POD + LSTM](#3-methodology-pod--lstm)
+4. [File structure](#4-file-structure)
+5. [Environment setup](#5-environment-setup)
+6. [config.yaml — all parameters](#6-configyaml--all-parameters)
+7. [Run the pipeline](#7-run-the-pipeline)
+8. [Summary / Checklist](#8-summary--checklist)
+
+---
+
+## 1. Introduction
+
+### The challenge of high-fidelity simulations
+
+Computational Fluid Dynamics (CFD) solvers — such as OpenFOAM — numerically solve the Navier–Stokes equations to predict how a fluid (air, water, etc.) moves through a geometry. To get accurate results, the solver must resolve all relevant spatial and temporal scales of the flow, which translates directly into very fine meshes and very small time steps.
+
+This accuracy comes at a steep computational price:
+- A single simulation of a realistic case can take hours or days on a cluster.
+- Engineering tasks like parameter optimisation or uncertainty quantification require **thousands** of such evaluations.
+- Running all of them with a full CFD solver is often completely infeasible.
+
+### Why machine learning?
+
+Data-driven surrogate models can learn the dynamics of a flow from a set of precomputed snapshots and then **predict** future states in a fraction of the time. The idea is to let the CFD solver run for a while, collect its output, train a neural network, and then let the network take over prediction — bypassing the expensive solver.
+
+The key challenge is **prediction drift**: purely data-driven models are trained once and their accuracy degrades over time as the flow evolves and the model encounters conditions not seen during training.
+
+### Adaptive prediction: the solution
+
+Adaptive prediction solves the drift problem by coupling the ML model with the live CFD solver in a closed loop. Instead of training once and predicting forever, the pipeline alternates between:
+
+1. **CFD solver** — generates new, accurate snapshots for a window of time.
+2. **ML model (POD-DL)** — trains on those snapshots and predicts the next window.
+3. **Repeat** — the solver restarts from near the end of the ML prediction, corrects any drift, and the cycle continues.
+
+This way, accuracy remains controlled over arbitrarily long prediction horizons and the model stays robust to shifts in the flow regime.
+
+> **The only file you need to edit is `config.yaml`. The only command you need to run is `python pyPseudo_Adaptive_parallel/directorpython.py`.**
+
+---
+
+## 2. The adaptive framework
+
+### How the loop works
+
+The pipeline alternates between blocks of CFD simulation and blocks of ML prediction:
+
+
+
+At each iteration:
+
+1. **OpenFOAM runs** from `T₀` to `T₁`, saving a snapshot of the flow field every `TIME_STEP_WRITE_OF` seconds.
+2. **POD-DL reads** those snapshots, applies truncated SVD to reduce them to a low-dimensional representation, and trains an LSTM on the resulting temporal coefficients.
+3. **LSTM predicts** the coefficients from `T₁` to `T₂` in an auto-regressive loop; the full flow field is reconstructed from those coefficients.
+4. **OpenFOAM restarts** from `T₂ − TIME_AHEAD` (a small overlap ensures continuity between the CFD and ML blocks).
+5. Steps 1–4 repeat `NUM_ITERATIONS` times.
+
+### Key design choices
+
+| Concept | What it means |
+|---|---|
+| `TIME_AHEAD` | A small temporal overlap (typically 5 snapshots × `dt`) so that OpenFOAM does not start from a discontinuity when it picks up after the ML block. |
+| Transfer learning | The LSTM is not retrained from scratch every iteration. If a checkpoint exists from the previous round, the model is loaded and fine-tuned on the new data — each successive round trains faster. |
+| `SOLO_PYTHON=true` | Skips OpenFOAM entirely. All snapshots must already exist in `CFD_DIR`. Useful for offline experiments, first validation, or debugging the ML module without running a full simulation. |
+
+---
+
+## 3. Methodology: POD + LSTM
+
+### Why dimensionality reduction?
+
+A typical CFD mesh has millions of cells and each field (velocity, pressure) is a vector at every cell. Feeding raw snapshots directly into a neural network would be computationally prohibitive. Proper Orthogonal Decomposition (POD) compresses the data: instead of predicting millions of numbers, the LSTM only needs to predict a handful of **temporal coefficients**.
+
+### POD via truncated SVD
+
+Given a snapshot matrix $\mathbf{X} \in \mathbb{R}^{N_{\text{cells}} \times K}$ where each column is one time snapshot:
+
+$$
+\mathbf{X} = \mathbf{U} \boldsymbol{\Sigma} \mathbf{V}^T \qquad \text{(full SVD)}
+$$
+
+$$
+\mathbf{X}_r \approx \mathbf{U}_r \boldsymbol{\Sigma}_r \mathbf{V}_r^T \qquad \text{(truncated to } r_0 \text{ dominant modes)}
+$$
+
+$$
+\mathbf{C} = \mathbf{V}_r^T \in \mathbb{R}^{r_0 \times K} \qquad \text{(temporal coefficients)}
+$$
+
+- $\mathbf{U}_r$ — spatial POD modes (basis vectors), shape: $N_{\text{cells}} \times r_0$
+- $\boldsymbol{\Sigma}_r$ — singular values ($\sigma_i^2 \propto$ energy of mode $i$)
+- $\mathbf{C}$ — temporal coefficients: how much each mode contributes at each time step
+
+The reconstruction error is measured by the **RRMSE**:
+
+$$
+RRMSE = \frac{\left\| \mathbf{X} - \mathbf{U}_r \boldsymbol{\Sigma}_r \mathbf{V}_r^T \right\|_F}{\left\| \mathbf{X} \right\|_F}
+$$
+
+A good choice of `NUM_MODES` ($r_0$) keeps this below ~5 %.
+
+### LSTM prediction
+
+The LSTM receives the last `INP_SEQ` columns of $\mathbf{C}$ (a sliding window of $L$ past coefficient vectors) and predicts the next one:
+
+$$
+\mathbf{c}(t-L+1),\, \ldots,\, \mathbf{c}(t) \;\xrightarrow{\text{LSTM}}\; \mathbf{c}(t+1)
+$$
+
+This is repeated auto-regressively for `NUM_PREDS` steps. The full predicted field is then reconstructed as:
+
+$$
+\hat{\mathbf{X}}_{\text{pred}} \approx \mathbf{U}_r \boldsymbol{\Sigma}_r \hat{\mathbf{C}}
+$$
+
+### Training
+
+- **Loss:** Mean Squared Error (MSE) between predicted and true temporal coefficients:
+
+$$
+\text{MSE} = \frac{1}{Npr} \sum_{n=1}^{N} \sum_{t=1}^{p} \sum_{k=1}^{r} \left(\hat{c}_{n,t,k} - c_{n,t,k}\right)^2 = \frac{1}{Npr} \sum_{n,t} \left\| \hat{\mathbf{c}}_{n,t} - \mathbf{c}_{n,t} \right\|_2^2
+$$
+
+- **Checkpoint:** if a model from the previous iteration exists, it is loaded and fine-tuned (transfer learning). If no checkpoint is found, training starts from scratch.
+- **Early stopping:** training halts when loss drops below `LOSS_THRESHOLD`.
+
+> **The two hyperparameters with the greatest impact on prediction quality are `NUM_MODES` ($r_0$) and `INP_SEQ` ($L$). Start with `NUM_MODES=5` and `INP_SEQ=8` and tune from there.**
+
+### Divergence monitoring: CE and TE
+
+To decide when to restart the CFD solver, the pipeline monitors two scalar error metrics at each predicted time step $t$:
+
+**Consistency Estimate (CE)** — measures how far the LSTM prediction $\hat{\boldsymbol{u}}(t)$ has drifted from the POD-reconstructed reference $\boldsymbol{u}(t)$ computed on the latest CFD window:
+
+$$
+\text{CE}(t) = \frac{\left\| \hat{\boldsymbol{u}}(t) - \boldsymbol{u}(t) \right\|}{\left\| \boldsymbol{u}(t) \right\|}
+$$
+
+**Truncation Estimate (TE)** — measures the POD basis error: how much information is lost by retaining only $r_0$ modes from the current CFD snapshot set:
+
+$$
+\text{TE}(t) = \frac{\left\| \hat{\boldsymbol{u}}(t) - \boldsymbol{u}(t) \right\|}{\left\| \boldsymbol{u}(t) \right\|}
+$$
+
+The CFD solver is automatically invoked when either metric exceeds its threshold (`CE_THRESHOLD` or `TE_THRESHOLD` in `config.yaml`). In fixed-interval mode both thresholds are set to $10^9$ (disabled) and the solver restarts after a fixed number of predicted snapshots.
+
+### Divergence monitoring: Mahalanobis distance and ensemble UQ
+
+Two additional criteria are available for more sophisticated adaptive triggering.
+
+**Mahalanobis distance** — measures how far a predicted coefficient vector $\hat{\mathbf{c}}_t$ departs from the training distribution. First, the mean and regularised covariance of the training coefficients $\{\mathbf{c}_t\}_{t=1}^{K}$ are computed:
+
+$$
+\boldsymbol{\mu} = \frac{1}{K} \sum_{t=1}^{K} \mathbf{c}_t
+$$
+
+$$
+\mathbf{D} = \frac{1}{K-1} \sum_{t=1}^{K} \left(\mathbf{c}_t - \boldsymbol{\mu}\right)\left(\mathbf{c}_t - \boldsymbol{\mu}\right)^T + \varepsilon \mathbf{I}_r, \quad \varepsilon > 0
+$$
+
+The squared Mahalanobis distance for any coefficient vector $\mathbf{a} \in \mathbb{R}^r$ is:
+
+$$
+d^2(\mathbf{a}) = \left(\mathbf{a} - \boldsymbol{\mu}\right)^T \mathbf{D}^{-1} \left(\mathbf{a} - \boldsymbol{\mu}\right)
+$$
+
+The solver is recalled when the prediction exceeds the threshold:
+
+$$
+d^2\!\left(\hat{\mathbf{c}}_t\right) > \theta_{\text{mah}}
+$$
+
+**Ensemble uncertainty quantification (UQ)** — an ensemble of $M$ predictors $\{F_\theta\}_{m=1}^{M}$ is trained. At each step $t$, each member produces $\hat{\mathbf{c}}_t^{(m)}$. The per-mode standard deviation is:
+
+$$
+s_k(t) = \mathrm{Std}\!\left(\hat{c}_{k,t}^{(m)}\right)
+$$
+
+A scalar uncertainty weighted by modal energy ($w_k = \sigma_k^2$) is then defined as:
+
+$$
+\sigma_E(t) = \sqrt{\frac{\displaystyle\sum_{k=1}^{r} \left(w_k\, s_k(t)\right)^2}{\displaystyle\sum_{k=1}^{r} w_k^2}}
+$$
+
+The solver is recalled when $\sigma_E(t)$ exceeds a user-defined threshold (`sigma_thr` in `config.yaml`).
+
+---
+
+## 4. File structure
+
+### What you need inside your OpenFOAM case directory
+
+```
+my_openfoam_case/
+│
+├── 0/ ← OpenFOAM initial conditions (U, p, …)
+├── constant/ ← mesh + fluid properties (unchanged)
+├── system/
+│ ├── controlDict ← auto-modified by directorpython.py — do not set startTime/endTime manually
+│ └── decomposeParDict ← numberOfSubdomains and n-tuple auto-updated from config.yaml
+│ └── ...
+│
+├── pyPseudo_Adaptive_parallel/ ← copy this folder from the repository
+│ ├── directorpython.py ← Python orchestrator: reads config.yaml and drives the full loop
+│ ├── field_read_write.py ← reads and writes OpenFOAM binary and ASCII fields
+│ ├── load_data.py ← snapshot loading and preprocessing
+│ ├── forecasting_and_uq.py ← SVD, LSTM training, prediction, and UQ metrics
+│ ├── Forecasting/
+│ │ ├── nn_lstm.py ← LSTM architecture definition (PyTorch)
+│ │ └── model_trainer.py ← training loop and checkpointing logic
+│ └── training_inference_module.py ← top-level training / inference interface
+│
+└── config.yaml ← the only file you need to edit
+```
+
+**How `directorpython.py` drives the loop:**
+
+For each iteration it automatically:
+1. Reads `config.yaml`
+2. Auto-detects OpenFOAM (or uses `ENV_OF` if set)
+3. Updates `system/controlDict` (`startTime`, `endTime`, `writeInterval`, `writeFormat`) via `foamDictionary`
+4. Runs `mpirun -np N -parallel` and writes the log to `log_OF__.txt`
+5. Runs `reconstructPar` and deletes `processor*/` folders
+6. Calls the ML module with the parameters from `config.yaml`
+7. Goes back to step 3 for the next iteration
+
+You never need to touch any of these files.
+
+---
+
+## 5. Environment setup
+
+### 5.1 Create a Python virtual environment
+
+You need **Python 3.10** (or 3.9+). Choose either `venv` or `conda`:
+
+```bash
+# Option A — venv (recommended, no extra install needed)
+python3 -m venv ~/envs/cfd_ml
+source ~/envs/cfd_ml/bin/activate
+
+# Option B — conda
+conda create -n cfd_ml python=3.10 -y
+conda activate cfd_ml
+```
+
+
+### 5.2 Install dependencies
+
+```bash
+pip install torch numpy scipy matplotlib pyyaml psutil
+```
+
+Verify that everything installed correctly:
+
+```bash
+python -c "import torch, yaml, psutil; print('OK')"
+# Expected output: OK
+```
+
+### 5.3 Point config.yaml to your environment
+
+In `config.yaml`, under `parametros_entorno`, set `ENV_CONDA` to the full path of your Python binary:
+
+```yaml
+parametros_entorno:
+ ENV_CONDA: "/home/youruser/envs/cfd_ml/bin/python" # venv
+ # ENV_CONDA: "/home/w460/youruser/.conda/envs/cfd_ml/bin/python" # Magerit conda
+ # ENV_CONDA: "NULL" # use system python3
+```
+
+`directorpython.py` auto-detects OpenFOAM by searching `/opt/openfoam*/etc/bashrc`. If your installation is elsewhere, set the full path:
+
+```yaml
+ ENV_OF: "/media/apps/avx512-2021/software/OpenFOAM/12-foss-2023a/OpenFOAM-12/etc/bashrc"
+ # ENV_OF: "NULL" # auto-detect
+```
+
+---
+
+## 6. config.yaml — all parameters
+
+The entire experiment is controlled from a single YAML file. Below is every section and every parameter.
+
+### Overview
+
+| Section | What it controls |
+|---|---|
+| `parametros_entorno` | OpenFOAM path, Python path, CPU cores, SOLO_PYTHON mode |
+| `parametros_flujo` | Timing: T₀, OF window, ML window, overlap, number of iterations |
+| `parametros_caso` | Case name, variables to predict |
+| `parametros_svd` | SVD/lcSVD model, number of modes, sensors |
+| `parametros_lstm` | LSTM architecture, training hyperparameters |
+| `parametros_umbrales` | CE/TE thresholds to trigger early OF restart |
+| `parametros_rutas` | Paths to CFD data, saved models, sensors |
+
+> **All parameters live here. You never need to open any `.py` file.**
+
+---
+
+### 6.1 `parametros_entorno` — Environment
+
+```yaml
+parametros_entorno:
+ ENV_OF: "NULL" # OpenFOAM bashrc path; NULL = auto-detect
+ ENV_CONDA: "/home/user/envs/cfd_ml/bin/python" # path to your Python binary
+ N_PROCESOS: 4 # MPI cores for OpenFOAM
+ DECOMP_N: [2, 2, 1] # domain decomposition: n1×n2×n3 must equal N_PROCESOS
+ SOLO_PYTHON: false # true = ML only; false = full OF + ML loop
+```
+
+| Parameter | Description |
+|---|---|
+| `ENV_OF` | Full path to OpenFOAM's `etc/bashrc`. `"NULL"` auto-detects from `/opt/openfoam*`. |
+| `ENV_CONDA` | Full path to the Python interpreter in your virtual environment. `"NULL"` uses system `python3`. |
+| `N_PROCESOS` | Number of MPI subdomains for `mpirun`. `directorpython.py` writes this to `numberOfSubdomains` in `system/decomposeParDict` automatically. |
+| `DECOMP_N` | Spatial decomposition tuple `[n1, n2, n3]` where `n1 × n2 × n3 = N_PROCESOS`. Written to `{method}Coeffs/n` in `decomposeParDict`. Example: 48 cores → `[6, 4, 2]`. |
+| `SOLO_PYTHON` | `true` skips OpenFOAM and only runs ML on pre-existing snapshots. `false` runs the full loop. |
+
+---
+
+### 6.2 `parametros_flujo` — Timing
+
+```yaml
+parametros_flujo:
+ INITIAL_TIME: 100 # start time of the first CFD block
+ TIME_PERIOD_OF: 100 # duration of each OpenFOAM window (seconds)
+ TIME_PERIOD_PY: 100 # duration of each ML prediction window (seconds)
+ TIME_STEP_WRITE_OF: 1 # OpenFOAM writeInterval (seconds)
+ TIME_STEP_WRITE_PY: 1 # time resolution of ML predictions (seconds)
+ TIME_AHEAD: 5 # OF–ML overlap (seconds); typically 5 × TIME_STEP_WRITE_OF
+ NUM_ITERATIONS: 2 # number of OF + ML rounds
+
+ # Only used when SOLO_PYTHON: true
+ START_TIME_SP: -- # first available snapshot
+ MID_TIME_SP: -- # end of training data
+ END_TIME_SP: -- # end of prediction
+```
+
+| Parameter | Description |
+|---|---|
+| `INITIAL_TIME` | Physical time at which the first OpenFOAM block starts. |
+| `TIME_PERIOD_OF` | How long (in simulated seconds) OpenFOAM runs per round before handing off to ML. |
+| `TIME_PERIOD_PY` | How far ahead (in simulated seconds) the LSTM predicts per round. |
+| `TIME_STEP_WRITE_OF` | OpenFOAM `writeInterval`. Sets the temporal resolution of the training snapshots. |
+| `TIME_STEP_WRITE_PY` | Time step between consecutive ML prediction outputs. Can be coarser than `TIME_STEP_WRITE_OF`. |
+| `TIME_AHEAD` | Overlap between the end of the ML block and the OF restart point. Prevents discontinuities at the handoff. Typically `5 × TIME_STEP_WRITE_OF`. |
+| `NUM_ITERATIONS` | Total number of OF→ML cycles. |
+| `START_TIME_SP` | (SOLO_PYTHON only) First time folder present in `CFD_DIR`. |
+| `MID_TIME_SP` | (SOLO_PYTHON only) Boundary between training data and prediction data. |
+| `END_TIME_SP` | (SOLO_PYTHON only) Last time instant to predict. |
+
+---
+
+### 6.3 `parametros_caso` — Case
+
+```yaml
+parametros_caso:
+ CASE_NAME: "cylinder" # used to name saved model files
+ VARIABLES:
+ - "U"
+ - "p"
+```
+
+| Parameter | Description |
+|---|---|
+| `CASE_NAME` | Short label for your experiment. Appears in the names of saved model files (e.g., `cylinder_0.003_1000_10_18_saved_models/`). |
+| `VARIABLES` | List of OpenFOAM field names to predict. `U` is a vector field (3 components); `p` is a scalar. Add as many as your case has. |
+
+---
+
+### 6.4 `parametros_svd` — Dimensionality reduction
+
+```yaml
+parametros_svd:
+ SVD_MODEL: "SVD" # "SVD" (standard) or "lcSVD" (low-cost, sensor-based)
+ NUM_MODES: 5 # number of POD modes r₀ to retain
+ NUM_SENSORS: -- # sensor points (lcSVD only)
+ RRMSE_CRITI: -- # acceptable RRMSE % for sensor selection (lcSVD only)
+ OVERLAP_POINTS: -- # spatial overlap between parallel processing chunks
+```
+
+| Parameter | Description |
+|---|---|
+| `SVD_MODEL` | `"SVD"` for standard truncated SVD (recommended). `"lcSVD"` for the low-cost sensor-based variant suited to very large meshes. |
+| `NUM_MODES` | Number of POD modes to keep. **Start with 5 and tune.** Increase if reconstruction RRMSE is too high; decrease if training is slow. This is the most influential hyperparameter. |
+| `NUM_SENSORS` | (lcSVD only) Number of sensor measurement points. |
+| `RRMSE_CRITI` | (lcSVD only) Target RRMSE (%) when optimising sensor placement. |
+| `OVERLAP_POINTS` | Spatial overlap between domain chunks when processing in parallel, to avoid boundary artefacts. |
+
+---
+
+### 6.5 `parametros_lstm` — LSTM architecture and training
+
+```yaml
+parametros_lstm:
+ MODEL_NAME: "lstm"
+ HIDDEN_SIZE: 100 # hidden units in the LSTM cell
+ NUM_LAYERS: 1 # stacked LSTM layers
+ INP_SEQ: 8 # input window length (past time steps fed to the model)
+ OUT_SEQ: 1 # steps predicted at once (always keep at 1)
+ STRIDE: 0
+ STEP: 1
+ VAL_SIZE: 0
+ EPOCHS: 1000 # maximum training epochs
+ LOSS_THRESHOLD: 1.0e-8 # stop early if loss drops below this value
+ BATCH_SIZE: 4
+ OPTIMIZER_NAME: "Adam"
+ OPTIMIZER_HPARAMS:
+ lr: 1.0e-3
+ weight_decay: 1.0e-4
+ VERSION: 0 # checkpoint version index
+ SNAP_STEP: 1.0
+ NUM_PREDS: 100 # number of auto-regressive predictions per ML round
+```
+
+| Parameter | Description |
+|---|---|
+| `HIDDEN_SIZE` | Width of the LSTM hidden state. Start with 100. Larger values are more expressive but slower. |
+| `NUM_LAYERS` | Number of stacked LSTM layers. 1 is usually enough. |
+| `INP_SEQ` | Length of the sliding input window (in time steps). **Start with 8 and tune.** This is the second most influential hyperparameter. |
+| `OUT_SEQ` | Always keep at 1. The model predicts one step at a time in auto-regressive mode. |
+| `EPOCHS` | Maximum training epochs. Training stops early when `LOSS_THRESHOLD` is reached. |
+| `LOSS_THRESHOLD` | MSE stopping criterion. Relax to `1e-6` if training takes too long. |
+| `BATCH_SIZE` | Number of (input, output) sequence pairs per gradient update. |
+| `OPTIMIZER_HPARAMS.lr` | Adam learning rate. `1e-3` is a good default. |
+| `VERSION` | Used to distinguish checkpoint files across experiments. |
+| `NUM_PREDS` | Number of auto-regressive prediction steps per ML round. |
+
+---
+
+### 6.6 `parametros_umbrales` — Thresholds (CE / TE)
+
+```yaml
+parametros_umbrales:
+ CE_THRESHOLD: 1.0e+9 # consistency estimate threshold
+ TE_THRESHOLD: 1.0e+9 # truncation estimate threshold
+```
+
+When set to `1e9` (the default), both thresholds are effectively **disabled** and the pipeline uses fixed time windows (`TIME_PERIOD_PY`). To enable automatic early restart of OpenFOAM when prediction quality degrades, set these to meaningful values based on your flow (consult Zou 2025 for derivation).
+
+---
+
+### 6.7 `parametros_rutas` — Paths
+
+```yaml
+parametros_rutas:
+ CFD_DIR: "/path/to/my_openfoam_case"
+ SAVED_MODELS_DIR: "./pyPseudo_Adaptive_parallel"
+ SENSORS_DIR: "./pyPseudo_Adaptive_parallel/sensors"
+```
+
+| Parameter | Description |
+|---|---|
+| `CFD_DIR` | Absolute path to the OpenFOAM case directory (contains `0/`, `constant/`, `system/`). |
+| `SAVED_MODELS_DIR` | Where LSTM checkpoint `.pt` files are saved. Relative paths are resolved from `CFD_DIR`. |
+| `SENSORS_DIR` | Sensor location files (only needed for lcSVD mode). |
+
+---
+
+## 7. Run the pipeline
+
+Two modes are available. Choose based on whether you already have CFD snapshots.
+
+---
+
+### 7.1 SOLO_PYTHON mode — offline prediction on pre-computed snapshots
+
+Use this when you already have all the OpenFOAM time folders and want to test the ML module without running the solver.
+
+**Step 1 — Enable SOLO_PYTHON and define the time split**
+
+```yaml
+parametros_entorno:
+ SOLO_PYTHON: false
+
+parametros_flujo:
+ START_TIME_SP: 100 # first available time folder
+ MID_TIME_SP: 200 # training uses [START → MID]
+ END_TIME_SP: 300 # ML predicts [MID → END]
+```
+
+**Step 2 — Point to the data**
+
+```yaml
+parametros_rutas:
+ CFD_DIR: "/path/to/my_case_snapshots" # must contain 100/, 101/, 102/, …
+```
+
+**Step 3 — Run**
+
+```bash
+cd /path/to/my_openfoam_case/
+python pyPseudo_Adaptive_parallel/directorpython.py
+```
+
+Log created automatically:
+
+```
+log_py_100_200.txt ← epoch / loss per iteration + RRMSE of reconstruction
+```
+
+---
+
+### 7.2 Full adaptive loop — OpenFOAM + ML
+
+Use this for a live simulation where the CFD solver and the ML model alternate.
+
+**Step 1 — Configure the timing**
+
+```yaml
+parametros_entorno:
+ SOLO_PYTHON: false
+ N_PROCESOS: 48 # directorpython.py updates numberOfSubdomains automatically
+ DECOMP_N: [6, 4, 2] # 6×4×2 = 48 — updates hierarchicalCoeffs/n automatically
+
+parametros_flujo:
+ INITIAL_TIME: 100
+ TIME_PERIOD_OF: 100 # OF runs for 100 s = 100 snaps at dt=1
+ TIME_PERIOD_PY: 100 # ML predicts 100 s = 100 snaps
+ TIME_STEP_WRITE_OF: 1
+ TIME_AHEAD: 5 # 5-snapshot overlap
+ NUM_ITERATIONS: 2
+```
+
+**Step 2 — Run**
+
+```bash
+cd /path/to/my_openfoam_case/
+python pyPseudo_Adaptive_parallel/directorpython.py
+```
+
+Expected log files (for `NUM_ITERATIONS=2`):
+
+```
+log_OF_100_200.txt ← OpenFOAM round 1
+log_py_200_300.txt ← ML round 1
+log_OF_295_400.txt ← OpenFOAM round 2 (restarts from T₁ − TIME_AHEAD)
+log_py_400_500.txt ← ML round 2
+```
+
+---
+
+### 7.3 Output files and how to interpret them
+
+After a successful run:
+
+```
+my_openfoam_case/
+├── 100/, 101/, …, 200/ ← CFD snapshots (OpenFOAM format)
+├── 200/, 201/, …, 300/ ← ML predictions (same OpenFOAM format)
+├── log_OF_100_200.txt ← OpenFOAM residuals log
+├── log_py_200_300.txt ← ML training + prediction log
+└── pyPseudo_Adaptive_parallel/
+ └── cylinder_1_100_100_5_saved_models/
+ ├── best_model_z_0.pt ← LSTM weights for field U
+ └── best_model_p_0.pt ← LSTM weights for field p
+```
+
+**Reading the logs:**
+
+- `log_OF_*.txt` — standard OpenFOAM log: PISO residuals, continuity errors, same as any standalone OpenFOAM run.
+- `log_py_*.txt` — ML log: epoch number, MSE loss, and SVD reconstruction RRMSE for each variable. A healthy run shows steadily decreasing loss and RRMSE below ~5%.
+- **Success:** last line reads `"All iterations completed successfully!"` — everything worked.
+- **ERROR:** check `ENV_OF` (OpenFOAM not found) or `ENV_CONDA` (Python not found), and confirm your OpenFOAM case runs correctly on its own before using the pipeline.
+
+**Visualising results in ParaView:**
+
+The ML prediction folders are standard OpenFOAM time folders. Open them alongside the CFD folders:
+
+```
+ParaView → File > Open → select your .foam file
+ → Apply → scrub through time → select field (U, p, …)
+```
+
+Both CFD and ML time steps appear in the same timeline, letting you compare them directly.
+
+---
+
+## 8. Summary / Checklist
+
+Use this checklist every time you set up a new experiment from scratch.
+
+### Step 1 — Install the environment (once per machine)
+
+```bash
+python3 -m venv ~/envs/cfd_ml
+source ~/envs/cfd_ml/bin/activate
+pip install torch numpy scipy matplotlib pyyaml psutil
+
+python -c "import torch, yaml, psutil; print('OK')"
+```
+
+### Step 2 — Copy the code into your OpenFOAM case
+
+```bash
+cp -r /path/to/repo/pyPseudo_Adaptive_parallel/ /path/to/my_openfoam_case/
+cp /path/to/repo/config.yaml /path/to/my_openfoam_case/
+```
+
+### Step 3 — Edit config.yaml
+
+These are the fields you **must** set before running:
+
+| Field | Section | What to put |
+|---|---|---|
+| `ENV_CONDA` | `parametros_entorno` | Full path to your Python binary |
+| `ENV_OF` | `parametros_entorno` | OpenFOAM bashrc path, or `"NULL"` |
+| `N_PROCESOS` | `parametros_entorno` | MPI cores (auto-written to `decomposeParDict`) |
+| `DECOMP_N` | `parametros_entorno` | `[n1, n2, n3]` with n1×n2×n3 = N_PROCESOS (auto-written to `decomposeParDict`) |
+| `SOLO_PYTHON` | `parametros_entorno` | `true` (offline) or `false` (full loop) |
+| `VARIABLES` | `parametros_caso` | Fields to predict, e.g. `["U", "p"]` |
+| `CASE_NAME` | `parametros_caso` | Short name for your experiment |
+| `CFD_DIR` | `parametros_rutas` | Absolute path to your OpenFOAM case |
+| `NUM_MODES` | `parametros_svd` | Start with 10, tune up/down |
+| `INP_SEQ` | `parametros_lstm` | Start with 8, tune up/down |
+| `INITIAL_TIME` / `TIME_PERIOD_OF` / `TIME_PERIOD_PY` | `parametros_flujo` | (full loop) your simulation timing |
+| `START_TIME_SP` / `MID_TIME_SP` / `END_TIME_SP` | `parametros_flujo` | (SOLO_PYTHON) your data split |
+
+### Step 4 — Run
+
+```bash
+cd /path/to/my_openfoam_case/
+python pyPseudo_Adaptive_parallel/directorpython.py
+```
+
+### Step 5 — Verify
+
+```bash
+tail -1 log_py_*.txt
+# Expected: "All iterations completed successfully!"
+```
+
+Then open ParaView, load your `.foam` file, and compare predicted vs CFD fields across the time timeline.
+
+---
+
+> **Summary:** a single `config.yaml` and a single `python` command replace all the scattered shell scripts and Python files of the original implementation. The pipeline works with any OpenFOAM case — point `CFD_DIR` to your case, set your variables, and run.
+
+---
+
+*ModelFLOWs-UPM · Adaptive CFD-LSTM · POD/SVD + Deep Learning*
+*pyPseudo_Adaptive_parallel/ + config.yaml*
+*Based on: X. Zou et al. (ModelFLOWs-UPM, 2025)*
diff --git a/_tutorials/tutorialAdaptive.md b/_tutorials/tutorialAdaptive.md
new file mode 100644
index 000000000000..61dae1a885f1
--- /dev/null
+++ b/_tutorials/tutorialAdaptive.md
@@ -0,0 +1,693 @@
+---
+layout: page
+title: "Adaptive CFD–POD–LSTM Pipeline"
+application: "Urban Flows"
+category: "AI & Data-Driven Models"
+tldr: "Step-by-step tutorial to run the generalized adaptive prediction pipeline combining OpenFOAM (CFD) and a POD+LSTM surrogate model. All parameters controlled from a single config.yaml file."
+author: "Xiangrui Zou, Carlos Sainz García, Mikel Navarro Huarte"
+sphinx_repository: "https://github.com/modelflows/adaptive-cfd"
+tutorial_file: "TUTORIAL.md"
+---
+
+# Overview
+
+This tutorial explains how to use the generalized **adaptive CFD–POD–LSTM pipeline** developed at ETSIAE–UPM. The pipeline couples an OpenFOAM CFD solver with a data-driven surrogate model (Proper Orthogonal Decomposition + Long Short-Term Memory network) in a closed loop. At each iteration, OpenFOAM generates a block of flow snapshots, the LSTM is trained on them and predicts the next block, and the solver restarts — keeping prediction accuracy controlled over arbitrarily long horizons.
+
+The generalization introduced in this work replaces all scattered shell and Python configuration files from the original implementation (Zou 2025) with a single `config.yaml` file and a single Python orchestrator (`directorpython.py`), making the pipeline portable to any OpenFOAM case.
+
+# What This Tutorial Covers
+
+- What CFD adaptive prediction is and why it is needed.
+- How the CFD–POD–LSTM loop works (adaptive framework).
+- How POD via truncated SVD reduces the problem dimensionality.
+- How the LSTM is trained and used for auto-regressive prediction.
+- How to install the required Python environment.
+- How to copy the pipeline into any OpenFOAM case.
+- How to configure every parameter in `config.yaml`.
+- How to run in offline mode (`SOLO_PYTHON=true`) on pre-computed snapshots.
+- How to run the full adaptive loop (`SOLO_PYTHON=false`) with a live OpenFOAM simulation.
+- How to read the output logs and visualise results in ParaView.
+
+# Related Links
+
+- Notebook: *(to be added)*
+- Video: *(to be added)*
+- Dataset: *(to be added)*
+- Application hub:
+
+# Contributors
+- Xiangrui Zou
+- Carlos Sainz García
+- Mikel Navarro Huarte
+
+---
+
+## Full Tutorial Content
+
+### Table of Contents
+
+1. [Introduction](#1-introduction)
+2. [The adaptive framework](#2-the-adaptive-framework)
+3. [Methodology: POD + LSTM](#3-methodology-pod--lstm)
+4. [File structure](#4-file-structure)
+5. [Environment setup](#5-environment-setup)
+6. [config.yaml — all parameters](#6-configyaml--all-parameters)
+7. [Run the pipeline](#7-run-the-pipeline)
+8. [Summary / Checklist](#8-summary--checklist)
+
+---
+
+## 1. Introduction
+
+### The challenge of high-fidelity simulations
+
+Computational Fluid Dynamics (CFD) solvers — such as OpenFOAM — numerically solve the Navier–Stokes equations to predict how a fluid (air, water, etc.) moves through a geometry. To get accurate results, the solver must resolve all relevant spatial and temporal scales of the flow, which translates directly into very fine meshes and very small time steps.
+
+This accuracy comes at a steep computational price:
+- A single simulation of a realistic case can take hours or days on a cluster.
+- Engineering tasks like parameter optimisation or uncertainty quantification require **thousands** of such evaluations.
+- Running all of them with a full CFD solver is often completely infeasible.
+
+### Why machine learning?
+
+Data-driven surrogate models can learn the dynamics of a flow from a set of precomputed snapshots and then **predict** future states in a fraction of the time. The idea is to let the CFD solver run for a while, collect its output, train a neural network, and then let the network take over prediction — bypassing the expensive solver.
+
+The key challenge is **prediction drift**: purely data-driven models are trained once and their accuracy degrades over time as the flow evolves and the model encounters conditions not seen during training.
+
+### Adaptive prediction: the solution
+
+Adaptive prediction solves the drift problem by coupling the ML model with the live CFD solver in a closed loop. Instead of training once and predicting forever, the pipeline alternates between:
+
+1. **CFD solver** — generates new, accurate snapshots for a window of time.
+2. **ML model (POD-DL)** — trains on those snapshots and predicts the next window.
+3. **Repeat** — the solver restarts from near the end of the ML prediction, corrects any drift, and the cycle continues.
+
+This way, accuracy remains controlled over arbitrarily long prediction horizons and the model stays robust to shifts in the flow regime.
+
+> **The only file you need to edit is `config.yaml`. The only command you need to run is `python pyPseudo_Adaptive_parallel/directorpython.py`.**
+
+---
+
+## 2. The adaptive framework
+
+### How the loop works
+
+The pipeline alternates between blocks of CFD simulation and blocks of ML prediction:
+
+
+
+At each iteration:
+
+1. **OpenFOAM runs** from `T₀` to `T₁`, saving a snapshot of the flow field every `TIME_STEP_WRITE_OF` seconds.
+2. **POD-DL reads** those snapshots, applies truncated SVD to reduce them to a low-dimensional representation, and trains an LSTM on the resulting temporal coefficients.
+3. **LSTM predicts** the coefficients from `T₁` to `T₂` in an auto-regressive loop; the full flow field is reconstructed from those coefficients.
+4. **OpenFOAM restarts** from `T₂ − TIME_AHEAD` (a small overlap ensures continuity between the CFD and ML blocks).
+5. Steps 1–4 repeat `NUM_ITERATIONS` times.
+
+### Key design choices
+
+| Concept | What it means |
+|---|---|
+| `TIME_AHEAD` | A small temporal overlap (typically 5 snapshots × `dt`) so that OpenFOAM does not start from a discontinuity when it picks up after the ML block. |
+| Transfer learning | The LSTM is not retrained from scratch every iteration. If a checkpoint exists from the previous round, the model is loaded and fine-tuned on the new data — each successive round trains faster. |
+| `SOLO_PYTHON=true` | Skips OpenFOAM entirely. All snapshots must already exist in `CFD_DIR`. Useful for offline experiments, first validation, or debugging the ML module without running a full simulation. |
+
+---
+
+## 3. Methodology: POD + LSTM
+
+### Why dimensionality reduction?
+
+A typical CFD mesh has millions of cells and each field (velocity, pressure) is a vector at every cell. Feeding raw snapshots directly into a neural network would be computationally prohibitive. Proper Orthogonal Decomposition (POD) compresses the data: instead of predicting millions of numbers, the LSTM only needs to predict a handful of **temporal coefficients**.
+
+### POD via truncated SVD
+
+Given a snapshot matrix $\mathbf{X} \in \mathbb{R}^{N_{\text{cells}} \times K}$ where each column is one time snapshot:
+
+$$
+\mathbf{X} = \mathbf{U} \boldsymbol{\Sigma} \mathbf{V}^T \qquad \text{(full SVD)}
+$$
+
+$$
+\mathbf{X}_r \approx \mathbf{U}_r \boldsymbol{\Sigma}_r \mathbf{V}_r^T \qquad \text{(truncated to } r_0 \text{ dominant modes)}
+$$
+
+$$
+\mathbf{C} = \mathbf{V}_r^T \in \mathbb{R}^{r_0 \times K} \qquad \text{(temporal coefficients)}
+$$
+
+- $\mathbf{U}_r$ — spatial POD modes (basis vectors), shape: $N_{\text{cells}} \times r_0$
+- $\boldsymbol{\Sigma}_r$ — singular values ($\sigma_i^2 \propto$ energy of mode $i$)
+- $\mathbf{C}$ — temporal coefficients: how much each mode contributes at each time step
+
+The reconstruction error is measured by the **RRMSE**:
+
+$$
+RRMSE = \frac{\left\| \mathbf{X} - \mathbf{U}_r \boldsymbol{\Sigma}_r \mathbf{V}_r^T \right\|_F}{\left\| \mathbf{X} \right\|_F}
+$$
+
+A good choice of `NUM_MODES` ($r_0$) keeps this below ~5 %.
+
+### LSTM prediction
+
+The LSTM receives the last `INP_SEQ` columns of $\mathbf{C}$ (a sliding window of $L$ past coefficient vectors) and predicts the next one:
+
+$$
+\mathbf{c}(t-L+1),\, \ldots,\, \mathbf{c}(t) \;\xrightarrow{\text{LSTM}}\; \mathbf{c}(t+1)
+$$
+
+This is repeated auto-regressively for `NUM_PREDS` steps. The full predicted field is then reconstructed as:
+
+$$
+\hat{\mathbf{X}}_{\text{pred}} \approx \mathbf{U}_r \boldsymbol{\Sigma}_r \hat{\mathbf{C}}
+$$
+
+### Training
+
+- **Loss:** Mean Squared Error (MSE) between predicted and true temporal coefficients:
+
+$$
+\text{MSE} = \frac{1}{Npr} \sum_{n=1}^{N} \sum_{t=1}^{p} \sum_{k=1}^{r} \left(\hat{c}_{n,t,k} - c_{n,t,k}\right)^2 = \frac{1}{Npr} \sum_{n,t} \left\| \hat{\mathbf{c}}_{n,t} - \mathbf{c}_{n,t} \right\|_2^2
+$$
+
+- **Checkpoint:** if a model from the previous iteration exists, it is loaded and fine-tuned (transfer learning). If no checkpoint is found, training starts from scratch.
+- **Early stopping:** training halts when loss drops below `LOSS_THRESHOLD`.
+
+> **The two hyperparameters with the greatest impact on prediction quality are `NUM_MODES` ($r_0$) and `INP_SEQ` ($L$). Start with `NUM_MODES=5` and `INP_SEQ=8` and tune from there.**
+
+### Divergence monitoring: CE and TE
+
+To decide when to restart the CFD solver, the pipeline monitors two scalar error metrics at each predicted time step $t$:
+
+**Consistency Estimate (CE)** — measures how far the LSTM prediction $\hat{\boldsymbol{u}}(t)$ has drifted from the POD-reconstructed reference $\boldsymbol{u}(t)$ computed on the latest CFD window:
+
+$$
+\text{CE}(t) = \frac{\left\| \hat{\boldsymbol{u}}(t) - \boldsymbol{u}(t) \right\|}{\left\| \boldsymbol{u}(t) \right\|}
+$$
+
+**Truncation Estimate (TE)** — measures the POD basis error: how much information is lost by retaining only $r_0$ modes from the current CFD snapshot set:
+
+$$
+\text{TE}(t) = \frac{\left\| \hat{\boldsymbol{u}}(t) - \boldsymbol{u}(t) \right\|}{\left\| \boldsymbol{u}(t) \right\|}
+$$
+
+The CFD solver is automatically invoked when either metric exceeds its threshold (`CE_THRESHOLD` or `TE_THRESHOLD` in `config.yaml`). In fixed-interval mode both thresholds are set to $10^9$ (disabled) and the solver restarts after a fixed number of predicted snapshots.
+
+### Divergence monitoring: Mahalanobis distance and ensemble UQ
+
+Two additional criteria are available for more sophisticated adaptive triggering.
+
+**Mahalanobis distance** — measures how far a predicted coefficient vector $\hat{\mathbf{c}}_t$ departs from the training distribution. First, the mean and regularised covariance of the training coefficients $\{\mathbf{c}_t\}_{t=1}^{K}$ are computed:
+
+$$
+\boldsymbol{\mu} = \frac{1}{K} \sum_{t=1}^{K} \mathbf{c}_t
+$$
+
+$$
+\mathbf{D} = \frac{1}{K-1} \sum_{t=1}^{K} \left(\mathbf{c}_t - \boldsymbol{\mu}\right)\left(\mathbf{c}_t - \boldsymbol{\mu}\right)^T + \varepsilon \mathbf{I}_r, \quad \varepsilon > 0
+$$
+
+The squared Mahalanobis distance for any coefficient vector $\mathbf{a} \in \mathbb{R}^r$ is:
+
+$$
+d^2(\mathbf{a}) = \left(\mathbf{a} - \boldsymbol{\mu}\right)^T \mathbf{D}^{-1} \left(\mathbf{a} - \boldsymbol{\mu}\right)
+$$
+
+The solver is recalled when the prediction exceeds the threshold:
+
+$$
+d^2\!\left(\hat{\mathbf{c}}_t\right) > \theta_{\text{mah}}
+$$
+
+**Ensemble uncertainty quantification (UQ)** — an ensemble of $M$ predictors $\{F_\theta\}_{m=1}^{M}$ is trained. At each step $t$, each member produces $\hat{\mathbf{c}}_t^{(m)}$. The per-mode standard deviation is:
+
+$$
+s_k(t) = \mathrm{Std}\!\left(\hat{c}_{k,t}^{(m)}\right)
+$$
+
+A scalar uncertainty weighted by modal energy ($w_k = \sigma_k^2$) is then defined as:
+
+$$
+\sigma_E(t) = \sqrt{\frac{\displaystyle\sum_{k=1}^{r} \left(w_k\, s_k(t)\right)^2}{\displaystyle\sum_{k=1}^{r} w_k^2}}
+$$
+
+The solver is recalled when $\sigma_E(t)$ exceeds a user-defined threshold (`sigma_thr` in `config.yaml`).
+
+---
+
+## 4. File structure
+
+### What you need inside your OpenFOAM case directory
+
+```
+my_openfoam_case/
+│
+├── 0/ ← OpenFOAM initial conditions (U, p, …)
+├── constant/ ← mesh + fluid properties (unchanged)
+├── system/
+│ ├── controlDict ← auto-modified by directorpython.py — do not set startTime/endTime manually
+│ └── decomposeParDict ← numberOfSubdomains and n-tuple auto-updated from config.yaml
+│ └── ...
+│
+├── pyPseudo_Adaptive_parallel/ ← copy this folder from the repository
+│ ├── directorpython.py ← Python orchestrator: reads config.yaml and drives the full loop
+│ ├── field_read_write.py ← reads and writes OpenFOAM binary and ASCII fields
+│ ├── load_data.py ← snapshot loading and preprocessing
+│ ├── forecasting_and_uq.py ← SVD, LSTM training, prediction, and UQ metrics
+│ ├── Forecasting/
+│ │ ├── nn_lstm.py ← LSTM architecture definition (PyTorch)
+│ │ └── model_trainer.py ← training loop and checkpointing logic
+│ └── training_inference_module.py ← top-level training / inference interface
+│
+└── config.yaml ← the only file you need to edit
+```
+
+**How `directorpython.py` drives the loop:**
+
+For each iteration it automatically:
+1. Reads `config.yaml`
+2. Auto-detects OpenFOAM (or uses `ENV_OF` if set)
+3. Updates `system/controlDict` (`startTime`, `endTime`, `writeInterval`, `writeFormat`) via `foamDictionary`
+4. Runs `mpirun -np N -parallel` and writes the log to `log_OF__.txt`
+5. Runs `reconstructPar` and deletes `processor*/` folders
+6. Calls the ML module with the parameters from `config.yaml`
+7. Goes back to step 3 for the next iteration
+
+You never need to touch any of these files.
+
+---
+
+## 5. Environment setup
+
+### 5.1 Create a Python virtual environment
+
+You need **Python 3.10** (or 3.9+). Choose either `venv` or `conda`:
+
+```bash
+# Option A — venv (recommended, no extra install needed)
+python3 -m venv ~/envs/cfd_ml
+source ~/envs/cfd_ml/bin/activate
+
+# Option B — conda
+conda create -n cfd_ml python=3.10 -y
+conda activate cfd_ml
+```
+
+
+### 5.2 Install dependencies
+
+```bash
+pip install torch numpy scipy matplotlib pyyaml psutil
+```
+
+Verify that everything installed correctly:
+
+```bash
+python -c "import torch, yaml, psutil; print('OK')"
+# Expected output: OK
+```
+
+### 5.3 Point config.yaml to your environment
+
+In `config.yaml`, under `parametros_entorno`, set `ENV_CONDA` to the full path of your Python binary:
+
+```yaml
+parametros_entorno:
+ ENV_CONDA: "/home/youruser/envs/cfd_ml/bin/python" # venv
+ # ENV_CONDA: "/home/w460/youruser/.conda/envs/cfd_ml/bin/python" # Magerit conda
+ # ENV_CONDA: "NULL" # use system python3
+```
+
+`directorpython.py` auto-detects OpenFOAM by searching `/opt/openfoam*/etc/bashrc`. If your installation is elsewhere, set the full path:
+
+```yaml
+ ENV_OF: "/media/apps/avx512-2021/software/OpenFOAM/12-foss-2023a/OpenFOAM-12/etc/bashrc"
+ # ENV_OF: "NULL" # auto-detect
+```
+
+---
+
+## 6. config.yaml — all parameters
+
+The entire experiment is controlled from a single YAML file. Below is every section and every parameter.
+
+### Overview
+
+| Section | What it controls |
+|---|---|
+| `parametros_entorno` | OpenFOAM path, Python path, CPU cores, SOLO_PYTHON mode |
+| `parametros_flujo` | Timing: T₀, OF window, ML window, overlap, number of iterations |
+| `parametros_caso` | Case name, variables to predict |
+| `parametros_svd` | SVD/lcSVD model, number of modes, sensors |
+| `parametros_lstm` | LSTM architecture, training hyperparameters |
+| `parametros_umbrales` | CE/TE thresholds to trigger early OF restart |
+| `parametros_rutas` | Paths to CFD data, saved models, sensors |
+
+> **All parameters live here. You never need to open any `.py` file.**
+
+---
+
+### 6.1 `parametros_entorno` — Environment
+
+```yaml
+parametros_entorno:
+ ENV_OF: "NULL" # OpenFOAM bashrc path; NULL = auto-detect
+ ENV_CONDA: "/home/user/envs/cfd_ml/bin/python" # path to your Python binary
+ N_PROCESOS: 4 # MPI cores for OpenFOAM
+ DECOMP_N: [2, 2, 1] # domain decomposition: n1×n2×n3 must equal N_PROCESOS
+ SOLO_PYTHON: false # true = ML only; false = full OF + ML loop
+```
+
+| Parameter | Description |
+|---|---|
+| `ENV_OF` | Full path to OpenFOAM's `etc/bashrc`. `"NULL"` auto-detects from `/opt/openfoam*`. |
+| `ENV_CONDA` | Full path to the Python interpreter in your virtual environment. `"NULL"` uses system `python3`. |
+| `N_PROCESOS` | Number of MPI subdomains for `mpirun`. `directorpython.py` writes this to `numberOfSubdomains` in `system/decomposeParDict` automatically. |
+| `DECOMP_N` | Spatial decomposition tuple `[n1, n2, n3]` where `n1 × n2 × n3 = N_PROCESOS`. Written to `{method}Coeffs/n` in `decomposeParDict`. Example: 48 cores → `[6, 4, 2]`. |
+| `SOLO_PYTHON` | `true` skips OpenFOAM and only runs ML on pre-existing snapshots. `false` runs the full loop. |
+
+---
+
+### 6.2 `parametros_flujo` — Timing
+
+```yaml
+parametros_flujo:
+ INITIAL_TIME: 100 # start time of the first CFD block
+ TIME_PERIOD_OF: 100 # duration of each OpenFOAM window (seconds)
+ TIME_PERIOD_PY: 100 # duration of each ML prediction window (seconds)
+ TIME_STEP_WRITE_OF: 1 # OpenFOAM writeInterval (seconds)
+ TIME_STEP_WRITE_PY: 1 # time resolution of ML predictions (seconds)
+ TIME_AHEAD: 5 # OF–ML overlap (seconds); typically 5 × TIME_STEP_WRITE_OF
+ NUM_ITERATIONS: 2 # number of OF + ML rounds
+
+ # Only used when SOLO_PYTHON: true
+ START_TIME_SP: -- # first available snapshot
+ MID_TIME_SP: -- # end of training data
+ END_TIME_SP: -- # end of prediction
+```
+
+| Parameter | Description |
+|---|---|
+| `INITIAL_TIME` | Physical time at which the first OpenFOAM block starts. |
+| `TIME_PERIOD_OF` | How long (in simulated seconds) OpenFOAM runs per round before handing off to ML. |
+| `TIME_PERIOD_PY` | How far ahead (in simulated seconds) the LSTM predicts per round. |
+| `TIME_STEP_WRITE_OF` | OpenFOAM `writeInterval`. Sets the temporal resolution of the training snapshots. |
+| `TIME_STEP_WRITE_PY` | Time step between consecutive ML prediction outputs. Can be coarser than `TIME_STEP_WRITE_OF`. |
+| `TIME_AHEAD` | Overlap between the end of the ML block and the OF restart point. Prevents discontinuities at the handoff. Typically `5 × TIME_STEP_WRITE_OF`. |
+| `NUM_ITERATIONS` | Total number of OF→ML cycles. |
+| `START_TIME_SP` | (SOLO_PYTHON only) First time folder present in `CFD_DIR`. |
+| `MID_TIME_SP` | (SOLO_PYTHON only) Boundary between training data and prediction data. |
+| `END_TIME_SP` | (SOLO_PYTHON only) Last time instant to predict. |
+
+---
+
+### 6.3 `parametros_caso` — Case
+
+```yaml
+parametros_caso:
+ CASE_NAME: "cylinder" # used to name saved model files
+ VARIABLES:
+ - "U"
+ - "p"
+```
+
+| Parameter | Description |
+|---|---|
+| `CASE_NAME` | Short label for your experiment. Appears in the names of saved model files (e.g., `cylinder_0.003_1000_10_18_saved_models/`). |
+| `VARIABLES` | List of OpenFOAM field names to predict. `U` is a vector field (3 components); `p` is a scalar. Add as many as your case has. |
+
+---
+
+### 6.4 `parametros_svd` — Dimensionality reduction
+
+```yaml
+parametros_svd:
+ SVD_MODEL: "SVD" # "SVD" (standard) or "lcSVD" (low-cost, sensor-based)
+ NUM_MODES: 5 # number of POD modes r₀ to retain
+ NUM_SENSORS: -- # sensor points (lcSVD only)
+ RRMSE_CRITI: -- # acceptable RRMSE % for sensor selection (lcSVD only)
+ OVERLAP_POINTS: -- # spatial overlap between parallel processing chunks
+```
+
+| Parameter | Description |
+|---|---|
+| `SVD_MODEL` | `"SVD"` for standard truncated SVD (recommended). `"lcSVD"` for the low-cost sensor-based variant suited to very large meshes. |
+| `NUM_MODES` | Number of POD modes to keep. **Start with 5 and tune.** Increase if reconstruction RRMSE is too high; decrease if training is slow. This is the most influential hyperparameter. |
+| `NUM_SENSORS` | (lcSVD only) Number of sensor measurement points. |
+| `RRMSE_CRITI` | (lcSVD only) Target RRMSE (%) when optimising sensor placement. |
+| `OVERLAP_POINTS` | Spatial overlap between domain chunks when processing in parallel, to avoid boundary artefacts. |
+
+---
+
+### 6.5 `parametros_lstm` — LSTM architecture and training
+
+```yaml
+parametros_lstm:
+ MODEL_NAME: "lstm"
+ HIDDEN_SIZE: 100 # hidden units in the LSTM cell
+ NUM_LAYERS: 1 # stacked LSTM layers
+ INP_SEQ: 8 # input window length (past time steps fed to the model)
+ OUT_SEQ: 1 # steps predicted at once (always keep at 1)
+ STRIDE: 0
+ STEP: 1
+ VAL_SIZE: 0
+ EPOCHS: 1000 # maximum training epochs
+ LOSS_THRESHOLD: 1.0e-8 # stop early if loss drops below this value
+ BATCH_SIZE: 4
+ OPTIMIZER_NAME: "Adam"
+ OPTIMIZER_HPARAMS:
+ lr: 1.0e-3
+ weight_decay: 1.0e-4
+ VERSION: 0 # checkpoint version index
+ SNAP_STEP: 1.0
+ NUM_PREDS: 100 # number of auto-regressive predictions per ML round
+```
+
+| Parameter | Description |
+|---|---|
+| `HIDDEN_SIZE` | Width of the LSTM hidden state. Start with 100. Larger values are more expressive but slower. |
+| `NUM_LAYERS` | Number of stacked LSTM layers. 1 is usually enough. |
+| `INP_SEQ` | Length of the sliding input window (in time steps). **Start with 8 and tune.** This is the second most influential hyperparameter. |
+| `OUT_SEQ` | Always keep at 1. The model predicts one step at a time in auto-regressive mode. |
+| `EPOCHS` | Maximum training epochs. Training stops early when `LOSS_THRESHOLD` is reached. |
+| `LOSS_THRESHOLD` | MSE stopping criterion. Relax to `1e-6` if training takes too long. |
+| `BATCH_SIZE` | Number of (input, output) sequence pairs per gradient update. |
+| `OPTIMIZER_HPARAMS.lr` | Adam learning rate. `1e-3` is a good default. |
+| `VERSION` | Used to distinguish checkpoint files across experiments. |
+| `NUM_PREDS` | Number of auto-regressive prediction steps per ML round. |
+
+---
+
+### 6.6 `parametros_umbrales` — Thresholds (CE / TE)
+
+```yaml
+parametros_umbrales:
+ CE_THRESHOLD: 1.0e+9 # consistency estimate threshold
+ TE_THRESHOLD: 1.0e+9 # truncation estimate threshold
+```
+
+When set to `1e9` (the default), both thresholds are effectively **disabled** and the pipeline uses fixed time windows (`TIME_PERIOD_PY`). To enable automatic early restart of OpenFOAM when prediction quality degrades, set these to meaningful values based on your flow (consult Zou 2025 for derivation).
+
+---
+
+### 6.7 `parametros_rutas` — Paths
+
+```yaml
+parametros_rutas:
+ CFD_DIR: "/path/to/my_openfoam_case"
+ SAVED_MODELS_DIR: "./pyPseudo_Adaptive_parallel"
+ SENSORS_DIR: "./pyPseudo_Adaptive_parallel/sensors"
+```
+
+| Parameter | Description |
+|---|---|
+| `CFD_DIR` | Absolute path to the OpenFOAM case directory (contains `0/`, `constant/`, `system/`). |
+| `SAVED_MODELS_DIR` | Where LSTM checkpoint `.pt` files are saved. Relative paths are resolved from `CFD_DIR`. |
+| `SENSORS_DIR` | Sensor location files (only needed for lcSVD mode). |
+
+---
+
+## 7. Run the pipeline
+
+Two modes are available. Choose based on whether you already have CFD snapshots.
+
+---
+
+### 7.1 SOLO_PYTHON mode — offline prediction on pre-computed snapshots
+
+Use this when you already have all the OpenFOAM time folders and want to test the ML module without running the solver.
+
+**Step 1 — Enable SOLO_PYTHON and define the time split**
+
+```yaml
+parametros_entorno:
+ SOLO_PYTHON: false
+
+parametros_flujo:
+ START_TIME_SP: 100 # first available time folder
+ MID_TIME_SP: 200 # training uses [START → MID]
+ END_TIME_SP: 300 # ML predicts [MID → END]
+```
+
+**Step 2 — Point to the data**
+
+```yaml
+parametros_rutas:
+ CFD_DIR: "/path/to/my_case_snapshots" # must contain 100/, 101/, 102/, …
+```
+
+**Step 3 — Run**
+
+```bash
+cd /path/to/my_openfoam_case/
+python pyPseudo_Adaptive_parallel/directorpython.py
+```
+
+Log created automatically:
+
+```
+log_py_100_200.txt ← epoch / loss per iteration + RRMSE of reconstruction
+```
+
+---
+
+### 7.2 Full adaptive loop — OpenFOAM + ML
+
+Use this for a live simulation where the CFD solver and the ML model alternate.
+
+**Step 1 — Configure the timing**
+
+```yaml
+parametros_entorno:
+ SOLO_PYTHON: false
+ N_PROCESOS: 48 # directorpython.py updates numberOfSubdomains automatically
+ DECOMP_N: [6, 4, 2] # 6×4×2 = 48 — updates hierarchicalCoeffs/n automatically
+
+parametros_flujo:
+ INITIAL_TIME: 100
+ TIME_PERIOD_OF: 100 # OF runs for 100 s = 100 snaps at dt=1
+ TIME_PERIOD_PY: 100 # ML predicts 100 s = 100 snaps
+ TIME_STEP_WRITE_OF: 1
+ TIME_AHEAD: 5 # 5-snapshot overlap
+ NUM_ITERATIONS: 2
+```
+
+**Step 2 — Run**
+
+```bash
+cd /path/to/my_openfoam_case/
+python pyPseudo_Adaptive_parallel/directorpython.py
+```
+
+Expected log files (for `NUM_ITERATIONS=2`):
+
+```
+log_OF_100_200.txt ← OpenFOAM round 1
+log_py_200_300.txt ← ML round 1
+log_OF_295_400.txt ← OpenFOAM round 2 (restarts from T₁ − TIME_AHEAD)
+log_py_400_500.txt ← ML round 2
+```
+
+---
+
+### 7.3 Output files and how to interpret them
+
+After a successful run:
+
+```
+my_openfoam_case/
+├── 100/, 101/, …, 200/ ← CFD snapshots (OpenFOAM format)
+├── 200/, 201/, …, 300/ ← ML predictions (same OpenFOAM format)
+├── log_OF_100_200.txt ← OpenFOAM residuals log
+├── log_py_200_300.txt ← ML training + prediction log
+└── pyPseudo_Adaptive_parallel/
+ └── cylinder_1_100_100_5_saved_models/
+ ├── best_model_z_0.pt ← LSTM weights for field U
+ └── best_model_p_0.pt ← LSTM weights for field p
+```
+
+**Reading the logs:**
+
+- `log_OF_*.txt` — standard OpenFOAM log: PISO residuals, continuity errors, same as any standalone OpenFOAM run.
+- `log_py_*.txt` — ML log: epoch number, MSE loss, and SVD reconstruction RRMSE for each variable. A healthy run shows steadily decreasing loss and RRMSE below ~5%.
+- **Success:** last line reads `"All iterations completed successfully!"` — everything worked.
+- **ERROR:** check `ENV_OF` (OpenFOAM not found) or `ENV_CONDA` (Python not found), and confirm your OpenFOAM case runs correctly on its own before using the pipeline.
+
+**Visualising results in ParaView:**
+
+The ML prediction folders are standard OpenFOAM time folders. Open them alongside the CFD folders:
+
+```
+ParaView → File > Open → select your .foam file
+ → Apply → scrub through time → select field (U, p, …)
+```
+
+Both CFD and ML time steps appear in the same timeline, letting you compare them directly.
+
+---
+
+## 8. Summary / Checklist
+
+Use this checklist every time you set up a new experiment from scratch.
+
+### Step 1 — Install the environment (once per machine)
+
+```bash
+python3 -m venv ~/envs/cfd_ml
+source ~/envs/cfd_ml/bin/activate
+pip install torch numpy scipy matplotlib pyyaml psutil
+
+python -c "import torch, yaml, psutil; print('OK')"
+```
+
+### Step 2 — Copy the code into your OpenFOAM case
+
+```bash
+cp -r /path/to/repo/pyPseudo_Adaptive_parallel/ /path/to/my_openfoam_case/
+cp /path/to/repo/config.yaml /path/to/my_openfoam_case/
+```
+
+### Step 3 — Edit config.yaml
+
+These are the fields you **must** set before running:
+
+| Field | Section | What to put |
+|---|---|---|
+| `ENV_CONDA` | `parametros_entorno` | Full path to your Python binary |
+| `ENV_OF` | `parametros_entorno` | OpenFOAM bashrc path, or `"NULL"` |
+| `N_PROCESOS` | `parametros_entorno` | MPI cores (auto-written to `decomposeParDict`) |
+| `DECOMP_N` | `parametros_entorno` | `[n1, n2, n3]` with n1×n2×n3 = N_PROCESOS (auto-written to `decomposeParDict`) |
+| `SOLO_PYTHON` | `parametros_entorno` | `true` (offline) or `false` (full loop) |
+| `VARIABLES` | `parametros_caso` | Fields to predict, e.g. `["U", "p"]` |
+| `CASE_NAME` | `parametros_caso` | Short name for your experiment |
+| `CFD_DIR` | `parametros_rutas` | Absolute path to your OpenFOAM case |
+| `NUM_MODES` | `parametros_svd` | Start with 10, tune up/down |
+| `INP_SEQ` | `parametros_lstm` | Start with 8, tune up/down |
+| `INITIAL_TIME` / `TIME_PERIOD_OF` / `TIME_PERIOD_PY` | `parametros_flujo` | (full loop) your simulation timing |
+| `START_TIME_SP` / `MID_TIME_SP` / `END_TIME_SP` | `parametros_flujo` | (SOLO_PYTHON) your data split |
+
+### Step 4 — Run
+
+```bash
+cd /path/to/my_openfoam_case/
+python pyPseudo_Adaptive_parallel/directorpython.py
+```
+
+### Step 5 — Verify
+
+```bash
+tail -1 log_py_*.txt
+# Expected: "All iterations completed successfully!"
+```
+
+Then open ParaView, load your `.foam` file, and compare predicted vs CFD fields across the time timeline.
+
+---
+
+> **Summary:** a single `config.yaml` and a single `python` command replace all the scattered shell scripts and Python files of the original implementation. The pipeline works with any OpenFOAM case — point `CFD_DIR` to your case, set your variables, and run.
+
+---
+
+*ModelFLOWs-UPM · Adaptive CFD-LSTM · POD/SVD + Deep Learning*
+*pyPseudo_Adaptive_parallel/ + config.yaml*
+*Based on: X. Zou et al. (ModelFLOWs-UPM, 2025)*
diff --git a/assets/img/Adaptive_CFD_Validation.png b/assets/img/Adaptive_CFD_Validation.png
new file mode 100644
index 000000000000..f9152d6f5c8f
Binary files /dev/null and b/assets/img/Adaptive_CFD_Validation.png differ
diff --git a/assets/img/Adaptive_Event_Triggered_Prediction.png b/assets/img/Adaptive_Event_Triggered_Prediction.png
new file mode 100644
index 000000000000..35ae2f35893e
Binary files /dev/null and b/assets/img/Adaptive_Event_Triggered_Prediction.png differ
diff --git a/assets/img/Adaptive_Framework.png b/assets/img/Adaptive_Framework.png
new file mode 100644
index 000000000000..be92f8922ad9
Binary files /dev/null and b/assets/img/Adaptive_Framework.png differ
diff --git a/assets/img/Adaptive_Geometry_Mesh.png b/assets/img/Adaptive_Geometry_Mesh.png
new file mode 100644
index 000000000000..7ae3647ee384
Binary files /dev/null and b/assets/img/Adaptive_Geometry_Mesh.png differ
diff --git a/assets/img/Adaptive_Inlet_Variation.png b/assets/img/Adaptive_Inlet_Variation.png
new file mode 100644
index 000000000000..3356e552529b
Binary files /dev/null and b/assets/img/Adaptive_Inlet_Variation.png differ
diff --git a/assets/img/Adaptive_RMSE_Re200.png b/assets/img/Adaptive_RMSE_Re200.png
new file mode 100644
index 000000000000..0d02558ed9d4
Binary files /dev/null and b/assets/img/Adaptive_RMSE_Re200.png differ
diff --git a/assets/img/Adaptive_RMSE_Re300.png b/assets/img/Adaptive_RMSE_Re300.png
new file mode 100644
index 000000000000..e005329d9cd1
Binary files /dev/null and b/assets/img/Adaptive_RMSE_Re300.png differ
diff --git a/assets/img/Adaptive_RMSE_Re300_adaptive.png b/assets/img/Adaptive_RMSE_Re300_adaptive.png
new file mode 100644
index 000000000000..392cad8ea8df
Binary files /dev/null and b/assets/img/Adaptive_RMSE_Re300_adaptive.png differ
diff --git a/assets/img/figs/cap3/9ed_cumulative_energy.png b/assets/img/figs/cap3/9ed_cumulative_energy.png
new file mode 100644
index 000000000000..902dd526f6ce
Binary files /dev/null and b/assets/img/figs/cap3/9ed_cumulative_energy.png differ
diff --git a/assets/img/figs/cap3/9ed_mode_decay.png b/assets/img/figs/cap3/9ed_mode_decay.png
new file mode 100644
index 000000000000..cc5648b8f9da
Binary files /dev/null and b/assets/img/figs/cap3/9ed_mode_decay.png differ
diff --git a/assets/img/figs/cap3/9ed_paraview.png b/assets/img/figs/cap3/9ed_paraview.png
new file mode 100644
index 000000000000..af543342e99b
Binary files /dev/null and b/assets/img/figs/cap3/9ed_paraview.png differ
diff --git a/assets/img/figs/cap3/cilindro_U_final.png b/assets/img/figs/cap3/cilindro_U_final.png
new file mode 100644
index 000000000000..60ddbfdf9d6a
Binary files /dev/null and b/assets/img/figs/cap3/cilindro_U_final.png differ
diff --git a/assets/img/figs/cap3/cilindro_U_t0.png b/assets/img/figs/cap3/cilindro_U_t0.png
new file mode 100644
index 000000000000..f8a543f25cd0
Binary files /dev/null and b/assets/img/figs/cap3/cilindro_U_t0.png differ
diff --git a/assets/img/figs/cap3/cilindro_p_final.png b/assets/img/figs/cap3/cilindro_p_final.png
new file mode 100644
index 000000000000..d4402d7311dc
Binary files /dev/null and b/assets/img/figs/cap3/cilindro_p_final.png differ
diff --git a/assets/img/figs/cap3/cilindro_p_t0.png b/assets/img/figs/cap3/cilindro_p_t0.png
new file mode 100644
index 000000000000..9d5682630598
Binary files /dev/null and b/assets/img/figs/cap3/cilindro_p_t0.png differ
diff --git a/assets/img/figs/cap3/readme.md b/assets/img/figs/cap3/readme.md
new file mode 100644
index 000000000000..78981922613b
--- /dev/null
+++ b/assets/img/figs/cap3/readme.md
@@ -0,0 +1 @@
+a
diff --git a/assets/img/figs/cap3/solvercfd.png b/assets/img/figs/cap3/solvercfd.png
new file mode 100644
index 000000000000..d3dc62efe824
Binary files /dev/null and b/assets/img/figs/cap3/solvercfd.png differ
diff --git a/assets/img/figs/cap3/zou_U_t100.png b/assets/img/figs/cap3/zou_U_t100.png
new file mode 100644
index 000000000000..e7488fc0666a
Binary files /dev/null and b/assets/img/figs/cap3/zou_U_t100.png differ
diff --git a/assets/img/figs/cap3/zou_U_t200.png b/assets/img/figs/cap3/zou_U_t200.png
new file mode 100644
index 000000000000..5e03d40347aa
Binary files /dev/null and b/assets/img/figs/cap3/zou_U_t200.png differ
diff --git a/assets/img/figs/cap3/zou_p_200.png b/assets/img/figs/cap3/zou_p_200.png
new file mode 100644
index 000000000000..731d8ac83627
Binary files /dev/null and b/assets/img/figs/cap3/zou_p_200.png differ
diff --git a/assets/img/figs/cap3/zou_p_t100.png b/assets/img/figs/cap3/zou_p_t100.png
new file mode 100644
index 000000000000..789fd9549fce
Binary files /dev/null and b/assets/img/figs/cap3/zou_p_t100.png differ
diff --git a/assets/img/figs/cummulative.png b/assets/img/figs/cummulative.png
new file mode 100644
index 000000000000..535d7d808f54
Binary files /dev/null and b/assets/img/figs/cummulative.png differ
diff --git a/assets/img/figs/diagrama.png b/assets/img/figs/diagrama.png
new file mode 100644
index 000000000000..31afdcd96565
Binary files /dev/null and b/assets/img/figs/diagrama.png differ
diff --git a/assets/img/figs/diagrama_solver.png b/assets/img/figs/diagrama_solver.png
new file mode 100644
index 000000000000..ff216176f977
Binary files /dev/null and b/assets/img/figs/diagrama_solver.png differ
diff --git a/assets/img/figs/readme.md b/assets/img/figs/readme.md
new file mode 100644
index 000000000000..012ea92e8117
--- /dev/null
+++ b/assets/img/figs/readme.md
@@ -0,0 +1 @@
+here
diff --git a/research/adaptive-prediction.md b/research/adaptive-prediction.md
index 9692678442fb..027b27c45839 100644
--- a/research/adaptive-prediction.md
+++ b/research/adaptive-prediction.md
@@ -1,27 +1,262 @@
---
layout: page
-title: Adaptive Prediction
-subtitle: Synergy of fluid dynamics and deep learning models
+category: "Adaptive Methodologies"
+topic: "CFD-Surrogate Prediction"
+thumbnail: "assets/img/Adaptive_Framework.png"
+tldr: "A divergence-aware adaptive CFD-surrogate framework that alternates between fast POD-DL forecasting and targeted OpenFOAM recalls for robust long-horizon prediction of unsteady flows."
+title: Divergence-aware Adaptive Prediction Framework for Accelerating CFD Simulations
+subtitle: Closed-loop OpenFOAM and POD-DL coupling for long-horizon unsteady-flow prediction
+published: true
---
-This section collects all the research done by our group regarding adaptive prediction frameworks coupling solvers with DL models.
-
-
- {% assign current_posts = site.research | where: "topic", "Adaptive Prediction" %}
- {% for post in current_posts %}
-
-
-
-
-
Category: {{ post.category }}
-
{{ post.tldr }}
- {% if post.thumbnail %}
-

- {% endif %}
-
-
Read More
-
-
-
- {% endfor %}
-
+
+# Divergence-aware Adaptive CFD-Surrogate Prediction
+
+Long-horizon prediction of unsteady flows remains a major challenge in computational fluid dynamics (CFD). Full CFD simulations are accurate, but they are expensive when many time steps, operating conditions, or design iterations are required. Data-driven reduced-order models can strongly accelerate prediction, but their autoregressive errors may gradually accumulate, especially when the forecast horizon is long or when the flow regime changes.
+
+This work develops a **divergence-aware adaptive prediction framework** that couples an OpenFOAM-based CFD solver with a **proper orthogonal decomposition-deep learning (POD-DL)** surrogate model. The central idea is straightforward:
+
+> use the fast surrogate when it is reliable, and recall CFD only when the prediction begins to diverge.
+
+The framework is designed for realistic online prediction scenarios, where future CFD ground truth is not available during deployment. Instead of comparing the prediction with reference data, the method monitors internal reliability indicators, especially ensemble uncertainty in the POD coefficient space.
+
+
+# Research Motivation
+
+CFD simulations play an essential role in fluid mechanics, combustion, meteorology, aerodynamics, and engineering design. However, resolving the relevant spatial and temporal scales of realistic unsteady flows often requires large computational resources.
+
+Reduced-order models provide an efficient alternative by compressing the flow field into a low-dimensional representation and learning its temporal evolution. However, purely offline surrogates face two important limitations:
+
+- **Long-horizon error accumulation:** small prediction errors are propagated and amplified during autoregressive forecasting.
+- **Poor robustness under regime changes:** a surrogate trained under one condition may lose reliability when the flow state or boundary condition changes.
+
+The proposed framework addresses these limitations by introducing an **adaptive CFD-surrogate loop**. The surrogate is not used blindly over the full prediction horizon. Instead, its reliability is monitored online, and CFD is recalled only when new high-fidelity information is required.
+
+
+# Methodology Overview
+
+The adaptive framework contains four main components:
+
+1. **CFD data generation** using OpenFOAM.
+2. **POD compression** of high-dimensional flow snapshots.
+3. **Deep-learning temporal prediction** in the reduced POD space.
+4. **Online divergence monitoring** and selective CFD recall.
+
+## Closed-loop CFD/POD-DL workflow
+
+The workflow starts with a short CFD simulation. The generated snapshots are used to build the initial POD basis and train a neural-network predictor. After training, the surrogate model advances the reduced state autoregressively. During this prediction stage, the monitoring module evaluates whether the forecast remains reliable.
+
+When the scheduled update time is reached, or when prediction divergence is detected, the workflow switches back to CFD. New snapshots are generated, the POD-DL model is retrained or updated, and surrogate prediction then resumes. In this way, the framework alternates between **expensive but accurate CFD simulation** and **fast but monitored POD-DL prediction**.
+
+
+
+
+
+Figure 1. Closed-loop adaptive framework coupling online CFD simulation and POD-DL prediction. CFD snapshots are compressed by POD, the reduced coefficients are predicted by a deep-learning model, and the monitoring module decides whether the forecast can continue or whether CFD should be recalled.
+
+## POD-DL surrogate model
+
+The high-dimensional CFD snapshot matrix is first mean-centered and decomposed using singular value decomposition. The dominant POD modes provide a compact basis for the unsteady flow structures. The corresponding temporal coefficients are standardized and used as the training data for a deep-learning sequence predictor.
+
+In the present implementation, the predictor is based on a stacked **long short-term memory (LSTM)** network followed by fully connected layers. The model takes a rolling window of past POD coefficients and predicts the next coefficient block. During long-horizon forecasting, the predicted coefficients are fed back into the input window, forming an autoregressive prediction loop.
+
+The predicted POD coefficients are finally transformed back to the physical space through inverse POD reconstruction.
+
+## Divergence detection and adaptive control
+
+Two adaptive strategies are considered.
+
+### Scheduled adaptive update
+
+In the scheduled mode, CFD is recalled after a fixed number of predicted snapshots. This is a simple and robust correction strategy. It periodically introduces new high-fidelity data and prevents irreversible growth of the prediction error.
+
+### Event-triggered adaptive update
+
+In the event-triggered mode, the framework recalls CFD only when the surrogate becomes unreliable. Several POD-DL predictors are trained with different random seeds and advanced simultaneously. Their disagreement is used to estimate **ensemble uncertainty** in the reduced POD space.
+
+The uncertainty is weighted by the retained POD singular values, so uncertainty in energetically important modes contributes more strongly to the final indicator. A dynamic threshold is estimated from the recent uncertainty history using a median-MAD rule. If the uncertainty exceeds the threshold for a sustained period, the current forecast is terminated and CFD is recalled.
+
+This event-triggered strategy is important because it does not require future CFD ground truth during prediction. The model decides when to stop based on its own reliability indicator.
+
+
+# CFD Dataset Generation and Validation
+
+The framework is assessed using the canonical problem of **three-dimensional flow past a circular cylinder**. This case is widely used for studying vortex shedding, wake instability, and unsteady-flow prediction.
+
+## Computational setup
+
+The CFD simulations are performed using **OpenFOAM** with the `pimpleFoam` solver for incompressible flow. The Reynolds-number range is approximately **Re = 160-400**. The computational domain has a height of 20D, an upstream length of 10D, a downstream length of 20D, and a spanwise length of 3D, where D is the cylinder diameter.
+
+The mesh contains approximately **741 thousand cells**, with refinement near the cylinder and in the wake region. The spanwise boundaries are periodic. The inlet velocity and kinematic viscosity are adjusted to obtain different Reynolds numbers while keeping the same geometry. Probe points are placed in the wake region to evaluate the temporal evolution of velocity and pressure predicted by the surrogate.
+
+
+
+
+
+Figure 2. Computational geometry and mesh for the circular-cylinder wake. The domain extends 10D upstream and 20D downstream of the cylinder, with a 20D cross-stream height. The mesh is refined around the cylinder and in the wake region where vortex shedding develops.
+
+## Numerical validation
+
+Before training the surrogate model, the CFD setup is validated using standard integral quantities:
+
+- mean drag coefficient,
+- mean base-pressure coefficient,
+- Strouhal number.
+
+The numerical results follow the experimental trends over the investigated Reynolds-number range. The mean drag coefficient decreases gradually with Reynolds number. The base-pressure coefficient remains close to the experimental values and captures the expected tendency. The Strouhal number remains around the characteristic vortex-shedding range for cylinder wakes. This validation confirms that the generated CFD snapshots provide a reliable basis for POD-DL training and adaptive prediction.
+
+
+
+
+
+Figure 3. Validation of the OpenFOAM setup using mean drag coefficient, mean base-pressure coefficient and Strouhal number. The simulated values are consistent with experimental trends over Re = 200-400.
+
+
+# Baseline POD-DL Prediction without Adaptation
+
+The first test evaluates the original POD-DL surrogate without adaptive correction. This baseline is useful because it shows why online adaptation is needed.
+
+## Case Re = 200
+
+At Re = 200, the wake exhibits an organized periodic vortex-shedding pattern. The leading POD modes capture the dominant coherent structures, and the predicted probe signals reproduce the main oscillatory behavior of velocity and pressure. The predicted amplitude and phase remain reasonable over a finite prediction window.
+
+
+
+
+
+Figure 4. Comparison between prediction and CFD ground truth at probe location (0.5D, 0.5D, 0) for the flow past a circular cylinder at Re=200.
+
+However, the RMSE gradually increases as the prediction horizon extends. The error growth is especially visible for the streamwise and cross-stream velocity components. This confirms that even for a relatively regular wake regime, small autoregressive errors accumulate over time.
+
+
+
+
+
+Figure 5. RMSE evolution for the baseline POD-DL prediction at Re = 200. The model captures the main periodic dynamics, but the error gradually increases during long-horizon autoregressive forecasting.
+
+## Case Re = 300
+
+At Re = 300, the wake becomes more complex. The prediction still captures the dominant oscillatory behavior, but the mismatch grows faster than in the Re = 200 case.
+
+
+
+
+
+Figure 6. Comparison between prediction and CFD ground truth at probe location (0.5D, 0.5D, 0) for the flow past a circular cylinder at Re=300.
+
+The RMSE also increases as the prediction horizon extends, and the error increases in comparison to Case Re=200.
+
+
+
+
+
+Figure 7. RMSE evolution for the baseline POD-DL prediction at Re = 300. Compared with Re = 200, the prediction mismatch grows more rapidly because the wake dynamics are less regular and more modal content is required.
+
+These results show that a purely offline POD-DL surrogate is not sufficiently robust for extended forecasts when the wake dynamics become more complex.
+
+
+# Scheduled Adaptive Prediction
+
+The scheduled adaptive strategy recalls CFD after a fixed number of predicted snapshots. In the representative test, the model is trained using an initial interval, predicts the next interval, then CFD is recalled to generate new snapshots and retrain the surrogate.
+
+The updated model significantly reduces the RMSE in the second prediction stage compared with the non-adaptive baseline. In particular, the errors of the main velocity components are effectively reset after retraining and then evolve again from a lower baseline.
+
+
+
+
+
+Figure 8. RMSE evolution for scheduled adaptive prediction at Re = 300. The transparent curves indicate the non-adaptive baseline, while the highlighted curves show the adaptive prediction. After CFD recall and retraining, the prediction error is reduced in the second forecast stage.
+
+
+The method also preserves key wake statistics, including:
+
+- mean and fluctuation level of turbulent kinetic energy,
+- temporal correlation of the streamwise velocity,
+- overall evolution of the wake dynamics.
+
+For a representative **200-snapshot** interval, the CFD computation requires 15906.9 s using 60 CPU cores, while the surrogate workflow requires 2591.7 s using 4 CPU cores. After normalizing by CPU core count, the effective speed-up is approximately **92 times** relative to CFD.
+
+
+# Event-triggered Divergence Detection
+
+Although scheduled updating is simple, it may recall CFD too early or too late because the update time is fixed in advance. The event-triggered strategy is more flexible because it links CFD recall directly to the reliability of the ongoing prediction.
+
+In this mode, three POD-DL predictors are trained with different random seeds. Their prediction spread provides an ensemble uncertainty indicator. A dynamic threshold is estimated from the recent uncertainty history. When the uncertainty exceeds this threshold persistently, the prediction is considered unreliable.
+
+The results show that the uncertainty trigger is consistent with physically meaningful degradation. The raw uncertainty signal is noisy, so a smoothed signal is used to identify robust trends. When the smoothed ensemble uncertainty rises above the dynamic threshold, the forecast is terminated and CFD is recalled. This avoids the need for future CFD ground-truth data during online prediction.
+
+
+
+
+
+Figure 9. Event-triggered divergence detection based on ensemble uncertainty. The dashed green curves show raw uncertainty, the solid green curves show smoothed uncertainty, and the dashed yellow curves show the dynamic threshold. CFD is recalled when the uncertainty exceeds the threshold persistently.
+
+This strategy provides a practical online solution when future CFD reference data are unavailable. It allows the surrogate to continue when the prediction is stable, and automatically switches back to CFD when the reduced-order forecast becomes unreliable.
+
+
+# Adaptive Prediction under Varying Inlet Velocity
+
+A more demanding test is performed under time-varying inlet velocity. The inlet velocity changes during prediction according to:
+
+$$
+U_{in}: 1 \rightarrow 2 \rightarrow 0.8 \rightarrow 1.5 \; \text{m/s}
+$$
+
+This corresponds to a Reynolds-number range of approximately **160-400**. The case represents a realistic scenario where the operating condition changes during simulation.
+
+The ensemble uncertainty increases sharply after each inlet-velocity change. This rise in uncertainty triggers termination of the current surrogate forecast and recall of the CFD solver. The boundary condition is then updated, new CFD snapshots are generated, and the surrogate is retrained.
+
+After retraining, the predicted drag and lift histories recover physically expected trends. This demonstrates that the adaptive framework can track regime changes and maintain predictive reliability under evolving operating conditions.
+
+
+
+
+
+Figure 10. Adaptive prediction under varying inlet velocity. The inlet velocity changes from 1 to 2, then to 0.8 and 1.5 m/s. The uncertainty indicator rises after each regime change, triggering CFD recall and surrogate retraining.
+
+
+# Key Findings
+
+The main findings can be summarized as follows:
+
+- **Baseline POD-DL prediction is accurate over a finite horizon**, but autoregressive errors grow gradually during long-time forecasting.
+- **Scheduled adaptive updating reduces accumulated error** by periodically recalling CFD and retraining the surrogate.
+- **Event-triggered updating provides an online reliability mechanism** based on ensemble uncertainty, without requiring future CFD ground truth.
+- **The adaptive framework can handle changing inlet conditions**, because uncertainty rises after regime changes and activates targeted CFD recall.
+- **The closed-loop CFD-surrogate workflow provides strong acceleration**, with an effective speed-up of approximately 92 times for a representative 200-snapshot interval.
+
+
+# CFD-Surrogate Pipeline
+
+This research can be organized as a reproducible workflow for adaptive prediction:
+
+1. **Generate CFD snapshots**
+ - Run OpenFOAM for an initial short interval.
+ - Save velocity and pressure fields at selected sampling times.
+
+2. **Construct the POD representation**
+ - Mean-center the snapshot matrix.
+ - Apply singular value decomposition.
+ - Retain the dominant POD modes and temporal coefficients.
+
+3. **Train the POD-DL predictor**
+ - Standardize modal coefficients.
+ - Train an LSTM-based temporal predictor.
+ - Use rolling-window autoregressive forecasting.
+
+4. **Monitor prediction reliability**
+ - Run ensemble predictors with different random seeds.
+ - Compute energy-weighted uncertainty in POD space.
+ - Estimate a dynamic threshold using recent uncertainty history.
+
+5. **Recall CFD when needed**
+ - Stop the surrogate forecast when divergence is detected.
+ - Restart OpenFOAM from the accepted predicted state or updated operating condition.
+ - Generate new high-fidelity snapshots and retrain the surrogate.
+
+# Related Publication
+
+Xiangrui Zou, Zhuoqun Zhao, Guillermo Barragán, Soledad Le Clainche.
+Divergence-aware adaptive prediction framework for accelerating CFD simulations of unsteady flows.
+[https://doi.org/10.48550/arXiv.2605.24150](https://doi.org/10.48550/arXiv.2605.24150)
+