Pipeline de automação · DICE + Gaussian

Workflow de simulação
Monte Carlo + DFT
em solvatação explícita

Documentação técnica: da geometria inicial à análise comparativa de espectros. Inclui monitoramento de jobs, controle de gargalo no cluster, limpeza de scratch, verificação automática de frequências imaginárias e geração semi-automatizada de tabelas de atribuição vibracional. Flexível em métodos, bases, solvatação e fatores de escala.

DICE · Monte Carlo Gaussian · DFT Python · cclib SLURM / PBS UEL · Londrina, PR
01 · Visão geral

Do arquivo de coordenadas
ao espectro comparativo

O workflow encadeia três domínios — mecânica quântica (Gaussian), mecânica estatística (DICE) e análise espectral (Python). A automação conecta essas camadas sem eliminar as decisões científicas.

Manual hoje
Automatizável
Semi-automatizado
Permanece científico
01Manual
Geometria inicial — otimização PM3
O .gjf inicial é submetido para otimização de baixo custo (PM3 ou HF/STO-3G). A geometria convergida é exportada como coords_optimized.xyz — porta de saída para todos os estágios seguintes.
02Auto
Geração de inputs em batch
A partir de coords_optimized.xyz e templates Jinja2, o gerador cria os .gjf para cada combinação método × base × configuração (in vacuo, PCM, SP). Scripts SLURM/PBS gerados junto. Trocar método é editar o config.yaml.
03Auto
Cálculos Gaussian → verificação automática
Jobs submetidos com encadeamento afterok. Após cada opt+freq, o freq_checker verifica frequências imaginárias e dispara rerun com calcfc se necessário. SP e solvente explícito são excluídos da verificação.
04Auto
Extração CHELPG → preparação do DICE
Cargas CHELPG extraídas dos .log e convertidas para parâmetros Lennard-Jones (.dfr + .pot). Caixa de solvatação com condições periódicas e dice.inp montados automaticamente.
05Auto
Simulação Monte Carlo (DICE)
DICE roda como subprocess. Termalização e amostragem configuráveis. Se o processo quebrar, o scratch_cleaner é acionado antes de qualquer resubmissão — o cluster fica limpo para o próximo job.
06Auto
Seleção da concha via RDF / MDDF
Output do DICE processado: RDF ou MDDF calculada, raio de corte configurável (padrão 2,5 Å). Moléculas dentro do corte compõem a concha exportada como shell_N.xyz.
07Auto
Single points com solvente explícito
A concha alimenta .gjf de SP para cada método, gerados pelos mesmos templates. Frequências imaginárias aqui são esperadas e não disparam reruns.
08Auto
Extração, scaling e tabelas
Parser lê todos os .log via cclib: frequências, Raman/IR, SCF, ZPVE, entropia, dipolo, HOMO/LUMO, gap óptico (TD-DFT). Scaling por método aplicado automaticamente. Tabelas .xlsx e .csv para os três ambientes.
09Auto
Espectros comparativos
Broadening Lorentziano/Gaussiano configurável. Sobreposição dos três ambientes por método, com barras de modos vibracionais. PNG 300 dpi e SVG vetorial — sem Origin.
09bSemi-auto
Tabela de atribuição vibracional
vibdisps dos mesmos .log do Passo 08 via cclib — nenhum job novo no Gaussian. Para cada modo relevante (atividade Raman > 5% do máximo), identifica os dois átomos com maior deslocamento e classifica a vibração como ν (estiramento) ou δ (deformação) pela geometria do vetor de deslocamento. Cruza com groups_<mol>.yaml — um arquivo de mapeamento átomo→grupo funcional definido uma vez por molécula. Entrega attribution_<mol>_<method>.csv com proposta de atribuição e grau de confiança por modo. O pesquisador valida linha a linha — a proposta não substitui o julgamento científico.
10Científico
Validação e análise das atribuições
O pesquisador parte da tabela do Passo 09b — não de uma tela em branco no GaussView. Confirma atribuições mistas, compara com a literatura para a molécula, decide quais modos discutir no texto e interpreta os shifts em termos de ligações de hidrogênio e efeitos de solvatação. A tabela validada alimenta diretamente o material suplementar.
11Científico
Escrita e submissão do manuscrito
Com figuras prontas, tabelas formatadas e propriedades organizadas, o tempo do pesquisador vai inteiramente para discussão, conclusões e submissão.

02 · Organização

