Análise e modificação do cadastro de usinas hidrelétricas

O hidr.dat é o arquivo binário de entrada do modelo NEWAVE que contém o cadastro completo das usinas hidrelétricas do sistema. Este arquivo é fundamental para a modelagem do sistema hidrotérmico, pois define as características técnicas e operacionais de cada usina hidroelétrica.

import pandas as pd

from inewave.newave import Hidr

# Leitura do arquivo hidr.dat
arq_hidr = Hidr.read("./newave/hidr.dat")

O cadastro completo das usinas pode ser acessado através da propriedade cadastro, que retorna um DataFrame pandas com todas as informações das usinas:

cadastro = arq_hidr.cadastro
print(f"Total de usinas hidrelétricas: {len(cadastro)}")
print("\nPrimeiras 5 usinas:")
print(cadastro.head())
Total de usinas hidrelétricas: 320

Primeiras 5 usinas:
                nome_usina  posto  ...  volume_referencia  tipo_regulacao
codigo_usina                       ...
1                 CAMARGOS      1  ...         792.000000               M
2                 ITUTINGA      2  ...          10.639303               D
3                               0  ...           0.000000
4             FUNIL-GRANDE    211  ...         265.859985               D
5                               0  ...           0.000000

[5 rows x 109 columns]

É possível analisar informações específicas das usinas. Por exemplo, verificar as características dos reservatórios:

print("Estatísticas dos volumes dos reservatórios (hm³):")
print(
    cadastro[["volume_minimo", "volume_maximo", "volume_vertedouro"]].describe()
)
Estatísticas dos volumes dos reservatórios (hm³):
       volume_minimo  volume_maximo  volume_vertedouro
count     320.000000     320.000000         320.000000
mean     1085.880696    2272.030265        1255.603759
std      2867.432795    6813.818268        3355.208199
min         0.000000       0.000000           0.000000
25%         0.000000       0.000000           0.000000
50%        41.500000      73.599998          53.740000
75%       891.804993    1226.000000         957.750000
max     27695.189453   54400.000000       28100.000000

Para análises operacionais, é comum examinar as capacidades instaladas e características das máquinas:

# Calculando a potência total instalada por usina
potencia_cols = [f"potencia_nominal_conjunto_{i}" for i in range(1, 6)]
maquinas_cols = [f"maquinas_conjunto_{i}" for i in range(1, 6)]

cadastro_com_potencia = cadastro.copy()
cadastro_com_potencia["potencia_total"] = 0

for i in range(1, 6):
    pot_col = f"potencia_nominal_conjunto_{i}"
    maq_col = f"maquinas_conjunto_{i}"
    cadastro_com_potencia["potencia_total"] += (
        cadastro_com_potencia[pot_col] * cadastro_com_potencia[maq_col]
    )

print("Usinas com maior capacidade instalada:")
cadastro_com_potencia.nlargest(10, "potencia_total")[
    ["nome_usina", "potencia_total"]
]
Usinas com maior capacidade instalada:
nome_usina potencia_total
codigo_usina
66 ITAIPU 14000.000000
288 BELO MONTE 10999.998413
275 TUCURUI 8535.000000
176 COMP PAF-MOX 4279.599915
44 I. SOLT. EQV 4251.500000
285 JIRAU 3750.000000
287 STO ANTONIO 3568.299927
34 I. SOLTEIRA 3444.000000
178 XINGO 3162.000000
175 P.AFONSO 4 2462.399963


Análise por submercado é uma prática comum no planejamento energético:

print("Distribuição de usinas por submercado:")
distribuicao_submercado = (
    cadastro.groupby("submercado")
    .agg({"nome_usina": "count", "volume_maximo": "sum"})
    .rename(
        columns={
            "nome_usina": "quantidade_usinas",
            "volume_maximo": "volume_total",
        }
    )
)
print(distribuicao_submercado)
Distribuição de usinas por submercado:
            quantidade_usinas   volume_total
submercado
0                         108       0.000000
1                         137  433229.129981
2                          42   53180.194229
3                          15   87780.509918
4                          18  152859.850777

Uma análise importante é verificar as usinas em cascata através dos códigos de jusante:

print("Exemplo de usinas com jusante definido:")
usinas_com_jusante = cadastro[cadastro["codigo_usina_jusante"] > 0]
print(f"Usinas em cascata: {len(usinas_com_jusante)} de {len(cadastro)} total")
usinas_com_jusante[["nome_usina", "codigo_usina_jusante"]].head(10)
Exemplo de usinas com jusante definido:
Usinas em cascata: 170 de 320 total
nome_usina codigo_usina_jusante
codigo_usina
1 CAMARGOS 2
2 ITUTINGA 4
4 FUNIL-GRANDE 6
6 FURNAS 7
7 M. DE MORAES 8
8 ESTREITO 9
9 JAGUARA 10
10 IGARAPAVA 11
11 VOLTA GRANDE 12
12 P. COLOMBIA 17


Modificação de dados cadastrais

Uma das principais vantagens do inewave é permitir modificações programáticas dos arquivos de entrada. Por exemplo, pode-se alterar volumes mínimos para estudos de sensibilidade:

# Criando uma cópia para modificação
cadastro_modificado = cadastro.copy()

# Aumentando em 10% o volume mínimo de todas as usinas
cadastro_modificado["volume_minimo"] = (
    cadastro_modificado["volume_minimo"] * 1.1
)

