Utilizar as funções nativas do MATLAB® é sempre a melhor opção?

“Everything should be made as simple as possible, but no simpler.” – Einstein

Você consegue se enxergar cenário a seguir?

Digita-se tudo corretamente, segue-se a lógica adequada, clica-se no “run” e... “ufa, o código rodou”.

Todos nós, estudantes de engenharia e engenheiros, passamos por este pequeno momento glorioso durante nossas soluções de problemas utilizando as mais diversas linguagens de programação, em especial o MATLAB™. Cá entre nós, depois de uma trabalheira danada, nada melhor que ver nosso código rodando.

Porém, logo após esta felicidade fugaz, vem outro desafio: será que a solução está correta? O gráfico, o vetor ou a matriz está condizente com o esperado? A resposta faz sentido? Em caso negativo, alteramos o método, implementamos algumas melhorias, o código roda, porém mais uma vez  a resposta continua não fazendo sentido. Temos certeza que fizemos tudo certo, então, talvez nossa inclinação seja simplesmente desistir. Afinal, se é tão difícil para o MATLAB™, nenhuma pessoa razoável pode esperar que obtivéssemos uma solução. Porém, como bons futuros engenheiros, não procuramos desculpas e nosso único recurso é desenvolver uma solução com base em nossos conhecimentos de métodos numéricos.

Antes de tudo, devemos ter em mente que nunca podemos confiar cegamente em todo resultado gerado por um computador. Existe uma velha máxima que diz: “se entrar lixo, sai lixo”. Infelizmente, algumas pessoas pensam que, independentemente do que entrou (os dados) e do que está acontecendo no interior (o algoritmo), o computador gera um resultado religiosamente correto, o que não é verdade.


Para que evitemos cair neste tipo de armadilha, vejamos um exemplo a seguir de como resolver numericamente uma EDO, mesmo que inicialmente o problema pareça não ter solução:

Enunciado. Uma vazão de água entra em um taque cilíndrico a uma taxa constante Qentrada, e enche o tanque até atingir o nível yalto. Neste ponto, a água escoa para fora do tanque através de um tubo de descarga circular (um sifão), produzindo uma fonte na saída do tubo. A fonte funciona até que o nível de água diminua para ybaixo; depois disso o sifão enche-se com ar e a fonte para. O ciclo é então repetido à medida que o tanque enche-se de água até atingir yalto e a fonte jorra de novo.

Quando o sifão está funcionando, o fluxo para fora Qsaída pode ser calculado com a fórmula baseada na Lei de Torricelli: Qsaída  = C*√(2*g*y)**r^2  (1)

FIGURA 1 - Tanque cilíndrico com sifão


Desprezando o volume de água no tubo, calcule e trace o nível de água no tanque como uma função do tempo durante 100s. Considere uma condição inicial de um tanque vazio y(0) = 0 e empregue os parâmetros seguintes para seus cálculos:

Rt = 0,05m; yalto = 0,1m; Qentrada = 50x10^-6 m^3/s; r = 0,007 m; C = 0,6; ybaixo = 0,025m; g = 9,81 m/s^2

Solução:
Quando a fonte está funcionando, a taxa de variação no volume V (m^3) do tanque é determinada por um balanço simples do fluxo que entra menos o fluxo que sai do tanque:

dV/dt = Qentrada - Qsaída   (2)

Como o tanque é cilíndrico, V = ℼ*Rt^2*y. Substituindo essa relação em (1) e em (2), obtemos:

 dy/dt =  [Qentrada -C*√(2*g*y)**r^2]/ℼ*Rt^2*y

Quando a fonte não está funcionando, o segundo termo no numerador vai à zero. Podemos incorporar esse mecanismo no modelo introduzindo uma nova variável adimensional nomeada sifão, que é igual a zero, quando a fonte está parada, e igual a um, quando a fonte está funcionando:


 dy/dt =  [Qentrada - sifao* C*√(2*g*y)**r^2]/ℼ*Rt^2*y