Estrutura de pastas
espelha o workflow

Cada pasta corresponde a um estágio. A numeração força a ordem de execução. O código reutilizável fica separado dos dados — ao aplicar o pipeline para uma nova molécula, copia-se a estrutura de dados, não o código.

filesystem — h2o_pipeline/
gerado pipeline dado cluster saída análise
h2o_pipeline/ — raiz: uma pasta por molécula estudada
├──
01_geometry/ otimização inicial de geometria
  ├──
input/
  │  └──
h2o.gjf gerado PM3 opt
  ├──
output/
  │  ├──
h2o_pm3.log dado
  │  └──
h2o_pm3.chk dado
  ├──
coords_optimized.xyz saída ⟶ porta de saída para estágios seguintes
  └──
job_pm3.sh gerado
├──
02_gaussian_batch/ inputs em batch por método × configuração
  ├──
HF/6-311++G(d,p)/ subpasta por método (flexível via config.yaml)
  │  ├──
in_vacuo.gjf gerado
  │  ├──
pcm.gjf gerado
  │  ├──
sp.gjf gerado
  │  └──
job.sh gerado
  ├──
B3LYP/6-311++G(d,p)/ mesma estrutura
  ├──
CAM-B3LYP/aug-cc-pVDZ/ mesma estrutura
  ├──
M06-2X/6-311++G(d,p)/ mesma estrutura
  ├──
logs/ *.log organizados após conclusão dos jobs
  └──
chelpg/ saída ⟶ cargas CHELPG para o DICE
├──
03_dice/ simulação Monte Carlo
  ├──
fortran_build/ binário separado dos dados
  │  ├──
Makefile
  │  └──
dice binário compilado
  ├──
setup/
  │  ├──
h2o.dfr gerado parâmetros do soluto
  │  ├──
h2o.pot gerado Lennard-Jones a partir de CHELPG
  │  └──
box.inp gerado caixa com condições periódicas
  ├──
run/
  │  ├──
dice.inp gerado
  │  └──
run_dice.sh gerado
  ├──
output_raw/
  │  ├──
dice.out dado
  │  ├──
cfgs/ configurações amostradas pelo MC
  │  └──
rdf.dat dado
  ├──
analysis/
  │  ├──
rdf_plot.py
  │  └──
mddf.py
  └──
shell_selected/
       ├──
last_config.xyz última geometria MC
       └──
shell_N.xyz saída ⟶ porta de saída para estágio 04
├──
04_explicit_solvent/ SP com concha de solvatação explícita
  ├──
HF/
  │  ├──
sp_explicit.gjf gerado montado a partir de shell_N.xyz
  │  ├──
sp_explicit.log dado
  │  └──
job.sh gerado
  ├──
B3LYP/ mesma estrutura
  ├──
CAM-B3LYP/ mesma estrutura
  └──
M06-2X/ mesma estrutura
├──
05_analysis/ parser, espectros, tabelas, propriedades
  ├──
parser/
  │  ├──
parser.py extração via cclib
  │  ├──
extractor.py ZPVE · entropia · dipolo · HOMO/LUMO
  │  └──
scaling.py aplica scaling factors por método
  ├──
spectra/
  │  └──
visualizer.py broadening · sobreposição · PNG/SVG
  ├──
tables/
  │  ├──
freq_scaled.xlsx saída
  │  └──
modes_comparison.csv saída
  ├──
properties/
  │  └──
properties_summary.csv saída ZPVE · ΔH · S · μ · HOMO-LUMO · Eg
  ├──
td_dft/
  │  ├──
uv_vis_spectrum.py UV-Vis via TD-DFT
  │  └──
optical_gap.csv saída
  ├──
attributions/ módulo 09b — tabelas de atribuição vibracional
  │  ├──
attribution_<mol>_b3lyp.csv saída
  │  ├──
attribution_<mol>_camb3lyp.csv saída
  │  └──
attribution_<mol>_m062x.csv saída
  ├──
figures/ PNG 300 dpi + SVG por método
  └──
report/
       ├──
summary.md
       └──
validation_notes.md
├──
pipeline/ código reutilizável — não misturar com dados
  ├──
generators/
  │  ├──
gen_inputs.py gera .gjf em batch
  │  └──
templates/ Jinja2: opt.gjf.j2 · freq.gjf.j2 · sp.gjf.j2
  ├──
parsers/
  │  ├──
log_parser.py frequências · Raman · IR · energias
  │  └──
