The goal is to simulate a real flat plate capacitor in 2D. The potential of the armature is fixed to +V and -V, on the edges of the simulation cell the potential is zero. Between the plates of the capacitor a dielectric material is placed. The capacity is also calculated by numerically determining the charge on the plates and integrating the flow of the electric field. The result is compared with a formula that does not take into account edge effects.
I am looking for ways to make it more efficient, and how to use some functions, because I don't think it looks really clear.
#include <iostream>
#include <string>
#include <cmath>
#include <fstream>
using namespace std;
int main(int argc, const char * argv[])
{
double Lx,Ly;//lunghezze lati cella di simulazione
int Nx,Ny;//numero punti lungo x e y
double Px,Py;//posizione rispetto all'origine del vertice della prima piastra
double Zx,Zy;//posizione rispetto all'angolo del bordo a destra del vertice della seconda piastra
double Lc;//spessore della piastra
double Hc;//altezza piastra
int dist_type;//tipo di distribuzione di carica: 1-uniforme
double thrs;//valore soglia per convergenza
double V;
double Nstampa;
int iCount;
int phi_bordo=0.;//funzione phi sul bordo
double hx,hy;
double diff=1e10;
ofstream fphi,fcharge, ffield;
cout.precision(10);
cout << "Lato cella lungo x\n";
cin >> Lx;
cout << "Lato cella lungo y\n";
cin >> Ly;
cout << "Numero punti lungo x\n";
cin >> Nx;
cout << "Numero punti lungo y\n";
cin >> Ny;
cout << "Vertice piastra 1 in x\n";
cin >> Px;
cout << "Vertice piastra 2 in y\n";
cin >> Py;
cout << "Vertice piastra 1 in x\n";
cin >> Zx;
Zx = Lx - Zx;
cout << "Vertice piastra 2 in y\n";
cin >> Zy;
cout << "Spessore piastra \n";
cin >> Lc;
cout << "Altezza piastra \n";
cin >> Hc;
cout << "Potenziale piastre (in Volt) \n";
cin >> V;
cout << "Soglia per convergenza \n";
cin >> thrs;
double* phi0= new double[Nx*Ny];//funzione da trovare
double* phi1= new double[Nx*Ny];//funzione da trovare
double* phi2= new double[Nx*Ny];//funzione da trovare
double* ftilde=new double[Nx*Ny];//distribuzione di carica e fattori bordo
double* mphi= new double[Nx*Ny];
hx=Lx/(Nx-1.);
hy=Ly/(Ny-1.);
iCount=0; //iCount corrisponde all'indice l delle dispense l = j*Nx + i con i,j indici rispettivamente di xi e yj
//infatti stiamo calcolando ftilde che è l'array indicizzato con l
for(int j=0;j<Ny;j++){
double y;
y=Ly*j/(Ny-1.);
for(int i=0;i<Nx;i++){
double x;
x=Lx*i/(Nx-1.);
if(x>=Px && x<=(Px+Lc) && y>=Py && y<=(Py+Hc)) {
ftilde[iCount]=V;
}
else if(x>=Zx && x<=(Zx+Lc) && y>=Zy && y<=(Zy+Hc)){
ftilde[iCount]=-V;
}
else{
ftilde[iCount]=0.;
}
//potenziale sul bordo della cella
if(i==0 || i==(Nx-1) || j==0 || j==(Ny-1)){
ftilde[iCount]+=phi_bordo;
}
iCount++;
}
}
//metti phi0 in condizioni iniziali quindi pari a 0
for(int i=0;i<Nx*Ny;i++){
phi1[i]=ftilde[i]; //vettore che costruiamo man mano e che è quello che ci serve: phi = ftilde + Mftilde + M^2ftilde ...
phi0[i]=ftilde[i]; //vettore che moltiplicato per Mtilde iterativamente ci dà M^n*ftilde
phi2[i]=0.; //vettore che poniamo ogni volta uguale a phi1 del ciclo precedente in modo da poter calcolare la differenza |phi1-phi2|
}
while(diff>thrs){
for(int i=0;i<Nx*Ny;i++){
mphi[i]=0; //mphi corrisponde al vettore dato dal prodotto M*phi
}
iCount=0.; //indice l di ftilde e di mphi
//aggiungi Mtilde/phi0
for(int j=0;j<Ny;j++){
for(int i=0;i<Nx;i++){
int jCount; //indice m che scorre lungo phi
double div;
//termine diagonale
// mphi[iCount]=phi0[iCount]*(-2.*(1./pow(hx,2)+1./pow(hy,2)));
//termini +1, -1
//punti ai bordi della cella sono esclusi dalla prime 4 condizioni, mentre i punti interni alle due piastre
//che sono SEMPRE DI BORDO e su cui non voglio calcolare l'equazione di Poisson li escludo con la condizione ftilde[icount]=0,
//che comprende tutti i punti non contenuti nelle piastre
if(i!=0 && i!=(Nx-1) && j!=0 && j !=(Ny-1) && ftilde[iCount] == 0){
div=(-2.*(1./pow(hx,2)+1./pow(hy,2))); //corrisponde a Mll, ovvero denominatore di tutti i termini di matrice Mtilde
// Ci ricordiamo che l = j*Nx + i;
if(i>0){
jCount=j*Nx+(i-1); // m = l-1
mphi[iCount]+=-phi0[jCount]/pow(hx,2)/div;
}
if(i<Nx-1){
jCount=j*Nx+(i+1);// m = l+1
mphi[iCount]+=-phi0[jCount]/pow(hx,2)/div;
}
if(j>0){
jCount=(j-1)*Nx+i;// m = l - Nx
mphi[iCount]+=-phi0[jCount]/pow(hy,2)/div;
}
if(j<Ny-1){
jCount=(j+1)*Nx+i;// m = l + Nx
mphi[iCount]+=-phi0[jCount]/pow(hy,2)/div;
}
}
iCount++;
}
}
//il motivo per cui non calcolo in ogni caso mphi per l indice che corrisponde a indici di punti di bordo è perchè
//la matrice Mtilde sarebbe pari a zero per tutti i termini diagonali e per tutte le righe in cui
//l è indice di un punto di bordo: infatti i valori che phi assume ai bordi sono già definiti in ftilde a cui aggiungo
//Mftilde + M^2ftilde.. che hanno invece componente di indice l di bordo nulla
//metti in phi1
for(int i=0;i<Nx*Ny;i++){
phi1[i]+=mphi[i];
phi0[i]=mphi[i];
}
//calcola differenza
diff=0;
for(int i=0;i<Nx*Ny;i++){
diff+=pow(phi1[i]-phi2[i],2);
}
diff=sqrt(diff);
cout << "Differenza: " << diff << "\n";
//copia phi0 in phi1
for(int i=0;i<Nx*Ny;i++){
phi2[i]=phi1[i];
}
}
double * Emat = new double [Nx*Ny*2];
double ** E = new double* [2];
for (int i=0;i<2*Nx*Ny;i++){
E[i] = &Emat[Nx*Ny*i];
}
//trovo componente x del campo elettrico
for(int j=0;j<Ny;j++){
for(int i=2;i<Nx-2;i++){
E[0][Nx*j+i] = (-phi1[Nx*j+(i+2)]+8*phi1[Nx*j+(i+1)]-8*phi1[Nx*j+(i-1)]+phi1[Nx*j+(i-2)])/(12*hx);
}
}
//trovo componente y del campo elettrico
for(int j=2;j<Ny-2;j++){
for(int i=0;i<Nx;i++){
E[1][Nx*j+i] = (-phi1[Nx*(j+2)+i]+8*phi1[Nx*(j+1)+i]-8*phi1[Nx*(j-1)+i]+phi1[Nx*(j-2)+i])/(12*hy);
}
}
//calcoliamo il flusso del campo elettrico uscente da un rettangolo chiuso che circonda la prima piastra
//mi basta calcolare flusso attorno a prima piastra perchè quello della seconda sarà in modulo uguale per simmetria
//integrale metodo Simpson
int j1, i1, j2, i2; //indici di coordinate vertici rettangolo su cui calcolare flusso
j1 = floor((Py/2.)*(Ny-1.)/Ly);
j2 = floor((Py+Hc+0.5*(Ly-Py-Hc))*(Ny-1.)/Ly);
i1 = floor((Px/2.)*(Nx-1.)/Lx);
i2 = floor((Px+Lc+0.5*(Zx-Px-Lc))*(Nx-1.)/Lx);
double Esbx = -E[1][Nx*j1+i1-1]; //prodotto scalare E*u con E uno step più a sx di i1 e u uscente da base sotto rettangolo
double Edbx = -E[1][Nx*j1+i2+1]; //u uscente base sotto rettangolo
double Esby = -E[0][Nx*(j1-1)+i1]; //prodotto scalare E*u con E uno step più in basso di j1 e u uscente da lato sinistro rettangolo
double Esay = -E[0][Nx*(j2+1)+i1];
double Edby = E[0][Nx*(j1-1)+i2]; //u uscente da lato destro
double Eday = E[0][Nx*(j2+1)+i2];
double Esax = E[1][Nx*j2+i1-1]; //u uscente da base sopra rettangolo
double Edax = E[1][Nx*j2+i2+1];
//flusso attraverso base bassa
double Intg = 0;
Intg = (1./3.)*hx*(Esbx+Edbx);
for(int i=i1; i<i2+1; i++){
Intg+=((i%2+1)*2)/3.*(-E[1][Nx*j1+i])*hx;
}
double Flusso1 = Intg;
cout << "Flusso1 " << Flusso1 << " Vm" << endl;
Flusso1 = abs(Flusso1);
Intg =0;
//flusso attraverso lato sinistro
Intg = (1./3.)*hy*(Esby+Esay);
for(int j=j1; j<j2+1; j++){
Intg+=((j%2+1)*2)/3.*(-E[0][Nx*j+i1])*hy;
}
double Flusso2 = Intg;
cout << "Flusso2 " << Flusso2 << " Vm" << endl;
Flusso2 = abs(Flusso2);
Intg =0;
//flusso attraverso base alta
Intg= (1./3.)*hx*(Esax+Edax);
for(int i=i1; i<i2+1; i++){
Intg+=((i%2+1)*2)/3.*(E[1][Nx*j2+i])*hx;
}
double Flusso3 = Intg;
cout << "Flusso3 " << Flusso3 << " Vm" << endl;
Flusso3 = abs(Flusso3);
Intg =0;
//flusso attraverso lato destro
Intg = (1./3.)*hy*(Edby+Eday);
for(int j=j1; j<j2+1; j++){
Intg+=((j%2+1)*2)/3.*(E[0][Nx*j+i2])*hy;
}
double Flusso4 = Intg;
cout << "Flusso4 " << Flusso4 << " Vm" << endl;
Flusso4 = abs(Flusso4);
//flusso corrisponde a carica interna/cost dielettrica nel vuoto
double Q = (Flusso1+Flusso2+Flusso3+Flusso4)*(8.8541878e-12);
cout << "Carica contenuta in una piastra: " << Q << " Coulomb" << endl;
double Cap = 0;
Cap = (double) Q/(2.*V);
cout << "Capacità reale " << Cap << " Farad" << endl;
double Capid = 0;
Capid = 8.8541878e-12*Hc/(Zx-Px-Lc);
cout << "Capacità ideale " << Capid << " Farad" << endl;
double Rap = 0;
Rap = Cap/Capid;
cout << "Rapporto capacità reale e ideale R " << Rap << endl;
//scrive grafici
fcharge.open("carica.dat",ios::out);
fphi.open("phi.dat",ios::out);
ffield.open("campo.dat",ios::out);
ffield.precision(5);
fcharge.precision(5);
fphi.precision(5);
iCount=0;
for(int j=0;j<Ny;j++){
double y;
y=Ly*j/(Ny-1.);
for(int i=0;i<Nx;i++){
double x;
x=Lx*i/(Nx-1.);
fcharge << x << " " << y << " " << ftilde[iCount]*(-2.*(1./pow(hx,2)+1./pow(hy,2))) << '\n';
fphi << x << " " << y << " " << -phi1[iCount] << '\n';
iCount++;
}
fphi << '\n';
fcharge << '\n';
}
for(int j=0;j<Ny;j++){
double y;
y=Ly*j/(Ny-1.);
for(int i=floor((Px)*(Nx-1.)/Lx)-12;i<floor((Zx+Lc)*(Nx-1.)/Lx)+12;i++){
double x;
x=Lx*i/(Nx-1.);
ffield << x << " " << y << " " << E[0][j*Nx+i] << " " << E[1][j*Nx+i] << '\n';
}
}
fcharge.close();
fphi.close();
ffield.close();
delete [] phi0;
delete [] phi1;
delete [] phi2;
delete [] ftilde;
delete [] mphi;
//delete [] Emat;
//delete [] E;
}