Neste contexto, a variável sifão pode ser pensada como um interruptor que “liga” e “desliga” a fonte. Essas variáveis de dois estados são chamadas de variáveis lógicas ou booleanas, nas quais zero é equivalente a falso, e um é equivalente a verdadeiro.

Em seguida, devemos relacionar sifão à variável dependente y. A princípio, a variável sifão é definida como zero sempre que o nível cair abaixo de ybaixo e como um sempre que o nível subir acima de yalto.

A função a seguir do MATLAB™ segue essa lógica no cálculo da derivada:

function dy = ex_edo(t,y)
global sifao
Rt = 0.05; r = 0.007; yalto = 0.1; ybaixo = 0.025; C = 0.6; g = 9.81; Qentrada = 0.00005;
if y(1) <= ybaixo
    sifao = 0;
elseif y(1) >= yalto
    sifao = 1;
end
Qsaida = sifao*C*sqrt(2*g*y(1))*pi*r^2;
dy = (Qentrada-Qsaida)/(pi*Rt^2);

Observe que, como seu valor deve ser mantido entre chamadas da função, a variável sifao é declarada como uma variável global. Embora o uso de variáveis globais não seja indicado (particularmente em programas extensos), esse uso é útil no contexto presente.

O programa a seguir emprega a função nativa ode45* para integrar ex_edo e gerar um gráfico da solução:

clc; close all; clear all;
global sifao
sifao = 0;
tspan = [0 100]; y0 = 0;
[tp,yp] = ode45(@ex_edo,tspan,y0);
plot(tp,yp);
xlabel('Tempo (s)')
ylabel('Nivel de agua no tanque (m)')

* ode45 – A função ode45 usa um algoritmo que usa simultaneamente fórmulas de Runge-Kutta de quarta e quinta ordens para resolver a EDO e fazer estimativas do erro para ajuste do tamanho do passo. O MATLAB™ recomenda que a ode45 é a melhor função para aplicar como uma “primeira tentativa” para a maioria dos problemas.

Como mostra a Figura 2, o resultado está claramente incorreto. Exceto para o processo de enchimento original, o nível parece começar a decrescer antes de atingir yalto. De modo similar, quando o sifão está jorrando água, ele é fechado bem antes do nível cair para ybaixo.

FIGURA 2 - Plot do código com função nativa do MATLAB

Neste ponto, suspeitando que o problema demande uma função computacional mais poderosa que a rotina confiável ode45, você pode ser tentado a usar algum dos outros solucionadores de EDOs do MATLAB, tais como ode23s ou ode23tb. Porém se você fizer isso, descobrirá, que embora rotinas produzam resultados um pouco diferentes, elas ainda geram solução incorretas.

A dificuldade surge porque a EDO é descontinua no ponto em que o sifão é desligado ou ligado. Por exemplo, quando o tanque está enchendo, a derivada depende apenas do fluxo que entra no tanque o e, para os parâmetros presentes, tem um valor constante de 6,366*10^-3 m/s. Entretanto, assim que o nível atinge yalto, o fluxo que sai entra em ação e a derivada cai de forma abrupta para -1,013*10^-2 m/s. Embora rotinas com controle adaptativo do tamanho do passo usadas pelo MATLAB™ funcionem de modo eficaz para vários problemas, elas muitas vezes têm problemas quando lidam com essas descontinuidades.

Como o problema resulta da aplicação de um tamanho de passo adaptativo por meio de uma descontinuidade, pode-se reverter o método utilizando um tamanho de passo pequeno e constante (que é uma abordagem mais simples). Podemos implementar essa estratégia de solução substituindo ode45 por uma outra função, de passo constante, criada manualmente. Vamos nomeá-la de rk4sys.

