Análise das configurações das usinas hidrelétricas

O arquivo confhd.dat contém as configurações das usinas hidrelétricas do sistema brasileiro, incluindo características físicas, períodos históricos e modificações específicas do estudo.

import plotly.express as px
import plotly.io as pio

from inewave.newave import Confhd

pio.templates.default = "ggplot2"

Leitura das configurações hidrelétricas

O arquivo confhd.dat é essencial para definir o sistema hidrelétrico que será simulado pelo NEWAVE:

arq_confhd = Confhd.read("./newave/confhd.dat")
print("Arquivo confhd.dat lido com sucesso")
Arquivo confhd.dat lido com sucesso

Análise geral das usinas

Examinando as características básicas do sistema hidrelétrico:

usinas = arq_confhd.usinas
print(f"Total de usinas hidrelétricas: {len(usinas)}")
print(f"Colunas disponíveis: {list(usinas.columns)}")

print("\nPrimeiras usinas:")
print(usinas.head())

# Análise por REE
usinas_por_ree = (
    usinas.groupby("ree")
    .agg({"codigo_usina": "count", "volume_inicial_percentual": "mean"})
    .round(2)
)
usinas_por_ree.columns = ["quantidade", "volume_inicial_medio"]

print("\nDistribuição de usinas por REE:")
print(usinas_por_ree)

# Visualização da distribuição por REE
fig = px.bar(
    usinas_por_ree.reset_index(),
    x="ree",
    y="quantidade",
    title="Distribuição de Usinas Hidrelétricas por REE",
    labels={"ree": "REE", "quantidade": "Número de Usinas"},
)
fig
Total de usinas hidrelétricas: 162
Colunas disponíveis: ['codigo_usina', 'nome_usina', 'posto', 'codigo_usina_jusante', 'ree', 'volume_inicial_percentual', 'usina_existente', 'usina_modificada', 'ano_inicio_historico', 'ano_fim_historico']

Primeiras usinas:
   codigo_usina    nome_usina  ...  ano_inicio_historico  ano_fim_historico
0             4  FUNIL-GRANDE  ...                  1931               1995
1            20       BATALHA  ...                  1931               1995
2            21   SERRA FACAO  ...                  1931               1995
3            27  CAPIM BRANC1  ...                  1931               1995
4            28  CAPIM BRANC2  ...                  1931               1995

[5 rows x 10 columns]

Distribuição de usinas por REE:
     quantidade  volume_inicial_medio
ree
1            39                 17.17
2            21                 20.69
3            11                 29.35
4             5                  6.87
5             1                  0.00
6             6                  8.17
7             4                 12.49
8             3                  7.42
9             5                  4.92
10           44                 19.48
11           11                 25.31
12           12                 11.55


Análise das usinas existentes vs. futuras

Classificando usinas por status de existência:

# Distribuição por status de existência
status_dist = usinas["usina_existente"].value_counts()
print("Distribuição por status:")
for status, count in status_dist.items():
    percentual = count / len(usinas) * 100
    print(f"- {status}: {count} usinas ({percentual:.1f}%)")

# Visualização do status
fig = px.pie(
    values=status_dist.values,
    names=status_dist.index,
    title="Distribuição de Usinas por Status de Existência",
)
fig
Distribuição por status:
- EX: 160 usinas (98.8%)
- NE: 2 usinas (1.2%)


Análise dos volumes iniciais

Verificando as condições iniciais dos reservatórios:

print("Análise dos volumes iniciais:")

vol_inicial = usinas["volume_inicial_percentual"]
print(f"- Volume inicial médio: {vol_inicial.mean():.1f}%")
print(f"- Volume inicial mediano: {vol_inicial.median():.1f}%")
print(f"- Desvio padrão: {vol_inicial.std():.1f}%")
print(f"- Mínimo: {vol_inicial.min():.1f}%")
print(f"- Máximo: {vol_inicial.max():.1f}%")

# Distribuição dos volumes iniciais
fig = px.histogram(
    usinas,
    x="volume_inicial_percentual",
    nbins=20,
    title="Distribuição dos Volumes Iniciais dos Reservatórios",
    labels={
        "volume_inicial_percentual": "Volume Inicial (%)",
        "count": "Número de Usinas",
    },
)
fig
Análise dos volumes iniciais:
- Volume inicial médio: 17.8%
- Volume inicial mediano: 0.0%
- Desvio padrão: 25.0%
- Mínimo: 0.0%
- Máximo: 100.0%


