Sei sulla pagina 1di 7

FreeFem++ mini tutorial

F. Nobile, S. Perotto

FreeFem++ e un codice per la risoluzione di equazioni alle derivate parziali


con elementi finiti, sviluppato da O. Pronneau, F. Hecht, e A. Le Hyaric. Il
software, alla versione 2.24-3, e il manuale di utilizzazione sono scaricabili li-
beramente da internet al sito http://www.freefem.org. E disponibile anche
una semplice interfaccia grafica FFedit, scritta nel linguaggio tcl, anchessa
scaricabile dalle pagine sopra citate.

1 Sintassi di base
FreeFem++ e un linguaggio di programmazione con una sintassi di base simile
al C++. E pertanto possibile dichiarare variabili e funzioni allinterno di uno
script FreeFem++, eseguire operazioni aritmetiche, utilizzare cicli o istruzioni
condizionali. Per la sintassi corretta riferirsi al Capitolo 4 del manuale di uti-
lizzazione di FreeFem++.

Esempio 1.1 Questo e un esempio di script in linguaggio FreeFem++.


// dichiarazione e assegnazione di interi e reali
int i=20;
real pi=4*atan(1.);

cout << "i=" << i << "\n";


cout << "pi=" << pi << "\n";

// dichiarazione di un array
real[int] a(i);

// esempio di ciclo for


int n;

for (n=0; n<i; n++)


{
a[n] = cos(n*pi/i);
cout << "a=" << a[n] << "\n";
}

//esempio di ciclo condizionale

1
int r=10;
while ( r>4 )
{
cout << "r= "<< r <<"\n";
r--;
}

// esempio di funzione
func real inverso(real x)
{
real ans;
ans=1./x;
return ans;
}

cout << "linverso di 5 e " << inverso(5.) << "\n\n";

La sequenza di comandi presentata nellEsempio precedente deve essere scritta


in un file di testo con estensione .edp, ed eseguita con il comando FreeFem++
nomefile.edp.
Se si usano i tools di interfaccia grafica (FFedit), ricordarsi di salvare le
istruzioni in un file, prima di eseguirle. Il risultato apparira sul terminale da cui
e stato lanciato il programma FFedit.
Attenzione I nomi delle variabili non possono contenere il carattere .
Inoltre, alcuni nomi sono riservati a variabili globali, ad esempio x, y, z, P, N,
area, ecc. E consigliabile non sovrascrivere queste variabili.

2 Generazione di griglia
Per una descrizione completa si veda il Capitolo 5 del manuale. Il primo passo
per generare un dominio e definirne il bordo. Cio puo essere fatto tramite il
comando border che permette di introdurre la parametrizzazione di una curva
in 2D:
border gamma(t=a,b) { x=f(t); y=g(t); label=1;}
In questo esempio la curva gamma e definita in funzione del parametro t che varia
tra gli estremi a e b. Label permette di associare un identificatore alla curva
(utile nellassegnazione delle condizioni al bordo). Una volta definita lentita
gamma, con listruzione gamma(m) si genera poi una struttura dati associata a m
sotto-intervalli di egual ampiezza sulla curva gamma. La curva puo essere visu-
alizzata utilizzando il comando plot (in FreeFem++ il comando plot permette
di visualizzare vari oggetti come curve, mesh, soluzioni elementi finiti, ecc.).

2
Esempio 2.1
// linea retta
real[int] v(2);
v[0]=.5; v[1]=sqrt(3.)/2;
border a(t=0,5) {x=t*v[0]; y=t*v[1]; label=1;}
plot(a(20), wait=1);

// cerchio
border b(s=0,2*pi) { x=cos(s); y=sin(s); label=2;}
plot(b(100), wait=1);

Una volta descritto il bordo del dominio (curva chiusa orientata), listruzione
buildmesh permette di generare una triangolazione del dominio contenuto alla
sinistra del bordo. La griglia risultante e un oggeto di tipo mesh.