dice_parser.py RDF · MDDF · seleção de shell
  ├──
analysis/
  │  ├──
spectra.py broadening · sobreposição · PNG/SVG
  │  └──
attribution.py módulo 09b — vibdisps → proposta de atribuição
  ├──
config/
  │  └──
groups/ mapeamento átomo→grupo por molécula
  │       ├──
epinefrina.yaml validação — atribuições já publicadas
  │       └──
ldopa.yaml catecol reutilizável de EP/NP
  ├──
utils/
  │  ├──
freq_checker.py verifica freqs imaginárias + rerun
  │  ├──
job_checker.py Normal termination · SCF failure
  │  └──
scratch_cleaner.py limpeza em caso de falha de processo
  └──
orchestrator/
       └──
run_pipeline.py ponto de entrada único — lê config.yaml
├──
config.yaml TODOS os parâmetros: métodos · bases · escala · scheduler · scratch
├──
README.md
└──
requirements.txt
Princípio de design
Cada estágio tem uma "porta de saída" bem definida: coords_optimized.xyz (01→02), chelpg/ (02→03), shell_N.xyz (03→04). Nenhum estágio lê diretamente de dentro de outro — a automação entre estágios é trivial e o debugging é localizado.

03 · Parâmetros configuráveis

Flexível em métodos,
bases e escala

Toda parametrização do pipeline reside em config.yaml. Trocar de método, adicionar uma função de base, ajustar o fator de escala ou mudar o modelo de solvatação é editar esse arquivo — nenhum código precisa ser tocado.

Métodos DFT / HF
Lista configurável de métodos
O campo methods aceita qualquer funcional reconhecido pelo Gaussian. Adicionar wB97X-D ou MP2 é incluir uma entrada na lista.
MétodoUso típico
HFReferência histórica, scaling 0.909
B3LYPVibracional geral
CAM-B3LYPUV-Vis / transferência de carga
M06-2XInterações não-covalentes
Funções de base
Mapeamento método → base
Cada método tem sua própria base. O mapeamento gera automaticamente a subpasta e o cabeçalho do .gjf.
MétodoBase padrão
HF6-311++G(d,p)
B3LYP6-311++G(d,p)
CAM-B3LYPaug-cc-pVDZ
M06-2X6-311++G(d,p)
Fatores de escala
Scaling por método — editável
Nenhum valor está hardcoded. Atualizar com referências NIST ou Merrick et al. é editar o config.
MétodoFatorFonte
HF0.909Merrick 2007
B3LYP0.967Merrick 2007
CAM-B3LYP0.983NIST
M06-2X0.983NIST
Solvatação
Modelo de solvente configurável
Trocar de água para etanol ou DMSO exige mudar duas linhas. Raio de corte MDDF também configurável.
ParâmetroPadrão
solventwater
pcm_modelPCM | SMD | CPCM
mddf_cutoff2.5 Å
n_molecules1330
config.yaml — estrutura completa
Todos os parâmetros em um só lugar
molecule: h2o
charge: 0  |  multiplicity: 1
solvent: water  |  pcm_model: PCM   # PCM | SMD | CPCM

methods:          # adicionar/remover aqui — sem tocar no código
  - name: HF         | basis: 6-311++G(d,p) | scaling: 0.909
  - name: B3LYP      | basis: 6-311++G(d,p) | scaling: 0.967
  - name: CAM-B3LYP  | basis: aug-cc-pVDZ   | scaling: 0.983
  - name: M06-2X     | basis: 6-311++G(d,p) | scaling: 0.983

freq_check:
  max_cycles: 3
  skip_stages: [sp, sp_explicit, explicit]

dice:
  n_steps_thermalization: 80000 | n_steps_sampling: 80000
  n_molecules_solvent: 1330     | mddf_cutoff: 2.5

scheduler: slurm   # slurm | pbs | local
memory: "8GB"  |  nproc: 8  |  walltime: "12:00:00"
slurm_partition: short

scratch:
  base_dir: /scratch/$USER/
  cleanup_on_fail: true
  keep_chk: false     # .chk são grandes — remover após extração
  notify_email: ""    # opcional: alerta em caso de falha

attribution:         # módulo 09b — tabela de atribuição vibracional
  enabled: true       # false desativa sem remover o módulo
  raman_threshold: 0.05  # fração do pico máximo para filtrar modos
  groups_file: pipeline/config/groups/ldopa.yaml
  # output: 05_analysis/attributions/attribution_<mol>_<method>.csv

