
Figure 1:Ryzhkov et al. (2016) QVP
Perfiles Cuasi-Verticales (Quasi-Vertical Profiles)¶
🧭 Introducción¶
En este cuadernillo exploraremos el uso de Perfiles Cuasi-Verticales (QVPs) como una técnica eficiente para analizar la estructura vertical de la atmósfera utilizando datos de radar meteorológico polarimétrico. Mostraremos cómo generar y visualizar QVPs a partir de datos almacenados en formato Zarr mediante el flujo de trabajo ARCO, y compararemos su eficiencia frente al enfoque tradicional basado en archivos binarios.
📚 Descripción general¶
✔ Qué es un Perfil Cuasi-Vertical (QVP) y su utilidad en radar meteorológico polarimétrico
✔ Cómo acceder a datos de radar preprocesados en formato Zarr con ARCO
✔ Cómo calcular QVPs para distintas variables radar
✔ Cómo comparar el flujo de trabajo ARCO con un enfoque tradicional basado en archivos binarios
✔ Visualización de perfiles verticales de reflectividad, reflectividad diferencial, correlación cruzada y fase diferencial
✅ Requisitos Previos¶
Concepto | Importancia | Enlace sugerido |
|---|---|---|
Necesario | Fundamentos del lenguaje y estructuras básicas | |
Necesario | Manipulación de arreglos y datos multidimensionales | |
Necesario | Conceptos básicos de adquisición y estructura de datos de radar | |
Útil | Concepto de almacenamiento en la nube y acceso eficiente a grandes volúmenes de datos | |
Útil | Fundamento teórico sobre Perfiles Cuasi-Verticales (Ryzhkov et al., 2016) |
Tiempo de ejecución estimado: 5-10 minutos
Requisitos computacionales:
RAM mínima: 4 GB
Conexión a internet estable (para acceso a datos Zarr en la nube)
Compatible con Binder y entornos locales
Librerías¶
Importamos las librerías necesarias para este cuaderno:
import numpy as np
import pandas as pd
import xarray as xr
import xradar
import fsspec
import gzip
import tempfile
import os
import matplotlib.pyplot as plt
import cmweather
from dask.distributed import Client, LocalCluster
1. 📡 ¿Qué es un Perfil Cuasi-Vertical (QVP)?¶
Los Perfiles Cuasi-Verticales (Quasi-Vertical Profiles, QVPs) constituyen una técnica utilizada en el análisis de datos de radar meteorológico polarimétrico. Esta técnica consiste en promediar la información de una elevación del radar (típicamente 20°) a lo largo de todos los azimuts para generar un perfil vertical simplificado, que permite representar la estructura vertical de la atmósfera en las cercanías del radar como se observa en la siguiente figura

