feat: adiciona Modelo 3 minimizando aeronaves com a consideração do tempo de solo (TAT) de 40 minutos
This commit is contained in:
119
create_nb.py
119
create_nb.py
@@ -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"))
|
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
|
nb.cells = cells
|
||||||
|
|
||||||
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
with open('modelos.ipynb', 'w', encoding='utf-8') as f:
|
||||||
|
|||||||
264
modelos.ipynb
264
modelos.ipynb
@@ -2,7 +2,7 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "d163e704",
|
"id": "eeb50313",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Alocação de Frotas (Fleet Scheduling) - Aeronaves C-97\n",
|
"# Alocação de Frotas (Fleet Scheduling) - Aeronaves C-97\n",
|
||||||
@@ -21,13 +21,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 1,
|
||||||
"id": "1cda871c",
|
"id": "3345f6bf",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:13.265917Z",
|
"iopub.execute_input": "2026-06-07T23:44:10.042075Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:13.265674Z",
|
"iopub.status.busy": "2026-06-07T23:44:10.041422Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:13.946892Z",
|
"iopub.status.idle": "2026-06-07T23:44:10.717819Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:13.946177Z"
|
"shell.execute_reply": "2026-06-07T23:44:10.717190Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "3aa25feb",
|
"id": "bb838c8d",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 1. Leitura e Limpeza dos Dados\n",
|
"## 1. Leitura e Limpeza dos Dados\n",
|
||||||
@@ -63,13 +63,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 2,
|
||||||
"id": "5f12213f",
|
"id": "af65863f",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:13.949035Z",
|
"iopub.execute_input": "2026-06-07T23:44:10.719344Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:13.948756Z",
|
"iopub.status.busy": "2026-06-07T23:44:10.719122Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:14.387479Z",
|
"iopub.status.idle": "2026-06-07T23:44:11.177567Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:14.386681Z"
|
"shell.execute_reply": "2026-06-07T23:44:11.176910Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "29a370cf",
|
"id": "f4e097f0",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 2. Análise dos Esquadrões e Matrículas (Aeronaves)\n",
|
"## 2. Análise dos Esquadrões e Matrículas (Aeronaves)\n",
|
||||||
@@ -111,13 +111,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": 3,
|
||||||
"id": "4f559f60",
|
"id": "01c6de00",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:14.388969Z",
|
"iopub.execute_input": "2026-06-07T23:44:11.179085Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:14.388784Z",
|
"iopub.status.busy": "2026-06-07T23:44:11.178943Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:14.401597Z",
|
"iopub.status.idle": "2026-06-07T23:44:11.193498Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:14.401016Z"
|
"shell.execute_reply": "2026-06-07T23:44:11.192832Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "3d637614",
|
"id": "03d676ac",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 3. Análise Estatística das Demandas Diárias\n",
|
"## 3. Análise Estatística das Demandas Diárias\n",
|
||||||
@@ -187,13 +187,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 4,
|
||||||
"id": "ca8b936f",
|
"id": "d1d53c92",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:14.403000Z",
|
"iopub.execute_input": "2026-06-07T23:44:11.195008Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:14.402870Z",
|
"iopub.status.busy": "2026-06-07T23:44:11.194881Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:14.726896Z",
|
"iopub.status.idle": "2026-06-07T23:44:11.521316Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:14.726161Z"
|
"shell.execute_reply": "2026-06-07T23:44:11.520564Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "782a4736",
|
"id": "5a340e61",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Diagrama de Rede dos Trechos\n",
|
"## Diagrama de Rede dos Trechos\n",
|
||||||
@@ -434,13 +434,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 5,
|
"execution_count": 5,
|
||||||
"id": "9fedd535",
|
"id": "233d1236",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:14.728337Z",
|
"iopub.execute_input": "2026-06-07T23:44:11.522783Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:14.728199Z",
|
"iopub.status.busy": "2026-06-07T23:44:11.522643Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:14.849420Z",
|
"iopub.status.idle": "2026-06-07T23:44:11.652315Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:14.848842Z"
|
"shell.execute_reply": "2026-06-07T23:44:11.651816Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -497,7 +497,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "551febf3",
|
"id": "19a69fa7",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 4. Fleet Assignment Model (FAM) - Nível Brasil\n",
|
"## 4. Fleet Assignment Model (FAM) - Nível Brasil\n",
|
||||||
@@ -514,13 +514,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 6,
|
"execution_count": 6,
|
||||||
"id": "bb15ee0b",
|
"id": "77122760",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:14.850971Z",
|
"iopub.execute_input": "2026-06-07T23:44:11.654036Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:14.850776Z",
|
"iopub.status.busy": "2026-06-07T23:44:11.653900Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:14.876417Z",
|
"iopub.status.idle": "2026-06-07T23:44:11.681677Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:14.875894Z"
|
"shell.execute_reply": "2026-06-07T23:44:11.681090Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -609,7 +609,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "37f9e364",
|
"id": "d52acdc3",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## 5. Modelo 2: Minimização de Aeronaves\n",
|
"## 5. Modelo 2: Minimização de Aeronaves\n",
|
||||||
@@ -623,13 +623,13 @@
|
|||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": 7,
|
||||||
"id": "08433efd",
|
"id": "e4289b56",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"execution": {
|
"execution": {
|
||||||
"iopub.execute_input": "2026-06-07T23:41:14.877926Z",
|
"iopub.execute_input": "2026-06-07T23:44:11.683322Z",
|
||||||
"iopub.status.busy": "2026-06-07T23:41:14.877797Z",
|
"iopub.status.busy": "2026-06-07T23:44:11.683175Z",
|
||||||
"iopub.status.idle": "2026-06-07T23:41:16.250728Z",
|
"iopub.status.idle": "2026-06-07T23:44:13.105611Z",
|
||||||
"shell.execute_reply": "2026-06-07T23:41:16.250087Z"
|
"shell.execute_reply": "2026-06-07T23:44:13.105024Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -653,15 +653,15 @@
|
|||||||
"|------------|----------|-----------|--------------------------|-----------|-----------|-----------|\n",
|
"|------------|----------|-----------|--------------------------|-----------|-----------|-----------|\n",
|
||||||
"| Aeronave 1 | SBGL | SBGR | Passageiro | 00:00 | 00:51 | 0.86 h |\n",
|
"| Aeronave 1 | SBGL | SBGR | Passageiro | 00:00 | 00:51 | 0.86 h |\n",
|
||||||
"| Aeronave 1 | SBGR | SBGL | Passageiro | 00:51 | 01:43 | 0.86 h |\n",
|
"| Aeronave 1 | SBGR | SBGL | Passageiro | 00:51 | 01:43 | 0.86 h |\n",
|
||||||
"| Aeronave 1 | SBGL | SBSJ | Passageiro | 01:43 | 02:24 | 0.69 h |\n",
|
"| Aeronave 1 | SBGL | SBBR | Passageiro | 01:43 | 04:02 | 2.33 h |\n",
|
||||||
"| Aeronave 1 | SBSJ | SBGL | Passageiro | 02:24 | 03:06 | 0.69 h |\n",
|
"| Aeronave 1 | SBBR | SBGL | Passageiro | 04:02 | 06:22 | 2.33 h |\n",
|
||||||
"| Aeronave 1 | SBGL | SBBR | Passageiro | 03:06 | 05:26 | 2.33 h |\n",
|
"| Aeronave 1 | SBGL | SBSJ | Passageiro | 06:22 | 07:03 | 0.69 h |\n",
|
||||||
"| Aeronave 1 | SBBR | SBGL | Passageiro | 05:26 | 07:45 | 2.33 h |\n",
|
"| Aeronave 1 | SBSJ | SBGL | Passageiro | 07:03 | 07:45 | 0.69 h |\n",
|
||||||
"| Aeronave 2 | SBGR | SBBR | Passageiro | 00:00 | 02:10 | 2.17 h |\n",
|
"| Aeronave 2 | SBGR | SBBR | Passageiro | 00:00 | 02:10 | 2.17 h |\n",
|
||||||
"| Aeronave 2 | SBBR | SBGR | Passageiro | 02:10 | 04:20 | 2.17 h |\n",
|
"| Aeronave 2 | SBBR | SBGR | Passageiro | 02:10 | 04:20 | 2.17 h |\n",
|
||||||
"| Aeronave 3 | SBAN | SBSJ | Vazio (Reposicionamento) | 00:00 | 02:08 | 2.14 h |\n",
|
"| Aeronave 3 | SBBR | SBAN | Passageiro | 00:00 | 00:18 | 0.30 h |\n",
|
||||||
"| Aeronave 3 | SBSJ | SBBR | Passageiro | 02:08 | 04:17 | 2.15 h |\n",
|
"| Aeronave 3 | SBAN | SBSJ | Vazio (Reposicionamento) | 00:18 | 02:26 | 2.14 h |\n",
|
||||||
"| Aeronave 3 | SBBR | SBAN | Passageiro | 04:17 | 04:35 | 0.30 h |\n"
|
"| Aeronave 3 | SBSJ | SBBR | Passageiro | 02:26 | 04:35 | 2.15 h |\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -832,6 +832,172 @@
|
|||||||
"print(\"\\n== QUADRO DE HORÁRIOS DIÁRIOS ==\")\n",
|
"print(\"\\n== QUADRO DE HORÁRIOS DIÁRIOS ==\")\n",
|
||||||
"print(tabulate(tabela_horarios, headers=[\"Aeronave\", \"Origem\", \"Destino\", \"Tipo\", \"Partida\", \"Chegada\", \"Duração\"], tablefmt=\"github\"))\n"
|
"print(tabulate(tabela_horarios, headers=[\"Aeronave\", \"Origem\", \"Destino\", \"Tipo\", \"Partida\", \"Chegada\", \"Duração\"], tablefmt=\"github\"))\n"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "2daf591f",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## 6. Modelo 3: Minimização de Aeronaves com TAT (Turnaround Time)\n",
|
||||||
|
"Nesta variação do Modelo 2, adicionamos uma restrição crítica da vida real: o Tempo de Solo ou **Turnaround Time (TAT)**.\n",
|
||||||
|
"Considerações:\n",
|
||||||
|
"- TAT estipulado: 40 minutos (0.66 horas) por cada voo (tempo necessário para desembarque, reabastecimento, verificações e embarque).\n",
|
||||||
|
"- 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.\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"id": "4d54330a",
|
||||||
|
"metadata": {
|
||||||
|
"execution": {
|
||||||
|
"iopub.execute_input": "2026-06-07T23:44:13.108816Z",
|
||||||
|
"iopub.status.busy": "2026-06-07T23:44:13.108593Z",
|
||||||
|
"iopub.status.idle": "2026-06-07T23:44:13.126370Z",
|
||||||
|
"shell.execute_reply": "2026-06-07T23:44:13.125849Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"\n",
|
||||||
|
"== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==\n",
|
||||||
|
"Status: Optimal\n",
|
||||||
|
"Número mínimo absoluto de aeronaves C-97 necessárias: 2\n",
|
||||||
|
"Tempo total de voo puro: 16.70 horas\n",
|
||||||
|
"Tempo total gasto em solo (TAT): 7.33 horas\n",
|
||||||
|
"Tempo total operacional comprometido: 24.04 horas\n",
|
||||||
|
"Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): 50.1%\n",
|
||||||
|
"\n",
|
||||||
|
"== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==\n",
|
||||||
|
"| Aeronave | Origem | Destino | Tipo | Partida | Chegada | Duração Voo |\n",
|
||||||
|
"|------------|----------|-----------|--------------------------|-----------|-----------|---------------|\n",
|
||||||
|
"| Aeronave 1 | SBGL | SBGR | Passageiro | 00:00 | 00:51 | 0.86 h |\n",
|
||||||
|
"| Aeronave 1 | SBGR | SBGL | Passageiro | 01:31 | 02:23 | 0.86 h |\n",
|
||||||
|
"| Aeronave 1 | SBGL | SBBR | Passageiro | 03:03 | 05:22 | 2.33 h |\n",
|
||||||
|
"| Aeronave 1 | SBBR | SBGL | Passageiro | 06:02 | 08:22 | 2.33 h |\n",
|
||||||
|
"| Aeronave 1 | SBGL | SBSJ | Passageiro | 09:02 | 09:43 | 0.69 h |\n",
|
||||||
|
"| Aeronave 1 | SBSJ | SBGL | Passageiro | 10:23 | 11:05 | 0.69 h |\n",
|
||||||
|
"| Aeronave 2 | SBGR | SBBR | Passageiro | 00:00 | 02:10 | 2.17 h |\n",
|
||||||
|
"| Aeronave 2 | SBBR | SBGR | Passageiro | 02:50 | 05:00 | 2.17 h |\n",
|
||||||
|
"| Aeronave 3 | SBBR | SBAN | Passageiro | 00:00 | 00:18 | 0.30 h |\n",
|
||||||
|
"| Aeronave 3 | SBAN | SBSJ | Vazio (Reposicionamento) | 00:58 | 03:06 | 2.14 h |\n",
|
||||||
|
"| Aeronave 3 | SBSJ | SBBR | Passageiro | 03:46 | 05:55 | 2.15 h |\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# 3. Modelagem Matemática Modelo 3 (Pulp) com TAT\n",
|
||||||
|
"prob3 = pulp.LpProblem(\"Minimizar_Aeronaves_TAT\", pulp.LpMinimize)\n",
|
||||||
|
"\n",
|
||||||
|
"# Variáveis\n",
|
||||||
|
"Y3 = pulp.LpVariable.dicts(\"Y3\", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')\n",
|
||||||
|
"N3 = pulp.LpVariable(\"N3\", lowBound=0, cat='Integer')\n",
|
||||||
|
"\n",
|
||||||
|
"TAT_horas = 40.0 / 60.0\n",
|
||||||
|
"\n",
|
||||||
|
"# Função Objetivo\n",
|
||||||
|
"prob3 += N3 + 0.0001 * pulp.lpSum([Y3[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])\n",
|
||||||
|
"\n",
|
||||||
|
"# Restrição de fluxo\n",
|
||||||
|
"for no in aeroportos_malha:\n",
|
||||||
|
" chegadas = pulp.lpSum([D[(i, no)] + Y3[(i, no)] for i in aeroportos_malha])\n",
|
||||||
|
" partidas = pulp.lpSum([D[(no, j)] + Y3[(no, j)] for j in aeroportos_malha])\n",
|
||||||
|
" prob3 += chegadas == partidas\n",
|
||||||
|
"\n",
|
||||||
|
"# Restrição de tempo com TAT\n",
|
||||||
|
"# Cada voo (D ou Y) consome tempo de voo + TAT\n",
|
||||||
|
"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])\n",
|
||||||
|
"prob3 += tempo_total_operacional <= N3 * 24.0\n",
|
||||||
|
"\n",
|
||||||
|
"# Solução\n",
|
||||||
|
"prob3.solve(pulp.PULP_CBC_CMD(msg=False))\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"\\n== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==\")\n",
|
||||||
|
"print(f\"Status: {pulp.LpStatus[prob3.status]}\")\n",
|
||||||
|
"print(f\"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N3.varValue)}\")\n",
|
||||||
|
"\n",
|
||||||
|
"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])\n",
|
||||||
|
"total_voos_realizados = sum([(D[(i, j)] + Y3[(i, j)].varValue) for i in aeroportos_malha for j in aeroportos_malha])\n",
|
||||||
|
"tempo_solo_total = total_voos_realizados * TAT_horas\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"Tempo total de voo puro: {tempo_voo_puro:.2f} horas\")\n",
|
||||||
|
"print(f\"Tempo total gasto em solo (TAT): {tempo_solo_total:.2f} horas\")\n",
|
||||||
|
"print(f\"Tempo total operacional comprometido: {tempo_voo_puro + tempo_solo_total:.2f} horas\")\n",
|
||||||
|
"if N3.varValue > 0:\n",
|
||||||
|
" 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\")\n",
|
||||||
|
"\n",
|
||||||
|
"# GERAR TABELA DE HORÁRIOS COM TABULATE E DATETIME\n",
|
||||||
|
"voos_para_fazer3 = []\n",
|
||||||
|
"D_copy3 = {k: v for k, v in D.items()}\n",
|
||||||
|
"\n",
|
||||||
|
"for i in aeroportos_malha:\n",
|
||||||
|
" for j in aeroportos_malha:\n",
|
||||||
|
" total_voos3 = D[(i, j)] + int(Y3[(i, j)].varValue)\n",
|
||||||
|
" for _ in range(total_voos3):\n",
|
||||||
|
" if D_copy3[(i, j)] > 0:\n",
|
||||||
|
" tipo = 'Passageiro'\n",
|
||||||
|
" D_copy3[(i, j)] -= 1\n",
|
||||||
|
" else:\n",
|
||||||
|
" tipo = 'Vazio (Reposicionamento)'\n",
|
||||||
|
" voos_para_fazer3.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]})\n",
|
||||||
|
"\n",
|
||||||
|
"tabela_horarios3 = []\n",
|
||||||
|
"aeronave_id3 = 1\n",
|
||||||
|
"\n",
|
||||||
|
"while voos_para_fazer3:\n",
|
||||||
|
" voo_atual = voos_para_fazer3.pop(0)\n",
|
||||||
|
" hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)\n",
|
||||||
|
" \n",
|
||||||
|
" tempo_delta = datetime.timedelta(hours=voo_atual['tempo'])\n",
|
||||||
|
" hora_chegada = hora_atual + tempo_delta\n",
|
||||||
|
" \n",
|
||||||
|
" tabela_horarios3.append([\n",
|
||||||
|
" f\"Aeronave {aeronave_id3}\",\n",
|
||||||
|
" voo_atual['origem'],\n",
|
||||||
|
" voo_atual['destino'],\n",
|
||||||
|
" voo_atual['tipo'],\n",
|
||||||
|
" hora_atual.strftime('%H:%M'),\n",
|
||||||
|
" hora_chegada.strftime('%H:%M'),\n",
|
||||||
|
" f\"{voo_atual['tempo']:.2f} h\"\n",
|
||||||
|
" ])\n",
|
||||||
|
" \n",
|
||||||
|
" # Próxima partida só depois do TAT de 40 min\n",
|
||||||
|
" hora_atual = hora_chegada + datetime.timedelta(minutes=40)\n",
|
||||||
|
" local_atual = voo_atual['destino']\n",
|
||||||
|
" \n",
|
||||||
|
" while True:\n",
|
||||||
|
" prox_voo = None\n",
|
||||||
|
" for idx, v in enumerate(voos_para_fazer3):\n",
|
||||||
|
" if v['origem'] == local_atual:\n",
|
||||||
|
" if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(hours=v['tempo'])).total_seconds() <= 24 * 3600:\n",
|
||||||
|
" prox_voo = voos_para_fazer3.pop(idx)\n",
|
||||||
|
" break\n",
|
||||||
|
" \n",
|
||||||
|
" if prox_voo:\n",
|
||||||
|
" tempo_delta = datetime.timedelta(hours=prox_voo['tempo'])\n",
|
||||||
|
" hora_chegada = hora_atual + tempo_delta\n",
|
||||||
|
" tabela_horarios3.append([\n",
|
||||||
|
" f\"Aeronave {aeronave_id3}\",\n",
|
||||||
|
" prox_voo['origem'],\n",
|
||||||
|
" prox_voo['destino'],\n",
|
||||||
|
" prox_voo['tipo'],\n",
|
||||||
|
" hora_atual.strftime('%H:%M'),\n",
|
||||||
|
" hora_chegada.strftime('%H:%M'),\n",
|
||||||
|
" f\"{prox_voo['tempo']:.2f} h\"\n",
|
||||||
|
" ])\n",
|
||||||
|
" hora_atual = hora_chegada + datetime.timedelta(minutes=40)\n",
|
||||||
|
" local_atual = prox_voo['destino']\n",
|
||||||
|
" else:\n",
|
||||||
|
" break\n",
|
||||||
|
" \n",
|
||||||
|
" aeronave_id3 += 1\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==\")\n",
|
||||||
|
"print(tabulate(tabela_horarios3, headers=[\"Aeronave\", \"Origem\", \"Destino\", \"Tipo\", \"Partida\", \"Chegada\", \"Duração Voo\"], tablefmt=\"github\"))\n"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
Reference in New Issue
Block a user