Análise da topologia do sistema

Examinando as conexões entre usinas (cascata):

print("Análise da topologia do sistema:")

# Identificando usinas de cabeceira (sem montante)
usinas_cabeceira = usinas[
    ~usinas["codigo_usina"].isin(usinas["codigo_usina_jusante"])
]
print(f"- Usinas de cabeceira: {len(usinas_cabeceira)}")

# Identificando usinas finais (sem jusante ou jusante = 0)
usinas_finais = usinas[
    (usinas["codigo_usina_jusante"] == 0)
    | (usinas["codigo_usina_jusante"].isna())
]
print(f"- Usinas finais: {len(usinas_finais)}")

# Analisando as conexões válidas
conexoes_validas = usinas[usinas["codigo_usina_jusante"] > 0]
print(f"- Conexões de cascata: {len(conexoes_validas)}")

# Distribuição das conexões por REE
if len(conexoes_validas) > 0:
    conexoes_por_ree = conexoes_validas.groupby("ree").size()
    print("\nConexões de cascata por REE:")
    for ree, count in conexoes_por_ree.items():
        print(f"- REE {ree}: {count} conexões")

# Verificando consistência da topologia
codigos_existentes = set(usinas["codigo_usina"])
jusantes_referenciados = set(
    usinas[usinas["codigo_usina_jusante"] > 0]["codigo_usina_jusante"]
)

jusantes_invalidos = jusantes_referenciados - codigos_existentes
if jusantes_invalidos:
    print(
        f"⚠️ Códigos de jusante inválidos encontrados: {len(jusantes_invalidos)}"
    )
else:
    print("✓ Topologia consistente - todos os códigos de jusante são válidos")
Análise da topologia do sistema:
- Usinas de cabeceira: 60
- Usinas finais: 40
- Conexões de cascata: 122

Conexões de cascata por REE:
- REE 1: 25 conexões
- REE 2: 14 conexões
- REE 3: 7 conexões
- REE 4: 4 conexões
- REE 6: 2 conexões
- REE 7: 3 conexões
- REE 9: 2 conexões
- REE 10: 44 conexões
- REE 11: 9 conexões
- REE 12: 12 conexões
⚠️ Códigos de jusante inválidos encontrados: 1

Análise por posto fluviométrico

Verificando a associação com postos de medição:

# Estatísticas dos postos
postos_stats = usinas["posto"].describe()
print(f"- Número de postos únicos: {usinas['posto'].nunique()}")
print(f"- Posto mínimo: {int(postos_stats['min'])}")
print(f"- Posto máximo: {int(postos_stats['max'])}")

# Verificando usinas por posto
usinas_por_posto = usinas["posto"].value_counts()
print(f"- Postos com mais usinas: {usinas_por_posto.head().to_dict()}")

# Postos com múltiplas usinas (possível cascata)
postos_multiplos = usinas_por_posto[usinas_por_posto > 1]
print(f"- Postos com múltiplas usinas: {len(postos_multiplos)}")

if len(postos_multiplos) > 0:
    print(f"- Total de usinas em postos múltiplos: {postos_multiplos.sum()}")

    # Visualização da distribuição
    fig = px.histogram(
        x=usinas_por_posto.values,
        title="Distribuição de Usinas por Posto",
        labels={"x": "Número de Usinas por Posto", "y": "Número de Postos"},
    )
    fig
- Número de postos únicos: 153
- Posto mínimo: 1
- Posto máximo: 319
- Postos com mais usinas: {155: 2, 270: 2, 158: 2, 156: 2, 255: 2}
- Postos com múltiplas usinas: 9
- Total de usinas em postos múltiplos: 18

Identificação de padrões regionais

Analisando características por região (REE):

# Análise detalhada por REE
analise_ree = (
    usinas.groupby("ree")
    .agg({
        "codigo_usina": "count",
        "volume_inicial_percentual": ["mean", "std", "min", "max"],
        "usina_modificada": "sum",
    })
    .round(2)
)

# Simplificando nomes das colunas
analise_ree.columns = [
    "total_usinas",
    "vol_medio",
    "vol_std",
    "vol_min",
    "vol_max",
    "modificadas",
]

