Equazioni differenziali alle derivate parziali

A. Andreazza, D. Galli, E. Spoletini

November 12, 2007
In questo esercizio proveremo a risolvere alcuni problemi classici della fisica descritti da equazioni differenziali alle derivate parziali.

Nella sessione di laboratorio bisognerà risolvere almeno un problema, gli altri potranno essere affrontati a casa, come possibili temi d’esame.

Alcune considerazioni generali

Tutti i problemi richiedono di discretizzare le equazioni differenziali su di un reticolo. Come suggerimento, anziché impostare una matrice, accedendo agli elementi tramite indici multipli, ad esempio

 
double M[dimx][dimy][dimz]; 
for (int ix=0; ix<dimx; ix++) { 
  for (int iy=0; iy<dimy; iy++) { 
    for (int iz=0; iz<dimz; iz++) { 
      M[ix][iy][iz]=0; 
    } 
  } 
}

conviene definire un vettore ed accederci indirizzando direttamente l’elemento opportuno:

 
double M[dimx*dimy*dimz]; 
for (int ix=0; ix<dimx; ix++) { 
  for (int iy=0; iy<dimy; iy++) { 
    for (int iz=0; iz<dimz; iz++) { 
      M[ix*dimy*dimz+iy*dimz+iz]=0; 
    } 
  } 
}

Sebbene la costruzione sembri più complicata, ha il vantaggio di permettere più facilmente di riutilizzare il codice. Ad esempio la seguente funzione che calcola la massima componente di un vettore:

 
double distanza(int N, double* v) { 
  double mx=0.; 
  for (int i=0; i<N; i++) { 
    if ( fabs(v[i])>mx ) mx=fabs(v[i]); 
  } 
  return mx; 
}

può essere chiamata nel secondo caso come
distanza(dimx*dimy*dimz,M)
ma non può essere utilizzata se M è una matrice a più indici.

Calcolo di un potenziale

In due dimensioni il potenziale elettrostatico deve soddisfare l’equazione:

∂2u   ∂2u
∂x2-+ ∂y2-= - ρ (x,y)
(1)

con delle opportune condizioni al contorno.

Il problema può essere discretizzato su un reticolo di passo Δx:

xi = x0 + iΔx
yi = y0 + jΔx
ui,j = u(xi,yj)

ui+1,j --2ui,j +-ui--1,j+ ui,j+1---2ui,j +-ui,j-1= - ρ(x ,y )
       Δx2                  Δx2               i j

Il procedimento di risoluzione può essere un metodo di rilassamento, in cui si immette una soluzione di prova u(0) e si applica la trasformazione

 (n+1)   1(  (n)     (n)     (n)     (n) )   Δx2ρ(xi,yj)
ui,j   = 4  ui+1,j + ui- 1,j + ui,j+1 + ui,j-1 +---4------

iterando il procedimento fino a quando la distanza massima tra u(n) ed u(n+1) è minore di una tolleranza data.

Determinare il potenziale su di un reticolo opportuno, con condizioni al contorno nulle sui bordi, per i seguenti problemi:

  1. potenziale generato da un conduttore circolare, quindi con l’ulteriore condizione al contorno u(x,y) = 1, ∘ -2---2-
  x + y 1, e densità di carica nulla ρ(x,y) = 0.
  2. potenziale generato da una densità di carica uniforme ρ(x,y) = 1, ∘x2--+y2- 1
  3. potenziale generato da una densità di carica di dipolo ρ(x,y) = x∕  -------
∘ x2 + y2,   -------
∘ x2 + y2 1

In tutti i casi dare la stima del campo elettrico nel punto (0,2).

Diffusione del calore

Processi diffusivi, tra cui la propagazione del calore, vengono descritti dall’equazione:

        (     )
∂u-  -∂-   ∂u-
∂t = ∂x   D∂x

Il problema ora è di trovare l’evoluzione temporale della funzione, data la sua condizione iniziale.

Possiamo discretizzare il problema su di un reticolo di passo Δx per la variabile spaziale e valutarne l’evoluzione temporale con passi Δt:

xi = x0 + iΔx
tn = nΔt
uni = u (xi,tn)
Di+1 ∕2 = D (xi +Δx ∕2)

                 (        )         (        )
uni+1--uni-= Di+1∕2-uni+1 --uni---Di-1∕2-uni --uni-1
   Δt                      Δx2

Si tratta quindi, dopo aver dato la condizione iniziale al tempo 0, di calcolare i valori negli intervalli successivi.

Il metodo è stabile se viene soddisfatta la condizione

2D Δt
-Δx2- ≤ 1
(2)