Esempio 2.2
// rettangolo [0 2]x[-1 0]
border a(t=0,2) {x=t; y=-1; label=1;}
border b(t=-1,0) {x=2; y=t; label=2;}
border c(t=0,2) {x=2-t; y=0; label=3;}
border d(t=0,1) {x=0; y=-t; label=4;}
plot(a(10)+b(5)+c(10)+d(5),wait=1);

mesh Th1 = buildmesh(a(10)+b(5)+c(10)+d(5));


plot(Th1,fill=1,wait=1,ps="rettangolo.eps");

// ellisse
border gamma(t=0,2*pi) {x=1+.5*cos(t); y=sin(t); label=5;}
mesh Th2 = buildmesh(gamma(20));
plot(Th2,fill=1,wait=1);

Attenzione Se il bordo e costituito da varie curve, si faccia attenzione a o-


rientare ciascuna curva correttamente, definendo lopportuna parametrizzazione.

Una mesh puo essere salvata su un file con listruzione savemesh e riletta da file
con listruzione readmesh

mesh th=buildmesh(....);
savemesh(th,"nomefile.msh");

mesh th1=readmesh("nomefile.msh");

3
Esempio 2.3
// scrittura su file mesh di output

savemesh(Th1,"rettangolo.msh");
savemesh(Th2,"ellisse.msh");

// lettura da file mesh di input

mesh th=readmesh("ellisse.msh");
plot(th);

Esercizio 2.1 Generare la mesh rettangolare dellEsempio 2.2 utilizzando una


diversa parametrizzazione del bordo in modo da addensare i punti della griglia
vicino al vertice (2, 0).
et 1
(Suggerimento: si provi usando una parametrizzazione del tipo 1 e 1
, oppure
1

log(1 + t(e 1)), con > 0 ).

Esercizio 2.2 Si generi una triangolazione del dominio compreso tra il cer-
chio di raggio r1 = .5 centrato in (0, .5) e il cerchio di raggio r2 = 2 centrato
nellorigine.

Esercizio 2.3 Si generi la triangolazione di un cerchio di raggio unitario a cui


manca uno spicchio di 30 gradi.

Esercizio 2.4 Si generi il dominio a forma di L

= [0, 2] [0, 1] [0, 1] [1, 2]

e una triangolazione raffinata attorno al vertice rientrante.

3 Spazi a elementi finiti - equazione di Laplace


Per risolvere equazioni alle derivate parziali in FreeFem++, come prima cosa
bisogna definire lo spazio o gli spazi ad elementi finiti con cui si vuole lavorare su
una data triangolazione. Questo puo essere fatto mediante la keyword fespace.
Ad esempio, se vogliamo definire lo spazio degli elementi finiti P1 continui sulla
mesh Th (precedentemente definita), procederemo nel modo seguente:
fespace Xh(Th,P1);
dove Xh e il nome della variabile di tipo fespace. In FreeFem++ sono disponibili
vari elementi finiti. Ricordiamo in particolare gli elementi costanti a tratti P0;
i lineari e quadratici continui P1 e P2 e le rispettive versioni discontinue P1dc e
P2dc, gli elementi P1-bolla P1b, ecc. Riferirsi al manuale, Capitolo 6, per una
lista completa. Una volta introdotto lo spazio ad elementi finiti, ad esempio Xh,
e possibile definire degli elementi dello spazio (funzioni ad elementi finiti):

4
Xh uh,vh;
E particolarmente facile interpolare, su uno spazio elementi finiti, funzioni def-
inite analiticamente, nonche interpolare una funzione ad elementi finiti su uno
spazio ad elementi finiti differente:
Esempio 3.1
mesh Th1=square(4,4);
mesh Th2=square(3,2);

plot(Th1,wait=1);
plot(Th2,wait=1);

fespace Xh(Th1,P2);
fespace Yh(Th1,P1);
fespace Vh(Th2,P1);

Xh u1=x^2 + y^2; // la funzione x^2 + y^2 e automaticamente


// interpolata sullo spazio Xh
plot(Th1,u1,nbiso=30,value=1,wait=1);

