Universidade Federal do Paraná

Programa de Pós-Graduação em Métodos Numéricos em Engenharia

Solução Numérica da Difusão de Calor 1D Transiente

Relatório do 1º Trabalho Computacional

Aluno: Romulo de Aguiar Beninca

link do github : https://rbeninca.github.io/cfd1/

link códigos : https://github.com/rbeninca/cfd1

1. Definição do Problema e Organização do Trabalho

O presente trabalho tem como objetivo desenvolver uma solução numérica para o problema clássico de difusão de calor unidimensional em regime transiente. O cenário físico consiste em uma placa plana de espessura $L$.

O trabalho foi desenvolido em JavaScript e versionado no github por isso todo o código fonte e arquivos de entrada e saída estão disponíveis para consulta e reprodução dos resultados, no link :https://rbeninca.github.io/cfd1/

Conforme instrução do professor, para a modelagem matemática, partimos da equação diferencial parcial parabólica que governa o fenômeno, assumindo propriedades constantes e ausência de geração interna de calor, conforme estudamos no capítulo 4. Os detalhes do problema, incluindo a equação governante foram dados na Figura 1.

Definição do Problema e Dados

Figura 1: Definição do problema, equação governante e parâmetros físicos.

Em suma, buscamos resolver numericamente a equação:

$$ \frac{\partial T}{\partial t} = \alpha \frac{\partial^2 T}{\partial x^2} $$

2. Dedução Numérica (Volumes Finitos)

2.1. Discretização e Método $\theta$

Para a solução numérica, adotou-se o Método dos Volumes Finitos (MVF). O domínio espacial $[0, L]$ foi particionado em $N$ volumes de controle uniformes de largura $\Delta x$. A integração da equação governante foi realizada tanto no espaço quanto no tempo.

Discretização do Domínio

Figura 2: Processo de discretização do domínio e integração da equação.

No que tange à discretização temporal, aplicou-se o Método $\theta$. Para este trabalho, optou-se especificamente pelo esquema de Crank-Nicolson ($\theta = 0.5$).

Aplicação do Método Theta

Figura 3: Desenvolvimento algébrico da equação discretizada para volumes internos.

2.2. Tratamento dos Contornos

Um ponto crucial nesta implementação é o tratamento das condições de contorno. A dedução completa para os volumes adjacentes aos contornos (esquerdo e direito) é apresentada na Figura 4.

Dedução dos Contornos

Figura 4: Dedução dos coeficientes modificados para os volumes de contorno.

3. Da Teoria à Prática: Implementação Computacional

A implementação do código foi realizada utilizando a linguagem JavaScript. Para otimizar o cálculo, utilizamos variáveis auxiliares para agrupar os termos do passo de tempo anterior ($n$) que compõem o termo fonte. A tabela abaixo relaciona a matemática deduzida com as variáveis do código JavaScript apresentado na Seção 5:

Termo / Conceito Expressão Algébrica (Dedução) Variável no Código (JS)
Termo Difusivo
(Parte Implícita)
$$ \theta \frac{\Delta t}{\Delta x} $$
termo_difusivo = theta * dt / dx
Termo de Acúmulo $$ \frac{\Delta x}{\alpha} $$
termo_acumulo = dx / alpha
Coeficiente Central $a_P$
(Contorno $i=1$)
$$ a_P = \frac{\Delta x}{\alpha} + 3 \theta \frac{\Delta t}{\Delta x} $$
aP[0] = 3.0 * termo_difusivo + termo_acumulo
Termo Fonte $b_P$
(Contribuição Central Explícita)
$$ \frac{\Delta x}{\alpha} - 3(1-\theta)\frac{\Delta t}{\Delta x} $$
difusivo_explicito = (1.0 - theta) * dt / dx
b1_contorno = termo_acumulo - 3.0 * difusivo_explicito

4. Análise dos Resultados e Simulação

Painel de Configuração da Simulação

4.1. Perfil de Temperatura ($t = t_F$)

A comparação entre a solução numérica e a analítica para o tempo final configurado.

4.2. Histórico da Temperatura Média

Evolução da temperatura média da placa em escala logarítmica (eixo Y), demonstrando o decaimento exponencial.

4.3. Tabela de Erros ($t = t_F$)