print("Comparação dos volumes mínimos (original vs modificado):")
comparacao = pd.DataFrame({
    "original": cadastro["volume_minimo"],
    "modificado": cadastro_modificado["volume_minimo"],
    "diferenca_pct": (
        (cadastro_modificado["volume_minimo"] - cadastro["volume_minimo"])
        / cadastro["volume_minimo"]
        * 100
    ),
})
print(comparacao.head())
Comparação dos volumes mínimos (original vs modificado):
              original  modificado  diferenca_pct
codigo_usina
1                120.0       132.0           10.0
2                 11.0        12.1           10.0
3                  0.0         0.0            NaN
4                304.0       334.4           10.0
5                  0.0         0.0            NaN

Para aplicar as modificações ao arquivo, atualiza-se o cadastro:

Criação de novos casos de estudo

É possível filtrar usinas para criar casos específicos. Por exemplo, analisando apenas usinas de um determinado submercado:

submercado_interesse = 1
usinas_submercado = cadastro[cadastro["submercado"] == submercado_interesse]

print(f"Usinas do submercado {submercado_interesse}:")
print(f"Quantidade: {len(usinas_submercado)}")
print(f"Volume total: {usinas_submercado['volume_maximo'].sum():.0f} hm³")
Usinas do submercado 1:
Quantidade: 137
Volume total: 433229 hm³

Validação de dados

O inewave permite implementar verificações de consistência dos dados:

def validar_cadastro(df):
    """Função para validar consistência do cadastro hidrelétrico"""
    problemas = []

    # Verificar se volume mínimo é menor que máximo
    vol_inconsistente = df[df["volume_minimo"] >= df["volume_maximo"]]
    if len(vol_inconsistente) > 0:
        problemas.append(
            f"Volumes inconsistentes: {len(vol_inconsistente)} usinas"
        )

    # Verificar se cota mínima é menor que máxima
    cota_inconsistente = df[df["cota_minima"] >= df["cota_maxima"]]
    if len(cota_inconsistente) > 0:
        problemas.append(
            f"Cotas inconsistentes: {len(cota_inconsistente)} usinas"
        )

    # Verificar usinas sem nome
    sem_nome = df[df["nome_usina"].isna() | (df["nome_usina"] == "")]
    if len(sem_nome) > 0:
        problemas.append(f"Usinas sem nome: {len(sem_nome)} usinas")

    return problemas


print("Validação do cadastro:")
problemas = validar_cadastro(cadastro)
if problemas:
    for problema in problemas:
        print(f"- {problema}")
else:
    print("✓ Cadastro validado com sucesso!")
Validação do cadastro:
- Volumes inconsistentes: 204 usinas
- Cotas inconsistentes: 202 usinas
- Usinas sem nome: 108 usinas

Exportação do arquivo modificado

Após as modificações, o arquivo pode ser exportado para uso no NEWAVE:

from io import BytesIO

# Exportação para buffer em memória (útil para APIs web ou bancos de dados)
buffer = BytesIO()
arq_hidr.write(buffer)
print(f"Arquivo modificado gerado em memória: {len(buffer.getvalue())} bytes")

# Para salvar em disco, use:
# arq_hidr.write("./hidr_modificado.dat")
Arquivo modificado gerado em memória: 253440 bytes

Análise de evaporação

Os coeficientes de evaporação são importantes para modelagem de reservatórios:

meses = [
    "jan",
    "fev",
    "mar",
    "abr",
    "mai",
    "jun",
    "jul",
    "ago",
    "set",
    "out",
    "nov",
    "dez",
]
colunas_evap = [f"evaporacao_{mes.upper()}" for mes in meses]

print("Estatísticas de evaporação por mês (mm):")
evaporacao_stats = cadastro[colunas_evap].describe()
print(evaporacao_stats.loc[["mean", "std", "min", "max"]])
Estatísticas de evaporação por mês (mm):
      evaporacao_JAN  evaporacao_FEV  ...  evaporacao_NOV  evaporacao_DEZ
mean        4.056250        5.393750  ...        5.153125        7.631250
std        31.160067       21.665626  ...       35.417417       37.037407
min       -80.000000      -67.000000  ...      -66.000000      -92.000000
max       171.000000      109.000000  ...      245.000000      223.000000

[4 rows x 12 columns]

Análise de produtibilidade

A produtibilidade específica é um parâmetro crucial para o desempenho das usinas:

print("Distribuição de produtibilidade específica:")
prod_stats = cadastro["produtibilidade_especifica"].describe()
print(prod_stats)

print("\nUsinas com maior produtibilidade específica:")
high_prod = cadastro.nlargest(5, "produtibilidade_especifica")
print(high_prod[["nome_usina", "produtibilidade_especifica", "submercado"]])
Distribuição de produtibilidade específica:
count    320.000000
mean       0.005041
std        0.004399
min        0.000000
25%        0.000000
50%        0.008649
75%        0.008922
max        0.009218
Name: produtibilidade_especifica, dtype: float64

Usinas com maior produtibilidade específica:
                nome_usina  produtibilidade_especifica  submercado
codigo_usina
86            BARRA GRANDE                    0.009218           2
281            PONTE PEDRA                    0.009168           1
228                COLIDER                    0.009153           1
230             SAO MANOEL                    0.009153           1
52                CANOAS I                    0.009124           1

Total running time of the script: (0 minutes 1.594 seconds)

Gallery generated by Sphinx-Gallery