function [tp,yp] = rk4sys(dydt,tspan,y0,h,varargin)
% rk4sys: Runge-Kutta de quarta ordem para um Sistema de EDOs
% [t,y] = rk4sys(dydt,tspan,y0,h,p1,p2,...): integra um sistema de EDOs com o método RK de quarta ordem
% entrada:
% dydt = nome do arquivo-M que avalia as EDOs
% tspan = [ti,tf]; instantes inicial e final com a saída geradas em intervalos de h, ou = [t0 t1 ...tf]; em instantes específicos
% h = tamanho do passo
% p1,p2,... = parâmetros adicionais usados por dydt
% saída:
% t = vetor da variável independente
% y = vetor de solução para a variável dependente
if nargin<4, error('são necessários pelo menos 4 argumentos de entrada')
end
if any(diff(tspan)<=0), error('tspan não está em ordem crescente'),
end
n = length(tspan);
ti = tspan(1); tf = tspan(n);
if n == 2
    t = (ti:h:tf)'; n = length(t);
    if t(n)<tf
        t(n+1) = tf;
        n = n+1;
    end
else
    t = tspan;
end
tt = ti; y(1,:) = y0;
np = 1; tp(np) = tt; yp(np,:) = y(1,:);
i = 1;
while(1)
    tend = t(np+1);
    hh = t(np+1) - t(np);
    if hh>h,hh = h;
    end
    while(1)
        if tt+hh>tend,hh = tend-tt;
        end
        k1 = dydt(tt,y(i,:),varargin{:})';
        ymid = y(i,:) + k1.*hh./2;
        k2 = dydt(tt+hh/2,ymid,varargin{:})';
        ymid = y(i,:) + k2.*hh./2;
        k3 = dydt(tt+hh/2,ymid,varargin{:})';
        yend = y(i,:) + k3.*hh;
        k4 = dydt(tt+hh,yend,varargin{:})';
        phi = (k1+2*(k2+k3)+k4)/6;
        y(i+1,:) = y(i,:) + phi*hh;
        tt = tt+hh;
        i=i+1;
        if tt>=tend,break,end
    end
    np = np+1; tp(np) = tt; yp(np,:) = y(i,:);
    if tt>=tf,break,end
end

Para o programa rodar só é necessário substituir a quarta linha do código apresentado anteriormente por: [tp,yp] = rk4sys(@ex_edo,tspan,y0,0.0625);

clc; close all; clear all;
global sifao
sifao = 0;
tspan = [0 100]; y0 = 0;
[tp,yp] = rk4sys(@ex_edo,tspan,y0,0.0625);
plot(tp,yp);
xlabel('Tempo (s)')
ylabel('Nivel de agua no tanque (m)')


O resultado obtido será:

FIGURA 3 - Plot do código com função criada rk4sys

Agora sim a solução evolui como esperado. O tanque enche até yalto e depois esvazia até atingir ybaixo,quando o ciclo se repete.

Situações como aquela descrita na Figura 1 são especialmente perigosas – isto é, embora a saída seja incorreta, ela não é obviamente incorreta; ou seja, a simulação não se torna instável ou produz níveis negativos. Na verdade, a solução se move para cima e para baixo na forma de uma fonte intermitente, embora incorreta.

Esperamos que este estudo de caso ilustre que mesmo uma grande parte de pacotes computacionais como o MATLAB™ não é infalível e nem sempre trazem funções nativas que necessitamos. Portanto, estudantes de engenharia e engenheiros sempre examinam saídas numéricas com um ceticismo saudável com base em suas experiências e conhecimentos dos problemas que estão resolvendo.
_________________________________________________________________________________

Este problema foi baseado no estudo de caso contido em:

CHAPRA, Steven C. Métodos numéricos aplicados com MATLAB para engenheiros e cientistas; tradução Rafael Silva Alípio. - 3. ed. - Porto Alegre: AMGH, 2013. Pgs 609-612.

O livro a seguir foi de grande ajuda também:

GILAT, Amos. MATLAB com aplicações em engenharia; tradução Rafael Silva Alípio. 4. ed. - Porto Alegre: Bookman, 2012.

Para ter acesso aos códigos, clique aqui.

Caso tenha ficado alguma dúvida ou tenha encontrado algum erro, sinta-se a vontade para enviar um e-mail para nós colaboradores do blog.

Filipe Natã




Comentários

Postagens mais visitadas deste blog

Diferenciação Numérica

Resolução numérica de EDOs de 2ª ordem - Método do tiro

Otimização Numérica