Risoluzione di equazioni differenziali -...
Transcript of Risoluzione di equazioni differenziali -...
Laboratorio di trattamento numerico deidati sperimentali
Maurizio Tomasi ﴾turno A2﴿
Giovedì 21 Dicembre 2017
Risoluzione di equazioni differenziali
Esercizi 10.0 e 10.1Nell'esercizio 10.0 si deve risolvere col metodo di Eulerol'equazione
mx (t) = −kx(t),
riscritto come
x (t) = −ω x(t),
che rappresenta il moto di un oscillatore armonico.
L'esercizio successivo ﴾10.1﴿ è identico, ma chiede di usare ilmetodo di Runge‐Kutta.
′′
′′02
Sistemi di ordine n > 1Il problema
mx (t) = −ω x(t),
ha come incognita la funzione x : R→ R, e coinvolge unaderivata seconda: x (t). Può essere però riscritto come un sistemadi equazioni contenenti solo derivate prime:
′′02
′′
{x (t) = v(t),′
v (t) = −ω x(t).′02
Sistemi di ordine n > 1A questo punto, la scrittura
può essere condensata nella forma
y = f(t,y),
dove ora y : R→R :
y = , f = .
{x (t) = v(t),′
v (t) = −ω x(t).′02
′
2
(x(t)v(t)
) (x(t)v(t)
) ( v(t)−ω x(t)0
2 )
Approssimazione numerica dellasoluzioneL'equazione
y = f(t,y),
rappresenta una equazione differenziale ﴾e non un sistema﴿ diprimo grado ﴾anziché di secondo﴿, ma abbiamo visto checorrisponde in realtà ad una equazione di secondo grado o a unsistema di equazioni di primo grado.
Vediamo ora come approssimare la soluzione
′
Metodo di Eulero ﴾esercizio 10.0﴿Data l'equazione differenziale e una condizione iniziale
il metodo di Eulero approssima la soluzione all'istante t+ δt
partendo dalla soluzione all'istante t = t :
y(t + δt) ≈ y(t ) + δty (t ) = y(t ) + δt f (t ,y).
{y = f(t,y),′
y(0) =y ,0
0
0 0′0 0
′0
Svolgimento esplicito dei calcoliPartiamo dall'istante t = 0, per cui abbiamo la condizione iniziale
y(0) =y = ,
con v = 1m/s. Il metodo di Eulero fornisce quindil'approssimazione
0 ( 0v0
)
0
y(0 + δt) =y + δt ⋅ f(0,y) =0
= + δt =( 0v0
) ( v0−ω ⋅ 00
2 )
= .(δt ⋅ v0v0
)
Confronto con la soluzione analiticaVediamo come si confronta questo con la soluzione nota
x(t) = sinω t.
Se supponiamo δt piccolo, affinché ω ⋅ δt≪ 1, allora
x(0 + δt) = sin ω ⋅ δt ≈ v δt,
che è esattamente la soluzione del metodo di Eulero. Per quantoriguarda la velocità, la soluzione analitica è
(δt) = v cosω δt ≈ v ,
ancora corrispondente alla soluzione di Eulero.
ω0
v00
0
ω0
v0 ( 0 ) 0
x 0 0 0
Calcolo esplicitoUsando come parametri
v = 1m/s, ω = 1Hz, δt = 10 s,
abbiamo che
y(0) = , y(0 + δt) = .
0 0−2
( 01m/s
) (10 m−2
1m/s)
Verifica dei calcoli nel codicePotete inserire una verifica esplicita della correttezza dei vostricalcoli nel codice, scrivendo un test:
void assert_close(double x, double y) { assert(std::fabs(x ‐ y) < 1e‐7);}
int main() { double deltat = 1e‐2; // ... for (int step = 0; step < N; ++step) { if (step == 0) { assert_close(solution[0], 0.0); assert_close(solution[1], 1.0); } else if (step == 1) { assert_close(solution[0], 1e‐2); assert_close(solution[1], 1.0); } // Update "solution" using Euler's method }
Metodo di Runge‐Kutta ﴾RK﴿È una generalizzazione del metodo di Eulero, che può esserepotenzialmente complicata da digerire! Se per Eulero l'incrementoda y(t ) a y(t + δt) era δt ⋅ f t ,y(t ) , in questo caso è
dove i quattro vettori k sono definiti come
0 0 ( 0 0 )
δt ⋅ ,6
k + 2k + 2k +k1 2 3 4
j
k1
k2
k3
k4
= f t ,y(t ) ,( 0 0 )
= f t + ,y(t ) + k ,( 0 2δt
0 2δt
1)
= f t + ,y(t ) + k ,( 0 2δt
0 2δt
2)
= f t + δt,y(t ) + δtk .( 0 0 3)
Metodo RK e metodo di EuleroNotate che se si cambia la definizione dei k , ponendo k =k =ktutti uguali a k , si ottiene esattamente la soluzione di Eulero.
j 2 3 4
1
Applicazione di RK all'esercizio 10.1La dipendenza di f dal tempo t che si vede nelle formule della slideprecedente non è utile in questo esercizio, perché la forza in giocoè F (t) = −ω x(t), che non dipende dal tempo in modo esplicito.
La dipendenza della forza F (t) dal tempo che è causata dallapresenza del termine x(t) non conta: nel calcolo di
k = f t + ,y(t ) + k
il valore di x(t) va calcolato in t = t , non in t = t + δt/2,perché fa riferimento il termine y(t ).
Il termine temporale sarà fondamentale nell'esercizio 10.4, dove sidovrà risolvere l'equazione di un oscillatore forzato.
02
2 ( 0 2δt
0 2δt
1)
0 0
0
Sviluppo dei calcoli per il metodo RKMostriamo con un conto esplicito come calcolare il primo stepdell'integrazione RK per il problema dell'oscillatore ﴾esercizio 10.1﴿.
Nel nostro caso la funzione f ha la proprietà di essere lineare,quindi rappresentabile tramite una matrice A:
A = .
Vale infatti che
f(y) = A ⋅ y = = .
L'effetto di A è analogo a una derivata: trasforma una coppiaposizione‐velocità in una coppia velocità‐accelerazione.
( 0−ω0
210
)
( 0−ω0
210
) (x(t)v(t)
) ( v(t)−ω x(t)0
2 )
Calcoli analitici con PythonPossiamo usare Python per svolgere i calcoli simbolici del metodoRK usando il pacchetto sympy . Se avete installato Miniconda,installatelo con conda install sympy ed avviate jupyter‐qtconsole . Digitate questi comandi:
from sympy import *init_printing()
# Define a few "symbols": variables which do not have a# definite numerical value. Strings are LaTeX representationdeltat = Symbol(r'\delta t', real=True)omega0 = Symbol(r'\omega_0', real=True)v0 = Symbol('v_0', real=True)
# Initial conditiony0 = Matrix([[0], [v0]])# Matrix representing the «f» operatorA = Matrix([[0, 1], [‐omega0**2, 0]])
Sviluppo dei calcoli per il metodo RKConsideriamo quindi un passo h e calcoliamo il valore di yall'istante t = h partendo da y ≡ y(t = 0).
Servono innanzitutto le quattro quantità k , k , k , k ; partiamodalla prima:
0
1 2 3 4
k1 = f t ,y(t ) =( 0 0 )= A ⋅y .0
Calcolo di k
Usiamo sympy per fare questo calcolo: digitate nella finestra«Jupyter QtConsole»
k1 = A @ y0k1 # Print the result
﴾in Python il prodotto tra matrici si effettua col simbolo @ ﴿.
Python risponderà scrivendo una formula LaTeX:
,
che corrisponde a k . Se volete il sorgente LaTeX della formula,eseguite
print(latex(k1))
1
(v00
)
1
Calcolo di k
Usiamo ancora sympy :
k2 = A @ (y0 + deltat * k1 / 2)k2
2
k2 = f t + ,y(t ) + k =( 0 2δt
0 2δt
1)
= A ⋅ y + k .( 0 2δt
1)
(v0
− ω v2δt
020)
Calcolo di k
I conti diventano sempre più complessi:
k3 = A @ (y0 + deltat * k2 / 2)k3
3
k3 = f t + ,y(t ) + k =( 0 2δt
0 2δt
2)
= A ⋅ y + k .( 0 2δt
2)
⎝⎜⎛v 1 − δt ω0 (
41 2
02)
− ω v2δt
020 ⎠
⎟⎞
Calcolo di k
k4 = A @ (y0 + deltat * k3)k4
4
k4 = f t + δt,y(t ) + δtk =( 0 0 3)= A ⋅ y + δtk( 0 3)
⎝⎜⎜⎛ v 1 − ω0 (
2δt2
o2)
−δt ⋅ ω v 1 − ωo20 (
4δt2
o2)⎠⎟⎟⎞
Calcolo dell'incrementoValutiamo ora il termine da sommare a y(t ) per ottenere y(t + δt):
incr = deltat / 6 * (k1 + 2 * (k2 + k3) + k4)incr.simplify()incr
0
0
⎝⎜⎜⎛ δtv 1 − ω0 (
6δt2
o2)
− δt 1 − ω2
ω vo20 2 (
12δt2
o2)⎠⎟⎟⎞
Verifica del risultato ﴾1/2﴿Possiamo renderci conto della bontà del risultato se calcoliamo losviluppo di Taylor intorno a t = 0 della soluzione analitica
x(t) = sinω t.
series(v0 / omega0 * sin(omega0 * deltat), deltat)
δtv − δt + δt +O δt
I primi due termini corrispondono esattamente al valore di incr[0] ﴾termine della posizione﴿ che abbiamo ricavato con sympy . L'erroredi RK in questo caso è quindi dell'ordine di δt .
ω0
v00
0 6ω vo20 3
120ω vo40 5 ( 6)
4
Verifica del risultato ﴾2/2﴿Facciamo ora lo stesso con la velocità:
v(t) = (t) = v cosω t.
series(diff(v0 / omega0 * sin(omega0 * deltat), deltat), deltat)
v − δt + δt +O δt
Questo corrisponde a v più il valore di incr[1] ﴾termine dellavelocità﴿. Notate che la discrepanza rispetto alla soluzione analiticastavolta è dell'ordine di δt .
x 0 0
0 2ω vo20 2
24ω vo40 4 ( 6)
0
6
Semplificare il risultatoPossiamo sostituire i simboli usati nel calcolo con i valori dati neltesto dell'esercizio 10.1: δt = 10 s, ω = 1 s , v = 1m/s.
(y0 + incr).subs([(deltat, 1e‐2), (omega0, 1), (v0, 1)])
La sostituzione restituisce il valore di y(0 + δt) ﴾step n = 1 dellasimulazione﴿:
.
−20
−20
(0.009999833333333330.999950000416667
)
Verificare l'implementazione di RKCome prima è importante scrivere un test all'inizio del codice in cuisi verifichi che si stiano facendo i calcoli in modo corretto:
int main() { double deltat = 1e‐2; // ... for (int step = 0; step < N; ++step) { if (step == 0) { assert_close(solution[0], 0.0); assert_close(solution[1], 1.0); } else if (step == 1) { assert_close(solution[0], 0.00999983333541667); assert_close(solution[1], 0.999950000833333); } // Update "solution" using RK's method }
Avvertenza importanteTutti i calcoli svolti qui per l'esercizio 10.1 non sono veri in generaleper l'algoritmo di Runge‐Kutta.
Questo è ovvio, dal momento che la nostra soluzione dipende da ω e v , che sono parametri del problema 10.1.0 0
Overloading degli operatoriPer svolgere l'esercizio 10.1 ﴾metodo RK﴿ è necessario fare molteoperazioni sui vettori. Ad esempio, nell'espressione
k = f t+ , y + k
si devono sommare tra loro y e δtk /2, che sono entrambivettori di 2 elementi ﴾posizione e velocità﴿.
Il C++ permette di ridefinire gli operatori matematici in modo chesiano in grado di operare non solo sui tipi base ﴾come int e double ﴿, ma anche sulle classi definite nei propri programmi.
2 (2δt
0 2δt
1)
0 1
Overloading degli operatori: esempioIl seguente esempio sfrutta la classe std::vector , e dovrebbeessere facile adattarlo al caso di una classe VettoreLineare :
#include <vector>typedef std::vector<double> vec;
vec operator+(const vec &a, const vec &b) { vec result(a.size()); for (int j = 0; j < a.size(); ++j) { result[j] = a[j] + b[j]; } return result;}
Il nome della funzione, operator+ , indica al C++ che è la funzioneda invocare quando si scrive v1 + v2 , nel caso in cui sia v1 che v2 siano istanze della classe vec .
Overloading degli operatoriGli operatori overloading si possono definire sia come funzioni checome metodi di classi:
class VettoreLineare {public: // … VettoreLineare operator+(const VettoreLineare &) const;};
La scelta dell'una o dell'altra implementazione è equivalente.
RK ed overloading degli operatoriTornando ad espressioni vettoriali come
y + k ,
se y0 e k1 sono istanze di VettoreLineare , il calcolo può essereimplementato in C++ nel modo seguente:
y0 + (deltat / 2.0) * k1
﴾senza cicli for !﴿, a patto che siano stati definiti gli operatori
VettoreLineare operator+(const VettoreLineare & a, const VettoreLineare & b);VettoreLineare operator*(double s, const VettoreLineare & v);
0 2δt
1