Risoluzione di equazioni differenziali -...

Post on 14-Feb-2019

229 views 1 download

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