Considerare i seguenti casi, usando come condizioni al contorno il fatto che la u mantenga le condizioni iniziali in tali punti (fisicamente il problema è quello di una sbarra termostatata agli estremi):

  1. D = 1, u(x,0) = exp(       )
 - x2∕2σ20 σ0 = 0.2: verificare che il profilo della u si mantiene gaussiano, con larghezza che aumenta con il tempo σ(t) = ∘ --------
  σ20 + 2Dt
  2. D = 1, u(x,0) = {
  0  x < 0
  1  x > 0 e verificare il raggiungimento della condizione di equilibrio con un passo che soddifa la (2) e poi provare ad aumentarlo sopra il limite definito dall’equazione, fino al punto di vedere l’instaurarsi di instabilità numeriche tipo quella in figura:
    PIC
  3. verificare qual è la condizione di equilibrio nei seguenti casi in cui il coefficiente di diffusione non è costante su tutto l’intervallo:

    1. D(x) = {
  5  |x| < 1∕2
  1  |x| > 1∕2 , u(x,0) = {
  0  x < 1
  1  x > 1
    2. D(x) = {
  1  |x| < 1∕2
  5  |x| > 1∕2 , u(x,0) = {
  0  x < 1
  1  x > 1

Equazione delle onde

L’equazione delle onde:

∂2u     ∂2u
--2 + v2-2-= 0
∂t     ∂x
(3)

descrive segnali che si propagano con una velocità v.

Anche in questo caso vogliamo trovare come soluzione l’evoluzione temporale di una condizione iniziale che, questa volta richiede di fornire sia la funzione al tempo 0, u(x,0), che la sua derivata temporale, ∂u∂t(x,0).

La discretizzazione sul reticolo:

x = x + iΔx
ti= n0Δt
nun= u (x,t )
 i      i n

porta ora ad equazioni che richiedono due passi temporali precedenti:

 n+1    n    n-1      n     n    n
ui----2ui2-+-ui--= v2u-i+1-- 2ui2+-ui--1
      Δt                  Δx

per cui bisogna ricordarsi di tenere i risultati di due iterazioni.

Per iniziare ad iterare, si possono costruire i due passi iniziali come

 0
ui- =1 u(xi,0)    ∂u
ui = u (xi,0)- Δt ∂t (xi,0)

anche in questo caso esiste una condizione di stabilità:

vΔt-≤ 1
 Δx
(4)

che stavolta risulta particolarmente critica.

Bisogna poi fornire delle condizioni sugli estremi dell’intervallo.

Affrontare i seguenti casi:

  1. con condizione di annullamento agli estremi e v = 1, studiare la propagazione di un pacchetto gaussiano:
                   (       )
 u(x,0)  =  exp - x2∕2σ2  σ = 0.2
∂u-
∂t(x,0)  =  0
    per valori di Δt che soddisfano o meno la (4): nelle figure seguenti sono riportato due casi in cui il passo era rispettivamente del 5% superiore ed inferiore al limite di stabilità:
    PICPIC
  2. Verificare la riflessione nei punti in cui cambia la velocità di propagazione: porre
                     {
                   2  x < 0
   v(x)    =       1  x > 0
                     (       2   2)
 u(x,0)    =     exp  - (x+ 2) ∕2σ σ = 0.2
∂u(x,0) =  2(x+2) exp (- (x+ 2)2∕2σ2)
∂t          σ2
    e verificare che l ’evoluzione consiste nella propagazione di un pacchetto gaussiano fino al punto x = 0, dove poi il pacchetto si divide in una componente che continua a propagarsi in avanti ed in un pacchetto riflesso. Valutare le altezze e le larghezze dei due pacchetti risultanti.
  3. Ripetere l’esercizio per un pacchetto gaussiano che inizia in x = 2 e si propaga in senso inverso
                          (       2   2)
  u(x,0)     =      exp - (x - 2)∕2σ  σ = 0.2
∂u-(x,0)  = - (x-2)- exp(- (x - 2)2∕2σ2)
∂t            σ2

Ancora ROOT...

In un esercizio come questo è importante sapere graficare bene i risultati. Per fare questo è bene salvare i dati in maniera che si possano ricostruire facilmente. Per cui potrebbe essere utile salvare i dati delle u che si vogliono visualizzare successivamente in un formato del tipo:

 
x0 deltax dimx 
y0 deltay dimy 
x0 y0 u(x0,y0) 
x0 y1 u(x0,y1) 


.

e fare una macro che legge le prime due righe, crea un opportuno istogramma 2D di dimensione dimx*dimy:

 
cin >> x0 >> deltax >> dimx; 
cin >> y0 >> deltay >> dimy; 
TH2F myHisto("nome","titolo", 
             dimx,x0-deltax/2.,x0+(dimx-0.5)*deltax, 
             dimy,y0-deltay/2.,y0+(dimy-0.5)*deltay);

(capite il motivo dei valori strani per i limiti dell’istogramma?) e poi lo riempie con un ciclo del tipo:

 
for (i=0; i<dimx*dimy; i++) { 
  cin >> x >> y >> w; 
  myHisto.Fill(x,y,w); 
}

Si noti che il metodo Fill di un istogramma permette di inserire un parametro in più rispetto al numero di dimensioni dell’istogramma, appunto per permettere di definire il peso che un particolare ingresso ha nel bin. Se nel bin c’è un solo ingresso, il suo peso definisce il valore dell’istogramma.

Nel disegnare gli istogrammi bidimensionali, può essere divertente cercare di giocare con le opzioni del metodo Draw. In particolare le opzioni più chiare sono



"CONT3" "SURF3"




PICPIC



dove la scala colori è stata ottenuta dando il comando
gStyle->SetPalette(1);
dalla linea di comando di ROOT prima di disegnare i vari istogrammi.

TFile

ROOT implementa una speciale classe di file per immagazinare i suoi oggetti. Questo fatto può essere utile per immagazzinare istogrammi e grafici, senza doversi tenere tutti i dati utilizzati per crearli (si noti che ROOT realizza automaticamente una compressione dei suoi file, cosa particolarmente utile per chi ha problemi di quota!).

Per aprire un file, basta usare l’istruzione:
TFile *myfile = new TFile("nomedelfile","NEW")
Le opzioni possibili sono:

NEW o CREATECrea un nuovo file. Se il file esiste già invia un messaggio di errore
RECREATE Crea un nuovo file. Se il file esiste già viene sovrascritto
UPDATE Permette di modificare il file aggiungendo nuovi oggetti
READ Apre il file solo in lettura

Se nessuna opzione viene specificata, il file viene aperto in modo READ. Una volta aperto il file, oggetti di root possono esservi scritti usando il metodo Write() che è definito per la maggior parte di essi. Ad esempio:

 
TFile *myfile = new TFile("ilmiofile.root","NEW"); 
TGraph *g=new TGraph(); 
TH1F h("mioisto","Un bell’istogramma",100,-1.,1.); 
/* 
 * fase di riempimento dell’istogramma e del grafico 
 *
g->Write(); 
h.Write(); 
myfile->Close();

Come ultima istruzione, abbiamo messo la chiusura del file.

Per accedere ai file creati, il sistema più sempice è di entrare in ROOT e creare un oggetto di tipo TBroser:
TBrowser b;
Questa dichiarazione apre un’interfaccia grafica che permette di navigare tra i file e gli istogrammi dentro i file.

Usare le classi di ROOT in un programma

Potrebbe essere comodo costruire un file di root, produrre gli istogrammi dal nostro programma e salvarli direttamente lì. Per fare questo bisogna imparare come accedere agli header files di ROOT ed alle sue librerie. La cosa è ragionevolmente semplice:

  1. nel file sorgente in cui viene utilizzata la classe TQualcosa, bisogna includere il file header TQualcosa.h:
    #include "TQualcosa.h"
  2. all’inizio del makefile, definire le due variabili:
    ROOTFLAGS := ‘root-config --cflags‘ 
    LIBROOT := ‘root-config --libs‘
    dove bisogna fare attenzione ai caratteri di accento "": il comando root-config, restituisce una stringa con le opzioni necessarie per eseguire varie operazioni. L’opzione --cflags selezione le opzioni da passare in compilazione al g++, mentre --libs crea un elenco delle librerie di ROOT da includere in fase di link.
  3. nelle righe del makefile che gestiscono la compilazione, aggiungere la variabile ROOTFLAGS:
     
    %.o : %.cxx 
           g++ --Wall $< ${ROOTFLAGS}
  4. nelle righe del makefile che gestiscono la fase di link, aggiungere la variabile LIBROOT:
     
    eqcalore : eqcalore.o 
           g++ -o $@ $^ ${LIBROOT}

Nel makefile, si noti l’utilizzo delle variabili predefinite:

$@il target
$^la lista completa delle dipendenze
$<la prima dipendenza

Questionario

Essendo gli esercizi abbastanza dettagliati, non c’è un questionario, ma siete pregati di mostrare ai docenti i vostri risultati, o almeno di confrontarli con quelli attesi mostrati nella pagina delle soluzioni derivate_soluzioni.html, in modo da avere conferma della loro correttezza.