208 lines
6.2 KiB
Markdown
208 lines
6.2 KiB
Markdown
# 📝 Summary: Memory Overflow Fix (Nov 11, 2025)
|
||
|
||
## Problem Statement
|
||
When running `01.prepare_data_on_server.ipynb`, Cell 5 (Sentinel-2 loading) failed with:
|
||
```
|
||
❌ Error loading data: Unable to allocate 403. TiB for an array with shape
|
||
(396, 563539, 992108) and data type uint16
|
||
```
|
||
|
||
This requested **403 Terabytes** of RAM - physically impossible.
|
||
|
||
## Root Cause
|
||
The `load_s2l2a_with_offset()` function was loading an entire **massive satellite tile** (563K × 992K pixels) instead of clipping to the specified AOI (105.5-106.4°E, 9.2-10.0°N).
|
||
|
||
Expected size: 10,000 × 10,000 pixels (100×100 km)
|
||
Actual size: 563,539 × 992,108 pixels (~5,600×9,900 km)
|
||
|
||
## Solution Implemented
|
||
|
||
### 1. Monthly Chunking Strategy
|
||
**Before:** Load 396 scenes → 403 TB allocation attempt → OOM crash
|
||
|
||
**After:** Split into 13 monthly chunks:
|
||
- Load month 1 (Sep 2022): 30 scenes → ~5 GB
|
||
- Load month 2 (Oct 2022): 28 scenes → ~4.5 GB
|
||
- ...
|
||
- Load month 13 (Sep 2023): 31 scenes → ~5 GB
|
||
- **Combine:** `xr.concat()` all monthly datasets
|
||
|
||
**Benefit:** Each load fits in memory (~5-15 GB), Dask distributes work across workers.
|
||
|
||
### 2. New Diagnostic Cell
|
||
**Cell 4** (NEW) - Added before Sentinel-2 loading:
|
||
```python
|
||
## DEBUG: Inspect what datacube wants to load
|
||
```
|
||
- Lists available S2 products
|
||
- Checks metadata for Jan 2023 (sample month)
|
||
- Shows actual bounds/CRS returned by datacube
|
||
- **Does NOT load raster data** (metadata query only)
|
||
|
||
**Purpose:** Identify if spatial subsetting is working correctly
|
||
|
||
### 3. Updated Loading Logic
|
||
**Cell 5** (MODIFIED) - Sentinel-2 data loading:
|
||
```python
|
||
## SENTINEL-2 LOADING: Monthly chunks to prevent OOM
|
||
```
|
||
|
||
Key changes:
|
||
- ✅ Loop through 13 month pairs
|
||
- ✅ Load each month separately
|
||
- ✅ Dask chunks: 512×512×1 (optimized for distributed workers)
|
||
- ✅ Error handling: if month fails, continue with next
|
||
- ✅ Progress tracking: [01/13], [02/13], etc.
|
||
- ✅ Final concat: combine all successful months
|
||
|
||
## Files Modified
|
||
|
||
### 1. `01.prepare_data_on_server.ipynb`
|
||
| Cell | Type | Change |
|
||
|------|------|--------|
|
||
| 4 | NEW | Diagnostic check (metadata query) |
|
||
| 5 | UPDATED | Monthly chunking strategy |
|
||
| 6-14 | Unchanged | Cloud mask, NDVI, aggregation, S1 load, save |
|
||
|
||
### 2. New Documentation Created
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `MEMORY_FIX_EXPLAINED.md` | Detailed explanation of problem & solution |
|
||
| `TROUBLESHOOT_S2_LOADING.md` | Quick troubleshooting guide |
|
||
| `CHANGES_SUMMARY.md` | This file |
|
||
|
||
## Expected Behavior After Fix
|
||
|
||
### Cell 5 Output
|
||
```
|
||
📡 Tải dữ liệu Sentinel-2 L2A từ S3...
|
||
AOI: (105.5, 106.4), (9.2, 10.0)
|
||
Time range: ('2022-09-01', '2023-10-01')
|
||
|
||
✅ Native CRS: EPSG:32648
|
||
|
||
[01/13] 2022-09-01 → 2022-10-01 ✓ 32 scenes
|
||
[02/13] 2022-10-01 → 2022-11-01 ✓ 28 scenes
|
||
[03/13] 2022-11-01 → 2022-12-01 ✓ 30 scenes
|
||
...
|
||
[13/13] 2023-09-01 → 2023-10-01 ✓ 31 scenes
|
||
|
||
🔗 Combining 13 monthly chunks...
|
||
✅ Success! Shape: {'time': 396, 'y': 10000, 'x': 10000}
|
||
Memory: 16.2 GB
|
||
|
||
<xarray.Dataset>
|
||
Dimensions: (time: 396, y: 10000, x: 10000)
|
||
Data variables:
|
||
red (time, y, x) uint16 dask.array<...>
|
||
nir (time, y, x) uint16 dask.array<...>
|
||
scl (time, y, x) uint8 dask.array<...>
|
||
```
|
||
|
||
**Duration:** 5-15 minutes (network + distributed processing)
|
||
|
||
### Data Variable
|
||
```python
|
||
data.shape # (396, 10000, 10000) ✓ CORRECT
|
||
data.dims # {'time': 396, 'y': 10000, 'x': 10000}
|
||
```
|
||
|
||
## Testing Instructions
|
||
|
||
1. **Open:** `01.prepare_data_on_server.ipynb`
|
||
2. **Run Cell 2:** Dask initialization (wait for cluster ready)
|
||
3. **Run Cell 3:** Set coordinates (automatic)
|
||
4. **Run Cell 4:** Diagnostic check (look for "✅ Found X scenes")
|
||
5. **Run Cell 5:** Load Sentinel-2 (watch progress bars)
|
||
6. **If success:** Continue to Cell 6+ (cloud masking, NDVI, etc.)
|
||
7. **If fail:** See `TROUBLESHOOT_S2_LOADING.md`
|
||
|
||
## Fallback Options (If Still Issues)
|
||
|
||
### Option A: Reduce Months Further
|
||
Split into weekly chunks if monthly still OOM:
|
||
```python
|
||
date_ranges = [
|
||
("2022-09-01", "2022-09-08"),
|
||
("2022-09-08", "2022-09-15"),
|
||
...
|
||
]
|
||
```
|
||
|
||
### Option B: Explicit Spatial Clipping
|
||
Add after line: `monthly_data = load_s2l2a_with_offset(...)`
|
||
```python
|
||
if monthly_data.sizes['y'] > 15000:
|
||
monthly_data = monthly_data.sel(
|
||
x=slice(longtitude_range[0], longtitude_range[1]),
|
||
y=slice(latitude_range[0], latitude_range[1]),
|
||
)
|
||
```
|
||
|
||
### Option C: Use Rasterio Directly
|
||
If datacube continues to fail, bypass it:
|
||
```python
|
||
import rasterio
|
||
from rasterio.io import MemoryFile
|
||
|
||
# Load S3 COGs directly with windowed reads
|
||
# More control, but requires S3 path knowledge
|
||
```
|
||
|
||
## Verification Checklist
|
||
|
||
After Cell 5 succeeds, verify:
|
||
- [ ] `data` variable exists
|
||
- [ ] `data.dims` shows ~10,000 pixels in x & y
|
||
- [ ] `data.dims['time']` is 396 (or close)
|
||
- [ ] All three bands present: red, nir, scl
|
||
- [ ] Memory usage is ~15-20 GB (not 400+ TB)
|
||
- [ ] Dask workers are healthy (not crashed)
|
||
- [ ] No persistent errors in logs
|
||
|
||
## Performance Notes
|
||
|
||
| Metric | Expected |
|
||
|--------|----------|
|
||
| Cell 4 duration | <1 minute |
|
||
| Cell 5 per month | 20-60 seconds |
|
||
| Cell 5 total | 5-15 minutes |
|
||
| Final data size | 15-20 GB |
|
||
| Worker memory/GB | 5-8 GB per month |
|
||
| Dask overhead | ~2 GB |
|
||
|
||
## Why This Works
|
||
|
||
1. **Memory bound:** 396 scenes × 10K×10K pixels × 2 bytes = **20 GB** ✓
|
||
- Can fit in server RAM (~100-500 GB total)
|
||
- Dask distributes across workers (each takes 5-15 GB chunk)
|
||
|
||
2. **Time efficient:** Monthly loading allows parallel tasks
|
||
- While month 1 computing NDVI, month 2 still loading
|
||
|
||
3. **Robust:** If one month fails (bad S3 object), others continue
|
||
- Get 92% of data rather than 0%
|
||
|
||
4. **Observable:** Progress bars + error messages
|
||
- Know exactly which month succeeded/failed
|
||
|
||
## Related Files
|
||
|
||
- `new_import_ODC.py` - Contains `load_s2l2a_with_offset()` function
|
||
- `00_START_HERE.md` - Setup instructions (no changes needed)
|
||
- `LOCAL_TRAINING_WORKFLOW.md` - Workflow overview (no changes needed)
|
||
|
||
## Status
|
||
|
||
✅ **Ready to test**
|
||
✅ **Documentation complete**
|
||
✅ **No breaking changes** (only improvements to Cell 4-5)
|
||
|
||
---
|
||
|
||
**Date:** November 11, 2025
|
||
**Affected Notebook:** `01.prepare_data_on_server.ipynb`
|
||
**Risk Level:** Low (modular fix, doesn't affect other cells)
|
||
**Testing Priority:** HIGH (run ASAP to verify effectiveness)
|