Nota
Go to the end to download the full example code.
Análise da programação de manutenção das térmicas
O arquivo manutt.dat contém a programação de manutenção das usinas termelétricas, especificando períodos de indisponibilidade para manutenção preventiva ou revisões programadas.
import pandas as pd
import plotly.express as px
import plotly.io as pio
from inewave.newave import Manutt
pio.templates.default = "ggplot2"
Leitura da programação de manutenção
O arquivo manutt.dat é crucial para o planejamento operativo, pois define quando as usinas térmicas estarão indisponíveis:
arq_manutt = Manutt.read("./newave/manutt.dat")
print("Arquivo manutt.dat lido com sucesso")
Arquivo manutt.dat lido com sucesso
Análise geral das manutenções
Examinando o escopo e características das manutenções programadas:
manutencoes = arq_manutt.manutencoes
print(f"Total de manutenções programadas: {len(manutencoes)}")
print(f"Colunas disponíveis: {list(manutencoes.columns)}")
print("\nPrimeiras manutenções:")
print(manutencoes.head())
# Análise por empresa
manut_por_empresa = (
manutencoes.groupby("nome_empresa")
.agg({
"codigo_usina": "nunique",
"duracao": ["sum", "mean"],
"potencia": "sum",
})
.round(2)
)
manut_por_empresa.columns = [
"usinas",
"duracao_total",
"duracao_media",
"potencia_total",
]
print("\nManutenções por empresa:")
print(manut_por_empresa)
# Análise por usina
manut_por_usina = (
manutencoes.groupby("nome_usina")
.agg({"duracao": ["sum", "count"], "potencia": "mean"})
.round(2)
)
manut_por_usina.columns = [
"duracao_total",
"num_manutencoes",
"potencia_media",
]
print(f"\nUsinas com manutenção: {len(manut_por_usina)}")
print("Usinas com mais manutenções:")
print(manut_por_usina.sort_values("num_manutencoes", ascending=False).head())
Total de manutenções programadas: 113
Colunas disponíveis: ['codigo_empresa', 'nome_empresa', 'codigo_usina', 'nome_usina', 'codigo_unidade', 'data_inicio', 'duracao', 'potencia']
Primeiras manutenções:
codigo_empresa nome_empresa codigo_usina ... data_inicio duracao potencia
0 68 PETROBRAS 211 ... 2025-08-06 26 172.00
1 68 PETROBRAS 211 ... 2025-08-06 26 172.00
2 68 PETROBRAS 211 ... 2025-08-06 26 186.00
3 68 PETROBRAS 97 ... 2025-08-09 25 49.87
4 92 TERNIUM 65 ... 2025-12-11 4 148.04
[5 rows x 8 columns]
Manutenções por empresa:
usinas duracao_total duracao_media potencia_total
nome_empresa
DIFERENCIAL 2 11 1.00 194.24
ELDORADO 1 3 3.00 50.00
ELETRONORTE 2 62 3.65 1811.15
ERB ARATING 1 19 19.00 16.79
IMETAME 1 10 2.50 37.36
KARKEY KARA 2 4 1.00 65.62
MYRTOS 1 30 2.50 111.96
PETROBRAS 11 1361 26.69 3609.50
SUAPE II 1 30 2.73 246.73
TERNIUM 1 4 4.00 148.04
Usinas com manutenção: 23
Usinas com mais manutenções:
duracao_total num_manutencoes potencia_media
nome_usina
ERMOMACAE 579 21 46.38
ROSPERI III 30 12 9.33
UAPE II 30 11 22.43
PARECIDA 35 10 41.80
AUA 3 27 7 199.02
Análise temporal das manutenções
Verificando a distribuição das manutenções ao longo do tempo:
print("Análise temporal das manutenções:")
# Convertendo datas se necessário
if not pd.api.types.is_datetime64_any_dtype(manutencoes["data_inicio"]):
manutencoes["data_inicio"] = pd.to_datetime(manutencoes["data_inicio"])
# Criando cópias com informações temporais
manut_temporal = manutencoes.copy()
manut_temporal["ano"] = manut_temporal["data_inicio"].dt.year
manut_temporal["mes"] = manut_temporal["data_inicio"].dt.month
manut_temporal["trimestre"] = manut_temporal["data_inicio"].dt.quarter
manut_temporal["data_fim"] = manut_temporal["data_inicio"] + pd.to_timedelta(
manut_temporal["duracao"], unit="D"
)
# Análise por ano
manut_por_ano = (
manut_temporal.groupby("ano")
.agg({"codigo_usina": "count", "duracao": "sum", "potencia": "sum"})
.round(2)
)
manut_por_ano.columns = [
"num_manutencoes",
"duracao_total",
"potencia_total",
]
print("Manutenções por ano:")
print(manut_por_ano)
# Visualização temporal
fig = px.bar(
manut_por_ano.reset_index(),
x="ano",
y="num_manutencoes",
title="Distribuição de Manutenções por Ano",
labels={"ano": "Ano", "num_manutencoes": "Número de Manutenções"},
)
fig
Análise temporal das manutenções:
Manutenções por ano:
num_manutencoes duracao_total potencia_total
ano
2025 113 1534 6291.39
Análise da duração das manutenções
Verificando padrões na duração e intensidade das manutenções:
print("Análise da duração das manutenções:")
duracao_stats = manutencoes["duracao"].describe()
print(f"- Duração média: {duracao_stats['mean']:.1f} dias")
print(f"- Duração mediana: {duracao_stats['50%']:.1f} dias")
print(f"- Duração mínima: {int(duracao_stats['min'])} dias")
print(f"- Duração máxima: {int(duracao_stats['max'])} dias")
print(f"- Desvio padrão: {duracao_stats['std']:.1f} dias")
# Distribuição das durações
fig = px.histogram(
manutencoes,
x="duracao",
nbins=20,
title="Distribuição da Duração das Manutenções",
labels={"duracao": "Duração (dias)", "count": "Número de Manutenções"},
)
fig
# Classificação por duração
manutencoes_copy = manutencoes.copy()
manutencoes_copy["tipo_manutencao"] = pd.cut(
manutencoes_copy["duracao"],
bins=[0, 7, 30, 90, float("inf")],
labels=[
"Curta (≤7d)",
"Média (8-30d)",
"Longa (31-90d)",
"Extensa (>90d)",
],
)
tipo_distribuicao = manutencoes_copy["tipo_manutencao"].value_counts()
print("\nClassificação por duração:")
for tipo, count in tipo_distribuicao.items():
percentual = count / len(manutencoes_copy) * 100
print(f"- {tipo}: {count} manutenções ({percentual:.1f}%)")
# Visualização da classificação
fig = px.pie(
values=tipo_distribuicao.values,
names=tipo_distribuicao.index,
title="Distribuição dos Tipos de Manutenção por Duração",
)
fig
Análise da duração das manutenções:
- Duração média: 13.6 dias
- Duração mediana: 4.0 dias
- Duração mínima: 1 dias
- Duração máxima: 60 dias
- Desvio padrão: 13.0 dias
Classificação por duração:
- Curta (≤7d): 60 manutenções (53.1%)
- Média (8-30d): 51 manutenções (45.1%)
- Longa (31-90d): 2 manutenções (1.8%)
- Extensa (>90d): 0 manutenções (0.0%)
Análise da potência afetada
Verificando o impacto das manutenções na capacidade instalada:
print("Análise da potência afetada pelas manutenções:")
potencia_stats = manutencoes["potencia"].describe()
print(f"- Potência média por manutenção: {potencia_stats['mean']:.1f} MW")
print(f"- Potência total afetada: {manutencoes['potencia'].sum():.1f} MW")
print(f"- Maior potência individual: {potencia_stats['max']:.1f} MW")
print(f"- Menor potência individual: {potencia_stats['min']:.1f} MW")
# Análise por faixa de potência
manutencoes_copy["faixa_potencia"] = pd.cut(
manutencoes_copy["potencia"],
bins=[0, 50, 200, 500, float("inf")],
labels=[
"Pequena (<50MW)",
"Média (50-200MW)",
"Grande (200-500MW)",
"Muito Grande (>500MW)",
],
)
faixa_distribuicao = manutencoes_copy["faixa_potencia"].value_counts()
print("\nDistribuição por faixa de potência:")
for faixa, count in faixa_distribuicao.items():
percentual = count / len(manutencoes_copy) * 100
potencia_media = manutencoes_copy[
manutencoes_copy["faixa_potencia"] == faixa
]["potencia"].mean()
print(
f"- {faixa}: {count} manutenções ({percentual:.1f}%, média: {potencia_media:.1f} MW)"
)
Análise da potência afetada pelas manutenções:
- Potência média por manutenção: 55.7 MW
- Potência total afetada: 6291.4 MW
- Maior potência individual: 211.7 MW
- Menor potência individual: 4.5 MW
Distribuição por faixa de potência:
- Pequena (<50MW): 81 manutenções (71.7%, média: 29.5 MW)
- Média (50-200MW): 29 manutenções (25.7%, média: 112.7 MW)
- Grande (200-500MW): 3 manutenções (2.7%, média: 211.7 MW)
- Muito Grande (>500MW): 0 manutenções (0.0%, média: nan MW)
Análise de sobreposições e conflitos
Identificando períodos de alta concentração de manutenções:
print("Análise de sobreposições e conflitos:")
# Criando timeline de manutenções
timeline_data = []
for _, manut in manut_temporal.iterrows():
for dia in pd.date_range(manut["data_inicio"], manut["data_fim"]):
timeline_data.append({
"data": dia,
"usina": manut["nome_usina"],
"potencia": manut["potencia"],
"empresa": manut["nome_empresa"],
})
if timeline_data:
timeline_df = pd.DataFrame(timeline_data)
# Potência total indisponível por dia
potencia_diaria = timeline_df.groupby("data")["potencia"].sum()
print(
f"- Período analisado: {potencia_diaria.index.min().date()} a {potencia_diaria.index.max().date()}"
)
print(f"- Potência média indisponível: {potencia_diaria.mean():.1f} MW/dia")
print(f"- Pico de indisponibilidade: {potencia_diaria.max():.1f} MW")
print(f"- Data do pico: {potencia_diaria.idxmax().date()}")
# Identificando dias críticos (alto nível de manutenção)
limite_critico = potencia_diaria.quantile(0.95) # Top 5% dos dias
dias_criticos = potencia_diaria[potencia_diaria >= limite_critico]
print(
f"\nDias críticos (≥{limite_critico:.1f} MW indisponível): {len(dias_criticos)}"
)
if len(dias_criticos) > 0:
print("Principais dias críticos:")
for data, potencia in dias_criticos.nlargest(5).items():
usinas_dia = timeline_df[timeline_df["data"] == data][
"usina"
].nunique()
print(f"- {data.date()}: {potencia:.1f} MW ({usinas_dia} usinas)")
# Visualização da evolução temporal
if len(potencia_diaria) > 1:
fig = px.line(
x=potencia_diaria.index,
y=potencia_diaria.values,
title="Evolução da Potência Indisponível por Manutenção",
labels={"x": "Data", "y": "Potência Indisponível (MW)"},
)
fig
Análise de sobreposições e conflitos:
- Período analisado: 2025-08-01 a 2026-01-01
- Potência média indisponível: 728.0 MW/dia
- Pico de indisponibilidade: 2108.2 MW
- Data do pico: 2025-08-07
Dias críticos (≥1613.5 MW indisponível): 8
Principais dias críticos:
- 2025-08-07: 2108.2 MW (8 usinas)
- 2025-08-11: 1968.5 MW (9 usinas)
- 2025-08-09: 1959.2 MW (8 usinas)
- 2025-08-10: 1959.2 MW (8 usinas)
- 2025-08-06: 1918.6 MW (8 usinas)
Total running time of the script: (0 minutes 0.155 seconds)