04 · Decisões de design

A lógica por trás
de cada escolha

O pipeline não é uma sequência de scripts colados — cada decisão de implementação tem uma razão técnica ou científica.

🔧
Templates Jinja2 para .gjf
Em vez de scripts que constroem strings, os inputs são gerados por templates parametrizados. Trocar rota, keywords ou modelo de cavidade é editar o template — não o código. Um template por tipo: opt.gjf.j2, freq.gjf.j2, sp.gjf.j2.
📍
XYZ como porta de saída
A geometria é sempre exportada como .xyz puro, não como .chk. O .chk é binário e depende da versão exata do Gaussian. O .xyz é portável, versionável no git e abre em qualquer visualizador.
🧱
Fortran do DICE intocado
O núcleo Monte Carlo permanece em Fortran — necessário para ~1330 moléculas com condições periódicas. Python atua como orquestrador: prepara, invoca como subprocess, processa. Não há ganho em reescrever o núcleo.
📊
cclib como parser agnóstico
A cclib lê Gaussian, ORCA, NWChem e outros com a mesma API. Adicionar suporte a ORCA no futuro não exige reescrever os parsers. Frequências, Raman, IR, ZPVE, HOMO/LUMO e TD-DFT cobertos nativamente.
Encadeamento afterok
O freq_checker e o orquestrador são submetidos como dependências do job principal. Se detecta problema, submete o rerun — que dispara novo checker. A cadeia se propaga até max_cycles.
📁
pipeline/ separado dos dados
O código fica em pipeline/, os dados em 01_ a 05_. Para uma nova molécula, copia-se a estrutura de dados e ajusta o config.yaml — o código não é duplicado nem modificado.
MDDF vs RDF
A MDDF é preferida à RDF clássica para moléculas com múltiplos sítios de interação porque usa a menor distância entre qualquer par de átomos soluto-solvente — não a distância entre centros de massa. Essencial para moléculas com grupos funcionais em regiões distintas da estrutura.

05 · Orquestração e controle

Cluster sempre trabalhando,
nunca sobrecarregado

A estratégia garante que o computador institucional esteja sempre ocupado com o próximo job disponível, mas que nunca mais de um estágio pesado rode simultaneamente — o gargalo é controlado por dependências de jobs, não por supervisão manual.

Encadeamento por dependência (afterok)
Cada job submete o próximo como dependência condicional — o job seguinte só entra na fila quando o anterior termina com sucesso. Se o job falhar, a cadeia para automaticamente.

afterok — continua somente em sucesso
afternotok — aciona tratamento de falha
afterany — aciona limpeza de scratch sempre
📋
Estados de job monitorados
COMPLETED→ dispara próxima etapa
RUNNINGaguardando — sem ação
PENDINGna fila — sem ação
FAILED→ limpeza scratch + alerta
TIMEOUT→ limpeza scratch + alerta
CANCELLED→ limpeza scratch
🔁
Cadeia completa de submissão — exemplo com HF in vacuo
# 1. Submete o cálculo principal JID_CALC=$(sbatch --parsable job_HF_in_vacuo.sh) # 2. freq_checker roda após sucesso JID_CHECK=$(sbatch --parsable --dependency=afterok:$JID_CALC run_freq_checker.sh) # 3. Limpeza de scratch roda SEMPRE (sucesso ou falha) JID_CLEAN=$(sbatch --parsable --dependency=afterany:$JID_CALC scratch_cleaner.sh) # 4. Próxima etapa só após checker OK JID_NEXT=$(sbatch --parsable --dependency=afterok:$JID_CHECK prepare_dice.sh) # 5. Tratamento de falha sbatch --dependency=afternotok:$JID_CALC handle_failure.sh
Gargalo controlado
O cluster tem recursos compartilhados. A cadeia afterok garante que o estágio 03 (DICE, pesado em memória e I/O) só seja submetido depois que todos os jobs do estágio 02 terminaram com sucesso. O pesquisador não precisa supervisionar — o scheduler controla a ordem.

Limpeza de scratch em caso de falha

O /scratch do cluster é rápido mas finito e compartilhado. Jobs interrompidos deixam arquivos temporários grandes (.chk, Gau-*, arquivos de restart do DICE) que ocupam espaço e podem corromper reruns. O scratch_cleaner.py cuida disso automaticamente.