Vol Posição $x$ (m) $T_{analitica}$ (°C) $T_{numerica}$ (°C) Erro Absoluto

4.4. Tabela Temporal de Temperaturas Médias

Evolução da temperatura média ao longo do tempo ($t = 0$ até $t = t_F$).

Tempo (s) $\overline{T}_{analitica}$ (°C) $\overline{T}_{numerica}$ (°C) Erro Absoluto

5. Implementação em JavaScript

Abaixo apresenta-se o núcleo do algoritmo numérico implementado, que roda no navegador para gerar os resultados acima.

O código foi criado baseando-se no método TDMA para resolver sistemas tridiagonais, e usando com base o código em fortran disponibilizado pelo professor. Devido a natureza da web o arquivo de entrada foi adaptado e apresenado abaixo, similarmente a o arquivo utilizado em fortran como entrada.

O código javascript esta organizado da seguinte forma :

// --- SOLVER TDMA (Tri-Diagonal Matrix Algorithm) --- function() { // Variáveis Globais de Gráfico para destruição correta let chartProfile = null; let chartHistory = null; // --- FUNÇÕES AUXILIARES --- const solucaoAnalitica = (pos, tempo, alpha, L) => Math.exp(-alpha * Math.pow(Math.PI/L, 2) * tempo) * Math.sin(Math.PI*pos/L); const mediaAnalitica = (tempo, alpha, L) => (2.0/Math.PI) * Math.exp(-alpha * Math.pow(Math.PI/L, 2) * tempo); function calcMediaTrapezoidal(arrT, dx, N, Ta, Tb, L) { let soma = 0.0; soma += 0.25 * (Ta + arrT[0]) * dx; for(let i=1; i < N; i++) { soma += 0.5 * (arrT[i-1] + arrT[i]) * dx; } soma += 0.25 * (arrT[N-1] + Tb) * dx; return soma / L; } function resolverTDMA(a, b, c, d) { let n = d.length; let cp = new Float64Array(n); let dp = new Float64Array(n); let solucao = new Float64Array(n); cp[0] = c[0] / a[0]; dp[0] = d[0] / a[0]; for (let i = 1; i < n; i++) { let m = a[i] - b[i] * cp[i - 1]; cp[i] = c[i] / m; dp[i] = (d[i] + b[i] * dp[i - 1]) / m; } solucao[n - 1] = dp[n - 1]; for (let i = n - 2; i >= 0; i--) { solucao[i] = cp[i] * solucao[i + 1] + dp[i]; } return solucao; } // --- FUNÇÃO DE INTERFACE (Lê Inputs e Chama Simulação) --- // Tornar esta função globalmente acessível para o botão onClick window.executarSimulacaoPeloUI = function() { // Ler valores const N = parseInt(document.getElementById('input_N').value); const M = parseInt(document.getElementById('input_M').value); const tF = parseFloat(document.getElementById('input_tF').value); const alpha = parseFloat(document.getElementById('input_alpha').value); const L = parseFloat(document.getElementById('input_L').value); const theta = parseFloat(document.getElementById('input_theta').value); const Ta = parseFloat(document.getElementById('input_Ta').value); const Tb = parseFloat(document.getElementById('input_Tb').value); executarSimulacao(N, M, tF, alpha, L, theta, Ta, Tb); } // --- SIMULAÇÃO PRINCIPAL --- function executarSimulacao(N, M, tFinal, alpha, L, theta, Ta, Tb) { let dx = L/N; let dt = tFinal/M; // Arrays de Dados let T = new Float64Array(N); let To = new Float64Array(N); let x = new Float64Array(N); let historicoTempo = [0]; let historicoT_num = []; let historicoT_anal = []; // Dados para relatório let report_aW = new Float64Array(N); let report_aE = new Float64Array(N); let report_aP = new Float64Array(N); let report_b_primeiro_passo = new Float64Array(N); // Inicialização for(let i=0; i= 0 ? '+' : '-'; let expVal = Math.abs(exponent).toString().padStart(2, '0'); return mantissa + 'E' + sign + expVal; } function gerarArquivoEntrada(N, M, tFinal, theta, alpha, L, Ta, Tb) { let txt = `trabalho1 ...... arq: nome do aquivo de saída de dados ${N} N: numero de volumes de controle ${M} M: numero de passos de tempo ${tFinal.toFixed(1)}d0 tf: tempo final [s] ${theta}d0 theta: esquema (0.0=Expl, 0.5=CN, 1.0=Impl) ${alpha.toExponential(2).replace('e','d')} alpha: difusividade termica [m2/s] ${L}d0 L: comprimento do dominio [m] ${Ta.toFixed(1)}d0 Ta: temperatura contorno esquerdo [C] ${Tb.toFixed(1)}d0 Tb: temperatura contorno direito [C] 1 freq: frequencia de escrita dos resultados`; document.getElementById('output-arquivo-entrada').textContent = txt; } function gerarArquivoSaida(N, M, tFinal, theta, alpha, L, Ta, Tb, aW, aP, aE, b, x, T, hTempo, hNum, hAnal) { let txt = "Solução Numérica da Eq. de Difusão de Calor 1D em Regime Transiente com Propriedades Constantes\n\n"; txt += "Dados de entrada\n\n"; txt += ` ${N} = N: número de volumes de controle\n`; txt += ` ${fmtE(alpha)} = alpha: difusividade térmica [m2/s]\n`; txt += ` ${fmtE(L)} = L: comprimento do domínio [m]\n`; txt += ` ${fmtE(Ta)} = Ta: temperatura do contorno esquerdo (x=0) [°C]\n`; txt += ` ${fmtE(Tb)} = Tb: temperatura do contorno direito (x=L) [°C]\n`; txt += ` ${fmtE(theta)} = theta: formulação temporal empregada\n\n\n`; txt += "Coeficientes e termos-fontes\nvol. oeste central leste termo-fonte\n"; for(let i=0; i${i+1}${x[i].toFixed(4)}${valAnal.toFixed(6)}${valNum.toFixed(6)}${err.toExponential(4)}`; } tbody.innerHTML = html; } function renderizarGraficos(N, x, T, tFinal, alpha, L, hTempo, hNum, hAnal) { // PERFIL const ctxP = document.getElementById('profileChart').getContext('2d'); if(chartProfile) chartProfile.destroy(); let dataAnal = []; for(let i=0; i<=100; i++) { let pos = i*(L/100); dataAnal.push({x: pos, y: solucaoAnalitica(pos, tFinal, alpha, L)}); } chartProfile = new Chart(ctxP, { type: 'scatter', data: { datasets: [ { label: 'Numérica (VF)', data: Array.from(x).map((pos, i) => ({x: pos, y: T[i]})), backgroundColor: '#2563eb', pointRadius: 6 }, { label: 'Analítica', data: dataAnal, type: 'line', borderColor: '#dc2626', borderWidth: 2, pointRadius: 0, fill: false, tension: 0.4 } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'linear', title: {display:true, text: 'Posição (m)'} }, y: { title: {display:true, text: 'Temperatura (°C)'}, min: 0 } } } }); // HISTÓRICO const ctxH = document.getElementById('historyChart').getContext('2d'); if(chartHistory) chartHistory.destroy(); chartHistory = new Chart(ctxH, { type: 'line', data: { labels: hTempo, datasets: [ { label: 'Média Numérica', data: hNum, borderColor: '#2563eb', backgroundColor: '#2563eb', showLine: false, pointStyle: 'rectRot', pointRadius: 6 }, { label: 'Média Analítica', data: hAnal, borderColor: '#dc2626', borderWidth: 2, pointRadius: 0 } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { title: {display:true, text: 'Tempo (s)'} }, y: { type: 'logarithmic', title: {display:true, text: 'Temp. Média (Log)'} } } } }); } // Inicialização Automática executarSimulacaoPeloUI(); })(); // --- SIMULAÇÃO --- // Esta função é chamada ao clicar em "Atualizar Simulação" function executarSimulacao(N, M, tFinal, alpha, L, theta, Ta, Tb) { let dx = L/N; let dt = tFinal/M; // ... (Configuração de Coeficientes e Loop Temporal omitidos para brevidade) ... }

Apêndice: Dados de Execução

Os dados abaixo são gerados dinamicamente com base nos parâmetros inseridos no painel de simulação.

Arquivo de Entrada (Representação)

Arquivo de Saída Gerado (trabalho1.txt)