Figure 2:Esquema conceptual de un perfil cuasi-vertical (QVP), donde se promedia azimutalmente una elevación específica del radar para construir un perfil vertical.
Fuente: Ryzhkov et al., 2016
Los QVPs resultan útiles para:
Estudiar la evolución microfísica de tormentas (como el crecimiento de cristales de hielo o procesos de fusión de hidrometeoros),
Monitorear la fase de precipitación,
Evaluar transiciones entre fases líquidas y sólidas en sistemas de precipitación.
from dask.distributed import Client, LocalCluster
# Create a local cluster with correct arguments
cluster = LocalCluster(
n_workers=4, # Number of worker processes
memory_limit='2GB' # Per worker memory limit
)
client = Client(cluster)
client2. 🗂️ Acceso a datos ARCO en formato Zarr¶
En esta sección accederemos a un conjunto de datos de radar meteorológico preprocesado, almacenado en formato Zarr v3 en un bucket público mantenido por el proyecto Pythia.
Los datos corresponden al radar KVNX (Vance AFB, Oklahoma) para el evento del 20 de mayo de 2011, y están disponibles gracias a la iniciativa ARCO-FAIR. Estos datos se encuentran almacenados de manera eficiente en la nube (Jetstream 2), lo que permite el acceso distribuido y escalable sin necesidad de descarga previa.
import xarray as xr
import xradar
import fsspec
# Ruta al archivo Zarr en la nube
url = "https://js2.jetstream-cloud.org:8001/"
path = "s3://pythia/radar/AtmosCol2025/KVNX.zarr"
# Abrimos nuestro dataset
dtree = xr.open_datatree(
path,
engine="zarr",
chunks={},
backend_kwargs={
"consolidated": False,
"storage_options": {
"anon": True,
"client_kwargs": {
"endpoint_url": url
}
}
}
)display(dtree)print(f"Tamaño del conjunto de datos es: {dtree.nbytes / 1024**3: .2f} GB")Tamaño del conjunto de datos es: 6.99 GB
2.1 🗂️ Exploración del DataTree¶
Una vez cargado el dataset en formato DataTree, podemos inspeccionar su estructura jerárquica. Esta estructura refleja la organización típica de un archivo de radar con múltiples elevaciones (sweeps), metadatos globales y parámetros del radar.
Podemos listar los nodos hijos principales del árbol de datos:
list(dtree.children)['VCP-12']Luego podemos explorar los niveles inferiores, por ejemplo:
# Exploramos los hijos del volumen VCP-12
list(dtree["VCP-12"].children)['georeferencing_correction',
'sweep_13',
'sweep_14',
'sweep_15',
'sweep_16',
'sweep_2',
'radar_parameters',
'sweep_0',
'sweep_1',
'sweep_10',
'sweep_12',
'sweep_11',
'sweep_5',
'sweep_4',
'sweep_7',
'sweep_6',
'sweep_8',
'sweep_9',
'sweep_3']Cada sweep_N representa un barrido del radar a un ángulo de elevación distinto. Además, encontramos nodos como radar_parameters y georeferencing_correction.
También es posible inspeccionar directamente una variable meteorológica, como la reflectividad horizontal (DBZH) en un sweep específico:
2.2 📁 Acceso a nodos con sintaxis de ruta (file-path syntax)¶
Una ventaja del uso de xarray-datatree es que permite acceder a cualquier nodo o variable utilizando rutas tipo archivo, lo que simplifica el manejo jerárquico de los datos.
Seleccionaremos la variable DBZH desde el nodo correspondiente utilizando la sintaxis tipo archivo:
# Forma recomendada
dbzh = dtree["/VCP-12/sweep_16/DBZH"]display(dbzh)3. ⚙️ Cálculo de Perfiles Cuasi-Verticales (QVPs)¶
Una vez explorada la estructura de datos, procedemos a calcular los Perfiles Cuasi-Verticales (QVPs). Este tipo de perfil se genera promediando una variable de radar a lo largo de todos los azimuts en un barrido de elevación fijo, lo que proporciona una vista simplificada pero útil de la estructura vertical de la atmósfera.
3.1. 📥 Selección del sweep y del período de interés¶
Trabajaremos con el sweep_16, correspondiente a un ángulo de elevación bajo, adecuado para QVPs. Además, seleccionamos un intervalo de tiempo entre 10:00 y 12:00 UTC del 20 de mayo de 2011, con el objetivo de reproducir los resultados presentados en Ryzhkov et al. (2016), quienes analizaron un sistema convectivo de mesoescala (MCS) observado con el radar KVNX en esa misma fecha.
ds_qvp = dtree["/VCP-12/sweep_16"].ds.sel(
vcp_time=slice("2011-05-20 10:00", "2011-05-20 12:00")
)3.2 🧮 Función para calcular un QVP¶
Una vez seleccionados los datos del barrido adecuado, definimos una función que calcule el Perfil Cuasi-Vertical (QVP) a partir de cualquier variable radar que siga el esquema tridimensional típico (vcp_time, azimuth, range).
La función realiza el promedio azimutal y, si la variable está en escala logarítmica (como dBZ), realiza la conversión a escala lineal antes del promedio y luego la convierte nuevamente a logarítmica. También transforma la dimensión range a height (en km) usando el ángulo de elevación medio.
import numpy as np
def compute_qvp(ds: xr.Dataset, var="DBZH") -> xr.DataArray:
"""
Calcula un QVP (Perfil Cuasi-Vertical) para una variable específica del radar.
Parámetros:
----------
ds : xr.Dataset
Dataset que contiene la variable radar.
var : str
Nombre de la variable a procesar (ej. 'DBZH', 'ZDR', 'RHOHV', 'PHIDP').
Retorna:
--------
xr.DataArray
QVP con coordenadas [vcp_time, height] y atributos preservados.
"""
units = ds[var].attrs.get("units", "")
# Conversión de logarítmico a lineal si es necesario
if units.startswith("dB"):
qvp = 10 ** (ds[var] / 10)
qvp = qvp.mean("azimuth", skipna=True)
qvp = 10 * np.log10(qvp)
else:
qvp = ds[var].mean("azimuth", skipna=True)
# Conversión de distancia a altura estimada (en km)
elev_rad = ds.sweep_fixed_angle.mean(skipna=True).values * np.pi / 180.0
qvp = qvp.assign_coords({
"range": (qvp.range.values * np.sin(elev_rad)) / 1000 # convierte a km
})
qvp = qvp.rename(f"qvp_{var}")
qvp = qvp.rename({"range": "height"})
return qvp3.3 🔄 Cálculo de QVPs para múltiples variables¶
Una vez definida la función compute_qvp(), procedemos a aplicarla a las principales variables polarimétricas del radar: DBZH, ZDR, RHOHV y PHIDP.
Estas variables ofrecen información complementaria sobre la estructura microfísica de la atmósfera:
DBZH: Reflectividad horizontal, relacionada con la intensidad de la precipitación.ZDR: Reflectividad diferencial, útil para distinguir tipos de hidrometeoros.RHOHV: Coeficiente de correlación cruzada, indica homogeneidad de las partículas.PHIDP: Fase diferencial, relacionada con el contenido de agua líquida.
%%time
qvp_ref = compute_qvp(ds_qvp, var="DBZH").compute()
qvp_zdr = compute_qvp(ds_qvp, var="ZDR").compute()
qvp_rhohv = compute_qvp(ds_qvp, var="RHOHV").compute()
qvp_phidp = compute_qvp(ds_qvp, var="PHIDP").compute()CPU times: user 1.02 s, sys: 157 ms, total: 1.17 s
Wall time: 12.4 s
📈 4. Visualización de Perfiles Cuasi-Verticales (QVPs)¶
4.1. 🖼️ Función para graficar QVPs en panel 2×2¶
Definimos una función para visualizar los Perfiles Cuasi-Verticales (QVPs) de las variables más comunes en radar polarimétrico. Esto facilita la comparación entre distintos métodos (Zarr vs binario) o eventos meteorológicos.
import matplotlib.pyplot as plt
import cmweather
def plot_qvp_panels(qvp_ref, qvp_zdr, qvp_rhohv, qvp_phidp):
"""
Visualiza perfiles cuasi-verticales (QVPs) en un panel 2x2 con formato científico completo.
Parámetros:
-----------
qvp_ref : xr.DataArray
Reflectividad (DBZH)
qvp_zdr : xr.DataArray
Reflectividad diferencial (ZDR)
qvp_rhohv : xr.DataArray
Coeficiente de correlación cruzada (RHOHV)
qvp_phidp : xr.DataArray
Fase diferencial (PHIDP)
title : str
Título de la figura
"""
fig, axs = plt.subplots(2, 2, figsize=(12, 6), sharex=True, sharey=True)
# Panel 1: Reflectividad (DBZH)
cf = qvp_ref.plot.contourf(
x="vcp_time", y="height", cmap="ChaseSpectral",
levels=np.arange(-10, 55, 1), ax=axs[0][0], add_colorbar=False
)
contour = qvp_ref.plot.contour(
x="vcp_time", y="height", levels=np.arange(-10, 50, 10),
colors="k", ax=axs[0][0]
)
axs[0][0].clabel(contour, fmt="%d", inline=True, fontsize=8)
axs[0][0].set_title(r"$Z$")
axs[0][0].set_ylabel(r"$Height \ [km]$")
axs[0][0].set_ylim(0, 7)
plt.colorbar(cf, ax=axs[0][0], label=r"$Reflectivity \ [dBZ]$")
# Panel 2: Reflectividad diferencial (ZDR)
cf1 = qvp_zdr.plot.contourf(
x="vcp_time", y="height", cmap="ChaseSpectral",
levels=np.linspace(-1, 5, 11), ax=axs[0][1], add_colorbar=False
)
contour = qvp_ref.plot.contour(
x="vcp_time", y="height", levels=np.arange(-10, 50, 10),
colors="k", ax=axs[0][1]
)
axs[0][1].clabel(contour, fmt="%d", inline=True, fontsize=8)
axs[0][1].set_title(r"$Z_{DR}$")
axs[0][1].set_ylabel("")
axs[0][1].set_xlabel("")
plt.colorbar(cf1, ax=axs[0][1], label=r"$Diff. \ Reflectivity \ [dB]$")
# Panel 3: RHOHV
cf2 = qvp_rhohv.plot.contourf(
x="vcp_time", y="height", cmap="Carbone11",
levels=np.arange(0.8, 1.01, 0.01), ax=axs[1][0], add_colorbar=False
)
contour = qvp_ref.plot.contour(
x="vcp_time", y="height", levels=np.arange(-10, 50, 10),
colors="k", ax=axs[1][0]
)
axs[1][0].clabel(contour, fmt="%d", inline=True, fontsize=8)
axs[1][0].set_title(r"$\rho _{HV}$")
axs[1][0].set_ylabel(r"$Height \ [km]$")
axs[1][0].set_xlabel(r"$Time \ [UTC]$")
plt.colorbar(cf2, ax=axs[1][0], label=r"$Cross-Correlation \ Coef.$")
# Panel 4: PHIDP
cf3 = qvp_phidp.plot.contourf(
x="vcp_time", y="height", cmap="PD17",
levels=np.arange(0, 110, 5), ax=axs[1][1], add_colorbar=False
)
contour = qvp_ref.plot.contour(
x="vcp_time", y="height", levels=np.arange(-10, 50, 10),
colors="k", ax=axs[1][1]
)
axs[1][1].clabel(contour, fmt="%d", inline=True, fontsize=8)
axs[1][1].set_title(r"$\theta _{DP}$")
axs[1][1].set_xlabel(r"$Time \ [UTC]$")
axs[1][1].set_ylabel("")
plt.colorbar(cf3, ax=axs[1][1], label=r"$Differential \ Phase \ [deg]$")
fig.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()4.2 📊 Visualización de QVPs usando función reutilizable¶
Ahora utilizamos la función plot_qvp_panels() para graficar los Perfiles Cuasi-Verticales (QVPs) de las variables DBZH, ZDR, RHOHV y PHIDP, obtenidos a partir de los datos del radar KVNX para el evento del 20 de mayo de 2011.
%%time
plot_qvp_panels(
qvp_ref=qvp_ref,
qvp_zdr=qvp_zdr,
qvp_rhohv=qvp_rhohv,
qvp_phidp=qvp_phidp,
)
CPU times: user 2.12 s, sys: 169 ms, total: 2.29 s
Wall time: 3.89 s
El flujo completo —desde el acceso a datos remotos hasta el cálculo y visualización de los QVPs— se realiza de forma muy eficiente gracias al formato Zarr y la compatibilidad con lectura directa vía fsspec.
Cálculo de QVPs: ~11.9 segundos
Visualización completa (panel 2×2): ~1.4 segundos
📝 Conclusiones¶
En este cuaderno comparamos dos enfoques para el cálculo de Perfiles Cuasi-Verticales (QVPs) a partir de datos de radar polarimétrico:
🌀 Flujo tradicional usando archivos binarios NEXRAD (
.gz)🧊 Flujo moderno FAIR usando datos preprocesados en formato Zarr (ARCO)
La siguiente tabla resume los tiempos aproximados y la aceleración lograda en cada etapa:
Etapa | ARCO-Zarr | Binario NEXRAD | Aceleración (×) |
|---|---|---|---|
Acceso / lectura de datos | ~0 s (lazy loading) | ~140 s | ~140× |
Cálculo de QVPs | ~11.9 s | ~1.6 s | 0.13× |
Visualización | ~1.4 s | ~1.4 s | = |
Total estimado | ~13.3 s | ~143.0 s | ~10.7× |
🔜 ¿Qué sigue?¶
En este cuaderno aprendiste a calcular y visualizar Perfiles Cuasi-Verticales (QVPs) utilizando datos de radar en formato Zarr. Te recomendamos explorar:
Estimación Cuantitativa de Precipitación (QPE): Aprende a estimar tasas de precipitación a partir de datos de radar polarimétrico.
Datos de reanálisis ERA5: Complementa el análisis de radar con datos de reanálisis atmosférico.
Artículo científico sobre QVPs: Lee el paper original de Ryzhkov et al. (2016) sobre la técnica QVP.
También puedes experimentar con:
Aplicar QVPs a otros eventos meteorológicos disponibles en el bucket de Jetstream2
Combinar QVPs con análisis de trayectorias atmosféricas
Automatizar el monitoreo continuo de QVPs para vigilancia operacional
📖 Referencias y recursos¶
Publicaciones científicas:
Ryzhkov, A. V., Zhang, P., Reeves, H., Kumjian, M., Tschallener, T., Trömel, S., & Simmer, C. (2016). Quasi-Vertical Profiles—A New Way to Look at Polarimetric Radar Data. Journal of Atmospheric and Oceanic Technology, 33(3), 551–562. Ryzhkov et al. (2016)
Herramientas y datos:
Datasets utilizados:
Radar KVNX (Vance AFB, Oklahoma) - 20 de mayo de 2011
Fuente ARCO-Zarr:
s3://pythia/radar/AtmosCol2025/KVNX.zarrFuente NEXRAD binario:
s3://noaa-nexrad-level2/
- Ryzhkov, A., Zhang, P., Reeves, H., Kumjian, M., Tschallener, T., Trömel, S., & Simmer, C. (2016). Quasi-Vertical Profiles—A New Way to Look at Polarimetric Radar Data. Journal of Atmospheric and Oceanic Technology, 33(3), 551–562. 10.1175/jtech-d-15-0020.1