feat: adiciona Modelo 3 minimizando aeronaves com a consideração do tempo de solo (TAT) de 40 minutos

This commit is contained in:
2026-06-07 20:44:13 -03:00
parent e478cc72e0
commit da42ebdad1
2 changed files with 334 additions and 49 deletions

View File

@@ -449,6 +449,125 @@ print("\\n== QUADRO DE HORÁRIOS DIÁRIOS ==")
print(tabulate(tabela_horarios, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração"], tablefmt="github"))
"""))
# Cell 13: Markdown Modelo 3
cells.append(nbf.v4.new_markdown_cell("""## 6. Modelo 3: Minimização de Aeronaves com TAT (Turnaround Time)
Nesta variação do Modelo 2, adicionamos uma restrição crítica da vida real: o Tempo de Solo ou **Turnaround Time (TAT)**.
Considerações:
- TAT estipulado: 40 minutos (0.66 horas) por cada voo (tempo necessário para desembarque, reabastecimento, verificações e embarque).
- O tempo total de dedicação de uma aeronave por ciclo passa a ser a soma do seu Tempo de Voo + 40 minutos de TAT no aeroporto de destino.
"""))
# Cell 14: Code Modelo 3
cells.append(nbf.v4.new_code_cell("""# 3. Modelagem Matemática Modelo 3 (Pulp) com TAT
prob3 = pulp.LpProblem("Minimizar_Aeronaves_TAT", pulp.LpMinimize)
# Variáveis
Y3 = pulp.LpVariable.dicts("Y3", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')
N3 = pulp.LpVariable("N3", lowBound=0, cat='Integer')
TAT_horas = 40.0 / 60.0
# Função Objetivo
prob3 += N3 + 0.0001 * pulp.lpSum([Y3[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
# Restrição de fluxo
for no in aeroportos_malha:
chegadas = pulp.lpSum([D[(i, no)] + Y3[(i, no)] for i in aeroportos_malha])
partidas = pulp.lpSum([D[(no, j)] + Y3[(no, j)] for j in aeroportos_malha])
prob3 += chegadas == partidas
# Restrição de tempo com TAT
# Cada voo (D ou Y) consome tempo de voo + TAT
tempo_total_operacional = pulp.lpSum([(D[(i, j)] + Y3[(i, j)]) * (tempos_voo[(i, j)] + TAT_horas) for i in aeroportos_malha for j in aeroportos_malha])
prob3 += tempo_total_operacional <= N3 * 24.0
# Solução
prob3.solve(pulp.PULP_CBC_CMD(msg=False))
print("\\n== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==")
print(f"Status: {pulp.LpStatus[prob3.status]}")
print(f"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N3.varValue)}")
tempo_voo_puro = sum([(D[(i, j)] + Y3[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])
total_voos_realizados = sum([(D[(i, j)] + Y3[(i, j)].varValue) for i in aeroportos_malha for j in aeroportos_malha])
tempo_solo_total = total_voos_realizados * TAT_horas
print(f"Tempo total de voo puro: {tempo_voo_puro:.2f} horas")
print(f"Tempo total gasto em solo (TAT): {tempo_solo_total:.2f} horas")
print(f"Tempo total operacional comprometido: {tempo_voo_puro + tempo_solo_total:.2f} horas")
if N3.varValue > 0:
print(f"Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): {((tempo_voo_puro + tempo_solo_total) / (N3.varValue * 24)) * 100:.1f}%\\n")
# GERAR TABELA DE HORÁRIOS COM TABULATE E DATETIME
voos_para_fazer3 = []
D_copy3 = {k: v for k, v in D.items()}
for i in aeroportos_malha:
for j in aeroportos_malha:
total_voos3 = D[(i, j)] + int(Y3[(i, j)].varValue)
for _ in range(total_voos3):
if D_copy3[(i, j)] > 0:
tipo = 'Passageiro'
D_copy3[(i, j)] -= 1
else:
tipo = 'Vazio (Reposicionamento)'
voos_para_fazer3.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]})
tabela_horarios3 = []
aeronave_id3 = 1
while voos_para_fazer3:
voo_atual = voos_para_fazer3.pop(0)
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
tempo_delta = datetime.timedelta(hours=voo_atual['tempo'])
hora_chegada = hora_atual + tempo_delta
tabela_horarios3.append([
f"Aeronave {aeronave_id3}",
voo_atual['origem'],
voo_atual['destino'],
voo_atual['tipo'],
hora_atual.strftime('%H:%M'),
hora_chegada.strftime('%H:%M'),
f"{voo_atual['tempo']:.2f} h"
])
# Próxima partida só depois do TAT de 40 min
hora_atual = hora_chegada + datetime.timedelta(minutes=40)
local_atual = voo_atual['destino']
while True:
prox_voo = None
for idx, v in enumerate(voos_para_fazer3):
if v['origem'] == local_atual:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(hours=v['tempo'])).total_seconds() <= 24 * 3600:
prox_voo = voos_para_fazer3.pop(idx)
break
if prox_voo:
tempo_delta = datetime.timedelta(hours=prox_voo['tempo'])
hora_chegada = hora_atual + tempo_delta
tabela_horarios3.append([
f"Aeronave {aeronave_id3}",
prox_voo['origem'],
prox_voo['destino'],
prox_voo['tipo'],
hora_atual.strftime('%H:%M'),
hora_chegada.strftime('%H:%M'),
f"{prox_voo['tempo']:.2f} h"
])
hora_atual = hora_chegada + datetime.timedelta(minutes=40)
local_atual = prox_voo['destino']
else:
break
aeronave_id3 += 1
print("== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==")
print(tabulate(tabela_horarios3, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração Voo"], tablefmt="github"))
"""))
nb.cells = cells
with open('modelos.ipynb', 'w', encoding='utf-8') as f: