3.1.b Amenaza por inundación fluvial
Crecientes del Amazonas en la regla de Leticia, periodos de retorno y mapas de inundación
La creciente del Amazonas es lenta y predecible: el río sube durante meses y alcanza su máximo entre marzo y junio. Esa regularidad es el pulso de inundación que sostiene la várzea Junk et al., 1989. La amenaza aparece cuando el pulso es más alto o más largo de lo habitual. Este cuaderno la caracteriza con tres fuentes complementarias:
la serie diaria de nivel de la estación LETICIA [48017010] del IDEAM, desde 1987;
el caudal simulado de GloFAS v4 en el cauce principal, desde 1984 Grimaldi et al., 2022;
los mapas globales de amenaza del JRC, con profundidades para periodos de retorno de 10 a 500 años Baugh et al., 2024.
1. Serie de nivel y control de calidad¶
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from IPython.display import Markdown, display
from leticia import fuentes, hidro, viz
from leticia.config import parametros
tabla_ideam = fuentes.nivel_rio("NV_MEDIA_D", con_aprobacion=True)
crudo = tabla_ideam["nivel_cm"]
nivel = hidro.limpiar_nivel(crudo)
aprob = tabla_ideam["aprobacion"]
display(pd.Series({
"Periodo": f"{nivel.first_valid_index():%Y-%m-%d} a {nivel.last_valid_index():%Y-%m-%d}",
"Días con dato": int(nivel.notna().sum()),
"Registros descartados por control de calidad (incluye 2007)": int(crudo.notna().sum() - nivel.notna().sum()),
"Datos definitivos (%)": round(100 * (aprob == "Definitivo").mean(), 1),
"Nivel máximo registrado (cm)": f"{nivel.max():.0f} ({nivel.idxmax():%Y-%m-%d})",
"Nivel mínimo registrado (cm)": f"{nivel.min():.0f} ({nivel.idxmin():%Y-%m-%d})",
}, name="valor").to_frame())fig, ax = plt.subplots(figsize=(11, 3.6))
ax.plot(nivel.index, nivel / 100, color=viz.AZUL_RIO, lw=0.6)
descartados = crudo[nivel.reindex(crudo.index).isna() & crudo.notna()]
ax.scatter(descartados.index, descartados / 100, s=6, color=viz.ROJO, label="descartado (control de calidad)", zorder=3)
ax.set_ylabel("nivel en la regla (m)")
ax.set_title("Nivel medio diario del Amazonas en Leticia")
ax.legend(loc="lower left")
fig.tight_layout()
El control de calidad elimina valores imposibles (ceros y registros bajo 1 m) y saltos de más de 1,2 m frente a la mediana móvil de 15 días: un río de este tamaño no cambia tanto de un día a otro. Además se excluye 2007: de febrero a diciembre de ese año la serie publicada queda entre 5 y 7 m por debajo de su climatología todos los meses (su máximo de aguas altas sería 11 m, cuando ningún otro año baja de 14 m), señal de un cambio del cero de la regla que no fue corregido. Sin esa exclusión, 2007 aparecería falsamente como la peor sequía del registro. Las lagunas de la serie (años con meses sin dato) obligan a exigir un mínimo de días en cada temporada para usar el máximo o el mínimo de ese año.
2. El ciclo anual: aguas altas y aguas bajas¶
clim = hidro.climatologia_diaria(nivel, base=(1991, 2020))
fig, ax = plt.subplots(figsize=(10, 4))
x = clim.index
ax.fill_between(x, clim["min"] / 100, clim["max"] / 100, color=viz.AZUL_RIO, alpha=0.12, label="mín–máx 1991–2020")
ax.fill_between(x, clim["p10"] / 100, clim["p90"] / 100, color=viz.AZUL_RIO, alpha=0.25, label="p10–p90")
ax.plot(x, clim["p50"] / 100, color=viz.AZUL_RIO, lw=1.5, label="mediana")
for anio, color in ((1999, "#7b3294"), (2012, "#c2a5cf"), (2015, "#008837"), (2021, "#a6dba0")):
s = nivel[nivel.index.year == anio]
ax.plot(s.index.dayofyear, s / 100, color=color, lw=1.1, label=str(anio))
ax.set_xticks([1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335])
ax.set_xticklabels(list("EFMAMJJASOND"))
ax.set_ylabel("nivel (m)")
ax.set_title("Hidrograma típico y años de grandes crecientes")
ax.legend(ncol=4, fontsize=7.5, loc="lower center")
fig.tight_layout()
3. Máximos anuales y tendencia¶
maximos = hidro.extremos_anuales(nivel, "max")
mk = hidro.mann_kendall(maximos["valor"])
umbral = parametros()["hidrologia"]["umbral_creciente_cm"]
fig, ax = plt.subplots(figsize=(10, 3.6))
colores = np.where(maximos["valor"] >= umbral, viz.ROJO, viz.AZUL_RIO)
ax.bar(maximos.index, maximos["valor"] / 100, color=colores)
ax.axhline(umbral / 100, color=viz.GRIS, ls="--", lw=1, label=f"umbral de referencia {umbral/100:.0f} m (ilustrativo)")
t = np.array(maximos.index, dtype=float)
ax.plot(t, (np.median(maximos["valor"]) + mk["sen"] * (t - np.median(t))) / 100, color="k", lw=1, label="pendiente de Sen")
ax.set_ylim(maximos["valor"].min() / 100 - 1, maximos["valor"].max() / 100 + 0.5)
ax.set_ylabel("máximo anual (m)")
ax.set_title("Nivel máximo de aguas altas por año (febrero–junio)")
ax.legend(loc="upper left")
fig.tight_layout()
display(Markdown(f"Años con dato suficiente: **{mk['n']}**. Tendencia de Mann-Kendall: z = {mk['z']:.2f}, p = {mk['p']:.2f}; "
f"pendiente de Sen **{mk['sen']:.1f} cm/año**. Los cinco máximos más altos: "
+ ", ".join(f"{a} ({v/100:.2f} m)" for a, v in maximos['valor'].nlargest(5).items()) + "."))
Lectura. Las mayores crecientes de la serie se concentran en 1999, 2012 y 2015, en línea con la intensificación de las crecientes amazónicas documentada desde finales de los años noventa: en la primera mitad del siglo XX una gran creciente ocurría cada ~20 años y desde 2000 ocurre una cada ~4 años Marengo et al., 2021Barichivich et al., 2018. Con unas tres décadas de datos y lagunas, la tendencia local no es estadísticamente concluyente; la señal regional, basada en el registro centenario de Manaos, sí lo es.
4. Periodos de retorno de la creciente¶
T = parametros()["periodos_retorno"]
ret = hidro.niveles_retorno_ic(maximos["valor"].values, T)
emp = hidro.periodo_retorno_empirico(maximos["valor"])
fig, ax = plt.subplots(figsize=(7.5, 4))
tt = np.logspace(np.log10(1.05), np.log10(200), 100)
params = hidro.ajustar_gev(maximos["valor"].values)
ax.plot(tt, hidro.nivel_retorno(params, tt) / 100, color=viz.AZUL_RIO, label="GEV ajustada")
ax.fill_between(ret.index, ret["ic5"] / 100, ret["ic95"] / 100, color=viz.AZUL_RIO, alpha=0.2, label="IC 90 % (bootstrap)")
ax.scatter(emp, maximos["valor"] / 100, color="k", s=14, zorder=3, label="máximos observados (Weibull)")
ax.set_xscale("log")
ax.set_xlabel("periodo de retorno (años)")
ax.set_ylabel("nivel (m)")
ax.set_title("Curva de frecuencia de crecientes en Leticia")
ax.legend()
fig.tight_layout()
display((ret / 100).round(2).rename(columns={"nivel": "nivel (m)", "ic5": "IC 5 %", "ic95": "IC 95 %"}))
cota = params[1] + params[2] / params[0] if params[0] > 0 else np.inf
display(Markdown(f"Parámetro de forma de la GEV (convención de SciPy): c = {params[0]:.2f}. "
+ (f"Con c > 0 la distribución tiene **cota superior** (≈ {cota/100:.2f} m): por encima de cierto nivel el agua se "
"expande sobre la várzea y el nivel sube cada vez menos." if params[0] > 0 else "La cola superior no está acotada.")))
La distribución generalizada de valores extremos (GEV) se ajusta por máxima verosimilitud a los máximos anuales Coles, 2001. Con ~35 años de datos, los niveles de 50 y 100 años son extrapolaciones con intervalos amplios; sirven para comparar escenarios, no para diseñar obras sin un estudio hidráulico.
5. Caudal simulado por GloFAS y relación con el nivel¶
q = fuentes.caudal_glofas()
coef, r2, comun = hidro.relacion_nivel_caudal(nivel, q)
fig, ax = plt.subplots(1, 2, figsize=(11, 3.8), gridspec_kw={"width_ratios": [1.6, 1]})
ax[0].plot(q.index, q / 1000, color=viz.GRIS, lw=0.5)
ax[0].set_ylabel("caudal (miles de m³/s)")
ax[0].set_title("Caudal diario GloFAS v4, Amazonas en Leticia")
ax[1].scatter(comun["caudal"] / 1000, comun["nivel"] / 100, s=2, alpha=0.3, color=viz.AZUL_RIO)
xx = np.linspace(comun["caudal"].min(), comun["caudal"].max(), 50)
ax[1].plot(xx / 1000, np.polyval(coef, xx) / 100, color="k")
ax[1].set_xlabel("caudal GloFAS (miles de m³/s)")
ax[1].set_ylabel("nivel IDEAM (m)")
ax[1].set_title(f"Nivel vs. caudal (R² = {r2:.2f})")
fig.tight_layout()
Lectura. GloFAS reproduce bien el ciclo anual y su relación con la regla de Leticia permite convertir los pronósticos de caudal en niveles esperados. Es la base de la alerta temprana de la sección d. El caudal medio del Amazonas frente a Leticia ronda los 40 000 m³/s, con crecientes de más de 70 000 m³/s.
6. ¿Qué tan bien reproduce GloFAS el río en Leticia?¶
Towner et al. compararon ocho modelos hidrológicos globales con 75 estaciones de la cuenca amazónica (1997–2015) y encontraron que la lluvia con que se alimenta cada modelo controla la mayor parte de su error, tanto en los caudales diarios como en los picos anuales Towner et al., 2019. Antes de usar GloFAS como base de una alerta conviene medir su desempeño en Leticia. Como el IDEAM publica nivel y no caudal, aquí no se usa el índice de Kling-Gupta de ese estudio sino medidas que no dependen de las unidades: correlación de rangos, desfase, fecha del pico anual y detección de los días más altos.
res, picos = hidro.comparar_simulacion(nivel, q)
filas = {
"días con nivel y caudal": f"{res['dias_comunes']:,}".replace(",", "."),
"correlación de Spearman diaria": f"{res['spearman_diario']:.2f}",
"desfase con la mejor correlación": f"GloFAS se adelanta {res['desfase_optimo_dias']} días (ρ = {res['spearman_desfase']:.2f})",
"fecha del pico anual, error mediano": f"{res['error_pico_mediano_dias']:+.0f} días (absoluto {res['error_pico_absoluto_dias']:.0f} días; {res['anios']} años)",
"correlación de los máximos anuales": f"{res['spearman_maximos_anuales']:.2f} ({res['anios']} años)",
"correlación de los mínimos anuales": f"{res['spearman_minimos_anuales']:.2f} ({res['anios_minimos']} años)",
"días sobre el percentil 95: detección": f"{100 * res['probabilidad_deteccion']:.0f} % (falsas alarmas {100 * res['razon_falsas_alarmas']:.0f} %)",
}
display(pd.Series(filas).to_frame("GloFAS v4 frente a la regla de Leticia"))
fig, ax = plt.subplots(1, 2, figsize=(11, 3.6))
ax[0].bar(picos.index, picos["error_dias"], color=np.where(picos["error_dias"] < 0, viz.AZUL_RIO, viz.TIERRA))
ax[0].axhline(0, color="k", lw=0.6)
ax[0].set_ylabel("días (negativo = GloFAS se adelanta)")
ax[0].set_title("Error en la fecha del pico anual")
ax[1].scatter(picos["max_sim"] / 1000, picos["max_obs"] / 100, color=viz.AZUL_RIO, s=25)
for a, r_ in picos.iterrows():
ax[1].annotate(str(a), (r_["max_sim"] / 1000, r_["max_obs"] / 100), fontsize=6.5, xytext=(2, 2), textcoords="offset points")
ax[1].set_xlabel("máximo anual GloFAS (miles de m³/s)")
ax[1].set_ylabel("máximo anual IDEAM (m)")
ax[1].set_title(f"Máximos anuales (ρ = {res['spearman_maximos_anuales']:.2f})")
fig.tight_layout()
Lectura. GloFAS reproduce muy bien el ciclo anual del río: la correlación diaria es alta y mejora si se corrige un adelanto de algo más de una semana. Pero adelanta el pico de la creciente unas cuatro semanas y ordena mal qué años traen las crecientes más altas: la correlación de los máximos anuales es baja y la mayoría de los días extremos observados no aparecen como extremos en GloFAS. Las aguas bajas las reproduce mejor. Es lo que anticipan Towner et al.: el error viene sobre todo de la lluvia que alimenta el modelo, más que de su calibración Towner et al., 2019. En la práctica, el pronóstico de GloFAS sirve para anticipar la tendencia del río (3.1.d), pero su magnitud debe contrastarse con la regla de Leticia y con los boletines del IDEAM antes de activar una alerta.
7. Mapas de amenaza: profundidad por periodo de retorno¶
from leticia import aoi
urbana = aoi.area_urbana().to_crs(4326)
fig, ax = plt.subplots(1, 3, figsize=(15, 6))
for a, rp in zip(ax, (10, 100, 500)):
d = fuentes.inundacion_jrc(rp)
viz.fondo(a, "bbox_regional")
im = a.imshow(np.where(d.values > 0, d.values, np.nan), extent=(float(d.x.min()), float(d.x.max()), float(d.y.min()), float(d.y.max())),
cmap="Blues", vmin=0, vmax=8, alpha=0.85, zorder=2)
urbana.boundary.plot(ax=a, color="yellow", lw=1, zorder=3)
viz.marco(a, "bbox_regional", f"Periodo de retorno {rp} años")
fig.colorbar(im, ax=ax, shrink=0.6, label="profundidad (m)")
Los mapas del JRC resultan de simular con LISFLOOD-FP, a ~90 m, los caudales de GloFAS para cada periodo de retorno. Muestran la llanura de inundación de la margen peruana y las islas, que se inundan con frecuencia, y las zonas bajas de la ciudad junto al río y a las quebradas. A esta resolución no distinguen calles ni rellenos: el análisis de riesgo (c) los complementa con la altura de cada lugar sobre el río.
8. Lo que muestran 37 años de satélite: frecuencia de agua¶
occ = fuentes.raster_estatico("jrc-gsw", "occurrence")
fig, ax = plt.subplots(figsize=(8, 6.4))
viz.fondo(ax, "bbox_regional")
v = occ.where(occ > 0)
im = ax.imshow(v.values, extent=(float(occ.x.min()), float(occ.x.max()), float(occ.y.min()), float(occ.y.max())), cmap="YlGnBu",
vmin=0, vmax=100, alpha=0.8, zorder=2)
urbana.boundary.plot(ax=ax, color="yellow", lw=1, zorder=3)
fig.colorbar(im, ax=ax, shrink=0.7, label="% de observaciones con agua (1984–2021)")
viz.marco(ax, "bbox_regional", "Ocurrencia de agua superficial (JRC GSW)")
Lectura. Los valores intermedios (10–60 %) señalan áreas que el agua cubre solo en aguas altas: lagos de Yahuarcaca, várzeas de las islas y orillas bajas. Son las primeras en inundarse. La ocurrencia también revela la migración del cauce: franjas que pasaron de agua a tierra o al revés, que se estudian en 3.3 Erosión y dinámica fluvial.
- Junk, W. J., Bayley, P. B., & Sparks, R. E. (1989). The flood pulse concept in river-floodplain systems. In Proceedings of the International Large River Symposium (Vol. 106, pp. 110–127).
- Grimaldi, S., Salamon, P., Disperati, J., Zsoter, E., Russo, C., Ramos, A., Carton, C., Barnard, C., Hansford, E., Gomes, G., & Prudhomme, C. (2022). GloFAS v4.0 hydrological reanalysis. European Commission, Joint Research Centre (JRC). http://data.europa.eu/89h/f96b7a19-0133-4105-a879-0536991ca9c5
- Baugh, C., Colonese, J., D’Angelo, C., Dottori, F., Neal, J., Prudhomme, C., & Salamon, P. (2024). Modelled flood inundation for different return period scenarios at the global scale. European Commission, Joint Research Centre (JRC). https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/CEMS-GLOFAS/flood_hazard/
- Marengo, J. A., Espinoza, J. C., Fu, R., Muñoz, J. C. J., Alves, L. M., Rocha, H. R., & Schöngart, J. (2021). Chapter 22: Long-term variability, extremes and changes in temperature and hydro meteorology in the Amazon region [Amazon Assessment Report 2021]. Science Panel for the Amazon. 10.55161/ZGJG8060
- Barichivich, J., Gloor, E., Peylin, P., Brienen, R. J. W., Schöngart, J., Espinoza, J. C., & Pattnayak, K. C. (2018). Recent intensification of Amazon flooding extremes driven by strengthened Walker circulation. Science Advances, 4(9), eaat8785. 10.1126/sciadv.aat8785
- Coles, S. (2001). An Introduction to Statistical Modeling of Extreme Values. Springer. 10.1007/978-1-4471-3675-0
- Towner, J., Cloke, H. L., Zsoter, E., Flamig, Z., Hoch, J. M., Bazo, J., & others. (2019). Assessing the performance of global hydrological models for capturing peak river flows in the Amazon basin. Hydrology and Earth System Sciences, 23(7), 3057–3080. 10.5194/hess-23-3057-2019