🔍
Detecta diretório de scratch
$SCRATCH_DIR do ambiente ou do config.yaml. Identifica o subdiretório pelo $SLURM_JOB_ID passado como argumento.
📦
Salva o que é recuperável
Antes de apagar: copia o .log parcial e o último .chk (se configurado) para o diretório de output do projeto. Podem conter geometria útil para resubmissão manual.
🗑
Remove temporários
Apaga: Gau-*, *.rwf, *.d2e, *.int, *.skr, restarts do DICE (*.rst, cfgs/ parciais). Mantém .log e .gjf originais.
📝
Registra no relatório
Grava no pipeline_events.log: timestamp, job ID, estágio, motivo da falha (FAILED / TIMEOUT / CANCELLED) e arquivos removidos.
🔔
Alerta por e-mail (opcional)
Se notify_email estiver no config.yaml, envia um resumo com nome do job, estágio e link para o log de eventos via mail.
Cluster liberado
Scratch limpo. Estado do pipeline gravado no log — retomar é ler o último evento e resubmeter a partir daquele estágio.
# scratch_cleaner.py — chamado via job SLURM ou diretamente python scratch_cleaner.py \ --job_id $SLURM_JOB_ID \ --stage 02_gaussian_batch/HF/in_vacuo \ --reason FAILED \ --save_partial_log # script de limpeza encadeado automaticamente (afterany) #!/bin/bash #SBATCH --job-name=scratch_clean #SBATCH --dependency=afterany:$JID_CALC #SBATCH --time=00:05:00 # detecta o estado do job pai e passa como argumento STATE=$(sacct -j $PARENT_JID --format=State --noheader | head -1 | xargs) python pipeline/utils/scratch_cleaner.py \ --job_id $PARENT_JID \ --config config.yaml \ --reason $STATE
Atenção — scratch vs. projeto
Nunca salvar dados permanentes no scratch. O diretório /scratch pode ser limpo pela administração sem aviso. Todos os .log, .xyz e saídas de análise devem ser escritos diretamente no diretório do projeto ou copiados pelo próprio job antes de terminar.

06 · Controle de qualidade

Verificador de frequências
imaginárias — freq_checker

Uma frequência negativa num cálculo de frequências (não SP) indica que a geometria não convergiu para um mínimo real — o sistema está num ponto de sela. O freq_checker.py automatiza a detecção e a correção.

freq_checker.py — lógica de execução
pipeline/utils/freq_checker.py
max_cycles configurável
Job opt+freq termina — cálculo de frequências concluído pelo Gaussian
Verifica Normal termination no .log — se ausente, status error imediato
Estágio SP ou explícito? → pula. Frequências imaginárias são esperadas nesses sistemas — verificar causaria reruns sem sentido.
Nenhuma frequência negativa → status OK. Cadeia continua para o próximo estágio.
Frequências negativas encontradas → extrai última Standard orientation como geometry_cycleN.xyz. Salvo para inspeção antes de qualquer resubmissão.
Gera novo .gjf com opt=(calcfc,maxcycles=100). O calcfc força cálculo analítico das constantes de força — estratégia padrão para pontos de sela.
Submete o rerun ao scheduler. Novo checker encadeado como afterok do rerun.
Limite de ciclos atingido → status max_cycles. Relatório JSON gerado, log de erro visível. Ação manual necessária.
Por que calcfc e não reotimizar direto?
Sem constantes de força analíticas, o otimizador Berny usa uma hessiana aproximada que pode ser ruim próximo a um ponto de sela — e continuar descendo pelo mesmo caminho errado. O calcfc constrói uma hessiana correta antes de qualquer passo, aumentando muito a probabilidade de convergir para o mínimo real.
# verificação de um .log específico python freq_checker.py \ --log 02_gaussian_batch/HF/in_vacuo.log \ --method HF \ --stage in_vacuo \ --max_cycles 3 # todos os métodos × estágios de uma vez python freq_checker.py --check_all --max_cycles 3 # dry run — simula sem submeter python freq_checker.py --check_all --dry_run # encadeamento — checker dispara automaticamente após o job JID=$(sbatch --parsable job_hf_in_vacuo.sh) sbatch --dependency=afterok:$JID run_freq_checker.sh
Estágios verificados vs. ignorados
O checker verifica in vacuo e PCM. Ignora automaticamente sp, sp_explicit e explicit — frequências imaginárias nesses estágios são consequência esperada da construção do sistema, não um erro de otimização.

07 · Atribuição vibracional

