feat: adiciona analise e Modelos 4, 5 e 6 exclusivos para a frota de aeronaves C-99A

This commit is contained in:
2026-06-07 20:57:37 -03:00
parent c21cc4eece
commit 76bed0f3a8
2 changed files with 990 additions and 70 deletions

View File

@@ -568,6 +568,296 @@ 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"))
"""))
# ==========================================
# SEÇÃO C-99
# ==========================================
cells.append(nbf.v4.new_markdown_cell("""# Alocação de Frotas - Aeronaves C-99A
Agora replicaremos a mesma lógica e os mesmos modelos (1, 2 e 3) para a aeronave C-99A (que será nomeada como Modelos 4, 5 e 6).
"""))
cells.append(nbf.v4.new_code_cell("""# 1. Filtro e Limpeza C-99A
df_c99 = df[df['Modelo'] == 'C-99A'].copy()
df_c99 = df_c99[df_c99['Localidade Decolagem'] != 0]
df_c99 = df_c99[df_c99['Localidade Pouso'] != 0]
df_c99['Trecho'] = df_c99['Localidade Decolagem'] + '-' + df_c99['Localidade Pouso']
df_c99['PAX'] = pd.to_numeric(df_c99['PAX']).fillna(0)
df_c99['Dep TimeStamp'] = pd.to_datetime(df_c99['Data de Decolagem'] + ' ' + df_c99['Hora de Decolagem'], format='mixed', dayfirst=True)
df_c99['Data Apenas'] = df_c99['Dep TimeStamp'].dt.date
CAPACIDADE_C99 = 50
matriculas_unicas_c99 = df_c99['Matrícula'].unique()
total_aeronaves_c99 = len(matriculas_unicas_c99)
print(f"Total de matrículas únicas C-99A (aeronaves disponíveis): {total_aeronaves_c99}")
# Demanda diária
demanda_diaria_c99 = df_c99.groupby(['Trecho', 'Data Apenas'])['PAX'].sum().reset_index()
import itertools
todos_trechos_c99 = demanda_diaria_c99['Trecho'].unique()
idx_c99 = pd.MultiIndex.from_product([todos_trechos_c99, datas_2025], names=['Trecho', 'Data Apenas'])
demanda_diaria_completa_c99 = pd.DataFrame(index=idx_c99).reset_index()
demanda_diaria_completa_c99 = pd.merge(demanda_diaria_completa_c99, demanda_diaria_c99, on=['Trecho', 'Data Apenas'], how='left').fillna({'PAX': 0})
estatisticas_trechos_c99 = demanda_diaria_completa_c99.groupby('Trecho').agg(
Media_PAX_Diario=('PAX', 'mean'),
Desvio_Padrao_PAX=('PAX', 'std'),
Total_PAX=('PAX', 'sum'),
Dias_Operados=('PAX', lambda x: (x > 0).sum())
).reset_index()
estatisticas_trechos_c99['Media_PAX_Dias_Operados'] = estatisticas_trechos_c99.apply(
lambda row: row['Total_PAX'] / row['Dias_Operados'] if row['Dias_Operados'] > 0 else 0, axis=1
)
estatisticas_trechos_c99['Score_Relevancia'] = estatisticas_trechos_c99['Total_PAX'] * estatisticas_trechos_c99['Dias_Operados']
estatisticas_trechos_c99 = estatisticas_trechos_c99.sort_values(by='Score_Relevancia', ascending=False)
NUM_TRECHOS_C99 = 10
top_trechos_c99 = estatisticas_trechos_c99.head(NUM_TRECHOS_C99)
display(top_trechos_c99)
plt.figure(figsize=(10, 6))
sns.barplot(data=top_trechos_c99, x='Trecho', y='Score_Relevancia', hue='Trecho', palette='magma', legend=False)
plt.title(f'Top {NUM_TRECHOS_C99} Trechos C-99A (PAX * Dias Operados)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# Rede C-99
G_c99 = nx.DiGraph()
for _, row in top_trechos_c99.iterrows():
origem, destino = row['Trecho'].split('-')
G_c99.add_edge(origem, destino, weight=row['Score_Relevancia'])
pos_c99 = nx.spring_layout(G_c99, k=2.5, iterations=100, seed=42)
plt.figure(figsize=(12, 8))
nx.draw_networkx_nodes(G_c99, pos_c99, node_size=1200, node_color='lightcoral', edgecolors='black')
nx.draw_networkx_edges(G_c99, pos_c99, edgelist=G_c99.edges(), arrows=True, arrowstyle='-|>', arrowsize=25, edge_color='gray', width=2, node_size=1200, connectionstyle='arc3,rad=0.15')
nx.draw_networkx_labels(G_c99, pos_c99, font_size=12, font_weight='bold')
plt.title('Diagrama de Rede - C-99A', fontsize=14)
plt.axis('off')
plt.show()
"""))
cells.append(nbf.v4.new_markdown_cell("""## MODELO 4: Minimização de Distância (Custo)
Equivalente ao Modelo 1, mas para C-99A.
"""))
cells.append(nbf.v4.new_code_cell("""# Preparação Modelo 4
rotas_lista_c99 = top_trechos_c99['Trecho'].tolist()
distancias_voo_c99 = {}
for r in rotas_lista_c99:
origem, destino = r.split('-')
distancias_voo_c99[r] = calc_distancia(origem, destino)
voos_requeridos_c99 = {}
for _, row in top_trechos_c99.iterrows():
voos = math.ceil(row['Media_PAX_Dias_Operados'] / CAPACIDADE_C99)
voos_requeridos_c99[row['Trecho']] = max(1, voos)
prob4 = pulp.LpProblem("Fleet_Assignment_C99_Brasil", pulp.LpMinimize)
X_c99 = pulp.LpVariable.dicts("X_c99", rotas_lista_c99, lowBound=0, cat='Integer')
prob4 += pulp.lpSum([distancias_voo_c99[r] * X_c99[r] for r in rotas_lista_c99])
for r in rotas_lista_c99:
prob4 += X_c99[r] >= voos_requeridos_c99[r]
prob4 += pulp.lpSum([X_c99[r] for r in rotas_lista_c99]) <= total_aeronaves_c99
prob4.solve(pulp.PULP_CBC_CMD(msg=False))
print("== MODELO 4 (C-99A) ==")
print(f"Status: {pulp.LpStatus[prob4.status]}")
print(f"Distância Total: {pulp.value(prob4.objective):.2f} km\\n")
for r in rotas_lista_c99:
print(f"Trecho: {r:10} | Alocados: {int(X_c99[r].varValue)} voo(s)")
"""))
cells.append(nbf.v4.new_markdown_cell("""## MODELO 5: Minimização de Frota C-99A (S/ TAT)
Equivalente ao Modelo 2.
"""))
cells.append(nbf.v4.new_code_cell("""# Velocidade Média C-99A
df_c99_spd = df_c99.copy()
df_c99_spd['Tempo_Horas'] = pd.to_timedelta(df_c99_spd['Tempo de Voo']).dt.total_seconds() / 3600
df_c99_spd['Dist_km'] = df_c99_spd.apply(get_dist_row, axis=1)
df_valid_c99 = df_c99_spd[(df_c99_spd['Tempo_Horas'] > 0) & (df_c99_spd['Dist_km'] > 0)].copy()
df_valid_c99['Velocidade'] = df_valid_c99['Dist_km'] / df_valid_c99['Tempo_Horas']
vel_media_c99 = df_valid_c99['Velocidade'].mean()
print(f"Velocidade média C-99A: {vel_media_c99:.2f} km/h")
aeroportos_malha_c99 = set()
for r in rotas_lista_c99:
o, d = r.split('-')
aeroportos_malha_c99.add(o)
aeroportos_malha_c99.add(d)
aeroportos_malha_c99 = list(aeroportos_malha_c99)
tempos_voo_min_c99 = {}
for o in aeroportos_malha_c99:
for d in aeroportos_malha_c99:
if o == d:
tempos_voo_min_c99[(o, d)] = 0
else:
dist = calc_distancia(o, d)
tempos_voo_min_c99[(o, d)] = int(round((dist / vel_media_c99) * 60))
D_c99 = {(o, d): 0 for o in aeroportos_malha_c99 for d in aeroportos_malha_c99}
for r in rotas_lista_c99:
o, d = r.split('-')
D_c99[(o, d)] = voos_requeridos_c99[r]
prob5 = pulp.LpProblem("Minimizar_Aeronaves_C99", pulp.LpMinimize)
Y5 = pulp.LpVariable.dicts("Y5", [(o, d) for o in aeroportos_malha_c99 for d in aeroportos_malha_c99], lowBound=0, cat='Integer')
N5 = pulp.LpVariable("N5", lowBound=0, cat='Integer')
prob5 += N5 + 0.0001 * pulp.lpSum([Y5[(i, j)] * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
for no in aeroportos_malha_c99:
chegadas = pulp.lpSum([D_c99[(i, no)] + Y5[(i, no)] for i in aeroportos_malha_c99])
partidas = pulp.lpSum([D_c99[(no, j)] + Y5[(no, j)] for j in aeroportos_malha_c99])
prob5 += chegadas == partidas
tempo_total_voo_c99 = pulp.lpSum([(D_c99[(i, j)] + Y5[(i, j)]) * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
prob5 += tempo_total_voo_c99 <= N5 * 1440
prob5.solve(pulp.PULP_CBC_CMD(msg=False))
print("\\n== RESULTADO DO MODELO 5 (C-99A) ==")
print(f"Status: {pulp.LpStatus[prob5.status]}")
print(f"Aeronaves mínimas: {int(N5.varValue)}")
tempo_gasto_c99 = sum([(D_c99[(i, j)] + int(Y5[(i, j)].varValue)) * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
print(f"Tempo total de voo: {int(tempo_gasto_c99)} min ({tempo_gasto_c99/60:.2f} h)\\n")
voos_para_fazer_c99 = []
D_copy_c99 = {k: v for k, v in D_c99.items()}
for i in aeroportos_malha_c99:
for j in aeroportos_malha_c99:
total_voos = D_c99[(i, j)] + int(Y5[(i, j)].varValue)
for _ in range(total_voos):
if D_copy_c99[(i, j)] > 0:
tipo = 'Passageiro'
D_copy_c99[(i, j)] -= 1
else:
tipo = 'Vazio'
voos_para_fazer_c99.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo_min_c99[(i, j)]})
tabela_horarios_c99 = []
aeronave_id = 1
while voos_para_fazer_c99:
voo_atual = voos_para_fazer_c99.pop(0)
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
tempo_delta = datetime.timedelta(minutes=int(voo_atual['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios_c99.append([f"Aeronave {aeronave_id}", voo_atual['origem'], voo_atual['destino'], voo_atual['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{voo_atual['tempo']} min"])
hora_atual = hora_chegada
local_atual = voo_atual['destino']
while True:
prox_voo = None
for idx, v in enumerate(voos_para_fazer_c99):
if v['origem'] == local_atual:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(minutes=int(v['tempo']))).total_seconds() <= 1440 * 60:
prox_voo = voos_para_fazer_c99.pop(idx)
break
if prox_voo:
tempo_delta = datetime.timedelta(minutes=int(prox_voo['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios_c99.append([f"Aeronave {aeronave_id}", prox_voo['origem'], prox_voo['destino'], prox_voo['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{prox_voo['tempo']} min"])
hora_atual = hora_chegada
local_atual = prox_voo['destino']
else:
break
aeronave_id += 1
print(tabulate(tabela_horarios_c99, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração"], tablefmt="github"))
"""))
cells.append(nbf.v4.new_markdown_cell("""## MODELO 6: Minimização de Frota C-99A (C/ 40 min TAT)
Equivalente ao Modelo 3.
"""))
cells.append(nbf.v4.new_code_cell("""# Modelo 6
prob6 = pulp.LpProblem("Minimizar_Aeronaves_C99_TAT", pulp.LpMinimize)
Y6 = pulp.LpVariable.dicts("Y6", [(o, d) for o in aeroportos_malha_c99 for d in aeroportos_malha_c99], lowBound=0, cat='Integer')
N6 = pulp.LpVariable("N6", lowBound=0, cat='Integer')
TAT_min = 40
prob6 += N6 + 0.0001 * pulp.lpSum([Y6[(i, j)] * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
for no in aeroportos_malha_c99:
chegadas = pulp.lpSum([D_c99[(i, no)] + Y6[(i, no)] for i in aeroportos_malha_c99])
partidas = pulp.lpSum([D_c99[(no, j)] + Y6[(no, j)] for j in aeroportos_malha_c99])
prob6 += chegadas == partidas
tempo_total_op_c99 = pulp.lpSum([(D_c99[(i, j)] + Y6[(i, j)]) * (tempos_voo_min_c99[(i, j)] + TAT_min) for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
prob6 += tempo_total_op_c99 <= N6 * 1440
prob6.solve(pulp.PULP_CBC_CMD(msg=False))
print("\\n== RESULTADO DO MODELO 6 (C-99A C/ TAT 40 MIN) ==")
print(f"Status: {pulp.LpStatus[prob6.status]}")
print(f"Aeronaves mínimas: {int(N6.varValue)}")
tempo_voo_c99_6 = sum([(D_c99[(i, j)] + int(Y6[(i, j)].varValue)) * tempos_voo_min_c99[(i, j)] for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
tot_voos_6 = sum([(D_c99[(i, j)] + int(Y6[(i, j)].varValue)) for i in aeroportos_malha_c99 for j in aeroportos_malha_c99])
solo_tot_6 = tot_voos_6 * TAT_min
print(f"Tempo total operacional comprometido: {tempo_voo_c99_6 + solo_tot_6} min ({(tempo_voo_c99_6 + solo_tot_6)/60:.2f} h)\\n")
voos_para_fazer_c99_6 = []
D_copy_c99_6 = {k: v for k, v in D_c99.items()}
for i in aeroportos_malha_c99:
for j in aeroportos_malha_c99:
tot = D_c99[(i, j)] + int(Y6[(i, j)].varValue)
for _ in range(tot):
if D_copy_c99_6[(i, j)] > 0:
tipo = 'Passageiro'
D_copy_c99_6[(i, j)] -= 1
else:
tipo = 'Vazio'
voos_para_fazer_c99_6.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo_min_c99[(i, j)]})
tabela_horarios_c99_6 = []
aeronave_id = 1
while voos_para_fazer_c99_6:
voo_atual = voos_para_fazer_c99_6.pop(0)
hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)
tempo_delta = datetime.timedelta(minutes=int(voo_atual['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios_c99_6.append([f"Aeronave {aeronave_id}", voo_atual['origem'], voo_atual['destino'], voo_atual['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{voo_atual['tempo']} 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_fazer_c99_6):
if v['origem'] == local_atual:
if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(minutes=int(v['tempo']))).total_seconds() <= 1440 * 60:
prox_voo = voos_para_fazer_c99_6.pop(idx)
break
if prox_voo:
tempo_delta = datetime.timedelta(minutes=int(prox_voo['tempo']))
hora_chegada = hora_atual + tempo_delta
tabela_horarios_c99_6.append([f"Aeronave {aeronave_id}", prox_voo['origem'], prox_voo['destino'], prox_voo['tipo'], hora_atual.strftime('%H:%M'), hora_chegada.strftime('%H:%M'), f"{prox_voo['tempo']} min"])
hora_atual = hora_chegada + datetime.timedelta(minutes=40)
local_atual = prox_voo['destino']
else:
break
aeronave_id += 1
print(tabulate(tabela_horarios_c99_6, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração"], tablefmt="github"))
"""))
nb.cells = cells
with open('modelos.ipynb', 'w', encoding='utf-8') as f: