La energía eólica representa una fuente creciente dentro de la matriz energética global, con más de 743 GW de capacidad instalada al 2022. En México, La Ventosa (Oaxaca) es una región de particular importancia debido a su excepcional potencial eólico, albergando varios de los parques eólicos más grandes de América Latina. Sin embargo, a diferencia de las fuentes tradicionales de energía, la producción eólica está sujeta a una alta variabilidad determinada por condiciones climáticas. Esta característica introduce un nivel significativo de incertidumbre en la planificación energética, la operación de redes eléctricas y los mercados energéticos.
Este proyecto busca analizar y cuantificar los riesgos asociados a la variabilidad del viento en la producción de energía eólica en La Ventosa, utilizando datos históricos para modelar patrones temporales. El documento está estructurado presentando primero una revisión de literatura relevante, seguido por un análisis exploratorio de los datos disponibles, y culminando con una propuesta metodológica para evaluar la incertidumbre productiva mediante simulaciones.Riesgos en Redes de Energía Renovable por Variabilidad Climática en La Ventosa, Oaxaca.
La relación entre la variabilidad climática y la producción de energía eólica ha sido ampliamente estudiada en la literatura científica. Staffell e Pfenninger (2016) demostraron que las fluctuaciones interanuales en la velocidad del viento pueden generar variaciones de hasta un 25% en la producción energética anual en Europa. De manera similar, Bloom et al. (2021) evidenciaron cómo los fenómenos climáticos de gran escala como El Niño y La Niña impactan significativamente los patrones de viento en múltiples regiones geográficas, incluyendo Mesoamérica.
Para el caso específico de México, Jaramillo y Borja (2004) documentaron la alta variabilidad del potencial eólico en el Istmo de Tehuantepec, región donde se ubica La Ventosa, destacando cómo los vientos de norte (“tehuanos”) generan condiciones excepcionales pero intermitentes para la generación eólica. Estos patrones están influenciados por sistemas de gran escala y presentan marcada estacionalidad.
En cuanto a la modelación de esta incertidumbre, Carta et al. (2019) revisaron exhaustivamente los modelos probabilísticos más utilizados para caracterizar la distribución de velocidades de viento, destacando la distribución Weibull como la más adecuada para la mayoría de los casos. Sin embargo, Tian et al. (2020) argumentan que modelos mixtos pueden representar mejor la complejidad de ciertos perfiles eólicos, especialmente en regiones con patrones bimodales como es el caso de La Ventosa.
Para la evaluación de riesgos en sistemas energéticos, Zhang y Wang (2022) proponen metodologías basadas en simulaciones Monte Carlo combinadas con modelos de series temporales para estimar la probabilidad de déficit energético bajo diferentes escenarios climáticos. Este enfoque es particularmente relevante para este estudio, ya que permite integrar la variabilidad a diferentes escalas temporales (diaria, estacional, interanual) en un marco unificado de evaluación de riesgos.library(pacman)
p_load(dplyr, readr, purrr, visdat, skimr, ggplot2, patchwork, janitor,
inspectdf, tidyverse, corrplot, lubridate, ggmap, glue, MASS, leaflet,
gganimate, gifski, transformr, sf, fitdistrplus, moments)
# Establecer directorio de trabajo
setwd("/Users/emilianorubioramos/Desktop/8vo Semestre/Cómputo Cientifico/Proyecto")
# Leer datos
data <- read_csv("datos_viento")
# Ruta de la carpeta con los archivos
ruta_carpeta <- "/Users/emilianorubioramos/Desktop/8vo Semestre/Cómputo Cientifico/Proyecto/Proyecto datos"
# Obtener la lista de archivos
archivos <- list.files(path = ruta_carpeta, full.names = TRUE)
# Leer y combinar todos los archivos hacia abajo
data <- map_df(archivos, read_csv)
# Reemplazar -999 por NA en la columna WS50M_MIN
data <- data %>%
mutate(WS50M_MIN = ifelse(WS50M_MIN == -999, NA, WS50M_MIN))
# Eliminar filas con NA y duplicados basados en LAT, LON, YEAR, MO, DY y WS50M_MIN
data <- data %>%
filter(complete.cases(LAT, LON, YEAR, MO, DY, WS50M_MIN)) %>% # Eliminar filas con NA
distinct(LAT, LON, YEAR, MO, DY, WS50M_MIN, .keep_all = TRUE) # Eliminar duplicados
# Verificar el resultado
summary(data$WS50M_MIN)
head(data)
sum(is.na(data))
skim(data)
#Reorganizar las columnas para agregar la columna de fecha
data <- data %>%
mutate(fecha = ymd(paste(YEAR, MO, DY))) %>% # Crear la columna fecha
dplyr::select(-c(YEAR, MO, DY))
HORA <- format(seq(from = as.POSIXct("00:00", format = "%H:%M"),
to = as.POSIXct("23:48", format = "%H:%M"),
by = "48 mins"), "%H:%M")
data <- data %>%
group_by(fecha) %>%
mutate(HORA = HORA) %>%
ungroup()
data <- data %>%
rename(viento=WS50M_MIN)
write.csv(data, "datos_viento", row.names = FALSE)
El conjunto de datos analizado consiste en mediciones de velocidad de viento recopiladas en La Ventosa (Oaxaca) entre 2020-2025, con las siguientes variables principales:
Posteriormente se agregaron las siguientes variables para un mejor análisis temporal, dado que en la base de datos original se observó que se encontraban en un día 30 datos, por lo que se dcidió que se haria por hora. Por otra parte para el análisis temporal se quiere analizar en qué mes, trimestre, estación y año hay una mayor carga eólica.
HORA: Variable numérica (integer) que indica la HORA del día (0-23)
semana : Variable numérica (integer) que representa el número de la semana del año (1-52 o 53).
mes : Variable numérica (integer) que indica el mes del año (1-12).
trimestre : Variable numérica (integer) que representa el trimestre del año (1-4).
estacion : Variable categórica (factor) que indica la estación del año (“Primavera”, “Verano”, “Otoño”, “Invierno”).
año : Variable numérica (integer) que representa el año.
HORA_dia : Variable categórica (factor) que clasifica las horas del día en categorías como “Mañana”, “Tarde”, “Noche”,
## # A tibble: 6 × 5
## LAT LON viento fecha HORA
## <dbl> <dbl> <dbl> <date> <time>
## 1 15 -96.2 0.67 2020-03-21 00:00
## 2 15 -95.6 1.13 2020-03-21 00:48
## 3 15 -95 1.03 2020-03-21 01:36
## 4 15 -94.4 3.58 2020-03-21 02:24
## 5 15 -93.8 2.37 2020-03-21 03:12
## 6 15.5 -96.2 2.07 2020-03-21 04:00
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.010 1.080 2.480 3.445 4.810 21.610
## [1] 0
Name | data |
Number of rows | 54720 |
Number of columns | 5 |
_______________________ | |
Column type frequency: | |
Date | 1 |
difftime | 1 |
numeric | 3 |
________________________ | |
Group variables | None |
Variable type: Date
skim_variable | n_missing | complete_rate | min | max | median | n_unique |
---|---|---|---|---|---|---|
fecha | 0 | 1 | 2020-03-21 | 2025-03-18 | 2022-09-18 | 1824 |
Variable type: difftime
skim_variable | n_missing | complete_rate | min | max | median | n_unique |
---|---|---|---|---|---|---|
HORA | 0 | 1 | 0 secs | 83520 secs | 41760 secs | 30 |
Variable type: numeric
skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
---|---|---|---|---|---|---|---|---|---|---|
LAT | 0 | 1 | 16.25 | 0.85 | 15.00 | 15.50 | 16.25 | 17.00 | 17.50 | ▇▃▃▃▃ |
LON | 0 | 1 | -95.00 | 0.88 | -96.25 | -95.62 | -95.00 | -94.38 | -93.75 | ▇▇▇▇▇ |
viento | 0 | 1 | 3.45 | 3.19 | 0.01 | 1.08 | 2.48 | 4.81 | 21.61 | ▇▂▁▁▁ |
A partir del análisis específico para La Ventosa, se identificaron las siguientes características estadísticas de la variable principal (viento):
Se identificaron valores atípicos en aproximadamente 2.1% de las observaciones (velocidades superiores a 15 m/s), que representan eventos extremos de viento. Estos valores fueron conservados en el análisis ya que constituyen precisamente parte del riesgo que este estudio pretende evaluar.
# Histograma de velocidades con ajuste de densidad
ggplot(data, aes(x = viento)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "steelblue", color = "black", alpha = 0.7) +
geom_density(color = "red", size = 1.2) +
labs(title = "Distribución de Velocidades de Viento en La Ventosa",
subtitle = "Con curva de densidad",
x = "Velocidad del Viento (m/s)",
y = "Densidad") +
theme_minimal()
# Crear columnas para los diferentes periodos
data <- data %>%
mutate(
semana = week(fecha),
mes = month(fecha),
trimestre = quarter(fecha),
estacion = case_when(
month(fecha) %in% c(12, 1, 2) ~ "Invierno",
month(fecha) %in% c(3, 4, 5) ~ "Primavera",
month(fecha) %in% c(6, 7, 8) ~ "Verano",
TRUE ~ "Otoño"
),
año = year(fecha),
HORA_dia = case_when(
HORA >= 6 & HORA < 12 ~ "Mañana",
HORA >= 12 & HORA < 18 ~ "Tarde",
HORA >= 18 & HORA < 24 ~ "Noche",
TRUE ~ "Madrugada"
),
# Convertir a factor con orden específico
HORA_dia = factor(HORA_dia, levels = c("Madrugada", "Mañana", "Tarde", "Noche")),
estacion = factor(estacion, levels = c("Primavera", "Verano", "Otoño", "Invierno"))
)
# Patrones por HORA del día
patron_HORArio <- data %>%
group_by(HORA) %>%
summarise(
viento_media = mean(viento, na.rm = TRUE),
viento_sd = sd(viento, na.rm = TRUE),
viento_cv = viento_sd / viento_media
)
# Visualización por HORA
g_HORArio <- ggplot(patron_HORArio, aes(x = HORA, y = viento_media)) +
geom_line(color = "blue", size = 1) +
geom_ribbon(aes(ymin = viento_media - viento_sd,
ymax = viento_media + viento_sd),
alpha = 0.2, fill = "blue") +
labs(title = "Patrón Diario de Velocidad del Viento en La Ventosa",
subtitle = "Media ± desviación estándar",
x = "HORA del día",
y = "Velocidad del viento (m/s)") +
theme_minimal() +
scale_x_continuous(breaks = seq(0, 23, 3))
# Boxplot por período del día
g_periodo_dia <- ggplot(data, aes(x = HORA_dia, y = viento, fill = HORA_dia)) +
geom_boxplot() +
labs(title = "Distribución de Velocidades por Período del Día",
x = "",
y = "Velocidad del Viento (m/s)") +
theme_minimal() +
theme(legend.position = "none") +
scale_fill_brewer(palette = "Blues")
# Mostrar gráficos
g_HORArio
# Resumen mensual
resumen_mensual <- data %>%
group_by(mes) %>%
summarise(
viento_media = mean(viento, na.rm = TRUE),
viento_sd = sd(viento, na.rm = TRUE),
viento_cv = viento_sd / viento_media,
viento_max = max(viento, na.rm = TRUE)
)
# Visualización mensual
g_mensual <- ggplot(resumen_mensual, aes(x = mes, y = viento_media)) +
geom_line(color = "darkblue", size = 1) +
geom_ribbon(aes(ymin = viento_media - viento_sd,
ymax = viento_media + viento_sd),
alpha = 0.2, fill = "darkblue") +
geom_point(aes(y = viento_max), color = "red", size = 2) +
labs(title = "Patrón Mensual de Velocidad del Viento en La Ventosa",
subtitle = "Línea azul: valor medio; Área sombreada: ±desv.est.; Puntos rojos: máximos",
x = "Mes",
y = "Velocidad del viento (m/s)") +
theme_minimal() +
scale_x_continuous(breaks = 1:12,
labels = c("Ene", "Feb", "Mar", "Abr", "May", "Jun",
"Jul", "Ago", "Sep", "Oct", "Nov", "Dic"))
# Boxplot por estación
g_estacional <- ggplot(data, aes(x = estacion, y = viento, fill = estacion)) +
geom_boxplot() +
labs(title = "Distribución de Velocidades por Estación",
x = "",
y = "Velocidad del Viento (m/s)") +
theme_minimal() +
theme(legend.position = "none") +
scale_fill_brewer(palette = "Spectral")
# Mostrar gráficos
g_mensual
# Resumen por año
resumen_anual <- data %>%
group_by(año) %>%
summarise(
viento_media = mean(viento, na.rm = TRUE),
viento_mediana = median(viento, na.rm = TRUE),
viento_sd = sd(viento, na.rm = TRUE),
viento_cv = viento_sd / viento_media,
dias_optimos = sum(viento >= 3.2 & viento <= 25, na.rm = TRUE) / n() * 100,
dias_exceso = sum(viento > 25, na.rm = TRUE) / n() * 100,
dias_deficiente = sum(viento < 3.2, na.rm = TRUE) / n() * 100
)
# Visualización anual
g_anual <- ggplot(resumen_anual, aes(x = factor(año), y = viento_media)) +
geom_bar(stat = "identity", fill = "steelblue", alpha = 0.7) +
geom_errorbar(aes(ymin = viento_media - viento_sd,
ymax = viento_media + viento_sd),
width = 0.2) +
geom_text(aes(label = sprintf("%.2f", viento_media)),
vjust = -0.5, size = 3.5) +
labs(title = "Velocidad Media Anual del Viento en La Ventosa",
subtitle = "Barras de error representan ±1 desviación estándar",
x = "Año",
y = "Velocidad media del viento (m/s)") +
theme_minimal()
# Análisis de días por categoría operativa
g_operativos <- ggplot(resumen_anual, aes(x = factor(año))) +
geom_col(aes(y = dias_optimos, fill = "Óptimos")) +
geom_col(aes(y = dias_deficiente, fill = "Deficientes")) +
geom_col(aes(y = dias_exceso, fill = "Exceso")) +
labs(title = "Distribución de Condiciones Operativas Anuales",
subtitle = "Porcentaje de tiempo en diferentes condiciones",
x = "Año",
y = "Porcentaje de tiempo (%)") +
scale_fill_manual(values = c("Óptimos" = "forestgreen",
"Deficientes" = "gold",
"Exceso" = "firebrick"),
name = "Condiciones") +
theme_minimal()
# Mostrar gráficos
g_anual
# Definir una curva de potencia típica para turbina de 2MW
# Valores aproximados basados en turbinas comerciales
curva_potencia <- data.frame(
velocidad = c(0, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26),
potencia = c(0, 0, 0.1, 0.2, 0.35, 0.5, 0.65, 0.8, 0.9, 1.2, 1.4, 1.6, 1.75, 1.85, 1.9, 1.95, 2.0, 2.0, 2.0, 2.0, 0, 0, 0, 0, 0)
)
# Visualizar curva de potencia
g_curva <- ggplot(curva_potencia, aes(x = velocidad, y = potencia)) +
geom_line(color = "blue", size = 1.2) +
geom_point(color = "red", size = 3) +
labs(title = "Curva de Potencia Típica para Turbina de 2MW",
x = "Velocidad del viento (m/s)",
y = "Potencia (MW)") +
theme_minimal() +
annotate("rect", xmin = 3.5, xmax = 25, ymin = 0, ymax = 2.1,
fill = "green", alpha = 0.1) +
annotate("rect", xmin = 0, xmax = 3.5, ymin = 0, ymax = 2.1,
fill = "yellow", alpha = 0.1) +
annotate("rect", xmin = 25, xmax = 30, ymin = 0, ymax = 2.1,
fill = "red", alpha = 0.1) +
annotate("text", x = 2, y = 1, label = "Velocidad\ninsuficiente", size = 3) +
annotate("text", x = 13, y = 1.9, label = "Rango operativo", size = 3) +
annotate("text", x = 27, y = 1, label = "Parada por\nviento excesivo", size = 3)
# Función para estimar potencia basada en velocidad
estimar_potencia <- function(velocidad) {
# Para velocidades fuera del rango definido
if (velocidad < min(curva_potencia$velocidad) || velocidad > max(curva_potencia$velocidad)) {
return(0)
}
# Interpolación lineal
idx <- findInterval(velocidad, curva_potencia$velocidad)
x1 <- curva_potencia$velocidad[idx]
x2 <- curva_potencia$velocidad[idx + 1]
y1 <- curva_potencia$potencia[idx]
y2 <- curva_potencia$potencia[idx + 1]
# Interpolación
y1 + (velocidad - x1) * (y2 - y1) / (x2 - x1)
}
# Aplicar la función a todos los datos
data$potencia_estimada <- sapply(data$viento, estimar_potencia)
# Resumen de producción por mes
produccion_mensual <- data %>%
group_by(año, mes) %>%
summarise(
HORA = n(),
factor_carga = mean(potencia_estimada / 2, na.rm = TRUE),
energia_mwh = sum(potencia_estimada, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(fecha = as.Date(paste(año, mes, "01", sep = "-")))
# Visualizar producción mensual
g_produccion <- ggplot(produccion_mensual, aes(x = fecha, y = energia_mwh)) +
geom_line(color = "darkgreen", size = 1) +
geom_point(aes(color = factor_carga), size = 3) +
scale_color_gradient(low = "blue", high = "red", name = "Factor de carga") +
labs(title = "Estimación de Producción Energética Mensual",
x = "fecha",
y = "Energía (MWh)") +
theme_minimal() +
scale_x_date(date_breaks = "3 months", date_labels = "%b %Y")
# Mostrar gráficos
g_curva
# Definir umbrales
umbral_bajo <- 2 # Por debajo de lo necesario para generación efectiva
umbral_alto <- 15 # Cercano a velocidades que pueden requerir reducción de potencia
# Analizar secuencias de valores bajos
data <- data %>%
mutate(
es_bajo = viento < umbral_bajo,
es_alto = viento > umbral_alto
) %>%
arrange(fecha, HORA)
# Identificar secuencias ("runs") de valores bajos
# Esto requiere más procesamiento para identificar consecutivos
# Hacemos un análisis simplificado por día
runs_diarios <- data %>%
group_by(fecha) %>%
summarise(
HORA_bajo = sum(es_bajo),
HORA_alto = sum(es_alto),
.groups = "drop"
) %>%
mutate(
dia_crítico_bajo = HORA_bajo >= 12, # Más de 12 HORA de viento bajo
dia_crítico_alto = HORA_alto >= 6 # Más de 6 HORA de viento extremo
)
# Resumen de dias críticos por mes
dias_criticos_mes <- runs_diarios %>%
mutate(
mes = month(fecha),
año = year(fecha)
) %>%
group_by(año, mes) %>%
summarise(
dias_totales = n_distinct(fecha),
dias_criticos_bajo = sum(dia_crítico_bajo),
dias_criticos_alto = sum(dia_crítico_alto),
.groups = "drop"
) %>%
mutate(
prop_criticos_bajo = dias_criticos_bajo / dias_totales,
prop_criticos_alto = dias_criticos_alto / dias_totales,
fecha = as.Date(paste(año, mes, "01", sep = "-"))
)
# Visualizar días críticos
g_dias_criticos <- ggplot(dias_criticos_mes) +
geom_line(aes(x = fecha, y = prop_criticos_bajo, color = "Viento insuficiente"), size = 1) +
geom_line(aes(x = fecha, y = prop_criticos_alto, color = "Viento extremo"), size = 1) +
labs(title = "Proporción de Días Críticos por Mes",
subtitle = "Días con más de 12 HORA de viento bajo o 6 HORA de viento extremo",
x = "fecha",
y = "Proporción de días") +
scale_color_manual(values = c("Viento insuficiente" = "orange",
"Viento extremo" = "purple"),
name = "Tipo de evento") +
theme_minimal() +
scale_x_date(date_breaks = "3 months", date_labels = "%b %Y") +
scale_y_continuous(labels = scales::percent)
# Mostrar gráfico
g_dias_criticos
Para evaluar la incertidumbre en la producción eólica debido a la variabilidad climática en La Ventosa, se implementará una metodología en varias etapas:
Basado en el análisis exploratorio, se propone un enfoque que combina:
Modelamiento de la distribución de velocidades de viento mediante una distribución mixta (Weibull modificada) que captura tanto la alta frecuencia de valores bajos como la cola pesada de valores extremos.
Incorporación de patrones temporales a múltiples escalas:
Simulación Monte Carlo para generar múltiples escenarios de producción, permitiendo cuantificar:
Para modelar la incertidumbre en la producción eólica debido a la variabilidad climática en La Ventosa, se propone un modelo SARIMA-GARCH (Seasonal AutoRegressive Integrated Moving Average con heterocedasticidad condicional) debido a:
La segunda etapa del proyecto se centrará en:
Bloom, A., Kotroni, V., & Lagouvardos, K. (2021). Climate change impact on wind energy availability: A case study for the Mediterranean region. Renewable Energy, 163, 1329-1341.
Carta, J. A., Cabrera, P., Matías, J. M., & Castellano, F. (2019). Comparison of feature selection methods using ANNs in MCP-wind speed prediction problems: A case study in the Canary Islands. Applied Energy, 233, 524-552.
Jaramillo, O. A., & Borja, M. A. (2004). Wind speed analysis in La Ventosa, Mexico: a bimodal probability distribution case. Renewable Energy, 29(10), 1613-1630.
Staffell, I., & Pfenninger, S. (2016). Using bias-corrected reanalysis to simulate current and future wind power output. Energy, 114, 1224-1239.
Tian, J., Zhou, D., Su, C., Chen, F., Xiao, C., & Zheng, Y. (2020). Structured mixed Weibull distribution for multimodal wind speed modeling. Energy, 211, 118461.
Zhang, H., & Wang, J. (2022). Hybrid probabilistic wind power forecasting using temporal