Reorganiza dados e adiciona modelo OAMRP
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -9,3 +9,7 @@ Desktop.ini
|
|||||||
# Local editor folders
|
# Local editor folders
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Python cache
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|||||||
188
software/oamrp_v1.py
Normal file
188
software/oamrp_v1.py
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
"""
|
||||||
|
============================================================================
|
||||||
|
AIRCRAFT ROUTING - ESQUADRAO ARARA (Transporte C-95/C-105)
|
||||||
|
============================================================================
|
||||||
|
Modelo MIP compacto que ATRIBUI aeronaves a missoes de transporte,
|
||||||
|
maximizando o numero (ponderado por prioridade) de missoes cumpridas,
|
||||||
|
respeitando:
|
||||||
|
- rota viavel (continuidade espacial e temporal por aeronave)
|
||||||
|
- turnaround minimo entre missoes
|
||||||
|
- limite de horas de celula ate a proxima inspecao (LRT)
|
||||||
|
- base inicial e horas acumuladas reais de cada aeronave
|
||||||
|
|
||||||
|
Diferenca chave vs. modelo civil (Bazargan): aqui a cobertura e' "<= 1"
|
||||||
|
(missao OPCIONAL, selecionada se compensar) e nao "= 1". O objetivo e'
|
||||||
|
prontidao/cumprimento de missao, nao custo.
|
||||||
|
|
||||||
|
Solver: CBC (vem junto com o PuLP, zero instalacao). Para instancias
|
||||||
|
maiores troque por HiGHS (ver nota no fim do arquivo).
|
||||||
|
|
||||||
|
Autor: gerado como ponto de partida. pt-BR nos comentarios, codigo limpo.
|
||||||
|
============================================================================
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pulp
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# 1) DADOS DE ENTRADA (substitua pelos dados reais do ARARA)
|
||||||
|
# ===========================================================================
|
||||||
|
# Bases (codigos curtos ficticios). 'S' marca bases HABILITADAS a inspecao.
|
||||||
|
# Na FAB o conjunto de bases habilitadas e' restrito -> isso aperta o routing.
|
||||||
|
BASES_INSPECAO = {"BR", "GL"} # ex.: Brasilia (hub do esq.) e Galeao
|
||||||
|
|
||||||
|
TURNAROUND_MIN_H = 1.0 # tempo minimo em solo entre 2 missoes (h)
|
||||||
|
LIMITE_HORAS_F = 100.0 # horas de celula ate inspecao obrigatoria
|
||||||
|
# (AJUSTE ao intervalo real da sua frota)
|
||||||
|
|
||||||
|
# ----- Aeronaves (tail number, base inicial, horas acumuladas desde a ult. insp.)
|
||||||
|
# 'horas_acum' = f_k. LRT inicial de cada aeronave = LIMITE_HORAS_F - f_k.
|
||||||
|
AERONAVES = [
|
||||||
|
# id base horas_acum
|
||||||
|
("FAB-2301", "BR", 12.0),
|
||||||
|
("FAB-2302", "BR", 71.0), # ja gastou muito -> pouco LRT disponivel
|
||||||
|
("FAB-2303", "GL", 30.0),
|
||||||
|
("FAB-2304", "BR", 95.0), # quase no limite -> mal consegue voar
|
||||||
|
]
|
||||||
|
|
||||||
|
# ----- Missoes de transporte: (id, origem, destino, partida_h, chegada_h, prioridade)
|
||||||
|
# partida/chegada em horas decimais dentro do horizonte (ex.: 0..72 = 3 dias).
|
||||||
|
# prioridade: peso da missao (missao critica pesa mais).
|
||||||
|
MISSOES = [
|
||||||
|
# id orig dest part cheg prio
|
||||||
|
("M01", "BR", "MN", 6.0, 10.0, 5), # logistica Amazonia (alta prio)
|
||||||
|
("M02", "MN", "BR", 12.0, 16.0, 5),
|
||||||
|
("M03", "BR", "RF", 7.0, 10.0, 3),
|
||||||
|
("M04", "RF", "BR", 11.5, 14.5, 3),
|
||||||
|
("M05", "BR", "CG", 8.0, 10.0, 2),
|
||||||
|
("M06", "CG", "BR", 11.0, 13.0, 2),
|
||||||
|
("M07", "GL", "BR", 6.5, 8.0, 4),
|
||||||
|
("M08", "BR", "BE", 9.0, 12.5, 4), # Belem
|
||||||
|
("M09", "BE", "BR", 14.0, 17.5, 4),
|
||||||
|
("M10", "BR", "GL", 15.0, 16.5, 1),
|
||||||
|
("M11", "BR", "MN", 30.0, 34.0, 5), # dia 2
|
||||||
|
("M12", "MN", "BR", 36.0, 40.0, 5),
|
||||||
|
]
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# 2) PRE-PROCESSAMENTO (gera conexoes viaveis)
|
||||||
|
# ===========================================================================
|
||||||
|
miss = {m[0]: dict(orig=m[1], dest=m[2], part=m[3], cheg=m[4], prio=m[5],
|
||||||
|
dur=m[4] - m[3]) for m in MISSOES}
|
||||||
|
acft = {a[0]: dict(base=a[1], f=a[2]) for a in AERONAVES}
|
||||||
|
IDS = list(miss.keys())
|
||||||
|
KS = list(acft.keys())
|
||||||
|
|
||||||
|
# Conexao i->j valida: destino de i == origem de j e ha turnaround suficiente
|
||||||
|
conex = [(i, j) for i in IDS for j in IDS
|
||||||
|
if i != j
|
||||||
|
and miss[i]["dest"] == miss[j]["orig"]
|
||||||
|
and miss[j]["part"] >= miss[i]["cheg"] + TURNAROUND_MIN_H]
|
||||||
|
|
||||||
|
# Aeronave k pode INICIAR pela missao i se i parte da base inicial de k
|
||||||
|
def pode_iniciar(k, i):
|
||||||
|
return miss[i]["orig"] == acft[k]["base"]
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# 3) MODELO MIP
|
||||||
|
# ===========================================================================
|
||||||
|
mdl = pulp.LpProblem("ARARA_Aircraft_Routing", pulp.LpMaximize)
|
||||||
|
|
||||||
|
# --- Variaveis
|
||||||
|
x = {(k, i): pulp.LpVariable(f"x_{k}_{i}", cat="Binary") for k in KS for i in IDS}
|
||||||
|
s = {(k, i): pulp.LpVariable(f"s_{k}_{i}", cat="Binary") # k inicia por i
|
||||||
|
for k in KS for i in IDS if pode_iniciar(k, i)}
|
||||||
|
c = {(k, i, j): pulp.LpVariable(f"c_{k}_{i}_{j}", cat="Binary") # k voa i depois j
|
||||||
|
for k in KS for (i, j) in conex}
|
||||||
|
z = {i: pulp.LpVariable(f"z_{i}", cat="Binary") for i in IDS} # missao coberta
|
||||||
|
|
||||||
|
# --- Objetivo: maximizar prioridade total das missoes cumpridas
|
||||||
|
mdl += pulp.lpSum(miss[i]["prio"] * z[i] for i in IDS), "Missoes_cumpridas_ponderadas"
|
||||||
|
|
||||||
|
# --- (R1) Cobertura: cada missao por NO MAXIMO uma aeronave
|
||||||
|
for i in IDS:
|
||||||
|
mdl += z[i] == pulp.lpSum(x[(k, i)] for k in KS), f"def_z_{i}"
|
||||||
|
# z[i] ja e' binaria, entao automaticamente <= 1
|
||||||
|
|
||||||
|
# --- (R2) Fluxo de entrada: se k voa i, ele iniciou por i OU chegou de outra missao
|
||||||
|
for k in KS:
|
||||||
|
for i in IDS:
|
||||||
|
entradas = pulp.lpSum(c[(k, jj, i)] for (jj, ii) in conex if ii == i)
|
||||||
|
ini = s[(k, i)] if (k, i) in s else 0
|
||||||
|
mdl += ini + entradas == x[(k, i)], f"in_{k}_{i}"
|
||||||
|
|
||||||
|
# --- (R3) Fluxo de saida: depois de i, k segue para no max uma proxima missao
|
||||||
|
for k in KS:
|
||||||
|
for i in IDS:
|
||||||
|
saidas = pulp.lpSum(c[(k, i, jj)] for (ii, jj) in conex if ii == i)
|
||||||
|
mdl += saidas <= x[(k, i)], f"out_{k}_{i}"
|
||||||
|
|
||||||
|
# --- (R4) Cada aeronave inicia no maximo uma rota
|
||||||
|
for k in KS:
|
||||||
|
inis = [s[(k, i)] for i in IDS if (k, i) in s]
|
||||||
|
if inis:
|
||||||
|
mdl += pulp.lpSum(inis) <= 1, f"start_{k}"
|
||||||
|
|
||||||
|
# --- (R5) Limite de horas de celula (LRT): total voado por k <= F - f_k
|
||||||
|
# Esta e' a restricao CENTRAL de manutencao (versao "orcamento de horas").
|
||||||
|
# -> A inspecao com RESET de horas em base habilitada e' a extensao da Fase 3.
|
||||||
|
for k in KS:
|
||||||
|
mdl += pulp.lpSum(miss[i]["dur"] * x[(k, i)] for i in IDS) <= \
|
||||||
|
LIMITE_HORAS_F - acft[k]["f"], f"horas_{k}"
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# 4) RESOLVER
|
||||||
|
# ===========================================================================
|
||||||
|
solver = pulp.PULP_CBC_CMD(msg=False) # troque por HiGHS se instalado
|
||||||
|
mdl.solve(solver)
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# 5) RELATORIO
|
||||||
|
# ===========================================================================
|
||||||
|
print("=" * 70)
|
||||||
|
print(f" STATUS: {pulp.LpStatus[mdl.status]}")
|
||||||
|
prio_tot = sum(miss[i]["prio"] for i in IDS)
|
||||||
|
prio_obt = int(pulp.value(mdl.objective))
|
||||||
|
cob = [i for i in IDS if z[i].value() and z[i].value() > 0.5]
|
||||||
|
print(f" Missoes cumpridas: {len(cob)}/{len(IDS)} "
|
||||||
|
f"| Prioridade obtida: {prio_obt}/{prio_tot}")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
def rota_da_aeronave(k):
|
||||||
|
"""Reconstroi a sequencia de missoes da aeronave k seguindo as conexoes."""
|
||||||
|
inicio = [i for i in IDS if (k, i) in s and s[(k, i)].value() and s[(k, i)].value() > 0.5]
|
||||||
|
if not inicio:
|
||||||
|
return []
|
||||||
|
seq = [inicio[0]]
|
||||||
|
while True:
|
||||||
|
atual = seq[-1]
|
||||||
|
prox = [j for (ii, jj) in conex if ii == atual
|
||||||
|
for j in [jj] if c[(k, atual, j)].value() and c[(k, atual, j)].value() > 0.5]
|
||||||
|
if not prox:
|
||||||
|
break
|
||||||
|
seq.append(prox[0])
|
||||||
|
return seq
|
||||||
|
|
||||||
|
for k in KS:
|
||||||
|
seq = rota_da_aeronave(k)
|
||||||
|
horas = sum(miss[i]["dur"] for i in seq)
|
||||||
|
lrt_ini = LIMITE_HORAS_F - acft[k]["f"]
|
||||||
|
if seq:
|
||||||
|
trechos = " -> ".join(
|
||||||
|
f"{i}({miss[i]['orig']}-{miss[i]['dest']} {miss[i]['part']:g}h)" for i in seq)
|
||||||
|
print(f"\n {k} [base {acft[k]['base']}, f={acft[k]['f']:g}h, LRT={lrt_ini:g}h]")
|
||||||
|
print(f" {trechos}")
|
||||||
|
print(f" horas voadas: {horas:g}h | LRT restante: {lrt_ini - horas:g}h")
|
||||||
|
else:
|
||||||
|
print(f"\n {k} [base {acft[k]['base']}, LRT={lrt_ini:g}h] -> sem missoes")
|
||||||
|
|
||||||
|
descobertas = [i for i in IDS if i not in cob]
|
||||||
|
if descobertas:
|
||||||
|
print("\n " + "-" * 60)
|
||||||
|
print(" Missoes NAO cumpridas:",
|
||||||
|
", ".join(f"{i}(prio {miss[i]['prio']})" for i in descobertas))
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# NOTA - usar HiGHS (recomendado p/ instancias maiores, sem licenca):
|
||||||
|
# pip install highspy
|
||||||
|
# solver = pulp.HiGHS(msg=False) # PuLP 3.x expoe HiGHS nativo
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
Reference in New Issue
Block a user