// interpolazione di u1 su Yh:
Yh u2;
u2 = u1;
plot(Th1,u2,nbiso=30,value=1,wait=1);

// interpolazione di u1 su Vh:
Vh u3;
u3 = u1;
plot(Th2,u3,nbiso=30,value=1,wait=1);

Listruzione problem permette di definire un problema variazionale su spazi agli


elementi finiti.

Esempio 3.2 Si vuole risolvere lequazione


(
u = 1 in = (0, 1)2 ,
(1)
u=0 su ,

utilizzando elementi finiti P1 continui.


border a(t=0,1){x=t;y=0;label=1;}
border b(t=0,1){x=1;y=t;label=2;}
border c(t=0,1){x=1-t;y=1;label=3;}
border d(t=0,1){x=0;y=1-t;label=4;}

5
mesh Th=buildmesh(a(20)+b(20)+c(20)+d(20));
plot(Th,wait=1);

fespace Xh(Th,P1);
Xh uh,vh;
func f = 1; // definisco analiticamente il termine forzante

// definizione del problema variazionale


problem laplace(uh,vh) =
int2d(Th)(dx(uh)*dx(vh) + dy(uh)*dy(vh)) //forma bilineare
- int2d(Th)(f*vh) //termine forzante
+ on(1,2,3,4,uh=0); //cond. di Dirichlet

laplace; // soluzione dellequazione


plot(Th,uh,nbiso=40,fill=1);

La keyword func permette di definire una funzione di x e y, analitica-


mente.
Listruzione int2d(mesh) esegue lintegrazione su tutti gli elementi della
griglia. Analogamente, int1d(mesh,n) esegue lintegrazione sul lato di
bordo etichettato con il valore n. E possibile specificare lordine della
formula di quadratura da utilizzare mediante int2d(Th,qforder=...).
Listruzione dx (resp. dy) calcola la derivata di una funzione elementi finiti
nella direzione x. Attenzione Listruzione dx non funziona se applicata
a una funzione definita analiticamente.

Le condizioni al bordo di Dirichlet sono imposte mediante il comando on,


soltanto sui tratti di bordo specificati nellistruzione.
E possibile associare un risolutore lineare al problema mediante lopzione
solver, nonche un precondizionatore precon=nome di una funzione

problem laplace(uh,vh,solver=CG,precon=P)= .......;


func real[int] P(real[int] & xx); { ........}

Le scelte possibili per il risolutore sono: solver = LU, CG, Crout, Cholesky,
GMRES, UMFPACK.

Invece di definire direttamente il problema variazionale, e possibile intro-


durre innanzi tutto una forma variazionale su una data triangolazione utiliz-
zando la keyword varf e in seguito il problema a partire dalla forma variazionale
introdotta. Il vantaggio e che da un elemento di tipo varf e possibile estrarre
le matrici e i termini noti associati al problema discreto.

6
Esempio 3.3 LEsempio 3.2 puo essere risolto alternativamente nel modo seguente:
border a(t=0,1){x=t;y=0;label=1;}
border b(t=0,1){x=1;y=t;label=2;}
border c(t=0,1){x=1-t;y=1;label=3;}
border d(t=0,1){x=0;y=1-t;label=4;}

mesh Th=buildmesh(a(20)+b(20)+c(20)+d(20));

// definizione del termine forzante


func f=1;

// definizione della forma variazionale


varf B(u,v)=int2d(Th)(dx(u)*dx(v)+dy(u)*dy(v)) // forma bilineare
-int2d(Th)(f*v) // termine forzante
+on(1,2,3,4,u=0); // condizioni al bordo

fespace Xh(Th,P1);
Xh uh,vh;

// matrice di stiffness
matrix K=B(Xh,Xh);

// termine noto: gli devo cambiare di segno perche ufficialmente lui


// sta a sinistra
Xh g;
g[]=B(0,Xh);
g=-g;

// risoluzione del sistema lineare


uh[]=K^-1*g[];
plot(Th,uh,nbiso=40,value=1,fill=1);