print("Características por REE:")
print(analise_ree)

# Criando análise de correlação entre REEs
if len(analise_ree) > 1:
    # Volume inicial médio vs número de usinas modificadas
    fig = px.scatter(
        analise_ree.reset_index(),
        x="vol_medio",
        y="modificadas",
        size="total_usinas",
        hover_data=["ree"],
        title="Relação: Volume Inicial Médio vs Usinas Modificadas por REE",
        labels={
            "vol_medio": "Volume Inicial Médio (%)",
            "modificadas": "Número de Usinas Modificadas",
            "total_usinas": "Total de Usinas",
        },
    )
    fig
Características por REE:
     total_usinas  vol_medio  vol_std  vol_min  vol_max  modificadas
ree
1              39      17.17    25.42      0.0    83.78           29
2              21      20.69    30.30      0.0    99.60            9
3              11      29.35    25.47      0.0    78.08           11
4               5       6.87     9.41      0.0    17.26            5
5               1       0.00      NaN      0.0     0.00            0
6               6       8.17    13.14      0.0    30.10            2
7               4      12.49    24.98      0.0    49.95            3
8               3       7.42    12.86      0.0    22.27            2
9               5       4.92    10.99      0.0    24.58            3
10             44      19.48    25.43      0.0    96.31           30
11             11      25.31    32.13      0.0   100.00            6
12             12      11.55    18.30      0.0    51.90            9

Criação de cenários alternativos

Demonstrando como modificar as configurações:

# Cenário 1: Aumentar volumes iniciais em REEs específicos
usinas_modificadas = usinas.copy()

# Aumentar volume inicial em 10% para REE 1 (limitado a 100%)
mask_ree1 = usinas_modificadas["ree"] == 1
if mask_ree1.any():
    volumes_novos = (
        usinas_modificadas.loc[mask_ree1, "volume_inicial_percentual"] * 1.1
    )
    volumes_novos = volumes_novos.clip(upper=100)  # Limitar a 100%
    usinas_modificadas.loc[mask_ree1, "volume_inicial_percentual"] = (
        volumes_novos
    )

    volume_original = usinas[mask_ree1]["volume_inicial_percentual"].mean()
    volume_novo = usinas_modificadas[mask_ree1][
        "volume_inicial_percentual"
    ].mean()

    print("Cenário - Volume inicial REE 1:")
    print(f"- Original: {volume_original:.1f}%")
    print(f"- Modificado: {volume_novo:.1f}%")
    print(f"- Incremento: {volume_novo - volume_original:.1f}%")

# Cenário 2: Marcar usinas específicas como modificadas
# Exemplo: marcar usinas com volume inicial baixo
mask_volume_baixo = usinas_modificadas["volume_inicial_percentual"] < 40
usinas_modificadas.loc[mask_volume_baixo, "usina_modificada"] = 1

modificadas_originais = usinas["usina_modificada"].sum()
modificadas_novas = usinas_modificadas["usina_modificada"].sum()

print("\nCenário - Usinas modificadas:")
print(f"- Original: {modificadas_originais}")
print(f"- Novo: {modificadas_novas}")
print(f"- Adicionadas: {modificadas_novas - modificadas_originais}")

# Salvando configuração modificada
arq_confhd.usinas = usinas_modificadas
# arq_confhd.write("./saida/confhd_modificado.dat")
print("\n✓ Configurações modificadas disponíveis para escrita")

print("\nDados processados disponíveis:")
print("1. usinas - DataFrame original")
print("2. usinas_modificadas - DataFrame com modificações")
print("3. analise_ree - Análise por REE")
print("4. arq_confhd - Objeto modificado para escrita")
Cenário - Volume inicial REE 1:
- Original: 17.2%
- Modificado: 18.9%
- Incremento: 1.7%

Cenário - Usinas modificadas:
- Original: 109
- Novo: 158
- Adicionadas: 49

✓ Configurações modificadas disponíveis para escrita

Dados processados disponíveis:
1. usinas - DataFrame original
2. usinas_modificadas - DataFrame com modificações
3. analise_ree - Análise por REE
4. arq_confhd - Objeto modificado para escrita

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

Gallery generated by Sphinx-Gallery