Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
Résumé
L'objectif de ce projet est de rechercher et d'étudier les solutions de l'équation
de Schrödinger indépendante du temps à une dimension pour les états liés d'une
particule en interaction avec un potentiel quelconque.
2 Problème 5
2.1 Principe de la méthode de résolution . . . . . . . . . . . . . . . . . . . . 5
2.2 Algorithme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Etudes de cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Page 2
1 Introduction
1.1 Formulation mathématique
L'équation de Schrödinger indépendante du temps à une dimension s'écrit ainsi :
~2 d2 φpxq
´ ` V pxqφpxq “ Eφpxq (1)
2m dx2
où :
‚ ~ est la constante de Planck réduite,
‚ m est la masse de la particule,
‚ x la variable de position,
‚ E son énergie totale
‚ V pxq le potentiel auquel la particule est soumise en x,
‚ φpxq la fonction d'onde solution de l'équation dépendant de x.
Elle peut également s'écrire sous la forme de Sturm-Liouville :
1
rppxqu1 pxqs ` qpxqupxq “ spxq
on obtient alors :
: ` 2m rE ´ V pxqs φpxq “ 0
φpxq
~2
où on reconnaît la forme de Sturm-Liouville avec :
ppxq “ 1
qpxq “ 2m
~2
rE ´ V pxqs
spxq “ 0
upxq “ φpxq (2)
u1 pxq “ dφpxq
dx
9
“ φpxq
d2 φpxq :
u2 pxq “ dx2 “ φpxq
1.2 Récurrence
La méthode est basée sur la discrétisation de l'espace continu représenté selon l'axe
(x Ox). On choisit ainsi un pas de discrétisation de la variable de position sur un do-
1
Page 3
où δ est le pas de discrétisation sur l'axe x1 Ox et qn “ qpxn q. Il est donc à priori possible de
calculer φn`1 ” φpxn`1 q à partir des valeurs φn ” φpxn q et φn´1 ” φpxn´1 q et de construire
par itérations successives la solution φ sur un domaine rxa ; xb s choisi convenablement (voir
la gure 1).
Cet algorithme est appelé méthode de Numerov . Il conduit grosso-modo à une
résolution de l'équation diérentielle à l'ordre 4.
φpxq
φn`1
φn e
rr enc
φn´1 récu
x1 x
O xn´1 xn xn`1
δ
V pxq
x1 x
O
´V0
Page 4
2 Problème
2.1 Principe de la méthode de résolution
On propose ici de résoudre l'équation de Schrödinger à une dimension et de déterminer
les fonctions d'onde φn pxq et les valeurs propres associées de l'énergie En d'une particule
de masse m dans le cas d'un puits de potentiel V pxq de forme arbitraire (gure 2).
On choisira un intervalle de travail rxa ; xb s encadrant susament largement la région
active du puits de potentiel (x „ Opaq). Le principe de l'algorithme consiste pour
une énergie test Et donnée à construire par récurrence la fonction φpxq dans deux
régions adjacentes :
‚ φl pxq sur l'intervalle de gauche rxa ; xm s
‚ φr pxq sur l'intervalle de droite rxm ; xb s
où xm est le point de contrôle, compris entre xa et xb , où les deux fonctions d'onde
respectivement de la branche gauche et de la branche droite doivent coïncider
ainsi que leurs dérivées (on note φ9 ” dφ dx
):
"
φl pxm q “ φr pxm q
(5)
φ9 l pxm q “ φ9 r pxm q
Si ces conditions ne sont pas réalisées à la précision souhaitée, c'est que l'énergie Et testée
n'est pas une valeur propre de l'équation et ne conduit donc pas à une solution physique.
On teste alors une autre valeur Et1 “ Et ` ∆E , où ∆E est un pas d'énergie qui mesure la
précision avec laquelle on scanne l'intervalle des énergies E possibles ; pour un système
lié, il s'agit de l'intervalle r´V0 ; 0s (gure 3).
V pxq E
x1 x
O
Ek`1
∆E
Ek
´V0
Page 5
φr,0 “ φpxb q “ 0 et φr,1 “ φpxb ´ δq “ εr
Les valeurs εl et εr sont des valeurs initiales choisies arbitrairement. Elles sont supposées
de petites amplitudes par rapport à l'amplitude maximale de la fonction d'onde dans la
région d'interaction (|x| À a).
Cette manière d'initialiser les valeurs de la fonction d'onde aux extrémités du domaine
(pour x=xa , xa ` δ , xb ´ δ , xb ) est justiée par le fait qu'on s'attend à ce que la particule
soit piégée dans la région du puits de potentiel (|φ|2 ‰ 0 pour |x| À a). En conséquence,
la fonction d'onde devra être essentiellement non nulle dans la région |x| À a mais elle
sera évanescente pour |x| ąą a (|φ|2 ÝÝÝÝÑ 0, gure 4). On approxime ce comportement
|x|Ñ8
en forçant les valeurs extrêmes à des valeurs proches de zéro. La récurrence construira
ensuite de proche en proche les valeurs de φpxq en partant de l'extrêmité du domaine
vers la région centrale, pour chacune des deux branches. De ce fait, la fonction d'onde
est supposée évanescente sur presque tout le domaine sauf la région du potentiel et
on doit ainsi pouvoir trouver une solution de carré sommable.
V pxq φpxq
φpxq ‰ 0
φpxq » 0 φpxq » 0
O
x1 x
´V0
Page 6
2.2 Algorithme
D'un point de vue algorithmique, la résolution du problème se décompose en plusieurs
étapes (gure 5) :
1. choix de l'intervalle rxa ; xb s et de la valeur de test xm ,
2. choix des nombres de pas Nl et Nr respectivement de longueurs δl =rxm ´ xa s{Nl
à gauche et δr =rxb ´ xm s{Nr à droite,
3. choix raisonnable de l'énergie minimum Emin à partir de laquelle chercher les
valeurs propres et de l'incrément d'énergie ∆E entre les valeurs testées,
4. choix d'un critère de tolérance pour la comparaison des dérivées,
5. choix d'une énergie Et candidate (Et ě Emin ),
6. aectations des valeurs φl,0 “ 0, φl,1 “ εl et φr,0 “ 0 et φr,1 “ εr ,
7. calcul des φl,i (i=2,. . .,Nl ) et φr,j (j =2,. . .,Nr ) grâce à la formule de récurrence,
8. mise à l'échelle de φr par rapport à φl pour garantir φl pxm q “ φr pxm q,
9. test de la correspondance au point xm des fonctions dérivées φ9 l pxm q et φ9 r pxm q,
10. acceptation ou rejet de la valeur Et comme valeur propre,
11. nouvelle tentative avec l'énergie Et1 “ Et ` ∆E (retour à l'étape no 5) et ainsi de
suite. . .
Page 7
φpxq
(a)
φl pxq φr pxq
δr
δl
x1 O x
´a{2 `a{2
xl,0 xl,1 xm xr,1 xr,0
xa xb
Région active
du potentiel
φ9 l pxm q φ9 r pxm q
φpxq
(b)
Point de contrôle
φr pxq
scaling φr pxq
φl pxq φl pxq
x1 O x
´a{2 `a{2
xa xb
xm
Figure 5 (a) Algorithme de construction des branches gauche et droite de φpxq ; (b)
test de la continuité de la dérivée de la fonction d'onde au point de contrôle xm après
mise à l'échelle des deux branches.
Page 8
V pxq V pxq
a a
x1 x x1 x
O O
V pxq V pxq
a a
x1 x x1 x
O O
c
b ´V1
V pxq V pxq
a
V2 a
c
1 1
x x x x
O O
´V0
(e) ´V0
(f)
Figure 6 Exemples de potentiels V pxq pouvant être étudiés. (a) Puits carré (cas ana-
lytique) ; (b) Puits harmonique (oscillateur) ; (c) Potentiel type Wood-Saxon ; (d) Double
puits (avec barrière partielle centrale) ; (e) Puits (harmonique) avec barrières coulom-
biennes extérieures ; (f) Réseau de puits (cristal).
Page 9
3 Séquence 1 : approche basique du problème
3.1 Problème simple
L'objectif de cette première séquence de travail est d'écrire et utiliser quelques pro-
grammes simples, relativement rustiques, qui vont permettre d'appliquer l'algorithme de
Numerov dans le cas du potentiel carré de largeur et de profondeur xées (gure 6 (a)).
Un programme type mettra en ÷uvre des fonctionnalités élémentaires du langage
C++. Il n'utilisera d'ailleurs pas de techniques propres au langage C++ (programmation
objet, généricité) ; cependant, il constituera une première implémentation fonctionnelle
de l'algorithme de Numerov et nous permettra dans les séances futures d'aborder une
approche plus générique de ce problème de la résolution de l'équation de Schrödinger en
utilisant des techniques plus évoluées de programmation en C++.
1. http://pdg.lbl.gov/2012/reviews/rpp2012-rev-phys-constants.pdf
Page 10
3.3 Exercice [1.1]
Nous allons procéder à l'écriture d'un programme simple en C++, prgnumerov1.cxx,
qui traite le cas des états liés de l'électron dans un puits carré centré sur l'origine de l'axe
x1 Ox avec :
‚ a=2 Å, la largeur du puits,
‚ V0 =20 eV, la profondeur du puits.
Point de contrôle
δl δr
x
xmin xm xmax
‚ La valeur de l'énergie test Et est choisie par l'utilisateur et le calcul des coef-
cients ql,i pour i P r0; Nl r et qr,j pour j P r0; Nr r est eectué une fois pour toute
car il ne dépend alors que de Et (formule 2).
‚ Les valeurs de ql,i et qr,j (formule (2)) respectivement pour les branches gauche et
droite de la fonction potentiel Vs pxq sont stockées dans deux tableaux statiques de
nombres réels (en double précision).
Page 11
‚ Les valeurs de φl,i et φr,j respectivement pour les branches gauche et droite de la
fonction d'onde sont stockées dans deux tableaux statiques de nombres réels (en
double précision) en utilisant la récurrence de la formule (3).
‚ Les valeurs de φl,i et φr,i sont chacune mises à l'échelle indépendamment de manière
à ce que la valeur absolue maximale d'une branche soit égale conventionnellement
à 1.
Remarque : Cette étape n'est pas strictement nécessaire mais elle permet de
faciliter l'achage par le choix d'une échelle conventionnelle raisonnable. En eet le
choix des valeurs initiales φl,1 “ εl et φr,1 “ εr , aussi petites soient elles, n'interdit
nullement que la fonction d'onde prenne des valeurs très grandes et délicates à
acher (diérence d'échelle entre les deux branches calculées indépendamment).
‚ Les valeurs de φr,i sont mises arbitrairement à l'échelle de manière à ce que
φl,Nl ´1 “ φr,Nr ´1 soit φl pxm q “ φr pxm q.
‚ Les valeurs de la fonction potentiel V pxq sont enregistrées dans un chier "vs.data"
utilisant un format texte (jeu de caractères ASCII) sur deux colonnes en utilisant
un pas ∆x susamment petit pour pouvoir visualiser convenablement la fonction
potentiel sous Gnuplot. Exemple :
Colonne 1 Colonne 2
xmin V pxmin q
.. ..
. .
xk V pxk q
.. ..
. .
xmax V pxmax q
‚ Les valeurs de la fonction d'onde calculée à gauche (φl ) et à droite (φr ) sont enre-
Page 12
gistrées dans un chier "phi.data" utilisant un format texte sur deux colonnes :
Colonne 1 Colonne 2
xmin φl,0
xmin ` hl φl,1 “ εl
xmin ` 2 hl φl,2
.. ..
. .
xi φl,i
.. ..
. .
xm ´ hl φl,Nl ´2
xm φl,Nl ´1
ê
ê
xm φr,Nr ´1
xm ` hr φr,Nr ´2
.. ..
. .
xj φr,j
.. ..
. .
xmax ´ 2 hr φr,2
xmax ´ hr φr,1 “ εr
xmax φr,0
ê
ê
On prendra soin de faire suivre chaque lot de données des deux branches par
deux lignes blanches ê de manière à pouvoir acher individuellement les deux
branches dans Gnuplot (datasets d'indices respectifs 0 et 1).
Le programme Gnuplot permettra de visualiser le résultat du calcul de la fonction
d'onde eectué grâce à la récurrence (3).
Pour une énergie test Et quelconque (pas une valeur propre), on observera visuellement
une discontinuité en xm pour la dérivée φ9 “ dφ dx
de la fonction d'onde candidate (gure
7 à gauche). En revanche, lorsque que Et coïncide avec une valeur propre du système,
on observe que la dérivée φ9 de la fonction d'onde est continue au point de contrôle xm
(gure 7 à droite).
En changeant la valeur de l'énergie test Et , on recherchera graphiquement avec Gnu-
plot et par une méthode d'encadrement les valeurs propres En (n “ 1, . . . ) de l'énergie
ainsi que la forme des fonctions d'onde associées φn pxq.
On pourra également étudier la stabilité du calcul en fonction de la taille du pas
d'intégration hl ou hr .
Page 13
Figure 7 Comparaison des dérivées de φ à gauche (rouge) et à droite (vert) du point
de contrôle xm (centre du cercle) : à gauche, cas φ9 l pxm q ‰ φ9 r pxm q ; à droite, cas φ9 l pxm q »
φ9 r pxm q dans le cas où E est une solution valeur propre de l'énergie.
On note :
x0 “ xm , x1 “ xm ´ hl , x2 “ xm ´ 2 hl , x3 “ xm ´ 3 hl
et
f0 “ φpx0 q, f1 “ φpx1 q, f2 “ φpx2 q et f3 “ φpx3 q
et
9 0 q, f:0 “ φpx
f90 “ φpx : 0 q et f p3q “ φp3q px0 q.
0
Page 14
3.5 Exercice [1.2]
An d'établir l'expression de f90 , on propose ici d'utiliser le logiciel Maxima 2 , un
programme capable d'eectuer des calculs symboliques et numériques.
Questions :
1. Dans un terminal, lancer la commande maxima :
Terminal
1 dupont@scssvr:~$ maxima
2
3 Maxima 5.24.0 http://maxima.sourceforge.net
4 using Lisp GNU Common Lisp (GCL) GCL 2.6.7 (a.k.a. GCL)
5 Distributed under the GNU Public License. See the file COPYING.
6 Dedicated to the memory of William Schelter.
7 The function bug_report() provides bug reporting information.
8 (%i1)
2. http://maxima.sourceforge.net/
Page 15
5. Résoudre, au moyen de la fonction solve, le système constitué par le tableau des
trois équations fvalues avec pour solutions les valeurs constituant le tableau des
trois inconnues unknowns :
Maxima
(%i6) solve(fvalues,unknowns);
La solution symbolique achée par Maxima donne l'expression analytique de f90 (l'in-
connue df0) en fonction des données connues du problème.
6. Quitter Maxima et créer un programme C++ prgderiv0.cxx qui utilise ˇ la formule
approchée obtenue précédemment pour évaluer numériquement dx ˇ
d sinpxq ˇ
, dérivée
ˇ approx
de la fonction sinpxq et la comparer avec la valeur exacte connue d sinpxq ˇ
dx ˇ
“ cospxq.
exact
Les deux valeurs de la dérivée de sinpxq ainsi calculées seront achées sur un intervalle
r´5; `5s en utilisant un pas h=0,05. Vérier ainsi que la formule approchée donne
un résultat correct.
7. Utiliser plusieurs valeurs du pas : h “ 0, 5 , h “ 5 10´2 , h “ 5 10´3 , h “ ˇ 5 10
´4
Page 16
3. Ecrire le programme prgnumerov2.cxx, inspiré de prgnumerov1.cxx et utilisant le
module deriv4points, qui ache dans la sortie standard la valeur de l'énergie test
Et ainsi que les estimations de φ9 l pxm q et φ9 r pxm q. Format sur une ligne :
Et φ9 l pxm q φ9 r pxm q
3.7 Conclusion
Cette séquence doit avoir permis de réaliser une première implémentation du problème
de physique. Elle est qualiée de manuelle car le programme nal écrit en C++ ne
permet a priori de ne traiter qu'un cas très particulier, celui du potentiel carré.
Ce premier travail permet néanmoins d'identier les diérents composants mise en
÷uvre dans la méthode de Numerov : structures de données (tableaux. . .), éléments al-
gorithmiques (boucles, branchements conditionnels. . .).
Il est néanmoins temps de passer à la vitesse supérieure en exploitant des concepts
du C++ plus élaborés et plus puissants, de manière à concevoir un outil logiciel pour
aborder la question de la résolution de l'équation 1 dans un cadre général.
Page 17
4 Séquence 2 : approche générique du problème
4.1 Introduction
Avant de commencer à concevoir des programmes utilisant des notions plus sophisti-
quées du C++, nous allons reconsidérer le projet en identiant les composants fondamen-
taux de la résolution du problème. On va donc se poser des questions un peu théoriques
avant de passer derrière le clavier et de se mettre à taper du code C++ bille en tête ! On
utilisera modestement le formalisme du langage de modélisation objet UML (Unied Mo-
deling Language 3 ) qui permet de représenter graphiquement l'architecture des éléments
logiciels participant à la conception d'un projet et leurs relations.
Nous partirons de la formalisation mathématique du problème physique (équation 1).
Nous nous poserons la question de traduire le problème en terme logiciel, avant de
proposer une implémentation pratique dans le langage de programmation C++.
La démarche que nous adopterons n'est pas la seule possible. En matière de pro-
grammation, il existe très souvent de nombreuses manières d'implémenter la solution
d'un problème. Déterminer la meilleure façon de procéder n'est pas une tâche toujours
évidente. Après tout, nous sommes intéressés ici par la solution numérique d'un pro-
blème de physique et on pourrait s'interroger sur l'intérêt d'utiliser tel ou tel langage
de programmation (C, C++, Fortran, Ada, Python. . .), tel ou tel algorithme (Numerov,
diérences nies. . .), pourvu qu'au nal on produise des résultats corrects.
Cependant, parmi l'ensemble des possibilités pour aborder un problème :
Il en existe toujours plus de mauvaises que de bonnes !
Nous essayerons de dénir et d'implémenter une approche du problème qui fasse partie
des meilleures possibles à notre portée, c'est-à-dire dans le cadre des compétences que
nous sommes capables de déployer sur la base de nos connaissances.
La notion même de meilleure approche dépend du contexte dans lequel on tra-
vaille. Une technique éprouvée et ecace pour un certain type de problème pourra s'avé-
rer moins performante, voire complètement inadaptée dans un autre cas. L'objectif de
ce travail est non seulement d'éclairer un problème physique particulier au moyen des
méthodes numériques et de leur mise en ÷uvre au moyen de la programmation informa-
tique, mais également de transmettre des outils d'analyse et une maîtrise de techniques
dont l'acquisition permettra à l'avenir d'aborder d'autres problèmes scientiques.
Page 18
environnement. On peut ici raisonnablement considérer trois éléments de base dans ce
problème :
1. la description du système physique (ici : l'électron dans le potentiel électrostatique
du noyau de l'atome d'hydrogène dans une version simpliste à une dimension) ;
elle constituera une donnée d'entrée fondamentale du problème,
2. l'algorithme (ici : la méthode de Numerov) ; il est le c÷ur du programme, c'est le
siège de tous les calculs numériques et donc de la consommation du CPU de l'or-
dinateur ; il travaille à partir de la donnée d'entrée décrivant le système physique,
3. la description de la solution du problème (ici : les énérgies propres du système et
les fonctions d'onde associées) ; c'est la donnée attendue en sortie de l'algorithme
au terme du traitement du problème. Cette donnée pourra ensuite être analysée,
visualisée graphiquement. . .
On peut aisément justier cette approche. On peut en eet imaginer de pouvoir ré-
soudre l'équation de Schrödinger pour l'atome d'hydrogène avec une autre méthode que
celle de Numerov, par exemple avec une méthode des diérences nies. De même, la mé-
thode de Numerov peut être utilisée pour calculer la solution de l'équation de Schrödinger
pour un nucléon dans le potentiel nucléaire d'un noyau atomique. Dans les deux cas, on
attend une solution qui puisse s'exprimer sous la même forme, c'est-à-dire une liste de
valeurs propres En (n P N ˚) et les fonctions d'onde φn pxq qui leur sont associées.
Le diagramme ci-dessous illustre que les deux concepts de système physique et
d' algorithme sont considérés indépendamment et qu'on devrait pouvoir en principe
associer un système physique arbitraire à un algorithme arbitraire.
Page 19
Systèmes physiques Algorithmes
interface
Couplage
Système physique X Algorithme A
générique
interface interface
Système physique Consomme Algorithme Produit Solution
On voit ici qu'il faut d'abord fournir la description du système physique avant toute
chose, ensuite l'algorithme utilise cette donnée pour eectuer des calculs, et enn on peut
Page 20
extraire de l'algorithme une expression de la solution du problème (En , φn pxq et n P N ˚).
On pourrait tout à fait représenter ce protocole de la même manière qu'on représente les
fonctions en mathématique (gure 11).
y = f pxq
Solution = Algorithme(Système physique)
Page 21
Le format d'achage en mode texte utilisera au moins 10 chires signicatifs et la
disposition suivante :
Colonne 1 Colonne 2
x0 Vs px0 “ ´2Åq
x1 Vs px1 “ x0 ` ∆xq
x2 Vs px2 “ x1 ` ∆xq
.. ..
. .
xmax Vs pxmax “ `2Åq
V pxq V pxq
a a
x1 x x1 x
O O
´V0 ´V0
Page 22
4.5 Interface de la fonction potentiel
On voit dans l'exercice [2.1] comment programmer diérents types simples de fonctions
potentiel . Cela signie que le système physique traité par l'algorithme de Numerov
sera totalement décrit par :
‚ la masse m représentée en C++ par un réel (en double précision) :
double m;
‚ le potentiel V pxq sera représenté en C++ par une fonction ayant comme signature :
double v(double);
Page 23
4.6 Exercice [2.2]
Questions :
1. Sur la base du programme prgfunc0.cxx de l'exercice [2.1], écrire un programme
prgfunc1.cxx qui ache, dans le même format que prgfunc0.cxx, les valeurs des
potentiels Vs , Vt et Vh pour x P r´10Å; `10Ås. Le programme permettra d'abord la
saisie des paramètres V0 et a qui seront transmis aux trois fonctions potentiel.
2. Produire le graphique.
3. Ajouter une fonction potentiel Va pxq telle que représentée sur la gure 13.
V pxq
x1 x
O
´V1
´V0
Page 24
De manière générale, il n'y a aucune raison qu'une autre fonction potentiel utilise
cette même signature. On peut envisager tous les cas de gure, y compris qu'une fonction
potentiel particulière dépendant de l'âge du capitaine ! En bref, on n'a pas encore trouvé
la solution pour décrire de manière générique l'interface de la fonction potentiel.
Notre objectif va donc être de restaurer une interface typique de l'équation 8, souhai-
table parce que simple, en permettant de nouveau à l'algorithme de manipuler n'importe
quelle fonction potentiel d'une manière uniée. Un indice important nous est livré en
considérant, par exemple pour le potentiel Va , la nature des diérents arguments de la
fonction Va px, V0 , V1 , aq lors de son invocation dans le programme prgfunc1.cxx. On
constate qu'à chaque fois que la fonction est invoquée dans la boucle d'achage, la va-
leur de x est modiée, alors que les valeurs des arguments V0 , V1 et a sont inchangées,
ces dernières ayant été dénies par l'utilisateur au tout début du programme en tant que
constantes fondamentales du système. Ceci nous montre clairement que la nature même
des paramètres V0 , V1 et a dière fondamentalement de celle de x. Les valeurs V0 , V1 et
a sont des paramètres statiques, qui tous ensemble donnent son identité à la fonction
Va , alors que x est une valeur dynamique qui dénit une grandeur physique (la position
sur l'axe x1 Ox) extérieure à la qualication de la fonction.
On aimerait donc pouvoir réécrire l'interface de la fonction Va sous cette forme :
"
R Ñ R
Va pV ,V ,aq (12)
Þ Ñ Va 0 1 pxq
x Ý
Page 25
vsth_params et va_params qui représentent respectivement les jeux de paramètres des
potentiels Vs , Vt et Vh d'une part (ils utilisent tous en eet a et V0 ), et du potentiel Va
d'autre part (utilisant V1 en plus de a et V0 ). Dans ce contexte, une structure vws_params
représenterait utilement un potentiel VW S de type Wood-Saxon bien connu en physique
nucléaire.
D'autre part si l'on souhaite manipuler des fonctions respectant l'interface (14), il
nous faudra bien trouver une façon de le faire en C. La proposition est ici d'utiliser, pour
toutes les fonctions potentiel imaginables, la signature C suivante :
double v(double x_, void * params_);
Page 26
30
31 double dx = 0.01;
32 std::cout.precision(10);
33 for (double x = -10.0; x <= 10.0 + 0.5 * dx; x += dx)
34 {
35 // Invocation de la fonction potentiel avec l'argument
36 // de position 'x' et l'adresse anonyme 'vs_params_ptr'
37 // qui pointe en mémoire vers la structure contenant les
38 // paramètres qualifiant la fonction :
39 std::cout << x << ' ' << vs(x, vs_params_ptr) << std::endl;
40 }
41 return 0;
42 }
43
44 double vs(double x_, void * params_)
45 {
46 // Reinterprétation de l'adresse anonyme 'params_' comme
47 // l'adresse d'une structure 'pars' de type 'vsth_params' :
48 vsth_params * pars = static_cast<vsth_params *>(params_);
49
50 // Extraction des paramètres statiques du potentiel stockés
51 // dans la structure. La fonction se charge ainsi elle-même
52 // d'accéder aux paramètres qui la qualifient, avant de les utiliser :
53 const double & a = pars->a;
54 const double & v0 = pars->v0;
55
56 // Calcul de la valeur du potentiel carré :
57 if (std::abs(x_) > +0.5 * a) return 0.0;
58 return -v0;
59 }
vsth_params
Page 27
3. Compléter le programme de manière à traiter d'un potentiel VW S de type Wood-Saxon
dont la forme mathématique est donnée par :
V0
VW S pxq “ ´ (15)
1 ` expp 0,5ˆx´a
b
q
On prendra par exemple : V0 =7,2 , a=5,6 et b=0,5 (sans aucune considération d'unités
physiques d'énergie et de longueur).
Page 28
7 {
8 double (*op)(double) = &mult3; // On déclare la variable 'op' de type
9 // pointeur de fonction de R dans R et
10 // on l'initialise avec l'adresse de
11 // la fonction 'mult3'.
12 double x = 2.0;
13 double y = (*op)(x); // On invoque 'mult3' à travers le pointeur 'op'
14 // en déréférençant ce dernier.
15 return 0;
16 }
Page 29
33
34 // Déclaration d'une variable pointeur de fonction initialisée
35 // avec l'adresse de la fonction potentiel carré :
36 potential_func_type v_pot = &vs;
37
38 // Vérification :
39 std::clog << "Adresse de la fonction vs : " << ((void*) &vs) << '\n';
40 std::clog << "Adresse stockée dans v_pot : " << ((void*) v_pot) << '\n';
41
42 // Algorithme principal :
43 for (double x = -10.0; x <= 10.0 + 0.5 * dx; x += dx)
44 {
45 std::cout << x << ' ' << (*v_pot)(x, v_params_address) << std::endl;
46 }
47
48 return 0;
49 }
50
51 double vs(double x_, void * params_)
52 {
53 vsth_params * pars = static_cast<vsth_params *>(params_);
54 const double & v0 = pars->v0;
55 const double & a = pars->a;
56 if (std::abs(x_) > +0.5 * a) return 0.0;
57 return -v0;
58 }
On constate désormais qu'à l'intérieur de la boucle d'achage, il n'est plus fait au-
cune référence explicite ni à la nature de la structure contenant les paramètres statiques
du potentiel, ni à la fonction potentiel utilisée eectivement. La boucle, qui constitue ici
l'algorithme principal du programme, ignore tout des détails internes de la fonction po-
tentiel Vs dont elle est chargée d'acher les valeurs sur un intervalle donné. Nous sommes
parvenus à concevoir une interface générique au problème.
Cette technique est utilisée dans de nombreuses situations. Citons en particulier celui
de la GNU Scientic Library (Bibliothèque Scientique GNU 4 ) qui utilise ce mécanisme
(type gsl_function) pour manipuler de manière transparente des fonctions arbitraires
dans les algorithmes numériques (recherche de racine, calcul de dérivée, intégration. . .).
4. http://www.gnu.org/software/gsl/
Page 30
Remarque : Un tel mécanisme peut être illustré par l'analogie suivante : une personne
habitant à l'adresse A souhaite faire parvenir en recommandé un courrier (une lettre par
exemple) à une autre personne habitant à l'adresse B. Pour ce faire, elle utilise un service
postal qui lui propose le protocole suivant pour pouvoir s'acquitter de cette tâche :
fournir l'adresse A de l'expéditeur,
fournir l'adresse B du destinataire,
payer un timbre en rapport avec le poids, l'encombrement du courrier à transporter
et la distance AB,
déposer le courrier dans la boîte à lettres à l'adresse A,
attendre un temps proportionnel à la distance AB,
réceptionner le reçu à l'adresse A.
La mission de transport peut alors être complètement réalisée. Notons ici que ce protocole,
ou interface fonctionnelle, peut être réalisé indépendamment du fait que le service postal
transporte une lettre ou un colis. Le facteur n'a même pas à savoir ce qu'il transporte. Une
forme d'anonymat et de condentialité est ainsi garantie et les détails internes au sujet
du contenu de l'objet transporté (enveloppe/colis) sont totalement ignorés par le service
l'algorithme alors qu'ils sont parfaitement connus par l'expéditeur et le destinataire
qui ont la possibilité d'accéder au contenu de l'objet à un moment particulier (fermeture
de l'enveloppe ou du colis avant l'envoi, ouverture à la réception).
Page 31
4.12 L'interface de la fonction potentiel selon C++ : objets fonc-
tion
La technique vue précédemment permet de faire connaître les paramètres statiques de
la fonction potentiel à cette dernière en les dissimulant derrière un argument pointeur
anonyme. Cette opération est réalisée au moment de l'invocation de la fonction :
paramètres statiques
+
V0 P R
structure
anonymat
a P R
x0 P R Vs pxq Vs px0 q P R
position fonction potentiel valeur calculée du potentiel
On va maintenant montrer comment exploiter des possibilités du langage C++ pour
obtenir une interface générique au travers de la mise en ÷uvre de la programmation
orientée objet.
Nous allons nous appuyer sur l'observation suivante : les structures que nous avons
créées dans les exercices précédents (structure vsth_params pour les fonctions vs, va. . .)
sont des compositions de données très simples. Jusqu'à maintenant, il s'agit au maximum
de trois nombres réels placés dans un même sac (un multiplet contenant : V0 , a. . .).
Un tel assemblage (composition) de variables pourrait fort bien être considéré comme un
objet, c'est-à-dire comme l'instance d'une classe (voir le cours de L2 Physique Langage
C++). Ceci est en particulier justié si l'on considère les faits suivants, par exemple dans
le cas du puits carré de potentiel Vs tel que nous l'avons déni jusqu'à maintenant :
‚ la valeur de V0 est nécessairement positive (éventuellement nulle si l'interaction
est évanescente),
‚ la valeur de a (une longueur) est nécessairement positive (non nulle).
Cela signie qu'en toute rigueur V0 et a sont plus que de simples nombres réels ; ce sont des
nombres réels dont les valeurs sont contraintes par le fait qu'à eux deux ils représentent
un système physique avec des contraintes fortes.
L'usage d'une structure de type :
C++
4 struct vsth_params
5 {
6 double v0;
7 double a;
8 };
ne permet pas d'exprimer de telles contraintes. Rien ne nous empêche dans ce cas d'initia-
liser malencontreusement les paramètres V0 et a avec des valeurs négatives parfaitement
absurdes, comme dans l'exemple suivant :
C++
1 vsth_params vs_pars;
2 vs_pars.v0 = -666.0; // unité arbitraire
3 vs_pars.a = -1.e+38; // unité arbitraire
Page 32
Ceci peut être considéré comme une fragilité de la conception en C. La structure de
données vsth_params n'est pas sécurisée.
Les classes sont l'outil parfait pour représenter une composition de données pri-
vées comme les grandeurs V0 et a tout en fournissant des mécanismes pour garantir
la cohérence de ces données : constructeur(s), destructeur, méthodes publiques acces-
seur /mutateur (getter /setter ), vérication de la validité de l'état interne de l'objet. . .
Ceci devrait orir une technique sûre de gestion des paramètres statiques qualiant n'im-
porte quelle fonction potentiel .
De plus, le C++ autorise la dénition (ou la redénition) des opérateurs reconnus par
le langage (+ , * ,. . .). En particulier, il est possible d'équiper une classe avec l'opérateur
... operator()(...) (invocation avec liste d'arguments et type de retour) qui sera
considéré tout simplement comme une des méthodes de la classe. C'est ce qu'on appelle
un objet fonction.
Le programme prgobjfunc0.cxx présente un exemple élémentaire de classe d'objet
fonction. La classe scaled_sum possède un unique attribut privé (ligne 9). On y déclare
(ligne 7) l'opérateur () de telle sorte que celui-ci accepte trois arguments de type double
et retourne une valeur également de type double. On peut donc considérer qu'il s'agit
d'une fonction de R3 dans R. Un objet fonction f de cette classe est instancié dans la
fonction principale (ligne 14) et on invoque son opérateur () en lui passant trois arguments
réels conformément à sa signature (ligne 18).
prgobjfunc0.cxx
1 class scaled_sum
2 {
3 public:
4 scaled_sum(double factor_);
5 // Déclaration de l'opérateur '()' comme une méthode
6 // publique de la classe 'scaled_sum' :
7 double operator()(double x_, double y_, double z_);
8 private:
9 double _factor_;
10 };
11
12 int main(void)
13 {
14 scaled_sum f(4.0); // Déclaration de 'f', objet-fonction de type 'scaled_sum'
15 double a = 2.0;
16 double b = 3.0;
17 double c = -6.0;
18 double y = f(a,b,c); /* Invocation de la méthode 'operator()'
19 * de la classe 'scaled_sum' avec 3 arguments double.
20 */
21 y = f.operator()(a,b,c); // Autre manière d'invocation de l'opérateur
22 return 0;
23 }
24
25 scaled_sum::scaled_sum(double factor_)
26 {
27 _factor_ = factor_;
28 }
Page 33
29
30 // Définition de l'opérateur '()' de R3 dans R :
31 double scaled_sum::operator()(double x_, double y_, double z_)
32 {
33 return _factor_ * (x_ + y_ + z_);
34 }
scaled_sum
Remarque : On ne doit pas perdre de vue que l'opérateur () est en fait une méthode
normale de la classe comme il est montré à la ligne 21 dans laquelle il est invoqué de
manière traditionnelle. Cette notation est toutefois moins naturelle que celle de la ligne
18.
Page 34
objet-fonction potentiel Vs
vs
V0 P R`˚
a P R`˚ u paramètres statiques (attributs)
x0 P R operator px P Rq Vs px0 q P R
position valeur calculée du potentiel
Questions :
1. Ecrire un programme prgobjfunc1.cxx qui reproduit le comportement du pro-
gramme prgfunc3.cxx de l'exercice [2.3]. On utilisera le formalisme C++ des objets
fonction pour implémenter la classe vs dont le diagramme UML est :
vs
− _v0_ : double
− _a_ : double
+ operator()(double)
2. Le potentiel carré Vs sera représenté au moyen d'un objet fonction de la classe vs. La
classe vs possèdera un constructeur initialisant convenablement les paramètres V0 et
a et un opérateur () adapté à la situation.
3. On procèdera de même pour les quatre autres potentiels Vt , Vh , Va et VW S :
vt vh
+ operator()(double) + operator()(double)
va vws
+ operator()(double) + operator()(double)
Page 35
à l'algorithme de calculer la valeur du potentiel sans connaître la nature réelle de la
fonction.
Avec les objets fonction du C++, on n'a pas encore gagné la partie. Chacune des
classes du programme prgobjfunc1.cxx de l'exercice précédent (vs, vt, vh, va et vws)
possède l'interface d'une fonction de R dans R. Dans ce sens, elles remplissent toutes le
contrat et peuvent être considérées comme de bons modèles de potentiel pour l'équation
de Schrödinger.
Il ne nous reste plus qu'à trouver le moyen de les faire manipuler par l'algorithme de
Numerov. Le C++ propose deux techniques pour atteindre la généricité tant désirée :
‚ Une approche statique mettant en ÷uvre le concept de patron (en anglais : tem-
plate ). Cette approche implique de demander au compilateur C++ de générer,
au moment de la compilation, l'interface (et donc le code correspondant) entre le
modèle de l'algorithme A et le modèle du système physique Σ (voir la gure 9).
Ceci demande d'utiliser des éléments syntaxiques spéciques du langage C++.
‚ Une approche dynamique s'appuyant sur le concept d'héritage de classe, plus par-
ticulièrement sur celui de classe mère abstraite. Dans ce cas, le compilateur
C++ délègue l'établissement de l'interface entre l'algorithme A et le modèle Σ
au moment de l'exécution du programme grâce à des mécanismes propres au lan-
gage.
Nous utiliserons ici la seconde approche. Ce choix n'enlève en rien aux mérites de la
première technique qui s'avère très ecace et très puissante dans de très nombreux cas.
Elle est d'ailleurs à la base de la conception de la bibliothèque standard de patrons du
C++ (STL 5 ) et de la bibliothèque Boost.
On a déjà rencontré le concept de composition à travers la mise en ÷uvre des
structures (en C) et des classes (en C++). Dans la forme simple qu'on a manipulée avec
la structure vsth_params dans le programme d'exemple prgfunc4.cxx, la composition
exprime l'idée d'appartenance. Ainsi la variable (objet) vs_pars déclarée en ligne 19
contient les variables (attributs) v0 et a, toutes deux de type double.
Dès lors que la relation entre deux objets A et B dans un programme peut être
naturellement conçue comme une relation d'appartenance exprimée par :
A possède B
on peut sérieusement envisager de créer une structure (ou une classe) pour le type de
l'objet A, avec un attribut représentant B .
Exemple :
C++
struct a_type
{
int b; // Par construction, une variable 'b' est contenue dans tout
// objet de type 'a_type'.
};
int main(void)
{
a_type a;
a.b = 1; // On manipule la variable 'b' contenue dans la variable 'a'.
5. http://www.cplusplus.com/reference/stl/
Page 36
return 0;
}
struct wheel
{
bool puncture; // crevaison
double pressure; // unité: bar
};
struct car
{
wheel front_right;
wheel front_left;
wheel back_right;
wheel back_left;
wheel spare;
};
Ici la relation entre le type wheel et le type car est clairement exprimée. Il ne viendrait
(sans doute) pas à l'idée d'un programmeur de créer un modèle de roue qui contient une
voiture ! On voit ainsi que la composition permet d'exprimer un certain type de relation
qui apparait naturellement lorsque l'on souhaite modéliser un système. En UML cela est
représenté par le diagramme :
0..1 4..5
car wheel
Ainsi, ce schéma dénit qu'une voiture possède 4 ou 5 roues (4..5) et qu'une roue peut
appartenir à aucune ou une seule voiture (0..1).
Il existe cependant une autre forme de relation qu'on peut établir entre diérents
éléments d'un système. Reprenons ainsi l'exemple de la voiture ! Il est établi qu'une
voiture possède des roues. Mais c'est aussi le cas d'une bicyclette, quoiqu'en nombre et
position diérents. Ainsi on exprimerait aisément :
C++
struct bike
{
wheel front;
wheel back;
};
Page 37
wheel car bike
+ puncture: bool + front_right: wheel + front: wheel
+ pressure: double + front_left: wheel + back: wheel
+ back_right: wheel
+ back_left: wheel
+ spare: wheel
Imaginons maintenant que notre objectif soit de concevoir un programme simulant les
conditions de circulation de divers types de véhicule sur un réseau routier. On s'interesse
à l'inuence du comportement des conducteurs sur le trac en fonction de leur état :
expérience de conduite, état de fatigue, alcoolémie. . . et de la densité de la circulation d'un
réseau donné. On dénit ainsi utilement un type de données représentant un conducteur :
C++
struct driver
UML :
{
driver unsigned int experience_points;
+ experience_points: unsigned int double tiredness;
+ tiredness: double
+ alcoholization: double double alcoholization; // g/l
};
Si l'on considère maintenant qu'une voiture, aussi bien qu'une bicyclette, possède
un conducteur pour pouvoir évoluer sur le réseau, on est amené assez naturellement à
compléter la conception des classes car et bike :
C++
struct car
{
wheel front_right;
wheel front_left;
wheel back_right;
wheel back_left;
wheel spare;
driver pilot; // Composition additionnelle
};
struct bike
{
wheel front;
wheel back;
driver pilot; // Composition additionnelle
};
On voit ici qu'une auto et un vélo ont quelque chose en commun : un pilote (chauf-
feur/cycliste) ! En fait on peut traduire cela par le fait suivant :
Une auto et un vélo sont des véhicules pilotés.
Le C++ permet d'exprimer cette notion au travers de l'héritage de classe. Ainsi on peut
dénir une nouvelle structure représentant un véhicule piloté qui possède un pilote :
Page 38
C++
struct driven_vehicle
{
driver pilot;
};
On spécie ensuite le fait qu'une voiture et un vélo sont des véhicules pilotés grâce à
l'héritage de classe. La classe driven_vehicle devient ici la classe mère des deux
classe lles car et bike. On dit que les classes car et bike dérivent ou héritent de la
classe driven_vehicle :
C++
1 struct car : public driven_vehicle
2 {
3 wheel front_right;
4 wheel front_left;
5 wheel back_right;
6 wheel back_left;
7 wheel spare;
8 };
9
10 struct bike : public driven_vehicle
11 {
12 wheel front;
13 wheel back;
14 };
La représentation UML des relations entres les classes ci-dessus est représentée sur le
diagramme suivant :
car bike
+ front_right: wheel + front: wheel
+ front_left: wheel + back: wheel
+ back_right: wheel
+ back_left: wheel
+ spare: wheel
Page 39
C++
1 int main(void)
2 {
3 car ma_charette;
4 ma_charette.front_right.puncture = false; // ^
5 ma_charette.front_right.pressure = 2.7; // | Accès aux
6 ma_charette.front_left.puncture = false; // | attributs
7 ma_charette.front_left.pressure = 2.7; // | propres
8 ma_charette.back_right.puncture = false; // | de la
9 ma_charette.back_right.pressure = 2.7; // | classe fille.
10 ma_charette.back_left.puncture = false; // |
11 ma_charette.back_left.pressure = 2.7; // v
12 ma_charette.driver.experience_points = 120; // ^ Accès à l'attribut
13 ma_charette.driver.tiredness = 0.23; // | de la classe mère.
14 ma_charette.driver.alcoholization = 0.0; // v
15
16 bike ton_clou:
17 ton_clou.front.puncture = false; // ^
18 ton_clou.front.pressure = 1.3; // | Accès aux attributs propres
19 ton_clou.back.puncture = false; // | de la classe fille.
20 ton_clou.back.pressure = 1.3; // v
21 ton_clou.driver.experience_points = 64; // ^ Accès à l'attribut
22 ton_clou.driver.tiredness = 0.11; // | de la classe mère.
23 ton_clou.driver.alcoholization = 0.0; // v
24
25 return 0;
26 }
Ce mécanisme est également valide pour les méthodes publiques (et protégées) de la classe
mère qui peuvent être réutilisées par les classes lles.
A noter toutefois que seuls les attributs ou méthodes publics de la classe mère peuvent
être utilisés librement à partir des classes lles. Les classes lles peuvent de plus utiliser
en interne les attributs ou méthodes protégés de la classe mère (c'est-à-dire de l'intérieur
de leurs propres méthodes).
Page 40
lignes de code qui n'ont pas besoin d'être dupliquées dans chacune des classes spécialisées.
La relation entre une classe lle A et sa classe mère B peut être exprimée par :
A est un B
On dit également que A est une spécialisation de B . Ainsi, la classe vwell est la dépositrice
des paramètres statiques de tous les potentiels qui sont de type puits de profondeur et de
largeur xés. La classe vs (le puits carré) fait partie d'une telle famille de potentiels et
héritent naturellement des caractéristiques internes de vwell : V0 et a.
Toutefois, l'objectif à atteindre est celui de la généricité. En eet nous devons trouver
un moyen pour que l'algorithme de Numerov utilise n'importe quel objet fonction po-
tentiel de manière transparente. Ce dont a besoin l'algorithme est de pouvoir calculer la
valeur du potentiel V px0 q associée à une position x0 . Autrement dit, c'est l'interface de
l'opérateur d'invocation operator() qui doit être mis en commun à tous les potentiels.
Le C++ propose un mécanisme d'héritage particulier pour représenter une telle fonc-
tionnalité imposée aux objets fonction potentiel : elles doivent hériter d'une même classe
abstraite possédant au moins une méthode virtuelle pure.
L'exemple suivant montre la syntaxe de déclaration d'une classe abstraite base_vehicle
qui dénit une interface générique pour tous les types de véhicules à travers un ensemble
de méthodes publiques :
C++
1 struct base_vehicule
2 {
3 public:
4 // Méthodes virtuelles pures de l'interface de la classe abstraite,
5 // c'est le contrat que doivent remplir les classes filles concrètes :
6 virtual void start() = 0;
7 virtual void stop() = 0;
8 virtual void turn_left() = 0;
9 virtual void turn_right() = 0;
10 }
On s'attend alors à ce que la classe car, considérée comme un certain type de véhicule,
dérive de la classe base_vehicule et fournisse une implémentation de ces méthodes, an
de remplir son contrat .
C++
1 struct drone : public base_vehicule
2 {
3 wheel front_right;
4 wheel front_left;
5 wheel back;
6 oper remote_control;
7 // Implémentation concrète de l'interface imposée
8 // par la classe mère 'base_vehicule' :
9 virtual void start();
10 virtual void stop();
11 virtual void turn_left();
12 virtual void turn_right();
Page 41
13 };
14
15 struct driven_vehicle : public base_vehicule
16 {
17 driver pilot;
18 // Pas d'implémentation de l'interface de la classe mère;
19 // cette classe est également abstraite.
20 // Les classes filles devront implémenter les 4 méthodes
21 // pour représenter des objets concrets.
22 };
23
24 struct car : public driven_vehicle
25 {
26 public:
27 wheel front_right;
28 wheel front_left;
29 wheel back_right;
30 wheel back_left;
31 wheel spare;
32
33 // Implémentation concrête de l'interface imposée
34 // par la classe grand-mère 'base_vehicule' :
35 virtual void start();
36 virtual void stop();
37 virtual void turn_left();
38 virtual void turn_right();
39 }
La représentation UML des relations entres les classes ci-dessus est représenté sur le
diagramme de la gure 14.
Page 42
Classe mère abstraite base_vehicle
+ start() +
+ stop() Méthodes virtuelles pures
+ turn_right() (interface contractuelle)
+ turn_left()
Classe intermédiaire abstraite
(ne réalise pas l'interface)
Généralisation
Spécialisation
driven_vehicle drone
+ front_right: wheel
+ pilot: driver + front_left: wheel
+ back: wheel
+ remote_control: oper
+ start()
+ stop()
+ turn_right()
car bike + turn_left()
+ front_right: wheel + front: wheel
+ front_left: wheel + back: wheel
+ back_right: wheel + spare: wheel
+ back_left: wheel
+ start()
+ spare: wheel
+ stop()
+ start()
+ turn_right()
+ stop()
+ turn_left()
+ turn_right()
+ turn_left()
Classes spécialisées concrêtes
(réalisent l'interface)
Page 43
4.18 Retour sur les unités
An de nous préparer à pouvoir modéliser n'importe quel système physique, il nous
faut trouver un moyen de travailler quelque soit l'échelle de distance et d'énergie carac-
téristique du système. La solution proposée dans la section 3.2 ne fonctionne que si l'on
utilise une combinaison particulière d'unités et de constantes fondamentales exprimées
dans un certain système d'unités. Il est possible de mettre en oeuvre une technique qui
va nous permettre de gérer n'importe quelle situation. Il s'agit pratiquement de dénir
des constantes C++ qui prennent des valeurs exprimant les rapports relatifs entre les
unités fondamentales d'un système conventionnel d'unités comme le système SI. Dans un
second temps, on dénira des constantes C++ représentant les constantes fondamentales
usuelles à partir du jeu des unités de base. Enn des unités dérivées pourront être dénies
à partir des unités de base et de ces constantes fondamentales. Cette approche est inspirée
de la bibliothèque CLHEP 6 .
On trouvera sur le Moodle :
http://foad2.unicaen.fr/moodle/course/view.php?id=11655
le chier units_constants.hpp qui dénit un ensemble d'unités et constantes fondamen-
tales usuelles. Le programme d'exemple suivant illustre l'utilisation de ce composant :
C++
1 // Standard library:
2 #include<iostream>
3
4 // The Numerov project:
5 #include<units_constants.hpp>
6
7 int main(void)
8 {
9 double m = 0.13 * numerov::kilogram;
10 double v = 0.2 * numerov::meter / numerov::second;
11 double k = 0.6 * numerov::newton / numerov::meter;
12 double x = 0.12 * numerov::meter;
13 double ec = 0.5 * m * v * v;
14 double ep = 0.5 * k * x * x;
15 std::cout << "x = " << x / numerov::mm << " mm" << std::endl;
16 std::cout << "m = " << m / numerov::g << " g" << std::endl;
17 std::cout << "Ec = " << ec / numerov::joule << " J" << std::endl;
18 std::cout << "Ec = " << ec / numerov::keV << " keV" << std::endl;
19 std::cout << "Ep = " << ep / numerov::joule << " J" << std::endl;
20 std::cout << "Ep = " << ep / numerov::keV << " keV" << std::endl;
21 std::cout << "e = " << numerov::electron_charge / numerov::coulomb
22 << " C" << std::endl;
23 std::cout << "c = " << numerov::c_light / (numerov::cm / numerov::ns)
24 << " cm/ns" << std::endl;
25 std::cout << "hbarc = " << numerov::hbarc / (numerov::MeV * numerov::fm)
26 << " MeV.fm" << std::endl;
27 return 0;
28 }
6. http://proj-clhep.web.cern.ch/proj-clhep/
Page 44
4.19 Exercice [2.8] : modélisation C++ du système physique
Nous sommes maintenant n prêts pour créer une classe représentant le système phy-
sique. D'après ce qu'on a discuté dans la section 4.3, le système est totalement décrit
par :
‚ la masse du système,
‚ le potentiel d'interaction.
L'utilisation d'une classe abstraite comme classe mère de toutes les classes d'objets
fonction potentiel va nous permettre de manipuler toute forme de potentiel au travers
d'un pointeur attribut de la classe. Le diagramme UML de la classe physical_system à
créer est présenté sur la gure 15.
physical_system vbase
physical_system vbase
0..* 1
Questions :
1. Créer le module C++ nommé numerov_utils. Dans l'espace de nom numerov, dénir
la classe abstraite vbase interface de tous les objets fonction potentiel ainsi que la
classe physical_system.
2. Créer le module C++ nommé potentials qui implémente, dans l'espace de nom
numerov, tous les objets fonction potentiel utilisés dans le programme prgobjfunc3.cxx.
3. Créer un programme prgobjfunc4.cxx qui reprend les fonctionnalités de prgobjfunc3.cxx
en s'appuyant sur les modules numerov_utils et potentials.
Page 45
4.20 Modélisation de la solution
Si l'on considère les trois éléments du projet exposés sur la gure 9, nous n'avons
implémenté que le premier. En attendant d'implémenter l'algorithme sous la forme d'une
classe, on peut désormais s'attaquer au problème de la modélisation de la solution du
problème.
On s'attend à ce que la résolution de l'équation de Schrödinger soit constituée d'un
ensemble de valeurs propres de l'énergie Ek associées chacune à une fonction d'onde φk pxq.
On représentera une telle relation en UML de la façon suivante :
où l'objet ensemble des solutions possède un certain nombre d'objets solutions indé-
pendantes (couple Ek , φk pxq). Ce nombre peut éventuellement être nul en l'absence de
solution.
Ainsi, on pourra créer une classe numerov_solution dont le diagramme UML est :
représente Ek
numerov_solution
− _energy_ : double
− _x_ : std::vector<double> représente les xj P ra, bs
− _phi_ : std::vector<double>
L'ensemble des solutions pourra être représenté ultérieurement par un tableau d'objets de
type numerov_solution, par exemple au moyen d'un std::vector<numerov_solution>,
classe générique patron disponible dans la bibliothèque standard STL.
Page 46
4.22 Modélisation de l'algorithme
Enn il est temps de proposer une modélisation orientée objet de l'algorithme de
Numerov. Nous en avons déjà proposé une implémentation élémentaire et non générique
dans l'exercice [1.1]. Il y a donc lieu désormais de concevoir un module numerov_algo
qui va implémenter la méthode de recherche de solution de l'équation 1 dans tous les cas
possibles, grâce à l'approche générique que nous avons implémentée dans cette deuxième
séquence de travail.
Il est ici utile de reprendre le programme prgnumerov2.cxx et d'en identier les élé-
ments dénis dans la fonction principale. Cette analyse du programme original permettra
de dégager les caractéristiques principales d'un objet algorithme.
Quels sont les structures de données utilisées au sein de l'algorithme ? On doit ici consi-
dérer que tout ce qui concerne le système physique (masse de la particule m et fonction
potentiel V pxq) relève de la modélisation de ce système par l'intermédiaire de la classe
physical_system. Il s'agit donc d'une ressource extérieure, référencée par l'utilisateur
par exemple par agrégation.
On utilisera ici l'adresse (pointeur) d'un objet de type physical_system, extérieur à
la classe, à partir duquel l'algorithme réalise les calculs (déni par l'utilisateur).
Ainsi tout ce qui reste est attribuable à l'algorithme lui-même. On établit alors l'in-
ventaire suivant :
les bornes de l'intervalle de travail rxmin ; xmax s (xées par l'utilisateur),
les nombres de pas Nl et Nr pour discrétiser l'espace (xés par l'utilisateur),
la valeur de la coordonnées du point de contrôle xm (xée par l'utilisateur),
les valeurs l et r utilisées pour amorcer la récurrence (3) (xées par l'utilisateur),
la taille des pas hl et hr (calculées automatiquement),
l'énergie de test Et (xée par l'utilisateur),
les tableaux des valeurs ql et qr des équations (4) (calculées automatiquement),
les tableaux des valeurs φl et φr (calculées automatiquement),
les valeurs des dérivées φ9 l pxm q et φ9 r pxm q (calculées automatiquement).
Page 47
une méthode configure(...) qui positionne les paramètres de réglages de
l'objet algorithme (choix de xa , xb , xm , Nl , Nr , l , r ),
une méthode set_physical_system(...) qui dénit une référence interne (par
agrégation) à un objet de type physical_system au sein de l'objet algorithme.
une méthode compute(...) qui réalise le calcul complet de la fonction d'onde
candidate pour l'énergie test Et demandée.
une méthode build_solution(...) qui permet d'extraire un objet de type
numerov_solution de la solution calculée par l'algorithme.
5. Implémenter la classe numerov_algo en prenant soin que le constructeur initialise
correctement les attributs de la classe avec des valeurs raisonnables .
6. Ecrire un ou plusieurs programmes de test qui permettront de retrouver les résultats
déjà établis avec les versions précédentes du programme (n de la séquence 1) et
d'étudier d'autres systèmes physiques que ceux correspondant au potentiel rectangu-
laire.
Potentiel V(x)
0 Fonction d'onde φ candidate
-10
-15
-20
-6 -4 -2 0 2 4 6
x (Ångström)
4.24 Conclusion
A l'issue de cette séquence, on dispose d'un outil générique complet permettant l'étude
de nombreux systèmes physiques. Cet outil est implémenté grâce à un ensemble de classes
et d'interfaces regroupées au sein d'un module écrit en C++. C'est une petite bibliothèque
numérique à part entière sur la base de laquelle il est possible de concevoir des programmes
spécialisés pour l'étude de cas physiques variés tombant dans la portée de la résolution
de l'équation 1.
L'intérêt de cette approche est de mettre à disposition un outil unique qui pourra être
réutilisé de nombreuses fois en se concentrant seulement sur les aspects et questions de
physique et non plus sur les techniques de programmation. L'apprentissage et la maîtrise
de ces techniques de programmation en C++ est donc essentiel pour nous permettre
d'aborder la famille de problèmes physiques qui nous intéressent aujourd'hui.
D'autres avantages viennent naturellement avec cette approche. La séparation claire
entre la mise en ÷uvre de la méthode de Numerov (notre bibliothèque/module) et son
Page 48
utilisation pour un cas particulier (l'étude d'un système physique spécique faite au
moyen d'un programme principal) permet :
de forcer les utilisateurs à analyser un problème de physique particulier de manière
à discriminer clairement les éléments qui relèvent de la physique de ceux qui ne
concernent que la technique numérique.
de maintenir et corriger plus facilement les diérents éléments logiciels mis en jeu
dans le cadre d'une étude.
de travailler de manière collaborative en répartissant les tâches de programmation
entre plusieurs partenaires respectant ensemble les interfaces et concepts proposés
par la bibliothèque.
d'étendre les fonctionnalités des éléments logiciels.
Il faut noter qu'il n'est pas strictement nécessaire d'utiliser un langage orienté objet
comme le C++ pour parvenir à une telle approche générique. Cependant un tel langage
permet de le faire de manière très naturelle au moyen de ses constructions et concepts
qui rendent possible une implémentation claire, ecace et puissante : classes et objets,
héritage, interfaces, foncteurs. . .
Page 49
5 Séquence 3 : étude spécique
On propose ici d'utiliser le module de résolution de l'équation de Schrödinger 1 dans
un cas particulier tiré par exemple de la gure 6.
Ecrire un ou plusieurs programmes permettant une telle étude et en présenter les
résultats marquants : existence et évolution des solutions en fonction des paramètres du
potentiel, analogie avec des systèmes physiques réels.
Page 50