| """ |
| Calculadora de Incendio - NT-01/2025 CBMGO |
| Calculos normativos: extintores, hidrantes, SPDA, iluminacao, lotacao. |
| """ |
| import math |
| from typing import Dict, List, Tuple |
|
|
|
|
| class CalculadoraIncendio: |
| """ |
| Calculadora normativa para sistemas de prevencao de incendio. |
| Baseada na NT-01/2025 CBMGO e normas ABNT associadas. |
| """ |
| |
| |
| TABELA_EXTINTORES = { |
| "A": {"area": 500, "cap": "2-A:10-B:C", "dist_max": 15}, |
| "B": {"area": 250, "cap": "2-A:20-B:C", "dist_max": 15}, |
| "C": {"area": 300, "cap": "2-A:10-B:C", "dist_max": 15}, |
| "D": {"area": 400, "cap": "2-A:20-B:C", "dist_max": 15}, |
| "E": {"area": 300, "cap": "2-A:10-B:C", "dist_max": 15}, |
| "F": {"area": 200, "cap": "2-A:20-B:C", "dist_max": 15}, |
| "G": {"area": 250, "cap": "2-A:40-B:C", "dist_max": 15}, |
| "H": {"area": 500, "cap": "2-A:10-B:C", "dist_max": 15}, |
| "I": {"area": 300, "cap": "4-A:20-B:C", "dist_max": 15}, |
| } |
| |
| |
| INDICES_LOTACAO = { |
| "residencial": 18.0, |
| "escritorio": 7.0, |
| "comercio_varejo": 3.0, |
| "supermercado": 2.5, |
| "restaurante": 1.5, |
| "bar_lanchonete": 1.2, |
| "hotel": 25.0, |
| "hospital": 15.0, |
| "escola": 2.0, |
| "auditorio": 0.7, |
| "teatro_cinema": 0.65, |
| "garagem": 30.0, |
| "industria_leve": 10.0, |
| "industria_pesada": 20.0, |
| "deposito": 50.0, |
| "ginasio": 0.5, |
| "academia": 4.0, |
| } |
| |
| def calcular_extintores(self, area_m2: float, grupo: str) -> Dict: |
| """Calcula extintores conforme NT-01/2025 Anexo B.""" |
| cfg = self.TABELA_EXTINTORES.get(grupo.upper(), self.TABELA_EXTINTORES["D"]) |
| qtd = max(1, math.ceil(area_m2 / cfg["area"])) |
| |
| |
| if area_m2 > 200: |
| qtd = max(2, qtd) |
| |
| return { |
| "quantidade_minima": qtd, |
| "capacidade_extintora": cfg["cap"], |
| "distancia_maxima_m": cfg["dist_max"], |
| "area_por_extintor_m2": cfg["area"], |
| "tipos_recomendados": self._tipos_extintor(grupo), |
| "referencia": f"NT-01/2025 Anexo B - Grupo {grupo.upper()} | ABNT NBR 12693" |
| } |
| |
| def _tipos_extintor(self, grupo: str) -> List[str]: |
| """Recomenda tipos de agente extintor por grupo de ocupacao.""" |
| g = grupo.upper() |
| if g in ["A", "E", "G"]: |
| return ["Po ABC", "Agua pressurizada (apenas para classe A)"] |
| elif g in ["B", "C", "D", "H"]: |
| return ["Po ABC", "CO2 (proximo a equipamentos eletronicos)"] |
| elif g in ["F", "I"]: |
| return ["Po ABC", "CO2", "Espuma (para liquidos inflamaveis se aplicavel)"] |
| return ["Po ABC"] |
| |
| def calcular_hidrantes(self, area_m2: float, altura_m: float, |
| ocupacao: str = "comercial") -> Dict: |
| """Calcula sistema de hidrantes conforme NT-01/2025 Art. 18.""" |
| |
| |
| if altura_m < 6 and area_m2 < 750: |
| sistema = "Mangueira de 1a intervencao (hidrante simples)" |
| vazao_lpm = 150 |
| pressao_min_mca = 10 |
| reserva_m3 = round(vazao_lpm * 30 / 1000, 1) |
| obrigatorio = False |
| elif altura_m < 12: |
| sistema = "Sistema de Mangueiras / Coluna Seca" |
| vazao_lpm = 300 |
| pressao_min_mca = 15 |
| reserva_m3 = round(vazao_lpm * 60 / 1000, 1) |
| obrigatorio = True |
| elif altura_m < 30: |
| sistema = "Hidrante com Reservatorio Elevado" |
| vazao_lpm = 450 |
| pressao_min_mca = 20 |
| reserva_m3 = round(vazao_lpm * 60 / 1000, 1) |
| obrigatorio = True |
| else: |
| sistema = "Chuveiros Automaticos + Hidrante (Alta Pressao)" |
| vazao_lpm = 600 |
| pressao_min_mca = 30 |
| reserva_m3 = round(vazao_lpm * 60 / 1000, 1) |
| obrigatorio = True |
| |
| |
| if "industrial" in ocupacao.lower() or "deposito" in ocupacao.lower(): |
| vazao_lpm = int(vazao_lpm * 1.5) |
| reserva_m3 = round(vazao_lpm * 60 / 1000, 1) |
| |
| return { |
| "sistema": sistema, |
| "obrigatorio": obrigatorio, |
| "vazao_minima_lpm": vazao_lpm, |
| "pressao_minima_mca": pressao_min_mca, |
| "reserva_tecnica_m3": reserva_m3, |
| "diametro_tubulacao_min_mm": 65 if vazao_lpm <= 300 else 100, |
| "referencia": "NT-01/2025 Art. 18 | ABNT NBR 13714" |
| } |
| |
| def calcular_lotacao(self, area_m2: float, uso: str) -> Dict: |
| """Calcula lotacao maxima conforme NT-01/2025 Tabela 2.""" |
| indice = self.INDICES_LOTACAO.get(uso.lower().replace(" ", "_"), |
| self.INDICES_LOTACAO["comercio_varejo"]) |
| lotacao = max(1, math.ceil(area_m2 / indice)) |
| |
| |
| if lotacao <= 50: |
| saidas = 1 |
| elif lotacao <= 200: |
| saidas = 2 |
| elif lotacao <= 1000: |
| saidas = 3 |
| else: |
| saidas = 4 |
| |
| |
| modulos = math.ceil(lotacao / 60) |
| largura_m = modulos * 0.55 |
| largura_m = max(0.90, largura_m) |
| |
| return { |
| "lotacao_maxima": lotacao, |
| "indice_m2_por_pessoa": indice, |
| "saidas_emergencia_minimo": saidas, |
| "largura_minima_saidas_m": round(largura_m, 2), |
| "modulos_saida": modulos, |
| "referencia": "NT-01/2025 Tabela 2 | ABNT NBR 9077" |
| } |
| |
| def calcular_spda(self, area_m2: float, altura_m: float, |
| uso: str = "comercial") -> Dict: |
| """Avalia necessidade de SPDA conforme NT-01/2025 Art. 25.""" |
| usos_criticos = ["hospital", "escola", "industria", "deposito_inflamavel", |
| "reuniao", "hotel"] |
| |
| obrigatorio = ( |
| area_m2 > 1000 or |
| altura_m > 20 or |
| any(u in uso.lower() for u in usos_criticos) |
| ) |
| |
| nivel_protecao = "IV" |
| if area_m2 > 5000 or altura_m > 45: |
| nivel_protecao = "II" |
| elif area_m2 > 2000 or altura_m > 30: |
| nivel_protecao = "III" |
| |
| return { |
| "obrigatorio": obrigatorio, |
| "nivel_protecao_nbr": nivel_protecao, |
| "justificativa": ( |
| "Obrigatorio: area > 1000m2" if area_m2 > 1000 |
| else "Obrigatorio: altura > 20m" if altura_m > 20 |
| else "Obrigatorio: uso critico" if obrigatorio |
| else "Verificar com projeto especifico" |
| ), |
| "norma": "ABNT NBR 5419", |
| "referencia": "NT-01/2025 Art. 25 | ABNT NBR 5419" |
| } |
| |
| def calcular_iluminacao_emergencia(self, area_m2: float, |
| altura_m: float, |
| lotacao: int = 0) -> Dict: |
| """Calcula sistema de iluminacao de emergencia conforme NT-01/2025.""" |
| obrigatorio = ( |
| altura_m > 12 or |
| lotacao > 50 or |
| area_m2 > 500 |
| ) |
| |
| if obrigatorio: |
| autonomia_h = 1.0 |
| nivel_lux_rota = 3 |
| nivel_lux_acesso = 10 |
| pontos_min = max(2, math.ceil(area_m2 / 100)) |
| else: |
| autonomia_h = 0 |
| nivel_lux_rota = 0 |
| nivel_lux_acesso = 0 |
| pontos_min = 0 |
| |
| return { |
| "obrigatorio": obrigatorio, |
| "autonomia_horas": autonomia_h, |
| "nivel_iluminamento_rota_lux": nivel_lux_rota, |
| "nivel_iluminamento_acesso_lux": nivel_lux_acesso, |
| "pontos_minimos_estimados": pontos_min, |
| "referencia": "NT-01/2025 Art. 20 | ABNT NBR 10898" |
| } |
| |
| def calcular_chuveiros_automaticos(self, area_m2: float, |
| altura_m: float, |
| ocupacao: str = "comercial") -> Dict: |
| """Avalia necessidade de chuveiros automaticos conforme NT-01/2025.""" |
| usos_obrigatorios = ["hotel", "hospital", "shopping", "industrial_alto", |
| "deposito_alto", "reuniao_grande"] |
| |
| obrigatorio = ( |
| altura_m > 30 or |
| (area_m2 > 2000 and "industrial" in ocupacao.lower()) or |
| any(u in ocupacao.lower() for u in usos_obrigatorios) |
| ) |
| |
| if obrigatorio: |
| densidade_mm_min = 6 if "industrial" in ocupacao.lower() else 5 |
| area_atuacao_m2 = 72 if altura_m > 30 else 144 |
| else: |
| densidade_mm_min = 0 |
| area_atuacao_m2 = 0 |
| |
| return { |
| "obrigatorio": obrigatorio, |
| "densidade_minima_mm_min": densidade_mm_min, |
| "area_atuacao_m2": area_atuacao_m2, |
| "tipo_chuveiro": "Resposta rapida" if obrigatorio else "N/A", |
| "referencia": "NT-01/2025 | ABNT NBR 10897" |
| } |
| |
| def calcular_tudo(self, dados: Dict) -> Dict: |
| """Executa todos os calculos para um projeto.""" |
| area = dados.get("area_m2", 0) |
| altura = dados.get("altura_m", 0) |
| grupo = dados.get("grupo", "D") |
| ocupacao = dados.get("ocupacao", "comercial") |
| uso = dados.get("uso", ocupacao) |
| |
| |
| lotacao_calc = self.calcular_lotacao(area, uso) |
| lotacao = dados.get("lotacao", lotacao_calc["lotacao_maxima"]) |
| |
| return { |
| "extintores": self.calcular_extintores(area, grupo), |
| "hidrantes": self.calcular_hidrantes(area, altura, ocupacao), |
| "lotacao": lotacao_calc, |
| "spda": self.calcular_spda(area, altura, ocupacao), |
| "iluminacao_emergencia": self.calcular_iluminacao_emergencia( |
| area, altura, lotacao), |
| "chuveiros_automaticos": self.calcular_chuveiros_automaticos( |
| area, altura, ocupacao), |
| } |
| |
| def formatar_relatorio(self, resultados: Dict, dados_projeto: Dict) -> str: |
| """Gera relatorio formatado dos calculos.""" |
| area = dados_projeto.get("area_m2", 0) |
| altura = dados_projeto.get("altura_m", 0) |
| grupo = dados_projeto.get("grupo", "D") |
| nome = dados_projeto.get("nome", "Edificacao") |
| |
| ext = resultados["extintores"] |
| hid = resultados["hidrantes"] |
| lot = resultados["lotacao"] |
| spd = resultados["spda"] |
| ilu = resultados["iluminacao_emergencia"] |
| chu = resultados["chuveiros_automaticos"] |
| |
| linhas = [ |
| "=" * 60, |
| "RELATORIO DE CALCULOS - PSCIP", |
| f"Edificacao: {nome}", |
| f"Area: {area:.1f} m2 | Altura: {altura:.1f} m | Grupo NT-01: {grupo}", |
| f"Ref: NT-01/2025 CBMGO", |
| "=" * 60, |
| "", |
| "1. EXTINTORES PORTATEIS", |
| f" Quantidade minima: {ext['quantidade_minima']} unidades", |
| f" Capacidade extintora: {ext['capacidade_extintora']}", |
| f" Dist. maxima ao extintor: {ext['distancia_maxima_m']} m", |
| f" Tipos: {', '.join(ext['tipos_recomendados'])}", |
| f" Base: {ext['referencia']}", |
| "", |
| "2. SISTEMA DE HIDRANTES", |
| f" Sistema: {hid['sistema']}", |
| f" Obrigatorio: {'SIM' if hid['obrigatorio'] else 'NAO'}", |
| f" Vazao minima: {hid['vazao_minima_lpm']} L/min", |
| f" Pressao minima: {hid['pressao_minima_mca']} mca", |
| f" Reserva tecnica: {hid['reserva_tecnica_m3']} m3", |
| f" Base: {hid['referencia']}", |
| "", |
| "3. LOTACAO E SAIDAS DE EMERGENCIA", |
| f" Lotacao maxima estimada: {lot['lotacao_maxima']} pessoas", |
| f" Indice: {lot['indice_m2_por_pessoa']} m2/pessoa", |
| f" Saidas de emergencia (minimo): {lot['saidas_emergencia_minimo']}", |
| f" Largura minima das saidas: {lot['largura_minima_saidas_m']} m", |
| f" Base: {lot['referencia']}", |
| "", |
| "4. SPDA", |
| f" Obrigatorio: {'SIM' if spd['obrigatorio'] else 'NAO'}", |
| f" Nivel de protecao: {spd['nivel_protecao_nbr']} (NBR 5419)", |
| f" Justificativa: {spd['justificativa']}", |
| "", |
| "5. ILUMINACAO DE EMERGENCIA", |
| f" Obrigatoria: {'SIM' if ilu['obrigatorio'] else 'NAO'}", |
| f" Autonomia: {ilu['autonomia_horas']} hora(s)", |
| f" Iluminamento rota de fuga: >= {ilu['nivel_iluminamento_rota_lux']} lux", |
| f" Pontos estimados: {ilu['pontos_minimos_estimados']}", |
| f" Base: {ilu['referencia']}", |
| "", |
| "6. CHUVEIROS AUTOMATICOS", |
| f" Obrigatorio: {'SIM' if chu['obrigatorio'] else 'NAO'}", |
| ] |
| |
| if chu["obrigatorio"]: |
| linhas.append(f" Densidade minima: {chu['densidade_minima_mm_min']} mm/min") |
| linhas.append(f" Area de atuacao: {chu['area_atuacao_m2']} m2") |
| linhas.append(f" Base: {chu['referencia']}") |
| |
| linhas.append("") |
| linhas.append("=" * 60) |
| |
| return "\n".join(linhas) |
|
|
|
|
| |
| _calc_instance = None |
|
|
| def get_calculadora() -> CalculadoraIncendio: |
| global _calc_instance |
| if _calc_instance is None: |
| _calc_instance = CalculadoraIncendio() |
| return _calc_instance |
|
|
|
|
| if __name__ == "__main__": |
| calc = CalculadoraIncendio() |
| dados = { |
| "nome": "Edificio Comercial Exemplo", |
| "area_m2": 1200, |
| "altura_m": 15, |
| "grupo": "D", |
| "ocupacao": "comercial", |
| "uso": "comercio_varejo" |
| } |
| resultados = calc.calcular_tudo(dados) |
| print(calc.formatar_relatorio(resultados, dados)) |
|
|