""" 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 de extintores por grupo (NT-01/2025 Anexo B) 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 de lotacao por ocupacao (pessoa/m2) - NT-01/2025 Tabela 2 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"])) # Minimo de 2 extintores por pavimento para areas > 200m2 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.""" # Determinar sistema necessario 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 # Ajuste para uso industrial de alto risco 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)) # Numero minimo de saidas de emergencia if lotacao <= 50: saidas = 1 elif lotacao <= 200: saidas = 2 elif lotacao <= 1000: saidas = 3 else: saidas = 4 # Largura minima das saidas 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) # Calcular lotacao se nao fornecida 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) # Instancia global _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))