feat: adiciona Modelo 2 para minimização do número absoluto de aeronaves
This commit is contained in:
101
create_nb.py
101
create_nb.py
@@ -272,6 +272,107 @@ for r in rotas_lista:
|
||||
print(f"\\nTotal de voos alocados na malha diária: {voos_totais} de {total_aeronaves} aeronaves disponíveis.")
|
||||
"""))
|
||||
|
||||
# Cell 11: Markdown Modelo 2
|
||||
cells.append(nbf.v4.new_markdown_cell("""## 5. Modelo 2: Minimização de Aeronaves
|
||||
Neste modelo, o objetivo é encontrar o **número mínimo absoluto de aeronaves** necessário para operar a malha diária estabelecida (os top trechos).
|
||||
Considerações solicitadas:
|
||||
- O tempo de voo é estimado usando a velocidade média real do C-97 em 2025.
|
||||
- As aeronaves podem voar até 24 horas por dia (não consideraremos o tempo de solo no chão nesta versão).
|
||||
- Se houver desbalanceamento de malha (mais voos saindo de A do que chegando em A), o modelo criará voos de reposicionamento vazios.
|
||||
"""))
|
||||
|
||||
# Cell 12: Code Modelo 2
|
||||
cells.append(nbf.v4.new_code_cell("""# 1. Calcular a velocidade média do C-97 em 2025
|
||||
# Converte a string 'HH:MM:SS' para timedelta e extrai horas
|
||||
df_c97_spd = df_c97.copy()
|
||||
df_c97_spd['Tempo_Horas'] = pd.to_timedelta(df_c97_spd['Tempo de Voo']).dt.total_seconds() / 3600
|
||||
|
||||
def get_dist_row(row):
|
||||
try:
|
||||
lat1, lon1, alt1 = df_aeroportos.loc[row['Localidade Decolagem'], ['lat', 'lon', 'elevation']]
|
||||
lat2, lon2, alt2 = df_aeroportos.loc[row['Localidade Pouso'], ['lat', 'lon', 'elevation']]
|
||||
return pm.geodetic2aer(lat1, lon1, alt1, lat2, lon2, alt2)[2] / 1000.0
|
||||
except:
|
||||
return np.nan
|
||||
|
||||
df_c97_spd['Dist_km'] = df_c97_spd.apply(get_dist_row, axis=1)
|
||||
|
||||
# Filtra voos válidos para calcular a velocidade média
|
||||
df_valid = df_c97_spd[(df_c97_spd['Tempo_Horas'] > 0) & (df_c97_spd['Dist_km'] > 0)].copy()
|
||||
df_valid['Velocidade'] = df_valid['Dist_km'] / df_valid['Tempo_Horas']
|
||||
vel_media_c97 = df_valid['Velocidade'].mean()
|
||||
print(f"Velocidade média calculada para o C-97: {vel_media_c97:.2f} km/h")
|
||||
|
||||
# 2. Configurar os nós (aeroportos) e demandas da malha das rotas top
|
||||
aeroportos_malha = set()
|
||||
for r in rotas_lista:
|
||||
o, d = r.split('-')
|
||||
aeroportos_malha.add(o)
|
||||
aeroportos_malha.add(d)
|
||||
aeroportos_malha = list(aeroportos_malha)
|
||||
|
||||
# Matriz de tempo de voo entre todos os nós da malha (para reposicionamento)
|
||||
tempos_voo = {}
|
||||
for o in aeroportos_malha:
|
||||
for d in aeroportos_malha:
|
||||
if o == d:
|
||||
tempos_voo[(o, d)] = 0
|
||||
else:
|
||||
dist = calc_distancia(o, d)
|
||||
tempos_voo[(o, d)] = dist / vel_media_c97
|
||||
|
||||
# Demanda fixa D_{i,j}
|
||||
D = {(o, d): 0 for o in aeroportos_malha for d in aeroportos_malha}
|
||||
for r in rotas_lista:
|
||||
o, d = r.split('-')
|
||||
D[(o, d)] = voos_requeridos[r]
|
||||
|
||||
# 3. Modelagem Matemática (Pulp)
|
||||
prob2 = pulp.LpProblem("Minimizar_Aeronaves", pulp.LpMinimize)
|
||||
|
||||
# Variáveis de reposicionamento vazio Y_{i,j}
|
||||
Y = pulp.LpVariable.dicts("Y", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')
|
||||
|
||||
# Variável de número de aeronaves N
|
||||
N = pulp.LpVariable("N", lowBound=0, cat='Integer')
|
||||
|
||||
# Função Objetivo: Minimizar N
|
||||
# Para desempatar soluções com mesmo N, damos um pequeno peso pro tempo de reposicionamento vazio (para pegar o roteamento mais curto também)
|
||||
prob2 += N + 0.0001 * pulp.lpSum([Y[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
|
||||
|
||||
# Restrição de conservação de fluxo para cada aeroporto (Chegadas == Partidas)
|
||||
for no in aeroportos_malha:
|
||||
chegadas = pulp.lpSum([D[(i, no)] + Y[(i, no)] for i in aeroportos_malha])
|
||||
partidas = pulp.lpSum([D[(no, j)] + Y[(no, j)] for j in aeroportos_malha])
|
||||
prob2 += chegadas == partidas
|
||||
|
||||
# Restrição de tempo (O tempo total de voo da malha não pode exceder N * 24h)
|
||||
tempo_total_voo = pulp.lpSum([(D[(i, j)] + Y[(i, j)]) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
|
||||
prob2 += tempo_total_voo <= N * 24.0
|
||||
|
||||
# Solução
|
||||
prob2.solve(pulp.PULP_CBC_CMD(msg=False))
|
||||
|
||||
print("\\n== RESULTADO DO MODELO 2 ==")
|
||||
print(f"Status: {pulp.LpStatus[prob2.status]}")
|
||||
print(f"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N.varValue)}")
|
||||
|
||||
tempo_gasto = sum([(D[(i, j)] + Y[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
|
||||
print(f"Tempo total de voo diário (incluindo reposicionamento vazio): {tempo_gasto:.2f} horas")
|
||||
if N.varValue > 0:
|
||||
print(f"Taxa de ocupação da frota (Tempo Voo / Tempo Disponível): {(tempo_gasto / (N.varValue * 24)) * 100:.1f}%\\n")
|
||||
|
||||
print("Voos de Reposicionamento Vazio Gerados (Y) para fechar o ciclo diário:")
|
||||
gerou_vazio = False
|
||||
for i in aeroportos_malha:
|
||||
for j in aeroportos_malha:
|
||||
if Y[(i, j)].varValue > 0:
|
||||
print(f" -> De {i} para {j}: {int(Y[(i, j)].varValue)} voo(s) vazio(s) | Tempo de voo: {tempos_voo[(i,j)]*Y[(i,j)].varValue:.1f}h")
|
||||
gerou_vazio = True
|
||||
if not gerou_vazio:
|
||||
print(" Nenhum voo de reposicionamento vazio foi necessário! A malha já é circular/balanceada.")
|
||||
"""))
|
||||
|
||||
nb.cells = cells
|
||||
|
||||
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
||||
|
||||
Reference in New Issue
Block a user