Alluvione: due trigger per nodo, suscettibilità dal mosaico ISPRA (Fase 3a) - #97
Merged
Conversation
…viousness (Fase 3a)
Il terzo pericolo, e quello che il progetto aveva già in casa: il mosaico
idraulico ISPRA alimentava il componente H del motore frane da tempo, e qui
diventa la suscettibilità di un pericolo suo.
score = suscettibilità × max(pluviale, fluviale)
**Un massimo, non una somma.** I due trigger sono modi diversi perché la
stessa cella finisca sott'acqua; una cella non è più allagata perché
potrebbero accadere entrambi. Sommarli lascerebbe che due segnali moderati ne
inventino uno grave.
**La suscettibilità moltiplica**, al contrario del termine `base`
dell'incendio: lì la roccia nuda resta sopra zero perché il fuoco arriva da
fuori, qui l'acqua va in basso e un crinale non si allaga per quanto piova
accanto. Sotto `susceptibility.floor` il motore va a zero.
**Una cella non mappata non è una cella sicura.** Il mosaico copre i bacini
ufficialmente studiati: "non studiato" non è "non allagabile", quindi fuori
mappa prende un valore di ripiego basso — visibile ma incapace di arrivare in
classe alta con la sola pioggia — e il breakdown dichiara `mapped: false`.
## La sonda fluviale, che era rotta
Il segnale GloFAS esistente campiona il **centroide** dell'AOI, che non sta
quasi mai su un fiume modellato: misurato, 0.0 per la Basilicata e `None` sul
Basento. Per il componente H delle frane era un bonus marginale; per
l'alluvione è metà del motore.
`basin_max=True` sonda il reticolo e prende il rapporto peggiore. Ma il
rapporto è patologico sui rigagnoli — un corso d'acqua da 0.01 m³/s che
arriva a 0.6 vale 62, e un massimo puro pesca esattamente quelli: misurato,
62.0 dove i fiumi veri erano a 1.25. Quindi contano solo i punti la cui
portata di base è almeno il 10% della maggiore dell'AOI. Verificato: Po in
Piemonte 1.04, Basilicata 1.25, fiumare pugliesi 7.5.
**Spento per le frane di proposito**: il campione V1 è stato valutato con il
segnale del centroide, e cambiarlo sotto lo stesso nome sposterebbe i suoi
numeri senza un backtest.
## EFAS: non implementato, e dichiarato
L'issue chiede le soglie di tempo di ritorno EFAS (2/5/20 anni) via CDS API.
Richiedono un account CDS e una dipendenza NetCDF per leggere il download: un
client che non si può eseguire non è un client, ed è la stessa trappola che
ho evitato altrove. GloFAS via Open-Meteo dà il ramo fluviale **oggi, senza
credenziali**, ed è già ingerito. Il posto dove EFAS entrerebbe è un blocco
`fluvial` in YAML che oggi legge un rapporto e domani leggerebbe un tempo di
ritorno.
## Il resto
`imperviousness_norm` da CLMS amplifica il **solo** ramo pluviale: il cemento
non fa crescere un fiume, fa scorrere la stessa pioggia. NULL finché il raster
non è configurato, e NULL non è 0.
Palette PuBu sulla mappa — l'acqua è l'unica delle tre che non si legge come
"caldo", ed è giusto che sia l'unica fredda. Il popup distingue il segnale del
fiume da quello della pioggia: sono mitigati da cose diverse.
## Test ristrutturati
Sette test usavano `flood` come "pericolo che nessun file conosce". Ora ogni
membro dell'enum ha schema, motore e configurazione, quindi lo scenario si
**costruisce** — togliendo il pericolo vero e rimettendolo — invece di
sfruttare un buco che non esiste più. È anche più fedele: prova il
meccanismo, non la coincidenza di quale pericolo è ancora scoperto.
Chiude #63. Refs #58
**I due segnali erano uno per AOI, copiati su ogni cella.** È l'errore che questo repo documenta già per la pioggia — 13 mm al centroide della Puglia contro 77 mm sulle celle che avevano davvero ceduto — e per l'alluvione è peggio, perché i due segnali *sono* il motore: un bacino in piena spingeva oltre la soglia di allerta celle di un bacino diverso. Ora entrambi sono campionati per nodo e ogni cella legge il più vicino. Misurato su 400 celle di Basilicata: 10 valori di pioggia distinti invece di uno, 7 celle con segnale fluviale e 393 senza — che è la risposta giusta, perché lì il fiume non c'è. Il passo del reticolo passa da 0.25° a 0.1°: a 25 km solo 2 nodi su 35 cadevano su un corso d'acqua modellato da GloFAS, quindi il ramo fluviale era di fatto spento; a 0.1° sono 20 su 176. Gli scalari di AOI restano per il componente H delle frane, che li ha sempre usati. **La finestra della soglia non era quella che si andava a prendere.** `pluvial.window_hours: 24` era validato, pubblicato nella scheda del modello e mai letto: il motore riceveva l'accumulo a 72 h e lo confrontava con soglie calibrate su 24 h, quindi 75 mm in tre giorni tranquilli davano 0.32 dove la finestra dichiarata dà 0, e un autunno normale saturava il trigger. Il campo che porta il segnale si chiama `flood_forecast_rain_72h_mm` ed è condiviso con l'ML feature store, dove il nome è una chiave persistita: rinominarlo invaliderebbe i modelli addestrati. Quindi la finestra resta 72 h e le **soglie si calibrano su quella** — 80 mm e 250 mm — e il workflow legge `window_hours` per andare a prendere esattamente quella finestra, così le due cose non possono più divergere. L'accumulo è ora delimitato a ore e non a giorni interi, che allungavano una finestra di 72 h fino a 96. **Umidità del suolo ignota valeva "suolo saturo".** Saltare lo smorzamento è numericamente identico al caso più allarmante: una risposta Open-Meteo degradata alzava il trigger di ~2.2× su un'intera AOI, e il mio stesso test ci si era appoggiato sopra. Ora l'ignoto smorza a metà fra i due estremi che la configurazione già definisce. **Una richiesta troppo grande faceva morire lo sweep.** 677 punti in una query string tornano con un corpo che non è JSON, e `resp.json()` alzava un `ValueError` fuori dalla degradazione. Ora le richieste vanno a lotti di 100 e un lotto fallito contribuisce dizionari vuoti, così la mappatura posizionale sui nodi resta allineata. Più due residui nei test — uno snapshot del registry preso dopo la sostituzione, che rimetteva lo stub come motore flood permanente, e un round-trip che smontava il motore vero senza rimetterlo — e un commento obsoleto in `.env.example`. Refs #63
This was referenced Sep 5, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chiude #63. Terzo e ultimo pericolo: tutti e tre i membri dell'enum hanno ora schema, motore, configurazione e workflow, e
check_scorablepassa per ognuno. Sblocca #58.Un massimo, non una somma. I due trigger sono modi diversi perché la stessa cella finisca sott'acqua; una cella non è più allagata perché potrebbero accadere entrambi. Sommarli lascerebbe che due segnali moderati ne inventino uno grave.
La suscettibilità moltiplica, al contrario del termine
basedell'incendio: lì la roccia nuda resta sopra zero perché il fuoco arriva da fuori, qui l'acqua va in basso e un crinale non si allaga per quanto piova accanto.Una cella non mappata non è una cella sicura. Il mosaico ISPRA copre i bacini ufficialmente studiati: fuori mappa la cella prende un valore di ripiego basso e il breakdown dichiara
mapped: false— visibile, ma incapace di arrivare in classe alta con la sola pioggia.Il dato che il progetto aveva già in casa
Il mosaico idraulico ISPRA alimentava il componente H del motore frane da tempo. Qui smette di essere un contributo a un altro pericolo e diventa la suscettibilità di un pericolo suo, senza nuovo ingest.
I segnali erano uno per AOI: la correzione più importante
La prima stesura leggeva pioggia e portata al centroide dell'AOI e li copiava su ogni cella. È l'errore che questo repo documenta già per la pioggia — 13 mm al centroide della Puglia contro 77 mm sulle celle che avevano davvero ceduto — e per l'alluvione è peggio, perché i due segnali sono il motore: un bacino in piena spingeva oltre la soglia di allerta celle di un bacino diverso.
Ora entrambi sono campionati per nodo e ogni cella legge il più vicino. Misurato su 400 celle di Basilicata: 10 valori di pioggia distinti invece di uno, 7 celle con segnale fluviale e 393 senza — che è la risposta giusta, perché lì il fiume non c'è.
Il passo passa da 0.25° a 0.1°: a 25 km solo 2 nodi su 35 cadevano su un corso d'acqua modellato da GloFAS, quindi il ramo fluviale era di fatto spento; a 0.1° sono 20 su 176.
Il rapporto di portata è patologico sui rigagnoli — un corso d'acqua da 0.01 m³/s che arriva a 0.6 vale 62. Misurato: un massimo puro dava 62.0 dove i fiumi veri erano a 1.25. Quindi un nodo conta come fiume solo se la sua portata di base è almeno il 10% della maggiore dell'AOI; gli altri riportano
None, che è "nessun corso d'acqua qui", non "un fiume in magra". Verificato: Po in Piemonte 1.04, Basilicata 1.25, fiumare pugliesi 7.5.EFAS: non implementato, e dichiarato
L'issue chiede le soglie di tempo di ritorno EFAS (2/5/20 anni) via CDS API. Richiedono un account CDS e una dipendenza NetCDF per leggere il download: un client che non si può eseguire non è un client. GloFAS via Open-Meteo dà il ramo fluviale oggi, senza credenziali, ed era già ingerito. Il punto d'innesto è il blocco
fluvialin YAML, che oggi legge un rapporto e domani leggerebbe un tempo di ritorno.Sette difetti corretti in revisione
Oltre ai segnali per AOI, tre di sostanza:
window_hours: 24era validato, pubblicato nella scheda del modello e mai letto: il motore riceveva 72 h di accumulo e lo confrontava con soglie calibrate su 24 h, quindi un autunno normale saturava il trigger. Il campo del segnale è condiviso con l'ML feature store, dove il nome è una chiave persistita e rinominarlo invaliderebbe i modelli — quindi le soglie si calibrano sulla finestra vera (80/250 mm su 72 h) e il workflow leggewindow_hoursper andare a prendere esattamente quella.resp.json()alzava fuori dalla degradazione. Ora lotti da 100, e un lotto fallito contribuisce dizionari vuoti così la mappatura sui nodi resta allineata.Più due residui nei test — uno snapshot del registry preso dopo la sostituzione, che rimetteva lo stub come motore flood permanente, e un round-trip che smontava il motore vero senza rimetterlo — e un commento obsoleto in
.env.example.Test ristrutturati
Sette test usavano
floodcome "pericolo che nessun file conosce". Ora lo scenario si costruisce — togliendo il pericolo vero e rimettendolo, o spostando il risolutore di percorso — invece di sfruttare un buco che non esiste più. È anche più fedele: prova il meccanismo, non la coincidenza di quale pericolo è ancora scoperto.Un limite da sapere
Senza il mosaico idraulico caricato (serve
GEOSERVER_SOURCE__DB_DSN) ogni cella prendesusceptibility.unmappede la mappa alluvione è uniforme: leggibile, ma non discriminante. È un caricamento dati, non codice mancante — e a differenza di CORINE per l'incendio, questo layer il progetto lo ingerisce già.Gate
ruff check+ruff format --check,mypy --strict: pulitipytest tests/unit: 681 passatitsc -b, ESLint, build puliti; 62 test Vitest in 20 filelimen monitor-once --hazard floodend-to-end su dati realiLa suite completa (140 test) non è stata eseguita: richiede ~4,5 ore su questa macchina per la ragione documentata su #90 — un solo test,
test_weekly_idrogeo_sync_handles_unreachable_isp_gracefully, ne prende 1678 s perché mocka ISPRA a 503 e la policy tenacity condivisa ritenta i 5xx con backoff per ogni AOI seminata.Refs #58