Módulo 09b —
proposta automática de atribuição

O Passo 09b lê os mesmos .log já produzidos no Passo 08 e entrega uma tabela de atribuição vibracional por método — sem nenhum job novo no Gaussian. O pesquisador parte de uma proposta preenchida, não de uma tela em branco no GaussView.

Por que este módulo existe
A banca de defesa identificou ausência de atribuições sistemáticas para três das seis moléculas estudadas — a crítica foi à falta de processo repetível, não à incapacidade de fazer a análise. Este módulo torna a atribuição parte do pipeline: assim como o broadening já é automático, a proposta inicial de atribuição também passa a ser gerada automaticamente.
attribution.py — lógica de execução
pipeline/analysis/attribution.py
nenhum job Gaussian adicional
vibdisps via cclib — deslocamentos atômicos [n_modos × n_átomos × 3] dos .log do Passo 08. Nenhum dado novo é requerido.
Filtra modos relevantes — mantém apenas modos com atividade Raman acima de raman_threshold × max(vibramans). Padrão: 5% do máximo.
Identifica par dominante — para cada modo, encontra os dois átomos com maior norma de deslocamento. Esses são os átomos que "lideram" a vibração.
Classifica tipo de vibração — calcula a componente do deslocamento relativo ao longo do vetor de ligação entre o par dominante. Razão > 0.6 → ν (estiramento); abaixo → δ (deformação).
Cruza com groups_<mol>.yaml — mapeia índices atômicos do par dominante para o grupo funcional correspondente (catecol, amina, anel aromático, etc.).
Atribui grau de confiançaalta quando um par único domina (>80% do deslocamento total); média quando há contribuição secundária relevante; baixa para modos muito misturados.
Gera attribution_<mol>_<method>.csv — uma linha por modo relevante com: número do modo, frequências vácuo/PCM/explícito, shifts, tipo de vibração, grupo funcional e confiança.
Formato do groups_<mol>.yaml
Definido uma vez por molécula
Mapeia índices atômicos do .xyz para grupos funcionais. O catecol de L-DOPA é reutilizável diretamente da epinefrina.
grupos:
  catecol_OH_1:      [1, 2]
  catecol_OH_2:      [3, 4]
  anel_aromatico:    [5, 6, 7, 8, 9, 10]
  amina:             [11, 12, 13]
  acido_carboxilico: [14, 15, 16]
Formato do CSV de saída
attribution_ldopa_b3lyp.csv
Cada linha é um modo relevante. Colunas: modo, freq_vac, freq_pcm, freq_exp, shift_pcm, shift_exp, átomo_1, átomo_2, tipo_vibr, grupo_funcional, confiança. Importável como tabela de material suplementar.
modofreq_vacshift_pcmtipogrupoconf.
24852.1−7.8δ(C−H)anel aromáticoalta
311302.5−13.4ν(C−O)catecolmédia
O que permanece humano
O módulo propõe — o pesquisador decide
TarefaPor quê permanece humana
Confirmar atribuições mistasRequer inspeção visual no GaussView para modos com contribuição distribuída entre vários átomos
Comparar com literaturaJulgamento científico — qual referência usar, quais discrepâncias são relevantes
Selecionar quais modos discutirDepende do argumento do artigo, não apenas da atividade espectral
Interpretar shifts em termos de HBRequer análise estrutural do cluster MC — informação fora do escopo do .log
# rodar atribuição para todos os métodos de uma molécula python pipeline/analysis/attribution.py \ --logs 05_analysis/parser/ \ --groups pipeline/config/groups/ldopa.yaml \ --output 05_analysis/attributions/ \ --threshold 0.05 # validação contra dados publicados de EP/NP (teste de qualidade) python pipeline/analysis/attribution.py \ --logs epinefrina/05_analysis/parser/ \ --groups pipeline/config/groups/epinefrina.yaml \ --validate epinefrina_atribuicoes_publicadas.csv # via orquestrador (roda automaticamente após o Passo 09) # config.yaml: attribution.enabled: true python pipeline/orchestrator/run_pipeline.py --from 09b
Estratégia de validação recomendada
Antes de aplicar a L-DOPA, rodar o módulo contra os dados já publicados de EP/NP e comparar com as atribuições feitas manualmente no artigo. Se a concordância for alta, o módulo está calibrado. O catecol de L-DOPA é estruturalmente idêntico ao de EP — o epinefrina.yaml cobre metade do mapeamento necessário.