1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
| // Programme C++ Laplace : résoudre une équation elliptique d'ordre deux par raccord en un point
// médian des courbes intégrées depuis les bords. Problème aux conditions limites de type Dirichlet.
//
// Système @ résoudre :
//
// Ru(Phi_0, Phi_1) ) = 0 avec Phi, Psi : deux paramètres de la variable r satisfaisant aux
// équations différentielles
//
// d Phi / dr = Psi
// d Psi / dr = l(l+1)/r^2 Phi
//
// sujettes aux conditions :
// Phi(eps) = PHI0 Psi(eps) = libre en r = eps << 1 ;
// Phi(2) = libre Psi(2) = PSI1 en r = 2.
//
// Les quatre courbes intégrées se rencontrent en r = RM avec RM = 1 une constante symboliques.
//
// Code inclus dans la distribution arbalete.tar pour le projet Schroedinger / M1 - Programmation 2014.
// Une notice en format pdf contient des information complémentaires / questions.
//
#include <iostream>
#include <cmath>
using namespace std ;
#include <cstdlib>
//
// Classe standard vector, mais classe perso matprod
//
#include <vector>
#include "matprod.h"
// Classse d'E/S standard et celle utilisée auparavant (optionnel)
//
#include <fstream>
#include "class_es.h"
// Constante symboliques :
//
#define KMAX 50 // Nombre d'itérations maximale pour converger
#define RM 1.0 // Position du point de raccord des courbes (pt milieu)
#define PHI1 -1.0/4 // Valeur de Phi en r = 2 (bord extérieur)
#define EPS 0.05 // Valeur de r = eps (bord intérieur)
#define PHI0 -1./EPS // Valeur de Phi(eps)
#define N 1000 // Nombre de points pour l'intégration (fnc integre)
double l = 0.10 ; // Variable globale l ]0, 2[ : double (pour exploration)
double rm = RM ; // Varaible globale pour la position du point de raccord
//
// Intégration (rapide) Euler 1 : de la position courante r, jusqu'à n*dr.
//
void integre( double *Psi, double *Phi, double r, double dr, unsigned int n ) {
unsigned int i ;
for( i = 0 ; i < n ; i++ )
{
*Phi += *Psi * dr ;
*Psi += l*(l+1)/r/r * (*Phi) * dr ;
r += dr ;
}
return ;
}
//
// Utilitaire pour calculer le produit scalaire entre deux vecteurs
// (pourrait être ajouté @ la classe matprod ou vecteur).
//
double vdot( std::vector<double> a, std::vector<double> b )
{
double somme = 0 ; int i=0, n ; n = max( a.size(), b.size() ) ; while( i < n ) { somme += a[i]*b[i]; i++ ; }
return somme ;
}
// Partie principale du programme :
// ================================
//
int main()
{
unsigned int i, n = N, Taille = 2;
matrice J(Taille), M(Taille) ;
std::vector<double> ori(Taille), ext(Taille), R(Taille), Rf(Taille), Delta(Taille), b(Taille) ;
//vect ori(Taille), ext(Taille), R(Taille), Delta(Taille) ;
// Paramètre d'intégration :
//
double Ru, lambda = 0.05 , Psi, Phi, Psi0, r, dr ;
// Initialise le vecteur de contraintes @ l'orgine (ori) et au bord externe (ext).
// deux contraintes pour 4 paramètres : Phi (indice 0) et Psi (indice 1) aux deux bords.
//
for( i = 0 ; i < Taille ; i++ ) { ori[i] = (double)0.0 ; ext[i] = (double)0.0; }
Psi0 = - (double)l / EPS * ( PHI0 ) ;
ori[0] = PHI0 ; ori[1] = Psi0 ;
ext[0] = PHI1 ; ext[1] = - (double)l / 2.0 * ( PHI1 ) ;
cout << " ori 0 et 1 : " << ori[0] << " " << ori[1] << " " << std::endl ;
cout << " ext 0 et 1 : " << ext[0] << " " << ext[1] << " " << std::endl ;
int k = 0 ; Ru = 1 ;
// Boucle itérative pour contrôler la qualité du raccord en r = rm :
//
while( k < KMAX && Ru > 1.e-8 ) {
// Position près de l'origine + pas d'intégration :
// r != 0 pour éviter une singularité
//
r = EPS ; dr = (rm -r ) / (n-1) ;
// On intègre de l'origine -> point milieu de la grille ( --> )
// Valeurs initiales pour (Phi, Psi ) :
//
Phi = ori[0] ; Psi = ori[1] ;
integre( &Psi, &Phi, r, dr, n ) ;
// Sauvegarde des valeurs atteintes en r = rm : point "milieu" M
// Sens ">" : 1iere colonne, indice 0
//
M.B[0][0] = Phi ; M.B[1][0] = Psi;
// On intègre de r = 2 vers le point milieu de la grille ( <-- )
// Sens "<" : 2ieme colonne, indice 1
//
r = 2 ; dr = (rm - r ) / (n-1 ) ;
// Valeurs initiales pour (Phi, Psi ) :
//
Phi = ext[0] ; Psi = ext[1] ;
integre( &Psi, &Phi, r, dr, n ) ;
// Sauvegarde des valeurs atteintes en r = rm : point "milieu" M
// Sens ">" : 2ieme colonne, indice 1
//
M.B[0][1] = Phi ; M.B[1][1] = Psi;
// Vecteur des différences R, sa norme au quarré Ru :
//
R[0] = ( M.B[0][0] - M.B[0][1] ) ; R[1] = (M.B[1][0] - M.B[1][1] ) ;
Ru = vdot(R,R) ;
cout << " k, Valeur scalaire de l'incertitude : " << k << " " << Ru << std::endl ;
// Sauvegarde le vecteur R de référence : change de signe pour résoudre (-> élimination gaussienne)
//
Rf[0] = - R[0] ; Rf[1] = - R[1] ;
// On reprend mais en effectuant les différences finies pour construire la matrice des variances J :
// on utlise le paramètre "lambda" en pourcentage (lambda %) pour varier les paramètres libres aux limites.
//
Phi = ori[0] ; Psi = (1+lambda)*ori[1] ;
integre( &Psi, &Phi, r, dr, n ) ;
// Calcule maintenant les **différences** avec les anciennes valeurs obtenues au point "milieu" M
// sens ">" : 1ere colonne indice 0
//
M.B[0][0] = Phi - M.B[0][0] ; M.B[1][0] = Psi - M.B[1][0] ;
// On intègre ensuite de r = 2 vers le point milieu de la grille ( <-- )
//
r = 2 ;
dr = (rm - r ) / (n-1 ) ;
// Valeurs initiales pour (Phi, Psi ) :
//
Phi = (1+lambda)*ext[0] ; Psi = 1*ext[1] ;
integre( &Psi, &Phi, r, dr, n ) ;
// Calcule maintenant les **différences** avec les anciennes valeurs obtenues au point "milieu" M
// sens "<" : 2ieme colonne, indice = 1 (*signe inversé*, voir définition de R)
//
M.B[0][1] = M.B[0][1] - Phi ; M.B[1][1] = M.B[1][1] - Psi ;
// Matrice des variances J : rappel - la matrice "M" comprend maintenant des différences
// Schéma simple de différences finies
//
J.B[0][0] = 2 * M.B[0][0] / ( lambda*ori[1] ) ; J.B[0][1] = 2 * M.B[0][1] / ( lambda*ext[0] ) ;
J.B[1][0] = 2 * M.B[1][0] / ( lambda*ori[1] ) ; J.B[1][1] = 2 * M.B[1][1] / ( lambda*ext[0] ) ;
matrice Jp ; Jp.copie(&J) ; // Copie of the original Jacobian matrix - test de cohérence validé : inversion de J.
// Elimination gaussienne : procède avec la matrice après pivots, solution de J*Delta = R
// (le signe négatif absorbé dans la définition de Rf).
//
Delta = reduction_gaussienne( J, Rf ) ;
// Nouvelles valeurs pour les conditions limites : mise à jour (@ chaque itération)
//
ori[1] += Delta[0] ; ext[0] += Delta[1] ;
k++ ;
}
cout << " Valeurs finales : k = " << k << " " << " Psi0 = " << ori[1] << " Phi(2) = " << ext[0] << std::endl ;
// Intégration des courbes, sauvegarde : s'assurer de la convergence ..
//
fstream fichier ; fichier.open( "Laplace.dat", ios::out ) ; cout << fichier.is_open() << std::endl ;
// On intègre de l'origine -> point milieu de la grille ( --> )
//
r = EPS ; dr = (rm -r ) / (n-1) ;
Phi = ori[0] ; Psi = ori[1] ;
for ( i = 0 ; i < n ; i++ )
{
fichier << r << " " << Phi << " " << Psi << std::endl ;
integre( &Psi, &Phi, r, dr, 1 ) ;
r += dr ;
}
// On intègre du bord < -- point milieu de la grille ( <-- )
//
r = 2 ; dr = (rm -r ) / (n-1) ;
Phi = ext[0] ; Psi = ext[1] ;
for ( i = 0 ; i < n ; i++ )
{
fichier << r << " " << Phi << " " << Psi << std::endl ;
integre( &Psi, &Phi, r, dr, 1 ) ;
r += dr ;
}
fichier.close() ;
return 1;
} |
Partager