Développement d`un programme de simulation n corps
Transcription
Développement d`un programme de simulation n corps
Gymnase Auguste Piccard Travail de maturité Développement d'un programme de simulation Auteur : Thibault n corps Maître référent : Wildi 3M8 28 octobre 2013 de Schoulepnikoff M. Laurent Résumé Les simulations n corps sont un outil parmi beaucoup d'autres pour étudier le comportement de notre univers. Elles permettent de mieux comprendre certains mécanismes comme la répartition de la matière dans l'univers, la formation des galaxies, les collisions de galaxies, la création et l'évolution d'amas d'étoiles et bien d'autres choses encore. Les simulations que les astronomes font de nos jours sont d'un très grand degré de complexité. On développe des modèles hydrodynamiques qui prennent en considération les températures, les pressions, la composition des gaz. Ces modèles tiennent compte de la création et de la mort d'étoile, ainsi que de la matière noire.Loin d'être de simples simulations gravitationnelles qui contiennent parfois plusieurs centaines de millions de corps, nécessitent de puissants moyens de calcul. Dans le cadre de ce travail, j'ai créé en langage C un programme de simulation n corps an de pouvoir appliquer les rudiments des techniques de simulations utilisées dans les milieux scientiques. Notamment le pas de temps dynamique, technique qui améliore grandement les performances du programme. Bien que le programme soit précis et ecace dans la simulation de systèmes contenant un petit nombre de corps, il laisse à désirer dans des systèmes plus complexes. Il existe néanmoins bien des pistes d'amélioration, comme l'utilisation d'une technique d'intégration plus précise, un pas de temps individuel pour chaque corps, et des techniques de découpage de l'espace à des ns de simplication. Table des matières 1 Introduction 3 2 Simulations 5 1.1 1.2 2.1 2.2 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . n corps Que sont les simulations n corps . . . . . . . . . . . . . . . . . . Les simulations n corps aujourd'hui[16] . . . . . . . . . . . . . . . 3 Méthode de calcul 3.1 3.2 3.3 Méthode d'Euler[1][2] . . . . 3.1.1 Exemple . . . . . . . . Étude du problème à n corps Dénition de l'erreur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pourquoi le langage C ? . . . . . . . . . . . Quelques notions . . . . . . . . . . . . . . . 4.2.1 Compilation . . . . . . . . . . . . . . 4.2.2 Programmation modulaire . . . . . . 4.2.3 Dénition et déclaration . . . . . . . 4.2.4 Fichier code source et chier header 4.2.5 Pointeurs . . . . . . . . . . . . . . . 4.2.6 Bibliothèque SDL . . . . . . . . . . Structure du programme . . . . . . . . . . . Fonctionnement basique du programme . . Utilisation du programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tests du programme . . . . . . . . . . . . . . . . . . . . . 5.1.1 Étude de l'erreur des simulations . . . . . . . . . . 5.1.2 Nombre de corps . . . . . . . . . . . . . . . . . . . Simulation à pas de temps variable . . . . . . . . . . . . . 5.2.1 Nécessité d'un algorithme à pas de temps variable 5.2.2 Algorithme à pas de temps variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Programmation 4.1 4.2 4.3 4.4 4.5 5 Résultats et analyse 5.1 5.2 1 . . . . . . . . . . . . . . . . . . . . . . . . 3 3 5 5 8 8 9 11 13 15 15 16 16 17 17 18 18 19 19 21 21 24 24 24 31 33 33 34 Simulation n corps 5.3 5.4 Thibault Wildi - 3M8 5.2.3 Test du programme à pas de temps Simulations de cas . . . . . . . . . . . . . 5.3.1 Figure de huit à 3 corps . . . . . . 5.3.2 Simulation d'amas . . . . . . . . . Limitations du programme . . . . . . . . . dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Conclusion 6.1 6.2 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Annexe 7.1 7.2 Raccourcis clavier de l'interface graphique Code source . . . . . . . . . . . . . . . . . 7.2.1 main.c . . . . . . . . . . . . . . . . 7.2.2 constants.h . . . . . . . . . . . . . 7.2.3 io_operations.h . . . . . . . . . . . 7.2.4 io_operations.c . . . . . . . . . . . 7.2.5 iterator.h . . . . . . . . . . . . . . 7.2.6 iterator.c . . . . . . . . . . . . . . 7.2.7 display2d.h . . . . . . . . . . . . . 7.2.8 display2d.c . . . . . . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 37 37 39 40 42 42 42 46 46 46 46 48 50 52 55 56 58 60 Chapitre 1 Introduction 1.1 Présentation Je me suis penché, dans ce travail de maturité, sur les lois qui gouvernent la mécanique céleste, plus précisément sur l'une des méthodes utilisées actuellement pour mettre les diérentes théories astronomiques à l'épreuve : les simulations n corps. Ces simulations cherchent à reproduire l'évolution de l'univers an de comprendre la formation des étoiles, des galaxies, des amas de galaxies. Je me suis d'abord intéressé aux techniques actuelles de simulation n corps. Quelles sont celles utilisées par les astronomes et de quels moyens disposentils ? Nous verrons que les techniques utilisées sont très complexes et tiennent compte d'une multitude de facteurs dépassant de loin une simple simulation gravitationnelle. Aussi, quelles sont les techniques qui peuvent être utilisées dans la création de mon programme de simulation n corps ? Nous verrons que, bien que souvent très complexes, certains aspects des techniques modernes peuvent être utilisés sans trop de peine. Après cela, vient la question de l'ecacité d'un programme écrit dans le cadre d'un travail de maturité. A quels résultats peut-on parvenir en utilisant un ordinateur personnel, tant en matière de performance que de précision ? An de répondre à cette question, nous testerons le programme. Nous verrons que nous sommes malheureusement limités par certaines des méthodes utilisées, et que pour les améliorer, il faudrait un investissement conséquent. Nous nirons en exposant clairement les lacunes du programme et nous nous demanderons quand et dans quelle mesure il peut être perfectionné. 1.2 Motivation Les sciences dures et la physique en particulier ont très tôt retenu mon attention. Très jeune, je me suis intéressé aux phénomènes qui m'entourent. Par 3 Simulation n corps Thibault Wildi - 3M8 ailleurs, j'aime me lancer dans des projets qui présentent une certaine part de dé. Ce projet a été pour moi l'opportunité de fournir un travail pour lequel la réponse ne gurait pas dans un cahier. Nous n'avions pas au départ d'idée précise sur la quantité de travail à eectuer pour obtenir un résultat que nous pourrions qualier de convenable. Mais cette inconnue, qui a parfois été, il est vrai, à la source de moments diciles, a été un moteur dans d'autres cas. Et lorsque l'on arrive à un résultat satisfaisant, la satisfaction justie amplement le travail accompli. 4 Chapitre 2 Simulations n corps 2.1 Que sont les simulations n corps Les simulations n corps sont des programmes informatiques où l'on simule l'interaction d'un nombre n de corps astronomiques. C'est-à-dire de particules ayant une certaine masse et pouvant représenter une poussière (au sens astronomique), une planète, une étoile ou une galaxie. Elles nous permettent d'étudier la formation de l'univers, des galaxies, des systèmes planétaire, ainsi que la répartition de la matière dans l'univers. Elles permettent aussi de vérier les diérents modèles astronomiques en comparant les résultats de la simulation aux observations. 2.2 Les simulations n corps aujourd'hui[16] De nos jours, les simulations n corps existent principalement à trois échelles : celle des systèmes planétaires, celle des amas d'étoiles, et celles des galaxies et des amas de galaxies. Les simulations n corps directes, c'est-à-dire où seules les forces gravitationnelles entrent en action, sont depuis longtemps dépassées. On travaille aujourd'hui sur des modèles tenant compte de l'hydrodynamique, c'est-à-dire des phénomènes liés aux gaz. On simule la compression des gaz, leurs températures ainsi que leur refroidissement par rayonnement, ce qui permet aux nuages de gaz de s'eondrer sur eux-mêmes pour former des étoiles plus ou moins grosses dont la durée de vie dépend de la masse. On tient aussi compte de l'explosion des super-novas, ces très grosses étoiles qui, de par leur masse, se consument très vite avant d'exploser en éjectant de la matière et en libérant de l'énergie. Il y a donc un mécanisme d'implosion-explosion auto-régulé dans la formation des étoiles. Un autre facteur que l'on prend aussi en considération est la composition dynamique des gaz et des étoiles. En eet, les réactions nucléaires au sein des étoiles produisent de nouveaux éléments plus lourds, modiant ainsi les proprié5 Simulation n corps Thibault Wildi - 3M8 2.1 Carte de densité d'une simulation n corps de la formation de l'univers, n = 256 · 106 corps[17]. Figure tés du gaz (notamment ses propriétés de refroidissement par rayonnement) et des prochaines générations d'étoiles 1 . Finalement, et peut-être le plus important, les modèles actuels tiennent compte de la supposée existence de la matière noire. Matière interagissant uniquement par gravité et dont l'existence n'est suggérée que par des observations indirectes. Les chercheurs ont besoin pour leurs simulations d'une grande puissance de calcul. Pour cela, ils disposent de "clusters" (littéralement "amas"). Ils sont formé d'unités de calcul, dites c÷urs, regroupées par centaines ou milliers, et se 1. Ce phénomène peut s'observer sans problème par spectroscopie, méthode permettant d'analyser la composition d'étoiles en fonction de la lumière émise. 6 Simulation n corps Thibault Wildi - 3M8 répartissant les calculs. La tailles de ces "clusters" peut varier grandement. L'EPFL, par exemple, dispose de deux "clusters" à des ns de simulation n corps, l'un de 6'000 c÷urs et l'autre 16'000 (mais présentant une architecture atypique). L'Observatoire de Genève quant à lui dispose d'un "cluster" de 300 c÷urs. Les simulations n corps n'utilisent que très rarement voire jamais la totalité des c÷urs à la fois. Car en raison des algorithmes de répartition des tâches entre les c÷urs, au-delà d'un certain nombre de c÷urs, le gain d'en ajouter de nouveaux est presque nul. Cela vient du fait qu'une certaine puissance de calcul est utilisée pour répartir et coordonner le travail entre les c÷urs, et aussi de ce que certains c÷urs ont tendance à nir leurs tâches avant les autres. De ce fait, une simulation type de l'ordre de n = 106 n'utilise que 16 ou 32 c÷urs. Des simulations comportant plus de corps peuvent aller jusqu'à 128, voire 256 c÷urs. Ainsi, plusieurs simulations peuvent tourner en parallèle sur un "cluster". Pour ce qui est de la durée des simulations, elle peut varier de quelques heures jusqu'à un extrême de six mois. On procède généralement à quelques simulations d'essai avec une résolution passablement basse avant de lancer une simulation plus longue ayant la résolution voulue. Le travail des scientiques impliqués dans le domaine réside donc dans le développement de modèles à la fois précis et économes en puissance de calcul. Ils doivent aussi développer des algorithmes de calcul utilisant de la façon la plus ecace possible les "clusters". En ce qui concerne la maîtrise de l'erreur des simulations, celle-ci est extrêmement dicile dans les systèmes où des modèles complexes (hydrodynamique, fusion nucléaire, etc.) entrent en jeux, car les diérents changements d'état de l'énergie la rendent très dicile à tracer. Néanmoins, on suit aussi l'évolution du moment cinétique, et celui-ci ne doit varier au maximum que de quelques pour-cent sur la durée d'une simulation. Étant donné la diculté de la chose, on m'a avoué que l'étude de l'erreur dans les simulations n corps était quelque peu laissée de côté (ce qui, il faut le dire, n'est pas une démarche très scientique). 7 Chapitre 3 Méthode de calcul 3.1 Méthode d'Euler[1][2] La méthode d'Euler est une méthode de résolution par approximation des équations diérentielles du premier ordre à partir d'une situation initiale. { ṙ(t) = f (t, r(t)) (3.1) r(t0 ) = y0 où : t est une variable r(t) est une fonction de t f (t, r(t)) est une fonction donnant la dérivée de r(t) au point (t; r(t)) t0 est un instant donné y0 est la valeur connue de r(t) à l'instant donné t0 On dénit un intervalle ∆t qui sera l'écart entre chaque itération tel que tn+1 = tn + ∆t (3.2) A partir de la condition initiale r(t0 ) = y0 , on cherche une estimation yn+1 de r(tn+1 ) en montant le long de la tangente r(t) au point (tn , yn ) sur un intervalle ∆t (c.f. gure 3.1). r(tn+1 ) ≈ yn+1 = yn + ∆tf (t, r(tn )) (3.3) On répète cette dernière étape en partant du point (tn+1 , yn+1 ) que l'on vient de trouver, et ainsi de suite pour toutes les itérations suivantes. Les premiers pas de la méthode d'Euler sont illustrés dans la gure 3.2. On observe entre les points (tn+1 ; r(tn+1 )) et (tn+1 ; yn+1 ) un écart ∆y . Cet écart est l'erreur produite par l'approximation dans l'équation (3.3). On remarque que plus le pas d'itération ∆t est petit, moins l'erreur ∆y est grande, 8 Simulation n corps Figure Thibault Wildi - 3M8 3.1 Une itération de la méthode d'Euler car la sécante se rapproche de la tangente. Donc, plus on veut avoir une approximation précise à l'aide la méthode d'Euler, plus il faut prendre un pas d'itération petit, au prix d'un nombre d'itérations plus important pour la même portion de courbe. 3.1.1 Exemple Prenons comme équation f˙ = 12 x et comme situation initiale, x0 = 1 et f (x0 ) = 41 , ce qui nous donne l'équation suivante : { f˙ = 21 x (3.4) f (1) = 14 Bien sûr, il n'y aucun intérêt autre que celui de l'exemple à résoudre cette équation à l'aide de la méthode d'Euler, car il est possible de la résoudre de façon analytique. En eet, il est possible de trouver la primitive f de f˙(x) = 12 x et ainsi de résoudre l'équation (3.4) : { ∫ f (x) = 12 x dx = 14 x2 + c (3.5) f (1) = 14 ⇒ c = 0 f (x) = 9 1 2 x 4 (3.6) Simulation n corps Thibault Wildi - 3M8 3.2 Premières itérations de la méthode d'Euler sur une courbe du type ax2 + b Figure Nous allons néanmoins résoudre cette équation à l'aide de la méthode d'Euler an de pouvoir comparer nos résultats à la solution analytique de l'équation (3.6). Choisissons un intervalle ∆x = 0.5 en partant de notre valeur initiale x0 = 1, ce qui nous donne : x1 = x0 + ∆x = 1 + 0.5 = 1.5 (3.7) Appliquons l'équation (3.3) : 1 1 f (x1 ) ≈ y1 = f (x0 ) + ∆xf˙(x0 ) = + 0.5 · = 0.5 (3.8) 4 2 Nous trouvons donc y1 = 0.5, alors que si nous cherchons f (x1 ) à l'aide de la solution analytique (équation (3.6)), nous trouvons F (x1 ) = 0.5625. Nous avons donc une erreur de 0.0625. Continuons le processus pour quelques points encore, ce qui nous donne le graphique proche de la gure 3.2. Relevons quelques points, ce qui nous donne le tableau 3.1. Nous remarquons que l'écart entre les points calculés à l'aide de la méthode d'Euler et la courbe théorique, c'est-à-dire l'erreur (∆y dans le tableau) augmente au fur et à mesure des itérations. En eet, lorsque l'on utilise la méthode 10 Simulation n corps Thibault Wildi - 3M8 xn 1 1.5 2 2.5 3 3.5 4 4.5 5 Table yn 0.25 0.5 0.875 1.375 2 2.75 3.625 4.625 5.75 f (xn ) 0.25 0.5625 1 1.5625 2.25 3.0625 4 5.0625 6.25 ∆y 0 0.06 0.125 0.188 0.25 0.313 0.375 0.438 0.5 3.1 Méthode d'Euler pour l'équation f˙(x) = 21 x avec X0 = 1 d'Euler pour résoudre une équation diérentielle, l'erreur créée à la dernière itération s'ajoute à l'erreur de l'itération précédente. Suivant l'équation de départ, cette erreur peut stagner voir régresser à certains moments. Mais, de manière générale, l'erreur augmente au l des itérations. 3.2 Étude du problème à n corps Les interactions entre les corps dans l'espace sont régies par la loi de la gravitation universelle d'Isaac Newton, que l'on vectoriellement : mi mj u⃗ij F⃗ij = −G (r⃗j − r⃗i )2 (3.9) où : F⃗ij est la force du corps i sur un corps j G est la constante du gravité universelle qui vaut 6.67 · 10−11 [N m2 · kg −2 ] u⃗ij est le vecteur unitaire allant du corps i vers le corps j r⃗j et r⃗i la position respective des corps j et i La présence du signe − devant G indique simplement que la force exercée par le corps i sur le corps j va de j vers i et non pas le contraire. Combinons l'équation (3.9) avec la deuxième loi du mouvement de Newton et considérons la présence de n corps agissant sur le corps j : { m m u⃗ F⃗ij = −G (r⃗ij −jr⃗i )ij2 (3.10) ∑n F⃗ij = mr⃗¨j i=1;i̸=j Nous obtenons : mj r⃗¨j = −G n ∑ i=1;i̸=j 11 mi mj u⃗ij (r⃗j − r⃗i )2 (3.11) Simulation n corps Thibault Wildi - 3M8 r⃗¨j = −G n ∑ i=1;i̸=j mi u⃗ij (r⃗j − r⃗i )2 (3.12) En posant : u⃗ij = (r⃗j − r⃗i ) |r⃗j − r⃗i | (3.13) Nous arrivons à l'équation : r⃗¨j = −G n ∑ mi (r⃗j − r⃗i ) |r⃗j − r⃗i |3 (3.14) i=1;i̸=j En considérant que ⃗r¨, et donc ⃗r˙ et ⃗r, varie en fonction du temps et en instaurant une situation initiale pour chacun des corps, nous arrivons à l'équation diérentielle suivante : ∑n r⃗¨j (t) = −G i=1;i̸=j r⃗1 (t0 ) = r⃗10 . . . r⃗n (t0 ) = r⃗n0 ˙ r⃗1 (t0 ) = v⃗10 ... ˙ r⃗n (t0 ) = v⃗n0 mi (r⃗j (t)−r⃗i (t)) |r⃗j (t)−r⃗i (t)|3 (3.15) Équation que nous allons résoudre à l'aide de la méthode d'Euler. Mais, comme celle-ci ne nous permet de résoudre que les équations diérentielles du premier ordre, nous allons devoir commencer par calculer les vitesses. L'équation devient : ∑n m (r⃗j (t)−r⃗i (t)) v⃗˙j (t) = −G i=1;i̸=j |ri⃗j (t)− r⃗i (t)|3 v⃗j (t) = r⃗˙j (t) (3.16) ... j = 1, · · · , n La formule entre chaque pas d'itération est alors : { ∑n m (r⃗j (t)−r⃗i (t)) v⃗j (tn+1 ) ≈ v⃗j (tn ) − ∆t · G i=1;i̸=j |ri⃗j (t)− r⃗i (t)|3 r⃗j (tn+1 ) ≈ r⃗j (tn ) + ∆t · v⃗j (tn ) (3.17) An d'obtenir une meilleure estimation des sécantes (c.f. gure 3.1), nous pouvons faire la moyenne des vitesses en tn et tn+1 . Ainsi, l'écart ∆y est réduit pour une meilleure précision : 12 Simulation n corps { Thibault Wildi - 3M8 r⃗j (tn+1 ) ≈ r⃗j (tn ) + ∆t · ∑n mi (r⃗j (t)−r⃗i (t)) i=1;i̸=j |r⃗j (t)−r⃗i (t)|3 v⃗j (tn+1 )−v⃗j (tn ) 2 v⃗j (tn+1 ) ≈ v⃗j (tn ) − ∆t · G (3.18) Il est à noter que dans le cadre des simulations n corps, nous appellerons le pas d'itération ∆t "pas de temps". 3.3 Dénition de l'erreur Comme nous avons l'avons dit plus haut, la méthode d'Euler génère une certaine erreur, et plus le pas de temps est petit plus l'erreur est réduite. Mais plus on diminue le pas de temps, plus on augmente le temps de calcul pour une simulation couvrant la même durée. Il faut donc trouver le meilleur compromis entre nos exigences de précision et les capacités de calcul à disposition. Et pour cela, nous avons besoin de pouvoir calculer l'erreur. Nous en arrivons donc à la question suivante : comment dénir l'erreur dans une simulation n corps ? La réponse se trouve dans la quantité d'énergie contenue dans le système. En eet, celle-ci, de par le principe de conservation de l'énergie, est théoriquement constante. Mais la méthode d'Euler générant une certaine erreur, la quantité d'énergie contenue dans le système va évoluer d'itération à itération. Nous pouvons donc comparer la quantité d'énergie calculée à un instant tn à la quantité d'énergie contenue dans la situation initiale t0 , ce qui nous donnera une information sur l'erreur induite sur les positions calculées. Il faut en eet faire la distinction entre l'erreur sur l'énergie totale et l'erreur sur les positions r⃗j . Bien sûr, il se peut qu'une erreur allant dans un sens compense une erreur allant dans l'autre, ce qui passerait inaperçu, mais nous n'allons pas tenir compte de ce cas de gure, en gardant à l'esprit que l'erreur sur l'énergie totale ne donne qu'une indication de l'erreur sur les positions r⃗j . L'énergie dans notre système est contenue sous deux formes : l'énergie cinétique, que nous allons noter T , et l'énergie potentielle, que nous allons noter U , E étant l'énergie mécanique totale[5]. E =T +U (3.19) 1 mv 2 2 (3.20) mi mj |⃗ ri − r⃗j | (3.21) Avec T et U : T = U = −G Le zéro de l'énergie potentielle étant placé à l'inni. 13 Simulation n corps Thibault Wildi - 3M8 En considérant n corps : E= n ∑ n ∑ mi mj mj v − G 2 |⃗ r i − r⃗j | j=1 i>j n ∑ 1 j=1 2 (3.22) Une autre façon de déterminer l'erreur générée par la méthode d'Euler dans une simulation n corps consiste à l'étudier dans les cas où la solution analytique est connue, c'est-à-dire sur un problème à deux corps ou sur certains cas particuliers de cas à trois corps. Nous avons donc deux méthodes pour avoir une mesure de l'erreur d'une simulation. La méthode consistant à mesurer l'énergie totale et observer sa variation, qui peut s'appliquer à toutes les simulations mais qui ne donne qu'une indication de l'erreur sur les positions r⃗j . Et la méthode consistant à simuler des cas dont la solution analytique est connue, qui nous donne une indication exacte de l'erreur sur les positions r⃗j , mais qui ne s'applique qu'à certains cas particuliers. Les simulations n corps ne représentant un intérêt que pour résoudre des cas dont la solution n'est pas connue, nous allons être obligé d'utiliser la méthode de l'énergie totale. Nous allons néanmoins caractériser cette méthode en essayant de trouver dans quelles conditions la méthode par mesure de l'énergie totale est représentative ou non de l'erreur sur les position r⃗j . Pour cela, nous allons comparer les deux méthodes de mesure de l'erreur sur sur un cas dont la solution est connue en variant les paramètres de la simulation. Nous allons examiner une situation qui nous est familière ; le cas de l'orbite terrestre. Nous allons simuler la rotation de la Terre autour du soleil en variant le pas de temps et en analysant l'erreur produite sur la période de révolution et le rayon de l'orbite. Nous étudierions aussi la façon donc varie l'erreur en fonction du temps et quelles congurations de corps produisent le plus d'erreur lors de la simulation. Nous aborderons aussi une méthode à pas de temps variable pour essayer de réduire l'erreur lors de moments où celle-ci a tendance à augmenter. 14 Chapitre 4 Programmation 4.1 Pourquoi le langage C ? Le langage C a été développé entre 1969 et 1973 par Dennis Ritchie au sein des Laboratoires Bell. Il fut à la base conçu pour le développement du système d'exploitation UNIX[8]. En eet, à l'époque, la grande majorité des systèmes d'exploitation étaient écrits en langage assembleur, langage très proche et directement lié au langage machine 1 . Le langage machine changeant entre chaque famille de processeurs, le langage assembleur aussi. Il fallait donc réécrire un système d'opération pour presque chaque ordinateur. C'est dans ce contexte qu'intervient le développement de UNIX, qui, à la base n'échappant pas à la règle, fut aussi écrit en assembleur. Certaines parties néanmoins furent écrites en langage B, un langage de haut niveau 2 . Mais celui-ci ayant certaines lacunes, Dennis Ritchie fut amené à lui apporter des modications. Ce langage deviendra le C. Le C possédant des fonctionnalités d'un langage de haut niveau et des caractéristiques permettant une gestion détaillée, la partie assembleur de UNIX fut en grande partie réécrite en C. Par la suite le langage continua d'évoluer et, en 1978, Brian Kernighan et Dennis Ritchie publièrent la première édition du livre "The C Programming Language", qui servit de norme[12]. Cette version du langage est appelée le K&R C. Puis, au fur et mesure de la popularité grandissante du langage, il fut développé de manière intensive et non cordonnée de façon à ce que chaque compilateur en supporte sa propre version[12]. C'est en 1989 qu'intervient la norme développée par l'institut national américain de normalisation dite ANSI C ou 1. Le langage assembleur n'est en fait qu'une simple "traduction" du langage machine. Dans la majorité des cas, chaque instruction correspond à une instruction du langage machine. 2. Tout est relatif bien sûr, le langage B, aujourd'hui obsolète, ainsi que le C sont considérés comme des langages de relativement bas niveau. 15 Simulation n corps Thibault Wildi - 3M8 C89, qui sera reprise par l'institut mondial de normalisation sous le nom de ISO C[12]. Une nouvelle normalisation du langage sortira en 1999 sous le nom de C99 et la dernière version en date est le C11 datant de décembre 2011. C'est cette dernière que j'ai utilisée pour mon programme. Le C est toujours aujourd'hui l'un des langages de programmation les plus répandus. Il a inspiré bien des langages comme JAVA, JavaScript, C++, C#, PHP et bien d'autres[7]. L'histoire du C ci-dessus explique très bien les raisons pour lesquelles j'ai été amener à rédiger mon programme en C. C'est un langage structuré et procédural 3 , mais possédant les outils pour gérer les ressources utilisées par la machine. Ce qui, dans le cadre de notre programme, est particulièrement intéressant dans l'optique d'optimisation des temps de calcul qui peuvent être relativement longs. En utilisant le C, nous bénécions aussi de son énorme popularité dans la mesure où une grande communauté s'est développée autour de lui. De ce fait, il existe de nombreuses bibliothèques 4 dont nous pouvons proter ainsi qu'une assistance certaine en cas de problèmes ou de questions. 4.2 Quelques notions Nous allons voir quelques notions qui sont particulières au C et nécessaires à la compréhension du programme écrit au cours de ce travail. 4.2.1 Compilation En C, la génération d'un exécutable à partir du code se fait principalement en deux étapes, la précompilation et la compilation, que l'on regroupe souvent sous le nom de compilation[12]. Lors de la première étape, le préprocesseur parcourt le code et le prépare à la compilation en procédant aux inclusions de chiers, aux remplacements des constantes et des macros, et à la suppression des commentaires[12]. Le C possède des commandes spéciques au préprocesseur, celles-ci se reconnaissent au signe # qui les précède. La deuxième étape consiste à la génération du code assembleur, de l'édition de liens et de l'optimisation, an de générer un chier exécutable[6]. Toutes ces étapes sont eectuées par un programme appelé "compilateur". Dans le cadre de ce projet, nous avons utiliser le compilateur C du "GNU Compiler Collection", abrégé GCC, et qui est un logiciel libre[9]. 3. Un langage procédural est un langage permettant de dénir des procédures (appelées fonctions en C), c'est-à-dire des portions de code que l'on peut appeler à tout moment lors de l'exécution du programme. 4. Une bibliothèque en programmation est en quelque sorte du code "pré-fabriqué" conçu dans le but de ne pas avoir à réécrire pour chaque programme la totalité du code. 16 Simulation n corps Thibault Wildi - 3M8 4.2.2 Programmation modulaire Le C est un langage qui permet la programmation modulaire. C'est-à-dire que l'on peut diviser le programme en plusieurs modules regroupant des groupes de fonctions agissant dans un domaine semblable[10]. Cela a comme avantage d'avoir un programme structuré dont chaque partie n'a accès qu'aux autres parties dont il a besoin, limitant ainsi grandement le risque de comportements imprévus du programme. En outre, lorsque l'on ne modie qu'une partie du programme, il n'est nécessaire de re-compiler que la partie qui a été modiée. Dans le cadre d'un relativement petit programme comme le nôtre, la diérence est insigniante mais sur de grands projets, elle est notable. Finalement, cela permet aux parties du code qui sont d'ordre générique d'être réutilisées dans d'autres programmes, en réutilisant les modules concernés. C'est le principe des bibliothèques. 4.2.3 Dénition et déclaration Il est important, pour comprendre la programmation modulaire en C, de comprendre la diérence entre la dénition et la déclaration d'une variable ou d'une fonction. La dénition d'une variable ou d'une fonction est l'étape où un identiant (le nom de la variable) et un espace mémoire sont alloués. La déclaration est la moment où la variable ou la fonction est pour ainsi dire "créée". Dans le cas de variables normales, la dénition et la déclaration ont lieu en même temps par la même instruction. Seules les variables dites extern sont dénies à un autre emplacement que la déclaration. Le mot-clé extern signiant simplement que la déclaration se trouve ailleurs[12]. La nuance est dicile à comprendre avec les variables, mais beaucoup plus simple pour ce qui est des fonctions. En eet, la dénition d'une fonction est simplement le nom de la fonction avec le type de retour et les types des paramètres. C'est ce que l'on appelle le prototype de la fonction[12]. C'est-à-dire toutes les informations qu'il faut pour appeler la fonction. Ainsi, l'espace mémoire pour la fonction et ses paramètres est alloué. La déclaration quant à elle contient le code contenu dans la fonction, ce que la fonction va exécuter. Bien sûr, il faut que dénition et déclaration concordent, sinon il y aura erreur au moment de la compilation. Si un bout de code utilise une fonction ou une variable, il n'est nécessaire pour lui que d'avoir accès à la dénition, c'est-à-dire à l'information lui permettant d'accéder à la variable ou au code de la fonction, et non à la déclaration. 17 Simulation n corps 1 2 3 4 5 6 7 8 9 10 extern int int int a; add ( a, prototype int int int int return a; add ( { a, int int Thibault Wildi - 3M8 b) ; b) // Definition d ' une variable a // Definition d ' une fonction add // Declaration de la variable // Declaration de de la avec son a fonction add c ; c = a + b; c ; } Code 4.1 Exemple de dénition et de déclaration 4.2.4 Fichier code source et chier header Comme nous l'avons vu plus haut, le C est un langage modulaire qui permet de séparer le code en plusieurs modules. Il faut néanmoins que les diérents modules qui en ont besoin puissent accéder les uns aux autres. Pour cela, il existe deux types de chiers en C, les chiers *.c , dits chiers sources, qui contiennent à proprement parler le code. Et les chiers *.h , dits chiers headers (chiers d'en-tête), qui contiennent les dénitions nécessaires à une utilisation du code par un autre module. Ainsi, chaque module consiste en un chier source et un chier header qui permet l'accès à ses fonctions. Ainsi, pour utiliser les fonctions d'un module depuis un autre, on mettra dans ce dernier la commande pré-compilateur #include suivie du nom du header entre guillemets, ce qui revient exactement à copier le code du header dans le chier source. 4.2.5 Pointeurs Le C fait ce que l'on pourrait appeler un usage abusif des pointeurs. Les pointeurs sont un type de variables qui repèrent une autre variable[12]. C'està-dire qu'une variable de type pointeur contient l'adresse mémoire d'une autre variable. Cela est utile, par exemple, pour le passage en paramètres de tableaux. En eet, nous n'allons pas envoyer chaque élément du tableau à la fonction appelée, mais le pointeur (l'adresse) du premier élément ; il est ensuite facile d'accéder aux autres éléments sachant que les tableaux stockent leurs éléments dans des cases mémoires consécutives. Les pointeurs sont déclarés de la façon suivante : Le type de la variable sur laquelle ils pointent, suivi d'un * , suivi du nom du pointeur. Pour accéder à la variable pointée par le pointeur, on utilise le préxe * devant le nom du pointeur[12]. Pour obtenir l'adresse d'une variable, on précède celle-ci d'un &. La notion de pointeur étant relativement complexe et le but de ce travail n'étant pas de faire un cours approfondi sur le C, je résumerai simplement la chose en disant que l'on peut faire dans une certaine mesure abstraction des pointeurs pour comprendre le code. Et que pointeur ou non, on fait en dénitif 18 Simulation n corps int int 1 a,b; 2 ∗i ; 3 4 i = &a ; 5 ∗ i = 5; Thibault Wildi - 3M8 // variables // Pointeur de type sur un int int // Le pointeur i // La variable pointee repere maintenant par i vaut la variable maintenant a 5 Code 4.2 Exemple d'utilisation de pointeurs référence à la même variable. 4.2.6 Bibliothèque SDL Comme nous l'avons relevé lors de notre présentation du C, l'un de ses avantages est qu'il est très répandu et qu'il dispose d'une grande communauté de programmeurs. De ce fait, il existe un grand nombre de bibliothèques payantes ou gratuites pour une multitude d'usage. Nous avons utilisé dans ce programme, an de créer une interface graphique, la bibliothèque C SDL (Simple DirectMedia Layer). La SDL est une bibliothèque gratuite sous licence GNU LGPL[11] (licence libre). Elle permet de créer des applications graphiques bidimensionnelles simples, ainsi que la gestion de diérents évènements comme les actions clavier ou souris. Elle est relativement simple à utiliser 5 et donc convient bien à ce travail dont le but n'est malgré tout pas de créer une application graphique mais un programme de simulation n corps. La SDL ne permettant pas d'acher du texte de manière formatée, nous avons aussi utilisé la bibliothèque SDL_ttf 2.0 ,bibliothèque satellite de la SDL permettant d'acher du texte à partir d'un chier de police d'écriture. 4.3 Structure du programme Voici les diérents modules du programme ainsi que les fonctions que contient chacun d'entre eux. La description précise de chaque fonction ainsi que de tous les paramètres de celles-ci se situent dans les cartouches de commentaires décrivant chaque fonction dans les chiers header des modules respectifs. main.c Ce module est le module principal par dénition Il ne contient que la fonction main() et gère l'ensemble du programme. C'est aussi là que sont déclarées et initialisées les variable globales. int main(...) Fonction lancée au début du programme, c'est elle qui gère les diérentes étapes du programme en lançant par exemple les itérations et l'interface graphique à la n du programme. 5. Tout est question de point de vue bien sûr, le module relatif à la partie graphique du programme représente quand même la majorité code. 19 Simulation n corps Thibault Wildi - 3M8 constants.h Ficher header rattaché à aucun chier source et contenant les différentes constantes utilisées par le programme, ainsi que que les dénitions du type BODY et des variable globales. io_operations.c Ce module contient les fonctions interagissant avec "l'extérieur" du programme, faisant des opérations d'entrée/sortie (Input/Output en anglais, abrégé IO). BODY* IO_inti(void) Fonction lisant le cher input.txt et initialisant chacune des variables globales nécessaires aux calculs. void IO_printBodyData_TXT(...) Fonction imprimant les paramètres de la simulation de manière formatée au moment où elle est appelée dans un cher .txt (texte), imprimant par exemple le temps, la position ou la vitesse de chacun des corps. void IO_printBodyData_CONSOLE(...) Fonction identique à la précédente, à l'exception que celle-ci imprime les diérents paramètres sur la console. void IO_printBodyData_BIN(...) Fonction identique aux précédentes, à l'exception que celle-ci imprime les diérents paramètres dans un cher .bin (binaire). iterator.c Module contenant la fonction d'itération. void calculateNextStep(...) Fonction d'itération calculant chaque nouvelle étape à partir des paramètres existants. Module contenant les fonctions relatives à l'interface graphique créée pour l'achage à l'écran des résultats des simulations bidimensionnelles. display2d.c void display2D(...) Fonction gérant l'interface graphique ainsi que les évènements clavier. void printDataOnScreen_Static(...) Fonction lisant le chier binaire produit par la fonction void IO_printBodyData_BIN(...) an d'acher à l'écran la trajectoire statique des corps. Ne fonctionne que si les données ont été imprimées suivant le schéma accepté par printDataOnScreen_Static(). Le schéma est déni à l'intérieur du code (c.f. display2d.h). void printDataOnScreen_Dynamic(...) Fonction identique à la précédente, sauf que celle-ci gère l'achage dynamique des corps. void void printAxisOnScreen(...) Fonction achant les axes à l'écran. Le programme une fois compilé a quand même besoin des chiers .dll (Dynamic Link Library, bibliothèque à lien dynamique en français) des bibliothèques utilisées, ainsi que du chier de police d'écriture "consola.ttf" pour fonctionner. Les bibliothèques utilisent des chiers .dll plutôt que d'inclure directement leur code source à l'intérieur de l'exécutable. Cela principalement an de permettre les mises à jour des bibliothèques sans devoir re-compiler le programme. 20 Simulation n corps Thibault Wildi - 3M8 4.4 Fonctionnement basique du programme Le programme procède de la façon suivante lors de son exécution : 1. Il lit le chier "Input.txt" situé dans le dossier Input et initialise les paramètres de la simulation à l'aide de la fonction IO_inti(). 2. Il lance une boucle et appelle la fonction calculateNextStep() à chaque itération avant d'imprimer les paramètres actuels de la simulation dans le chier "Output.bin" situé dans le dossier Output à l'aide de la fonction IO_printBodyData_BIN(). 3. Une fois la simulation terminée, il imprime les spécications de la simulation dans le chier "Spec.txt" situé dans le dossier Output. 4. Ensuite, si c'est une simulation bidimensionnelle et que des points ont été bel et bien été imprimés dans le chier "Output.bin", le programme lance une interface graphique permettant de visualiser les résultats à l'aide de la fonction display2D(). 4.5 Utilisation du programme Le programme nécessite donc, pour fonctionner, le chier "Input.txt" situé dans le dossier Input. Nous allons maintenant nous pencher sur le format d'écriture des données dans ce cher "Input.txt". BODY_NB DIMENTIONS G_CONST RUN_TIME DELTA_T PRT_PRCT USE_DYMC_DELTA_T DYMC_DELTA_T_ACC Nombre_de_corps Nombre_de_dimensions Constante_de_gravitation Longueur_de_la_simulation Pas_de_temps Pourcentage_d'écriture Pas_de_temps_variable Précision_pas_de_temps_variable C1_pos_x | C1_pos_y | C1_vit_y | C1_vit_y | C1_mass | C2_pos_x | C2_pos_y | C2_vit_y | C2_vit_y | C2_mass | C3_pos_x | C3_pos_y | C3_vit_y | C3_vit_y | C3_mass | etc Figure 4.1 Format d'entrée du chier "Input.txt" Dans la gure 4.1, le texte en italique est à remplacer par des valeurs. Voici les détails de chaque paramètre : Le nombre de corps dans la simulation, permet au programme de savoir le nombre de lignes à lire plus bas dans la partie d'initialisation des paramètres des corps. Nombre_de_corps 21 Simulation n corps Thibault Wildi - 3M8 Nombre de dimensions. Le fonctionnement du programme n'est pas garanti pour des valeurs autres que 2 ou 3. Cette valeur permet au programme de savoir le nombre de valeurs à lire dans la partie d'initialisation des corps plus bas. Nombre_de_dimensions Constante de gravitation, généralement la constante de gravitation universelle, soit 6.67384 · 10−11 . Peut aussi être une valeur tout à fait arbitraire. Constante_de_gravitation Longueur_de_la_simulation Longueur de la simulation en secondes [s]. Pas de temps utilisé pour la simulation en secondes [s]. Pas de temps maximal dans le cas de simulations avec pas de temps variable. Détermine avec la longueur de la simulation le nombre d'itérations. Pas_de_temps Pourcentage d'étapes dont les paramètres sont imprimés dans le chier "Output.bin". Par exemple, si le paramètre est à 1, seule une étape est imprimée dans le chier pour cent calculées. Cela est très utile dans le cas de grosses simulations où un nombre important de points sont calculés an de ne pas générer un trop gros chier et de réduire le temps d'exécution. Pourcentage_d'écriture Utiliser un pas de temps variable ou non ? Si oui, la valeur vaut 1 ; si non, la valeur vaut 0. Pas_de_temps_variable Paramètre de précision en cas d'utilisation de pas de temps variable. Si on n'utilise pas de pas de temps variable, cette valeur est ignorée. Nous parlerons plus tard de la signication de cette valeur et des algorithmes de simulation utilisant un pas de temps variable. Précision_pas_de_temps_variable et C1_pos_y Coordonnées initiales du premier corps en mètres [m]. En cas de simulation à trois dimensions, on ajoutera une troisième coordonnée C1_pos_z. Il ne faut pas oublier la barre verticale de séparation entre chaque entrée ainsi qu'en n de ligne. C1_pos_x et C1_vit_y Vitesse initiale sur chaque axe du premier corps en mètres par seconde [m/s]. En cas de simulation à trois dimensions, on ajoutera la cordonnée z. C1_vit_x C1_mass Masse du premier corps en kilogrammes [kg]. ... Cordonnée du deuxième corps et ainsi de suite. Même format à chaque ligne, chacune représentant un corps. C2_pos_x Les paramètres Constante_de_gravitation, Longueur_de_la_simulation, Pas_de_temps, Pourcentage_d'écriture, Précision_pas_de_temps_variable et tous les paramètres individuels des corps acceptent la notation scientique. Le format est le suivant : NBe ± PUISSANCE. La constante de gravitation universelle s'écrit par exemple 6.67384e − 11. Attention, les nombres sont notés selon le format anglo-saxon ; les décimales sont séparées par un point et non par une virgule. 22 Simulation n corps Thibault Wildi - 3M8 Le programme ne vérie pas le format avant de lire les données. De fait, une erreur de formatage dans le chier "Input.txt" donnera lieu à une erreur à l'exécution du programme. Il sut de lancer la simulation qui démarre à l'exécution du programme et qui peut durer de moins d'une seconde à plusieurs minutes voire plusieurs heures, suivant les paramètres de départ. A la n du programme, celui-ci aura produit dans le dossier Output, un chier "Output.bin" contenant les coordonnées des corps à intervalles réguliers pendant la simulation (intervalle déni par Pourcentage_d'écriture ). Ainsi qu'un chier "Spec.txt", contenant les coordonnées des corps au départ et à la n de la simulation, et les paramètres de la simulation. Ensuite, si un chier "Output.bin" est produit et si la simulation est bidimensionnelle, le programme ouvre une fenêtre permettant de visualiser le résultat de la simulation. 23 Chapitre 5 Résultats et analyse 5.1 Tests du programme Nous allons maintenant faire passer une série de tests au programme que nous avons conçu an de pouvoir optimiser notre utilisation et mieux interpréter le résultat de nos simulations. Pour cela, nous allons nous pencher sur la relation entre les paramètres de départ d'une simulation, comme le pas de temps, le nombre de corps, leurs positions et vitesses initiales, et le résultat de cette simulation en termes d'erreur et de temps de calcul notamment. 5.1.1 Étude de l'erreur des simulations Comme nous l'avons dit dans la partie théorique, nous disposons de deux manières de mesurer l'erreur de notre simulation. La première reposant sur le principe de conservation de l'énergie, consistant à calculer l'énergie totale du système, censée rester constante au l du temps, et à quantier l'erreur accumulée par la simulation à un instant t en comparant l'énergie totale du système à cet instant t avec l'énergie dans la situation initiale de la simulation. Cette méthode fonctionne pour tous les cas, mais ne donne qu'une indication de l'erreur sur la position des corps r⃗j . La seconde méthode est de simuler des cas dont nous connaissons la solution, car ils peuvent être résolus de façon analytique, et de mesurer directement l'erreur sur les positions r⃗j . La deuxième méthode étant limitée aux cas connus, nous allons être obligé d'utiliser la première. Néanmoins, an de pouvoir vérier la abilité de nos simulations, nous allons tenter de dénir dans quelle mesure et à quelles conditions l'erreur sur l'énergie totale est représentative de l'erreur sur les positions r⃗j . Pour cela, nous allons lancer des simulations dans lesquelles la solution nous est connue an de pouvoir comparer l'erreur sur l'énergie et l'erreur sur les positions r⃗j . 24 Simulation n corps Figure Thibault Wildi - 3M8 5.1 Simulation du cas soleil-Terre - Résultats Nous allons aussi proter des tests visant à étudier l'erreur pour observer le comportement du temps de calcul. Ce qui sera intéressant pour pouvoir prévoir le temps de calcul dans des simulations plus complexes où il devient important. Cas du système soleil-Terre Comme nous l'avons dit dans la partie théorique, nous allons simuler un cas dont la solution nous est connue, celui de la Terre autour du soleil. En lançant le programme avec une série de diérents pas de temps sur une durée d'une année, nous obtenons le tableau de la gure 5.1 et le graphique de la gure 5.2. Comme nous pouvions nous y attendre, le temps de calcul est assez exactement inversement proportionnel au pas de temps. Autrement dit, proportionnel au nombre d'itérations totales. Aussi, nous voyons que l'erreur sur la distance soleil-Terre est proportionnelle au pas de temps et que dans une certaine mesure, l'erreur énergétique aussi. Le fait que les deux erreurs ne soient pas proportionnelles entre elles s'explique par le fait que l'erreur énergétique est composée de l'énergie potentielle qui dépend de l'inverse de la distance soleil-Terre, ainsi que de l'énergie cinétique dont ne tient pas compte l'erreur sur la distance orbitale. La mesure de l'énergie contenue dans le système au cours du temps est donc conrmée dans ce cas particulier comme moyen de mesurer l'erreur dans une simulation. Dans des systèmes plus complexes, elle sera notre seule façon de quantier l'erreur. Problème pythagoricien Un autre moyen de tester notre programme est de l'utiliser pour résoudre un système dont la solution a été trouvée et qui est universellement reconnue. Nous pourrons ainsi comparer nos résultats aux résultats qui ont été publiés. 25 Simulation n corps Figure Thibault Wildi - 3M8 5.2 Simulation du cas soleil-Terre - Graphique Le problème pythagoricien est une problème de gravitation bidimensionnel à trois corps résolu en 1967 avec l'arrivée des premières simulations n corps[15]. La situation initiale est celle de la gure 5.3. Ce problème est intéressant car il va nous permettre de tester notre programme sur un système où le mouvement des corps est plutôt chaotique, c'està-dire qu'il n'apparait aucun motif répétitif dans la trajectoire des corps. Mentionnons aussi que la simulation utilise une constante de gravitation valant 1[15] et que les unités sont celles du système international des unités. Aussi les corps ont une vitesse initiale nulle. La solution de ce problème ayant été calculée par d'autres avant nous[15], nous aurons une base de comparaison pour nos résultats. En lançant une série de simulations sur une durée de 6[s] ayant la situation initiale décrite par la gure 5.3, nous obtenons le tableau de la gure 5.4 représenté graphiquement dans la gure 5.5. Nous constatons à nouveau que le temps de calcul est inversement proportionnel au pas de temps. Nous remarquons, par contre, que l'erreur énergétique évolue de façon totalement chaotique jusqu'à ce que le pas de temps soit inférieur à un certain seuil audelà duquel elle varie proportionnellement au pas de temps. Nous parlerons 26 Simulation n corps Thibault Wildi - 3M8 5.3 Problème pythagoricien - Situation initiale, The Astronomical Journal 1967[15] Figure Figure 5.4 Problème pythagoricien - Résultats 27 Simulation n corps Figure Thibault Wildi - 3M8 5.5 Problème pythagoricien - Graphique de l'origine de ce comportement dans la section suivante. Nous pouvons néanmoins en déduire que pour chaque simulation, il existe un certain seuil au delà duquel l'erreur énergétique n'est pas représentative. En eet, prenons comme exemple la simulation ayant comme pas de temps 0.125[s], l'erreur n'est que de 72% par rapport aux autres simulations ayant un pas de temps de la même échelle. Si, ensuite, on compare cette simulation à la simulation de pas de temps 1.91 · 10−6 [s] ayant une erreur de 79%, on peut penser à première vue que les trajectoires devraient être très proches vu la proximité du taux d'erreur. Dans la gure 5.6 qui illustre les diérents résultats des simulations ainsi que la trajectoire théorique, on observe cependant que les trajectoires sont très dissemblables. Nous constatons qu'avec le pas de temps de 0.125[s], les corps 2 et 3 (se référant à la numérotation de la situation initiale de la gure 5.3) se croisent et continuent leurs chemins, tandis que dans le deuxième cas, les corps se tournent autour avant de repartir dans la direction d'où ils sont venus ce qui est beaucoup plus proche des trajectoires théoriques. C'est le cas que nous avons décrit dans la partie théorique où les erreurs sur les positions r⃗j sont dans une conguration telle que l'énergie totale du système est proche de la valeur initiale. Les erreurs dans un sens compensant les erreurs dans l'autre. Pour éviter cette situation, il faut s'assurer d'avoir pris un pas de temps susamment petit pour être dans la zone où l'erreur énergétique est représentative 28 Simulation n corps Thibault Wildi - 3M8 5.6 Problème pythagoricien - Comparaison des simulations ; de gauche à droite : les trajectoires théoriques, la simulation de pas de temps 1.25 · 10−1 [s] , la simulation de pas de temps 1.91 · 10−6 [s] Figure et, ensuite, nous pourrons moduler le pas de temps an d'obtenir le meilleur compromis entre la précision et le temps de calcul. Source de l'erreur Nous pouvons aussi nous demander en regardant les résultats du tableau de la gure 5.4 d'où vient le comportement chaotique et la si grande taille des erreurs. En regardant les trajectoires de gure 5.7 qui représente la simulation du cas pythagoricien avec un pas de temps de 4.88 · 10−4 , nous constatons que les corps 2 et 3 sont en fait éjectés lorsqu'ils passent l'un près de l'autre. Cela s'explique par le fait que lorsque deux corps sont proches, c'est-à-dire lorsque la distance qui les sépare devient comparable à la distance qu'ils parcourent entre chaque itération, l'erreur faite en partant du principe que l'accélération est constante tout au long de l'itération devient très importante, tant en intensité qu'en direction. Nous verrons que c'est ce même phénomène qui est à la base de la grande majorité de l'erreur dans toutes les simulations. Le phénomène devient extrême dans les cas où, par le hasard du pas de temps, deux corps se retrouvent presque superposés à la suite d'une itération. Cela donne lieu à une accélération colossale pendant l'itération suivante, ce qui éjectera les corps en direction opposée si bien qu'ils seront trop éloignés à la prochaine itération pour êtres freinés par une accélération dans le sens inverse. En réalité, si deux corps étaient passés si proches l'un de l'autre, la forte accélération aurait été appliquée pendant seulement un très court moment et aurait été immédiatement compensée par une accélération de la même ampleur une fois que les corps se seraient croisés. 29 Simulation n corps Thibault Wildi - 3M8 5.7 Problème pythagoricien - Éjection de corps, la vue inférieure est un agrandissement de la vue supérieure Figure 30 Simulation n corps Thibault Wildi - 3M8 Le comportement aléatoire de l'erreur en fonction du pas temps s'explique quant à lui par le fait que l'erreur étant générée quand deux objets se retrouvent soudainement proches l'un de l'autre à la suite quelques itérations, elle dépend grandement de la distance séparant les deux objets quand ils sont au plus proche. Cette distance dépend de façon pseudo-aléatoire du pas de temps jusqu'à ce que celui-ci devienne susamment petit pour que l'on retrouve les mêmes congurations de rencontre entre les diérents pas de temps. Il existe plusieurs techniques pour résoudre le problème dit des "close encounters" (que l'on traduirait approximativement par les "rencontres proches"). Il est possible bien sûr, d'améliorer la technique d'intégration an d'obtenir une meilleure précision entre les itérations 1 , mais cela nécessiterait un bagage mathématique que je n'ai pas encore. Des techniques qui nous sont accessibles, il y en a deux : diminuer le pas de temps ou introduire un paramètre dit "d'adoucissement", c'est-à-dire rajouter une distance arbitraire entre chaque objet. Ainsi, nous sommes dans un système "sans collision" où malgré l'ajout d'une certaine erreur sur les distances et, par là, sur la simulation, nous réduisons drastiquement l'erreur créée par les "close encounters". 5.1.2 Nombre de corps An de pouvoir connaître les limites de notre programme, nous allons également nous pencher sur l'impact du nombre de corps sur le temps de calcul. Dans une moindre mesure, nous allons regarder si le nombre de corps a un impact sur l'erreur. En lançant une série de simulations avec un pas de temps constant de 5 · 10−7 [s] sur le cas pythagoricien et en ajoutant à chaque simulation un corps dont chaque coordonnée a été tirée aléatoirement entre -5 et 5 et dont la masse a été tirée aléatoirement entre 1 et 5, nous obtenons le tableau de la gure 5.8. Ce nous donne ensuite le graphique de la gure 5.9. Nous constatons que le temps de calcul dépend du carré du nombre de corps, ce qui était prévisible car nous calculons chaque interaction et que le nombre d'interactions dépend de n2 ou plus précisément de n(n+1) . 2 Pour ce qui est de l'erreur, nous constatons que celle-ci varie de façon chaotique, avec peut-être une légère tendance à augmenter, la probabilité d'avoir des "close encounters" augmentant avec le nombre de corps. Avec ce test, nous nous rendons compte qu'il est dicile de faire des simulations avec un grand nombre de corps. Non seulement, le temps de calcul augmente considérablement mais aussi, pour contrer la probabilité d'avoir des "close encounters", il faut encore diminuer le pas de temps ce qui augmente à nouveau 1. La méthode d'Euler est la plus simple des méthodes d'intégration mais est aussi de loin la plus imprécise. 31 Simulation n corps Figure Figure Thibault Wildi - 3M8 5.8 Étude sur le nombre de corps - Résultats 5.9 Étude sur le nombre de corps - Graphique 32 Simulation n corps Figure Thibault Wildi - 3M8 5.10 Problème pythagoricien - Variation de l'énergie totale le temps de calcul. Il devient donc impératif de développer un système qui nous permet de pouvoir réduire l'erreur créée par les "close encounters" tout en gardant un temps de calcul raisonnable. 5.2 Simulation à pas de temps variable 5.2.1 Nécessité d'un algorithme à pas de temps variable Comme nous l'avons vu, la plupart de l'erreur est créée lors des "close encounters", mais dans quelle proportion. La graphique de la gure 5.10 nous montre la variation de l'énergie totale du système au cours du temps dans le cas de la simulation du problème pythagoricien avec un pas de temps de 5 · 10−7 [s]. Nous observons que l'énergie du système est constante jusqu'au temps 1.88[s] où elle change brutalement pour aller se placer à −10.1[J] jusqu'à la n de la simulation. L'instant 1.88[s] correspondant exactement au moment où les corps 2 et 3 passent au plus proche l'un de l'autre. Ce sont donc bien les "close encounters" qui sont à la source de la grande majorité de l'erreur. Comme nous l'avons vu, la solution se trouve dans la diminution du pas de temps, mais il y a là un réel gaspillage de puissance de calcul car nous navons besoin d'un pas de temps plus court que pendant le très court instant (le changement de niveau d'énergie semble instantané sur le graphique) où les corps sont au plus près. Nous en arrivons à la nécessité d'améliorer le programme pour que celui-ci puisse diminuer le pas de temps lorsque c'est nécessaire. 33 Simulation n corps Thibault Wildi - 3M8 5.2.2 Algorithme à pas de temps variable Nous savons maintenant qu'il nous faut diminuer le pas de temps pendant les "close encounters", mais à quelles valeurs se rattacher concrètement pour xer le pas de temps à chaque itération pendant l'exécution du programme ? Il faut que le programme puisse réagir aux brusques changements de niveau de l'énergie totale en temps réel. Ces brusques changements peuvent s'observer par des pics dans la variation de l'énergie entre deux itérations ou, plus correctement, dans la dérivée de l'énergie. Nous calculons celle-ci de la façon suivante : En+1 − En (5.1) ∆t Nous calculons donc la dérivée de l'énergie à chaque itération avant de la comparer à la limite xée dans le chier Input.txt du dossier Input par le paramètre DYMC_DELTA_T_ACC. Après plusieurs essai, j'ai débouché sur la méthode suivante suivante. Si la dérivée calculée est plus grande que la limite xée, on réduit le pas de temps pour la prochaine itération. Si la dérivée est plus petite que la limite et si le pas de temps est inférieur au pas de temps maximal (paramètre DELTA_T du cher Input.txt ), on augmente le pas de temps pour la prochaine itération. Il faut néanmoins rajouter une limite inférieure au pas de temps. Je me suis en eet retrouvé lors de certaines simulations de test avec des pas de temps ·1015 fois plus petits que le pas de temps initial, ce qui, bien que sur une période extrêmement réduite, prendrait quand même plusieurs années de calcul. Après quelques essais, il s'est avéré que xer une limite inférieure à 2 · 10−5 fois le pas de temps de départ est un bon compromis entre précision et temps de calcul. Ė = 5.2.3 Test du programme à pas de temps dynamique Pour pouvoir utiliser le pas de temps dynamique au mieux, nous allons tester cette nouvelle version du programme sur le même cas que précédemment, c'està-dire sur le problème pythagoricien. En lançant une série de simulations avec un pas de temps de base de 5·10−7 et en faisant varier la limite supérieure sur la dérivée de l'énergie totale (nous l'appellerons dès lors coecient de précision), nous obtenons le graphique de la gure 5.11. Nous observons que jusqu'à un coecient d'environ 0.5[J/s], le temps de calcul ne varie pas ou augmente de façon insigniante, tandis que l'erreur diminue de manière drastique. Cela s'explique par le fait que la durée pendant laquelle le pas de temps variable est appliqué est extrêmement faible mais susante pour contrer l'erreur. A partir d'un coecient de précision de 0.5[J/s] le temps de calcul augmente de façon importante. Ce comportement vient du fait que le coecient de précision est devenu susamment petit pour que le pas de temps variable atteigne le plancher xé à 2 · 10−5 du pas de temps de départ. Le coecient détermine maintenant à quel moment le pas de temps variable va intervenir et descendre 34 Simulation n corps Figure Thibault Wildi - 3M8 5.11 Problème pythagoricien - Test avec pas de temps dynamique jusqu'au plancher, et ce moment est plus sensible au coecient vu que la dérivée change beaucoup moins rapidement. De même, comme la majorité de l'erreur est produite pendant la "close encounter", une fois le pas de temps plancher atteint, elle diminue beaucoup moins vite. Le fait de diminuer le pas de temps plus tôt impactant peu sur la quantité d'erreur, il devient à ce moment plus rentable de diminuer le pas de temps maximal qui est directement lié au pas de temps plancher. Nous voyons qu'il faut donc trouver pour chaque simulation un coecient de précision qui soit adapté à la situation, l'erreur pouvant être réduite de manière drastique jusqu'à un certain point sans inuencer ou presque sur le temps de calcul. Le coecient sera ajusté sur quelques simulations de mise au point sur une durée plus courte mais comprenant quand même quelques "close encounters". Suivant nos exigences, les coecients peuvent varier, mais un bon compromis dans ce cas pourrait se situer autour de 0.4[J/s]. Si nous nous penchons sur ce qui se passe pendant un "close encounter", nous obtenons le graphique de la gure 5.12. Le test, a été eectué avec un pas de temps de base de 5 · 10−7 et un coecient de précision de 0.4[J/s]. Nous observons que le pas de temps reste à 5 · 10−7 jusqu'à ce que l'énergie varie de façon susamment marquée pour activer le pas de temps dynamique. Ensuite celui-ci descend an de maintenir la dérivée de l'énergie à sa valeur limite. Cela s'observe par la pente constante de la courbe de l'énergie lorsque le pas de temps dynamique est activé. Nous voyons aussi que pendant le moment où les deux corps sont au plus 35 Simulation n corps Thibault Wildi - 3M8 5.12 Problème pythagoricien - Évolution du pas de temps lors d'un "close encounter" Figure près, le niveau d'énergie remonte, créant deux pics dans les pas de temps. Ils sont dus au fait que lorsque l'énergie remonte, il y a un moment où la dérivée de celle-ci passe à zéro, faisant augmenter le pas de temps pendant un court instant. Finalement, si l'on regarde attentivement, on observe que la courbe du pas de temps forme un léger replat lorsqu'elle est au plus bas. C'est que le pas de temps a atteint la butée xée à 2 · 10−5 du pas de temps de base. Le plancher n'étant atteint que pendant un très court instant, il ne détériore pas la qualité de la simulation par rapport au cas où nous n'aurions pas limité le pas de temps. D'autre part, le fait que le plancher ne soit atteint que pendant un très court instant prouve bel est bien qu'un coecient de précision de 0.4[J/s] est un bon compromis pour cette simulation. On voit sur la gure 5.13, la trajectoire des corps lors de la "close encounter". Seul un point est aché pour 2000 calculé. Néanmoins, on remarque très bien le rapprochement des pointe du au pas de temps diminuant, malgré la vitesse croissante des corps. On remarque aussi quatre espaces correspondant aux moments où le pas de temps à augmenté à nouveau avant que les points se rapprochent susamment pour donner une ligne continue correspondant au moment où le pas de temps était au plus bas. 36 Simulation n corps Thibault Wildi - 3M8 5.13 Problème pythagoricien - "Close encounter" avec pas de temps dynamique Figure 5.3 Simulations de cas Bien que, le but de ce travail soit avant tout de développer un programme de simulation n corps, nous avons essayé celui-ci sur quelques cas pratiques. 5.3.1 Figure de huit à 3 corps Il existe, dans les systèmes auto-gravitant, la possibilité de trouver certains cas avec n > 2 qui, grâce à des congurations comportant des symétries, peuvent être résolus de manière analytique[18]. Dans certains de ces cas particuliers, les corps parcourent la même trajectoire de manière périodique. Bien sûr, ces cas ne fonctionnent que pour des congurations très précises et la moindre perturbation peut s'amplier et casser l'harmonie. La simulation d'un de ces cas sera donc aussi un bon moyen de vérier que notre programme fonctionne correctement. Nous allons essayer de simuler un cas où trois corps dessinent un huit. La conguration initiale est illustrée dans la gure 5.14, les valeurs précises sont situées dans le tableau de la gure 5.15. Comme dans le cas pythagoricien, la constante de gravitation vaut l'unité. Après une simulation sur une période de 300[s] soit cinq minutes, ce qui correspond à un peu moins de 50 "orbites" sachant que les corps mettent un peut plus six secondes pour parcourir la gure de huit, les corps sont toujours sur la bonne trajectoire. Les trajectoires dessinées par les corps au bout d'environ 300[s] sont représentées dans la gure 5.16. 37 Simulation n corps Figure 5.14 Figure de huit - Illustration de la situation initiale[19] Figure Figure Thibault Wildi - 3M8 5.15 Figure de huit - Situations initiales[19] 5.16 Figure de huit - Trajectoire des corps au temps t ≈ 300 38 Simulation n corps Figure Thibault Wildi - 3M8 5.17 Études des amas - Ségrégation des masses Il est intéressant de noter qu'au bout de 300[s], l'erreur n'est que de 0.037% alors que nous avons utilisé un pas de temps de 5 · 10−7 . Cela vient du fait que les corps restant à distance, il n'y a jamais de "close encounter" et donc l'erreur reste très basse. Aussi, bien que le pas de temps variable ait été activé, il ne s'est pas déclenché. Après coup, j'ai relancé une simulation sur une période de 3600[s] soit une heure, après près de deux heures de calcul, la gure de huit était toujours respectée et l'erreur n'était que de 0.44%. 5.3.2 Simulation d'amas Il est aussi intéressant d'étudier le comportement d'amas de corps. Après de nombreux tests avec n entre 5 et 20 et bien des heures de calcul, on voit apparaitre un phénomène récurant : La formation de sous-systèmes à deux voire trois corps. Ces couples, qui sont composés de corps orbitant l'un autour de l'autre sont formés par les corps lourds du système. On observe de façon générale dans la simulation d'amas auto-gravitant, un phénomène de ségrégation des masses. Cela est dû au fait que quand deux corps ont une "close encounter", leur énergie cinétique a tendance à s'égaliser. Ce qui se traduit globalement par une accélération des corps légers et un ralentissement des corps lourds. Ainsi les corps lourds ont tendance à se regrouper au centre du système, tandis que les corps légers sont soit éjectés soit envoyés dans des orbites hautement elliptiques. La gure 5.17 illustre ce phénomène dans un système à seulement quatre corps pour plus de clarté. Les corps violet et bleu ont une masse approximativement 39 Simulation n corps Thibault Wildi - 3M8 quatre fois supérieure aux corps rouge et jaune. On observe qu'après quelques "close encounters", les objets légers rouge et bleu se sont fait éjecter, tandis que les objets lourds violet et bleu se sont mis à tourner l'un autour de l'autre. On peut noter que malgré une énergie totale du système négative, c'est-à-dire que l'énergie potentielle a le dessus sur l'énergie cinétique, les corps légers ont été éjectés. Une partie de l'énergie potentielle contenue entre les deux corps lourds, ainsi qu'une partie de leur énergie cinétique a été transférée aux corps légers, leurs permettant d'atteindre une vitesse leur permettant de se libérer du système. 5.4 Limitations du programme Nous nous pouvons maintenant tirer des conclusions sur les possibilités et les limites que nous ore le programme. Nous avons vu que le temps de calcul augmente avec le carré du nombre de corps, ce qui limite grandement le nombre maximum de que l'on peut simuler. De plus, comme nous l'avons vu dans la dernière section, on observe de manière générale le regroupement des corps lourds au centre et l'éjection des corps légers dans des orbites hautement elliptiques. Ce phénomène, bien qu'intéressant à observer, tend à créer une grande erreur. En eet, les corps lourds évoluant très proches les uns des autres, nous avons un nombre de "close encounters" considérable. Et si nous utilisons un pas de temps dynamique, celui-ci nit par être actif en permanence. De plus, lorsque l'on augmente le nombre de corps, la probabilité de "close encounters" augmente aussi, réduisant l'ecacité du programme. Un autre inconvénient lorsque que l'on fait une simulation sur un système avec un grand nombre de corps en utilisant un pas de temps dynamique, est que l'on diminue le pas de temps non seulement pour calculer les trajectoires des deux corps en "close encounter" mais aussi de tous les autres corps du système. Ce qui devient considérablement coûteux en temps de calcul lorsque que l'on augmente le nombre de corps. Pour toutes ces raisons, bien que le programme puisse être précis et relativement ecace avec un nombre restreint de corps, il se prête mal à des simulations avec un nombre de corps supérieur à quelques dizaines. Il faudrait, pour pallier à ces problèmes, revoir nos techniques d'intégration, et utiliser des algorithmes de simplication an que le temps de calcul n'augmente que linéairement avec le nombre de corps. Aussi, si chaque objet avait un pas de temps distinct, cela réduirait considérablement l'impact des "close encounters" en termes de temps de calcul sur les systèmes qui possèdent un nombre important de corps, car nous pourrions ne réduire le pas de temps que pour le couple d'objets concerné. D'autre part, une fois le programme de simulation développé, il faut pouvoir visionner les résultats. Pour cela, j'ai créé une interface graphique pouvant acher les trajectoires des corps et leurs mouvements en temps réel. Néanmoins, 40 Simulation n corps Thibault Wildi - 3M8 cette interface reste rudimentaire, et n'est pas très ecace pour analyser les résultats d'une simulation sur une longue durée ou contenant un nombre important de corps, l'écran devenant dicilement lisible. Cette interface est aussi limitée à l'achage de simulations à 2 dimensions. Bien que mon programme puisse normalement faire des simulations dans trois dimensions, je n'ai jamais pu vérier l'exactitude des trajectoires calculées, ne disposant pas de moyen de visualisation. 41 Chapitre 6 Conclusion 6.1 Commentaires Mon objectif, dans ce travail de maturité, était de comprendre et d'appliquer les mécanismes de simulation n corps dans la conception d'un programme personnel, puis dans la mesure du possible d'étudier, à l'aide de ce programme, certain cas concrets. Il s'est révélé que la simple conception et mise au point d'algorithme de simulation dans un programme est un travail déjà très intéressant. Les techniques utilisées sont nombreuses et complexes, et la manière dont elles inuencent la qualité et les performances de la simulation sont un sujet d'étude digne d'intérêt. Bien que les performances du programme n'aient pas tout à fait été celles espérées, je pense quand même que l'analyse du comportement du programme et le traitement de cas simples ont été intéressants et ouvrent de nombreuses voies pour l'amélioration celui-ci. Des solutions comme l'amélioration de la technique d'intégration, un pas de temps individuel pour chaque corps, et des techniques de division de l'espace en cellules pour des n de simplication peuvent être apportée. D'autre part, pour ce qui est de combler la lacune dans la visualisation des résultats, je pense aujourd'hui que si je devait réécrire un programme, je le ferais en langage python. Celui-ci possède des fonctions graphiques puissantes tout en gardant une approche et une syntaxe proches de celles du C. Aussi, certains outils ont été développés par des scientiques an de visualiser les résultats de simulations n corps dans ce langage. 6.2 Remerciements Je tiens à remercier avant tout mon maître référant, M. Laurent de Schouqui m'a suivi, aidé et conseillé tout au long de ce travail. D'autre part, j'aimerais aussi remercier le Dr. Yves Revaz, chercheur au laboratoire d'astrophysique de l'EPFL pour m'avoir consacré une partie de son lepnikoff, 42 Simulation n corps Thibault Wildi - 3M8 temps et avoir répondu à mes nombreuses questions. 43 Table des gures 2.1 Carte de densité d'une simulation n corps de la formation de l'univers, n = 256 · 106 corps[17]. . . . . . . . . . . . . . . . . . . 3.1 3.2 Une itération de la méthode d'Euler . . . . . . . . . . . . . . . . Premières itérations de la méthode d'Euler sur une courbe du type ax2 + b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4.1 Format d'entrée du chier "Input.txt" . . . . . . . . . . . . . . . 21 5.1 5.2 5.3 Simulation du cas soleil-Terre - Résultats . . . . . . . . . . . . . Simulation du cas soleil-Terre - Graphique . . . . . . . . . . . . . Problème pythagoricien - Situation initiale, The Astronomical Journal 1967[15] . . . . . . . . . . . . . . . . . . . . . . . . . . . Problème pythagoricien - Résultats . . . . . . . . . . . . . . . . . Problème pythagoricien - Graphique . . . . . . . . . . . . . . . . Problème pythagoricien - Comparaison des simulations ; de gauche à droite : les trajectoires théoriques, la simulation de pas de temps 1.25 · 10−1 [s] , la simulation de pas de temps 1.91 · 10−6 [s] . . . Problème pythagoricien - Éjection de corps, la vue inférieure est un agrandissement de la vue supérieure . . . . . . . . . . . . . . Étude sur le nombre de corps - Résultats . . . . . . . . . . . . . . Étude sur le nombre de corps - Graphique . . . . . . . . . . . . . Problème pythagoricien - Variation de l'énergie totale . . . . . . Problème pythagoricien - Test avec pas de temps dynamique . . Problème pythagoricien - Évolution du pas de temps lors d'un "close encounter" . . . . . . . . . . . . . . . . . . . . . . . . . . . Problème pythagoricien - "Close encounter" avec pas de temps dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Figure de huit - Illustration de la situation initiale[19] . . . . . . Figure de huit - Situations initiales[19] . . . . . . . . . . . . . . . Figure de huit - Trajectoire des corps au temps t ≈ 300 . . . . . Études des amas - Ségrégation des masses . . . . . . . . . . . . . 25 26 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11 5.12 5.13 5.14 5.15 5.16 5.17 44 6 9 27 27 28 29 30 32 32 33 35 36 37 38 38 38 39 Bibliographie ://fr.wikipedia.org, Méthode d'Euler. ://en.wikipedia.org, Euler method. ://en.wikipedia.org, N-body simulation. ://en.wikipedia.org, n-body problem. ://en.wikipedia.org, Potential energy. ://fr.wikipedia.org, C (langage). ://en.wikipedia.org, C (programming language). ://www.codingunit.com/the-history-of-the-c-language, The History of the C Language. [9] http ://gcc.gnu.org/ GCC, the GNU Compiler Collection, site internet du GNU Compiler Collection. [10] http ://fr.wikipedia.org, Programmation modulaire. [11] http ://www.libsdl.org/, Site De la bibliothèque C, SDL (Simple DirectMedia Layer). [12] Bernard Cassagne, Introduction au langage C, 1998. [13] Sverre J.Aarseth, Gravitational N-Body Simulations : Tools and Algorithms, Cambridge Monographs on Mathematical Physics, 2010. [14] Mauri Valtonen & Hannu Karttunen, The Three-Body Problem, 2006. [15] Victor Szebehely & C. Frederik Peters, Complete Solution of a General Problem of Three Bodies, The Astronomical Journal, Volume 72, Number 7, September 1967. [16] Entretien datant 17 octobre 2013 avec Yves Revaz, collaborateur scientique au laboratoire d'astronomie de l'EPFL. [17] http ://obswww.unige.ch/ revaz/pNbody/rst/Gallery.htmlrs, site dédié au module python pNbody de visualisation de simulation n corps, développé par le chercheur Yves Revaz . [18] http ://www.scholarpedia.org/article/N-body_choreographies, Site présentant certain cas de gure n corps. [19] By Alain Chenciner and Richard Montgomery, A remarkable periodic solution of the three-body problem in the case of equal masses, Annals of Mathematics, 152 (2000), 881-901. [1] [2] [3] [4] [5] [6] [7] [8] http http http http http http http http 45 Chapitre 7 Annexe 7.1 Raccourcis clavier de l'interface graphique Commande globale : Flèches directionnelles : Déplacer le graphique + et - du NumPad : Augmenter ou diminuer l'échelle d'achage i : Prendre une capture d'écran (elle est sauvegardée sous Output/Image.bmp ) d : Passer au mode d'achage dynamique s : Passer au mode d'achage statique a : Acher ou non les axes Esc : Quitter le programme Commande spécique au mode de vue statique : ; et : pour respectivement diminuer et augmenter le pourcentage de points aché à l'écran, impact sur les performances Commande spécique au mode de vue dynamique : ; et : pour respectivement augmenter ou diminuer la vitesse d'écoulement du temps t : Acher ou non les trajectoires e : Eacer les trajectoires p : Arrêter l'écoulement du temps r : Revenir au temps 0 7.2 Code source 7.2.1 main.c 46 Simulation n corps 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 #include #include #include #include #include #include #include #include /∗ ∗ Thibault Wildi - 3M8 < s t d i o . h> // Used for I /O <math . h> // Used for rounding Operations <t i m e . h> // Used for time < s t d b o o l . h> // Used for Boolean operations operations functions " c o n s t a n t s . h" " i o _ o p e r a t i o n s . h" " i t e r a t o r . h" " d i s p l a y 2 d . h" int int unsigned long int double double double double double double double double double int int Extern variable ∗ ∗/ defining BODY_NB; DIMENTIONS ; bool STEP = 0; USE_DYMC_DELTA_T; DYMC_ACCURACY; TIME = 0; MAX_DELTA_T; DELTA_T ; RUN_TIME ; G_CONST; SYSTEM_ENERGY; THEORETICAL_ENERGY ; PRT_PRCT; char main ( FILE ∗ outPutFile_BIN { argc , output = 0; // Will be our pointer on our = 0; // Will be our pointer on our // Will be our // Will count // Initializes // Deleting output file if // Deleting output file if file ∗ outPutSpec_TXT FILE ∗∗ a r g v ) specification file ∗ outPutEnergy_TXT ∗ bodyArray ; FILE BODY clock_t ticks = 0; ticks ; for bodyArray time = array of body number of clock calculation IO_inti ( ) ; bodies from input file r e m o v e (OUTPUTFILE_BIN_NAME) ; existing r e m o v e (OUTPUTSPEC_TXT_NAME) ; existing r e m o v e ( " Output / E n e r g y . t x t " ) ; /∗ ∗ if Opening ∗ ∗/ files outPutSpec_TXT = (PRT_PRCT != f o p e n (OUTPUTSPEC_TXT_NAME, "w" ) ; 0) { outPutFile_BIN = f o p e n (OUTPUTFILE_BIN_NAME, "wb" ) ; outPutEnergy_TXT = f o p e n ( " Output / E n e r g y . t x t " , "w" ) ; } f p r i n t f ( outPutSpec_TXT , " Initial s i t u a t i o n : \ n" ) ; IO_printBodyData_TXT ( outPutSpec_TXT , b o d y A r r a y , t r u e , false , /∗ ∗ for ' , ' , Calculation ticks = true , ' | ') ; process ∗ ∗/ clock () ; ( STEP = // 0; TIME < RUN_TIME ; For every STEP++) iteration { c a l c u l a t e N e x t S t e p ( bodyArray ) ; if // ( STEP%( // int Calculate next STEP ) r o u n d ( 1 /PRT_PRCT) Print only PRT_PRCT { 47 == 0 && PRT_PRCT != steps in output file 0) false , Simulation n corps 58 IO_printBodyData_BIN ( o u t P u t F i l e _ B I N , b o d y A r r a y , t r u e , t r u e , t r u e , false , false ) ; 59 60 61 62 63 // Print data } if ( STEP%( { int ) r o u n d ( 1 0 /PRT_PRCT) f p r i n t f ( outPutEnergy_TXT , " == 0 && PRT_PRCT != %.15 e | %.15 e 0) |%.15 e | \ n " , TIME , DELTA_T,SYSTEM_ENERGY) ; 64 65 66 67 68 69 70 } } ticks /∗ ∗ c l o c k ( )− t i c k s ; = Printing data in specification f p r i n t f ( outPutSpec_TXT , " Final file ∗ ∗/ s i t u a t i o n : \ n" ) ; IO_printBodyData_TXT ( outPutSpec_TXT , b o d y A r r a y , t r u e , double false , 71 ' , ' , ' | ') ; systemError = THEORETICAL_ENERGY 72 73 74 (THEORETICAL_ENERGY ∗ false , SYSTEM_ENERGY) / f p r i n t f ( outPutSpec_TXT , "\ nSteps : %d \ n " , STEP ) ; f p r i n t f ( outPutSpec_TXT , " Delta %.4 e \ n " ,MAX_DELTA_T) ; f p r i n t f ( outPutSpec_TXT , " Calculation f p r i n t f ( outPutSpec_TXT , " Start THEORETICAL_ENERGY) ; 76 77 − true , 100; t i c k s ) /CLOCKS_PER_SEC) ) ; 75 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 Thibault Wildi - 3M8 f p r i n t f ( outPutSpec_TXT , " Finish f p r i n t f ( outPutSpec_TXT , " Delta T: − Time : Energy : %.8 f [ s ] \ n" , ( ( ( float ) %.10 e \n" , − E n e r g y : % . 1 0 e \ n " , SYSTEM_ENERGY) ; − E n e r g y : % . 1 0 e \ n " , (SYSTEM_ENERGY − THEORETICAL_ENERGY) ) ; f p r i n t f ( outPutSpec_TXT , /∗ ∗ if Closing files " Error : %.6 f %%\n " , s y s t e m E r r o r ) ; ∗ ∗/ f c l o s e ( outPutSpec_TXT ) ; (PRT_PRCT != 0) { f c l o s e ( outPutFile_BIN ) ; f c l o s e ( outPutEnergy_TXT ) ; } /∗ ∗ if Display results if possible ∗ ∗/ (DIMENTIONS == 2 && PRT_PRCT != 0) { d i s p l a y 2 D (OUTPUTFILE_BIN_NAME) ; } } return 1; Code 7.1 main.c 7.2.2 constants.h 1 #ifndef 2 #define 3 4 #include 5 #include CONSTANTS_H_INCLUDED CONSTANTS_H_INCLUDED < s t d i o . h> type variable < s t d b o o l . h> 6 7 Needed // Used // Defines for FILE for Boolean operations #define MAX_DIMENTIONS 3 the #define 9 #define 8 // declaration max dimensions accepted ( this is used for creation of defines the BODY struct ) OUTPUTFILE_TXT_NAME " Output / Output . t x t " file OUTPUTSPEC_TXT_NAME " Output / S p e c . t x t " file // Defines output // Defines output name name 48 Simulation n corps #define 11 #define 12 #define 13 #define 14 #define 15 #define 10 Thibault Wildi - 3M8 OUTPUTFILE_BIN_NAME " Output / Output . b i n " file // Defines output " Output / I m a g e . bmp" // Defines output " Input / Input . txt " // Defines input 1820 // Defines screen 980 // Defines screen // How many name OUTPUTIMAGE_BMP image name INPUTFILE_NAME file name SCREEN_X width SCREEN_Y hight MAX_PT_ON_SCREEN 2 5 0 0 0 are read from file and printed on screen ( spread on all points simulation length ) 16 17 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 18 ∗ NAME : BODY 19 ∗ MEMBRES : 20 ∗ d o u b l e p o s [ MAX_DIMENTIONS ] W i l l c o n t a i n body ' s p o s i t i o n 21 ∗ 22 ∗ 23 ∗ 24 ∗ coordinates (x , y , z ) double at time t+d e l t a _ t double double before Will flushing ( t in before += contain delta_t body ' s position ) Will contain body ' s speed Will contain body ' s speed body ' s mass [m/ s ] n e x t _ s p d [ MAX_DIMENTIONS ] t+d e l t a _ t double [m] s p d [ MAX_DIMENTIONS ] coordinates (x , y , z ) time in n e x t _ p o s [ MAX_DIMENTIONS ] flushing ( mass ; t += delta_t Will [ kg ] at ) contain in 25 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / 26 27 { 28 p o s [ MAX_DIMENTIONS ] ; 29 n e x t _ p o s [ MAX_DIMENTIONS ] ; 30 s p d [ MAX_DIMENTIONS ] ; 31 n e x t _ s p d [ MAX_DIMENTIONS ] ; 32 mass ; 33 } BODY; 34 35 / ∗ ∗ E x t e r n a l d e c l a r a t i o n s ∗ ∗/ 36 BODY_NB; /∗ ∗ Number o f b o d i e s t o be r e a d i n inputfile ∗ ∗/ 37 DIMENTIONS ; /∗ ∗ Number o f d i m e n s i o n s ( e x p e c t e d 2 o r 3 ) ∗ ∗/ 38 b o o l USE_DYMC_DELTA_T; is t o be u s e d ( t r u e o r false ) ∗ ∗/ /∗ ∗ I f dynamic t i m e s t e p 39 DYMC_ACCURACY; f a c t o r when d y n a m i c t i m e s t e p is u s e d ∗ ∗/ /∗ ∗ Precision 40 TIME ; /∗ ∗ Global v a r i a b l e f o r time i n seconds ∗ ∗/ 41 DELTA_T ; /∗ ∗ Time s t e p w i d t h ( t i m e b e t w e e n e a c h i t e r a t i o n ) in seconds ∗ ∗/ 42 MAX_DELTA_T; /∗ ∗ Maximum t i m e s t e p w i d t h ( t i m e b e t w e e n e a c h i t e r a t i o n ) in seconds ∗ ∗/ 43 RUN_TIME ; /∗ ∗ Run t i m e i n s e c o n d s ∗ ∗/ 44 G_CONST; /∗ ∗ Universal gravitational constant , theoretically 6 , 6 7 4 E−11 ∗ ∗/ 45 SYSTEM_ENERGY; /∗ ∗ System ' s total e n e r g y ∗ ∗/ 46 THEORETICAL_ENERGY ; /∗ ∗ System ' s initial total energy ∗ ∗/ 47 PRT_PRCT; /∗ ∗ P e r c e n t a g e o f s t e p t o be p r i n t e d i n o u t p u t file ( to save size and t i m e ) ∗ ∗ / STEP ; 48 calculation s t e p s ∗ ∗/ /∗ ∗ W i l l c o u n t t h e number o f 49 50 / / CONSTANTS_H_INCLUDED typedef struct double double double double double extern extern extern extern extern extern extern int int double double double double extern extern extern extern extern double double double double double extern unsigned long int #endif 49 Simulation n corps Thibault Wildi - 3M8 Code 7.2 constants.h 7.2.3 io_operations.h 1 #ifndef 2 #define 3 4 #include 5 6 #include IO_OPERATIONS_H_INCLUDED IO_OPERATIONS_H_INCLUDED < s t d b o o l . h> // Used for Boolean operations " c o n s t a n t s . h" 7 8 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 9 ∗ NAME : BODY∗ I O _ i n t i ( ) 10 ∗ 11 ∗ DESCRIPTION : Reads t h e initialization ( input ) file and c r e a t e s 12 ∗ 13 14 15 16 17 18 19 20 ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ 21 ∗ 22 ∗ 23 ∗ array of OUTPUTS 35 ∗ 36 ∗ them to there initial : : Type Value of NOTES to the array false BODY∗ : : Expects : Contains the address of following "BODY_NB first entity format in the input file ( from number_of_bodies DIMENTIONS number_of_dimensions G_CONST gravity_constant RUN_TIME run_time DELTA_T delta_t PRT_PRCT print_percentage USE_DYMC_DELTA_T use_dynamic_delta_t −> the bodies : ") 28 ∗ 34 ∗ initializing : RETURN 27 ∗ 33 ∗ before None 26 ∗ 32 ∗ objects position PARAMETERS 25 ∗ 30 ∗ 31 ∗ and : INPUTS 24 ∗ 29 ∗ speed mass an of ( True −> 1 , 2) DYMC_DELTA_T_ACC dynamic_delta_t_accuracy pos_x | pos_y | pos_z | spd_x | spd_y | spd_z | mass | pos_x | pos_y | pos_z | spd_x | spd_y | spd_z | mass | . . . END" After DYMC_DELTA_T_ACC, E x p e c t s DYMC_DELTA_T_ACC e v e n if use of each line static is an DELTS_T, body . w i l l be d i s r e g a r d e d though . 37 ∗ value 38 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / 39 BODY∗ I O _ i n t i ( ) ; 40 41 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 50 " Simulation n corps 42 ∗ 43 ∗ 44 ∗ 45 46 47 48 ∗ ∗ ∗ ∗ NAME ∗ void coding , INPUTS bool char DESCRIPTION bool dataSeparator , : so Prints each char ∗ IO_printBodyData_TXT ( prt_time , digit the is prt_pos , char bodies ' considered bool o u t P u t F i l e N a m e ,BODY prt_spd , bool bodySeparator ) ; data to being an a . txt file ( ASCII character ) : PARAMETERS : FILE ∗ outPutFile Pointer on output bodyArray Pointer on the file , EXPECTS OPEN FILE ! ! ! ! BODY∗∗ pointers ∗ ∗ ∗ ∗ ∗ 55 ∗ 56 57 58 59 60 61 62 : bodyArray , prt_mass , 49 ∗ 50 51 52 53 54 Thibault Wildi - 3M8 on the array of bodies bool prt_time Print bool prt_pos Print position ? bool prt_spd Print Speed ? bool prt_mass Print char dataSeparator Char that will be used BodySeparator Char that will be used to separate values to separate Bodies char time ? Mass ? ∗ ∗ OUTPUTS : ∗ RETURN : ∗ none ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / IO_printBodyData_TXT ( FILE ∗ o u t P u t F i l e ,BODY∗ b o d y A r r a y , bool void prt_time , char bool prt_pos , dataSeparator , bool prt_spd , bool char prt_mass , bodySeparator ) ; 63 64 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 65 ∗ NAME : v o i d IO_printBodyData_CONSOLE (BODY∗ b o d y A r r a y , bool prt_time , 66 67 68 69 70 71 72 73 74 75 76 ∗ ∗ ∗ ∗ ∗ ∗ DESCRIPTION INPUTS prt_pos , char : prt_spd , bool pr t _ m a s s , char the bodies ' data out on console : : BODY∗∗ pointers ∗ ∗ ∗ ∗ ∗ bool bodySeparator ) ; Prints PARAMETERS 77 ∗ 78 79 80 81 82 83 84 bool dataSeparator , on the bodyArray Pointer on the array of bodies bool prt_time Print bool prt_pos Print position ? bool prt_spd Print Speed ? bool prt_mass Print char dataSeparator Char that will be used BodySeparator Char that will be used to separate values to separate Bodies char time ? Mass ? ∗ ∗ OUTPUTS : ∗ RETURN : ∗ none ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / IO_printBodyData_CONSOLE (BODY∗ b o d y A r r a y , b o o l prt_time , bool void prt_pos , bool prt_spd , bool prt_mass , bodySeparator ) ; char dataSeparator , char 85 86 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 87 ∗ NAME : v o i d IO_printBodyData_BIN ( FILE ∗ o u t P u t F i l e ,BODY∗ bodyArray , 88 89 ∗ 90 ∗ prt_spd , DESCRIPTION printed bool bool as prt_time , bool prt_energy , bool prt_pos , bool prt_mass ) ; : Prints true the bodies ' numbers ) 51 data to an . bin file ( number are Simulation n corps 91 92 93 94 ∗ ∗ ∗ ∗ INPUTS 95 ∗ 96 ∗ 97 98 99 100 101 102 103 104 105 106 107 108 109 ∗ ∗ ∗ ∗ ∗ Thibault Wildi - 3M8 : PARAMETERS : FILE ∗ outPutFile Pointer on output bodyArray Pointer on the file , EXPECTS OPEN FILE ! ! ! ! BODY∗∗ pointers on the IO_DEVICE outPutDevice : array of bodies File , outPutDevice Console or Out defines the Both bool prt_time Print bool prt_pos Print time ? position ? bool prt_spd Print Speed ? bool prt_mass Print Mass ? bool prt_energy Print System ' s total energy ? ∗ OUTPUTS : ∗ RETURN : ∗ none ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / IO_printBodyData_BIN ( FILE ∗ o u t P u t F i l e ,BODY∗ b o d y A r r a y , bool void prt_time , bool prt_energy , bool prt_pos , bool prt_spd , bool prt_mass ) ; #endif // IO_OPERATIONS_H_INCLUDED Code 7.3 io_operations.h 7.2.4 io_operations.c 1 #include 2 #include 3 #include 4 #include 5 6 #include 7 #include < s t d i o . h> // Used for I /O Operations < s t d l i b . h> // Used for memory < s t d b o o l . h> // Used for Boolean <math . h> // Used for rounding ( file reading / writing ) / allocation processes ( malloc calloc ) operations operations " c o n s t a n t s . h" 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 " i o _ o p e r a t i o n s . h" BODY∗ IO_inti ( { FILE void ∗ inPutFile double double Will contain Will contain Will contain int ) ; // pointer towards input file spd2 ; // speed squared for initial energy calculation sep ; purpose // separation between two bodies for energy calculation purpose /∗ ∗ d, i , j ; Reading inPutFile Opening = input INPUTFILE fscanf ( inPutFile Reading number number the , , "r") ; mode // "r" &BODY_NB) ; // " DIMENTIONS %i " , &DIMENTIONS) ; // dimensions , gravity , Reading of time r e a d −o n l y " BODY_NB %i " , fscanf ( inPutFile run ∗ ∗/ bodies of fscanf ( inPutFile Reading in of fscanf ( inPutFile Reading file f o p e n (INPUTFILE_NAME, " G_CONST %l e " , &G_CONST) ; // constant " RUN_TIME %l e " , the simulation 52 &RUN_TIME) ; // Simulation n corps 24 fscanf ( inPutFile Reading 25 the , Reading the , 27 the , use 28 29 30 31 32 the /∗ ∗ for 33 ( i = the 0; { // 36 37 for &PRT_PRCT) ; to be time step time step // written &USE_DYMC_DELTA_T) ; // boolean " DYMC_DELTA_T_ACC %l e " , &DYMC_ACCURACY) ; // accuracy initializing = of (d = array ∗ ∗/ (BODY) ) ; // i ++) // bodies 0; Initializes body Bodies < BODY_NB; the sizeof the c a l l o c (BODY_NB, array i Initializes 34 35 , and ∗ bodyArray Creating // 0.01; Creating BODY points dynamic dynamic ∗= PRT_PRCT of " USE_DYMC_DELTA_T %i " , of fscanf ( inPutFile Reading &MAX_DELTA_T) ; " PRT_PRCT %l e " , percentage fscanf ( inPutFile Reading " DELTA_T %l e " , DELTA_T fscanf ( inPutFile 26 d < DIMENTIONS ; d++) positions { fscanf ( inPutFile // 38 39 40 Copies read , "%l e position | " , value to &b o d y A r r a y [ i ] . p o s [ d ] ) ; body } // 41 42 for (d = 0; Initializes d < DIMENTIONS ; d++) speed { fscanf ( inPutFile // 43 44 Copies read , speed "%l e | value " , to &b o d y A r r a y [ i ] . s p d [ d ] ) ; body } fscanf ( inPutFile // 45 46 47 48 49 50 51 52 53 Initializes , "%l e | " , &b o d y A r r a y [ i ] . m ass ) ; mass } /∗ ∗ Calculating initial THEORETICAL_ENERGY = for ( i = 0 ; total energy ∗ ∗/ 0; i <BODY_NB ; i ++) { for spd2 = 0; ( d =0; Calculating 54 55 56 57 58 59 d<DIMENTIONS ; kinetic d++) // energy { s p d 2 += pow ( b o d y A r r a y [ i ] . s p d [ d ] , 2 ) ; } THEORETICAL_ENERGY += for ( j=i +1; Calculating 60 61 62 j <BODY_NB; potential ∗ 0.5 b o d y A r r a y [ i ] . mass ∗ spd2 ; j ++) // energy { for sep Calculate 63 64 65 66 67 68 69 70 71 72 73 74 75 Thibault Wildi - 3M8 = 0; ( d =0; d<DIMENTIONS ; separation d++) // distance { sep += pow ( b o d y A r r a y [ i ] . p o s [ d ] − bodyArray [ j ] . pos [ d ] , 2 ) ; } sep = s q r t ( sep ) ; THEORETICAL_ENERGY b o d y A r r a y [ j ] . mass / −= G_CONST ∗ b o d y A r r a y [ i ] . mass ∗ sep ; } } f c l o s e ( inPutFile ) ; } void return bodyArray ; IO_printBodyData_TXT ( prt_time , bool prt_pos , FILE ∗ bool o u t P u t F i l e ,BODY∗ prt_spd , 53 bool char bodyArray , prt_mass , bool Simulation n corps 76 77 78 79 bodySeparator ) i ,d; ( prt_time == true ) // Print time if needed followed by separator { f p r i n t f ( outPutFile , " %.5 e %c%c " , TIME , dataSeparator , bodySeparator ) ; } for ( i = if { 0; i <BODY_NB; ( p r t _ p o s == for { (d = 0; i ++) // true ) d<DIMENTIONS ; For every body : // Print position if needed d++) { f p r i n t f ( outPutFile , " %.5 e %c " , bodyArray [ i ] . pos [ d ] , dataSeparator ) ; 91 92 93 94 95 96 97 } if } ( p r t _ s p d == for { (d = 0; true ) // d<DIMENTIONS ; Print speed if needed d++) { f p r i n t f ( outPutFile , " %.5 e %c " , bodyArray [ i ] . spd [ d ] , dataSeparator ) ; 98 99 100 101 102 } if } ( p r t _ m a s s == true ) // Print time if needed { f p r i n t f ( outPutFile , " %.5 e %c " , b o d y A r r a y [ i ] . mass , dataSeparator ) ; 103 104 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 static int if body 82 83 84 85 86 87 88 89 90 111 112 113 char dataSeparator , { 80 81 105 106 107 108 109 110 Thibault Wildi - 3M8 } f p r i n t f ( o u t P u t F i l e , "%c " , b o d y S e p a r a t o r ) ; separator // Print body char } f p r i n t f ( o u t P u t F i l e , " \n" ) ; // Print NewLine at EOF } void IO_printBodyData_CONSOLE (BODY∗ prt_pos , bool prt_spd , bool bodySeparator ) { static int if bool prt_time , dataSeparator , char bool i ,d; ( prt_time body char bodyArray , prt_mass , == true ) // Print time if needed followed by separator { printf (" %.5 e %c%c " , TIME , dataSeparator , bodySeparator ) ; } for { ( i if { = 0; i <BODY_NB; ( p r t _ p o s == for ( d = i ++) // true ) 0; d<DIMENTIONS ; For every body : // Print position if needed d++) { printf (" %.5 e %c " , bodyArray [ i ] . pos [ d ] , dataSeparator ) ; } if } { ( p r t _ s p d == for ( d = true ) 0; // d<DIMENTIONS ; Print speed if needed d++) { printf (" %.5 e %c " , bodyArray [ i ] . spd [ d ] , } } 54 dataSeparator ) ; Simulation n corps 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 if ( p r t _ m a s s == true ) // Print time if needed { printf (" %.5 e %c " , b o d y A r r a y [ i ] . mass , dataSeparator ) ; } p r i n t f ( "%c " , b o d y S e p a r a t o r ) ; // Print body separator char } p r i n t f ( " \n" ) ; // Print NewLine at EOF } void FILE ∗ IO_printBodyData_BIN ( prt_time , bool prt_energy , o u t P u t F i l e ,BODY∗ bool bodyArray , prt_pos , bool prt_spd , // time if bool bool prt_mass ) { static int if i ; ( prt_time == true ) { f w r i t e (&TIME , } if ( prt_energy sizeof double == ( Print requested ) ,1 , outPutFile ) ; true ) // Print system ' s total energy if requested 152 153 154 155 156 157 158 159 160 { f w r i t e (&SYSTEM_ENERGY, } for ( i = 0 if { ; i <BODY_NB ( p r t _ p o s == ; sizeof double ( i ++) // true ) { f w r i t e (& b o d y A r r a y [ i ] . p o s , outPutFile ) ; 161 162 163 164 165 166 167 168 169 170 171 Thibault Wildi - 3M8 if ) ,1 , outPutFile ) ; For every body // Print position sizeof double ( ) , : if requested DIMENTIONS , } ( p r t _ s p d == true ) // { f w r i t e (& b o d y A r r a y [ i ] . s p d , outPutFile ) ; if Print speed sizeof double ( ) , if requested DIMENTIONS , } ( p r t _ m a s s == true ) // { f w r i t e (& b o d y A r r a y [ i ] . mass , } Print time sizeof double ( ) , if 1, requested outPutFile ) ; } } Code 7.4 io_operations.c 7.2.5 iterator.h 1 #ifndef 2 #define ITERATOR_H_INCLUDED ITERATOR_H_INCLUDED 3 4 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 5 ∗ NAME : v o i d c a l c u l a t e N e x t S t e p (BODY∗ b o d y A r r a y ) 6 ∗ 7 ∗ DESCRIPTION : C a l c u l a t e s the next p o s i t i o n o f e v e r y body o f bodyArray 8 9 10 11 ∗ ∗ ∗ ∗ 12 ∗ 13 ∗ 14 ∗ calculates INPUTS with interaction system ' s total all the bodies of bodyArray + : PARAMETERS : BODY∗ of of energy pointers OUTPUTS on the bodyArray Pointer bodies : RETURN : none 55 on the array Simulation n corps Thibault Wildi - 3M8 15 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / 16 c a l c u l a t e N e x t S t e p (BODY∗ b o d y A r r a y ) ; 17 18 / / ITERATOR_H_INCLUDED void #endif Code 7.5 iterator.h 7.2.6 iterator.c 1 #include 2 3 #include 4 5 void 6 7 double 8 double 9 double 10 static double 11 static double 12 static double 13 static double 14 static int 15 16 if <math . h> // Use for math function ( like " pow ( x , y ) " ) " c o n s t a n t s . h" c a l c u l a t e N e x t S t e p (BODY∗ bodyArray ) { a c c [ DIMENTIONS ] ; ( or // Will contain the acceleration f r c [ DIMENTIONS ] ; // Will contain the forces d i s [ DIMENTIONS ] ; // Will contain distances // Will contain the distance // Will contain the squared force between per unit object on of mass ) each coordinate sep ; between to bodies ( separation ) spd2 ; speed of object for system energy calculation nextSystemEnregy ; // Will hold the energy of the energyDerivative ; // Will hold the calculated calculated energy derivative i , j ,d; 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ( STEP == DELTA_T = MAX_DELTA_T; } /∗ ∗ for Calculating next nextSystemEnregy ( i =0; every = position i <BODY_NB; i ++) calculate next body for every body ∗ ∗/ 0; // For position { for spd2 frc to = ( 0; d =0; d<DIMENTIONS ; d++) // Set 0 { f r c [ d]=0; s p d 2 += pow ( b o d y A r r a y [ i ] . s p d [ d ] , 2 ) ; Calculating total speed nextSystemEnregy // Calculating for every { // ( j = 0 body if Except += kinetic ; // squared } 0.5 ∗ b o d y A r r a y [ i ] . mass ∗ spd2 ; energy j <BODY_NB ; j ++) // For o ( j != body i ) b { sep // 38 39 40 41 42 0) { // Reset for = 0; separation Calculate ( d =0; distance between d<DIMENTIONS ; separation b and dis [d] = to 0 distances { sep o d++) bodyArray [ j ] . pos [ d ] += pow ( d i s [ d ] , 2 ) ; } 56 − bodyArray [ i ] . pos [ d ] ; Simulation n corps 43 44 45 sep // 59 60 61 62 63 64 65 66 67 68 69 70 for For every ( s q r t ( sep ) ; d =0; frc [d] ∗ ] . mass dis [d] 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 calculate d++) force that body o applies to body += G_CONST ∗ b o d y A r r a y [ j ] . mass ∗ bodyArray [ i / pow ( s e p , 3 ) ; } if // ( j > Calculating i ) potential energy { nextSystemEnregy b o d y A r r a y [ i ] . mass / −= ∗ G_CONST b o d y A r r a y [ j ] . mass ∗ sep ; } } } for every ( d =0; d<DIMENTIONS ; dimension calculate d++) acceleration object // For // For b { acc [ d ] = frc [d] / b o d y A r r a y [ i ] . mass ; } for every ( d =0; d<DIMENTIONS ; dimension calculate d++) next speed of object b { bodyArray [ i ] . next_spd [ d ] = bodyArray [ i ] . spd [ d ] + DELTA_T ∗ acc [ d ] ; } for every ( d =0; d<DIMENTIONS ; dimension calculate d++) next // position of object For b { bodyArray [ i ] . next_pos [ d ] bodyArray [ i ] . spd [ d ] 71 72 73 74 75 76 77 d<DIMENTIONS ; dimension { 51 52 53 54 55 56 57 58 = b 46 47 48 49 50 Thibault Wildi - 3M8 spd and + = bodyArray [ i ] . pos [ d ] bodyArray [ i ] . next_spd [ d ] ) /2 ; // + DELTA_T ∗ Averaging next_spd } } /∗ ∗ if Calculating new time ( (USE_DYMC_DELTA_T == step true ) if dynamic time && ( STEP > 0) ) step enabled ∗ ∗/ { // Calculating energy variation speed ( derivative ) step energyDerivative = f a b s ( ( ( nextSystemEnregy − during last SYSTEM_ENERGY) / DELTA_T) /THEORETICAL_ENERGY) ; step if ( energyDerivative is to be { DELTA_T else if > DYMC_ACCURACY) ∗= is to ( energyDerivative be If time // If time 0.7; } step // reduced < (DYMC_ACCURACY ∗0.7) ) grown { DELTA_T ∗= 1.1; } big if (DELTA_T > MAX_DELTA_T) // If time step too // If time step too { DELTA_T = MAX_DELTA_T; else if } ( DELTA_T < (MAX_DELTA_T/ 5 0 0 0 0 ) ) small 57 ( Simulation n corps 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 Thibault Wildi - 3M8 { DELTA_T = MAX_DELTA_T/ 5 0 0 0 0 ; } } /∗ ∗ for Flushing the ( i = 0 for { ( ; d =0; ∗ ∗/ data SYSTEM_ENERGY = nextSystemEnregy ; i <BODY_NB ; i ++) d<DIMENTIONS ; d++) { bodyArray [ i ] . pos [ d ] = bodyArray [ i ] . next_pos [ d ] ; bodyArray [ i ] . spd [ d ] = bodyArray [ i ] . next_spd [ d ] ; } } TIME += DELTA_T ; // Flush time } Code 7.6 iterator.c 7.2.7 display2d.h 1 #ifndef 2 #define 3 4 #include 5 #include DISPLAY_H_INCLUDED DISPLAY_H_INCLUDED 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <SDL/SDL . h> // Used for graphical <SDL/ SDL_ttf . h> // Used for text with interface SDL / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ NAME : display2D ( char ∗ void DESCRIPTION : Displays objects dataFileName_BIN ) ; trajectory on screen ( 2D only ) : INPUTS PARAMETERS file to OUTPUTS read : char ∗ dataFileName body information , for Contains EXPECTS . bin name of FILES ! ! ! ! : RETURN : None NOTES : Expects . bin BDY_1_Y BDY_2_X BDY_2_Y . . . FILE , with format : TIME ENERGY BDY_1_X TIME ENERGY BDY_1_X BDY_1_Y . . . 20 ∗ 21 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / 22 display2D ( ∗ dataFileName_BIN ) ; 23 24 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 25 ∗ NAME : v o i d p r i n t D a t a O n S c r e e n _ S t a t i c ( FILE ∗ d a t a F i l e _ B I N , double const s c a l e , d o u b l e c o n s t ∗ f o c u s C e n t e r , SDL_Surface ∗ s c r e e n int s c r e e n P r e c i s i o n , TTF_Font ∗ f o n t ) ; S D L _ S u r f a c e ∗ bodyIMG , 26 ∗ 27 ∗ DESCRIPTION : P r i n t s bodyIMG on s c r e e n a t e a c h p o s i t i o n r e d i n void 28 29 30 31 ∗ ∗ ∗ ∗ 32 ∗ 33 ∗ char , datafile INPUTS : PARAMETERS : FILE ∗ dataFile_BIN DataFile , EXPECTS OPEN BIN FILE ! ! ! ! ! ! double scale double ∗ focusCenter meters containing center of screen Printing coordinates 58 double ( in scale array meters ) of in pixel size 2 per Simulation n corps Thibault Wildi - 3M8 34 ∗ SDL_Surface positions 35 ∗ be to be image printed position of index only 1/5 of the used on N SDL_surface on ∗ bodyIMG SDL_surface containing screenPrecision ∗ Will screenPrecision points will TTF_font OUTPUTS ∗ screen witch the the printed int 37 ∗ ∗ ∗ ∗ ∗ ∗ to SDL_Surface body 36 ∗ 38 39 40 41 42 are be ( ex : printed be printed only screenPrecision = 5 −> ) font Font pointer that will be writing : RETURN : None NOTES : Expects . . bin BDY_1_Y BDY_2_X BDY_2_Y . . . FILE , with format : TIME ENERGY BDY_1_X TIME ENERGY BDY_1_X BDY_1_Y . . . 43 ∗ 44 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / 45 p r i n t D a t a O n S c r e e n _ S t a t i c ( FILE ∗ d a t a F i l e _ B I N , scale , ∗ f o c u s C e n t e r , S D L _ S u r f a c e ∗ s c r e e n , S D L _ S u r f a c e ∗∗ bodyIMG , s c r e e n P r e c i s i o n , TTF_Font ∗ f o n t ) ; 46 47 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 48 ∗ NAME : v o i d p r i n t D a t a O n S c r e e n _ D y n a m i c ( FILE ∗ d a t a F i l e _ B I N , double const s c a l e , d o u b l e c o n s t ∗ f o c u s C e n t e r , SDL_Surface ∗ s c r e e n S D L _ S u r f a c e ∗ bodyIMG , i n t s c r e e n P r e c i s i o n , TTF_Font ∗ f o n t ) ; 49 ∗ P r i n t s bodyIMG an a n i m a t i o n o f t h e b o d i e s m o v i n g 50 ∗ DESCRIPTION : 51 ∗ 52 ∗ INPUTS : 53 ∗ PARAMETERS : 54 ∗ FILE ∗ d a t a F i l e _ B I N D a t a F i l e , EXPECTS OPEN BIN void double scale double ∗ focusCenter meters 56 ∗ 57 ∗ containing center canvas text of screen SDL_Surface 58 ∗ and are to be are to be 60 ∗ image to be position of index only 1/5 of the used on N array of pixel size per 2 meters ) SDL_surface on witch the ∗ canvas SDL_surface on witch the ∗ bodyIMG SDL_surface containing the ∗ Will screenPrecision points will be printed ( ex : be printed only screenPrecision = 5 −> ) font Font pointer that will be writing bool activated OUTPUTS ∗ screen screenPrecision TTF_font 62 ∗ ( in in printed int 61 ∗ double coordinates scale printed SDL_Surface body Printing printed SDL_Surface trajectories 59 ∗ ∗ ∗ ∗ ∗ ∗ , FILE ! ! ! ! ! ! 55 ∗ 63 64 65 66 67 double const double const int or printTrajectories If trajectories display is not : RETURN : None NOTES : Expects . . bin BDY_1_Y BDY_2_X BDY_2_Y . . . FILE , with format : TIME ENERGY BDY_1_X TIME ENERGY BDY_1_X BDY_1_Y . . . 68 ∗ 69 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / 70 p r i n t D a t a O n S c r e e n _ D y n a m i c ( FILE ∗ d a t a F i l e _ B I N , scale , ∗ f o c u s C e n t e r , SDL_Surface ∗ s c r e e n , SDL_Surface ∗ c a n v a s , S D L _ S u r f a c e ∗∗ bodyIMG , s c r e e n P r e c i s i o n , TTF_Font ∗ f o n t , b o o l void double const printTrajectories ) ; double const int 71 72 / ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 73 ∗ NAME : v o i d p r i n t A x i s O n S c r e e n ( SDL_Surface ∗ s c r e e n , SDL_Surface ∗ a x i s X , SDL_Surface ∗ a x i s Y ) ; 74 ∗ 75 ∗ DESCRIPTION : Prints a x i s on s c r e e n 76 ∗ 59 Simulation n corps 77 ∗ 78 ∗ 79 ∗ INPUTS : SDL_Surface axis are to be printed axis image to be axis image to be axis Scale bars axis Scale bars SDL_Surface 81 ∗ printed SDL_Surface 82 ∗ printed SDL_Surface 83 ∗ to be to be 85 ∗ on on ∗ axisX SDL_surface containing the X ∗ axisY SDL_surface containing the Y ∗ scaleBarX SDL_surface containing the X SDL_surface containing the Y witch the ∗ scaleBarY font Font pointer that will be writing double scale double ∗ focusCenter meters 86 ∗ SDL_surface printed TTF_font used ∗ screen printed SDL_Surface 84 ∗ 93 94 : PARAMETERS 80 ∗ 87 88 89 90 91 92 Thibault Wildi - 3M8 containing center of screen Printing double coordinates ( in scale array in of pixel size per 2 meters ) ∗ OUTPUTS : ∗ RETURN : ∗ None ∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗ / p r i n t A x i s O n S c r e e n ( SDL_Surface ∗ s c r e e n , SDL_Surface ∗ a x i s X , SDL_Surface ∗ a x i s Y , SDL_Surface ∗ s c a l e B a r X , SDL_Surface ∗ s c a l e B a r Y , TTF_Font ∗ f o n t , scale , ∗ focusCenter ) ; void #endif double const // double const DISPLAY_H_INCLUDED Code 7.7 display2d.h 7.2.8 display2d.c 1 #include 2 #include 3 #include 4 #include 5 #include 6 7 #include 8 #include 9 10 void char ∗ < s t d i o . h> // Used for I /O Operations // Used for mathematical <SDL/SDL . h> // Used for graphical < s t d b o o l . h> // Used for Boolean <SDL/ SDL_ttf . h> // Used for text ( file reading / writing ) <math . h> function ( absolute function ) interface operations with SDL " c o n s t a n t s . h" " d i s p l a y 2 d . h" 11 12 13 14 15 16 17 display2D ( { /∗ ∗ enum double double double double Setting 19 20 up {STATIC , viewing bodies mode bodies on ∗ ∗/ variables DYNAMIC} viewingMode = STATIC ; true position in Will contain // Contains // Contains meters centerPosition [ 2 ] ; of the focus max_X = 0, maximum positions point min_X = 0, in the max_Y = middle 0, of min_Y = meterToPixelRatio ; to // type bodyPosition [ 2 ] ; coordinates ratio 18 dataFileName_BIN ) convert the position in meters to the the screen 0; // Contains // Contains position in the the pixels screen bool pause bool print bool print pause = value false ; for printAxis axis = dynamic viewing // Contains // Contains // Contains mode true ; value printTrajectories trajectories for = false ; dynamic viewing 60 mode the Simulation n corps 21 int screenPrecision ; precision : 22 23 24 25 26 27 28 29 30 31 32 33 Will (−> precision FILE ∗ window SDL_Surface grid on body surface SDL_Surface axis bar SDL_Event receive 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 only every 1/10 step = 10% Will of pixel = NULL ; be draw our bodies on dynamic hold N∗ index is printed ) // Pointer // Will be our // Will be the // Will be our // Will be our // Will be our // Used to // Used for mode bodyImg [ 1 0 ] ; for the ∗ axisX printing the ∗ axisY = NULL, trajectories = NULL ; ∗ scaleBarY = NULL, = NULL ; event ; or TTF_Font events ∗ font = NULL ; fonts o; /∗ ∗ Setting SD L_I nit ( up some SDL | SDL_INIT_TIMER Initializing SDL // Initializing SDL_ttf Loading = SDL_SetVideoMode ( Creating // canvas axisX 0, 0, 0) ; // axisY // // scaleBarX SDL_FillRect ( canvas , SDL_FillRect ( axisX , 1, body // SDL_FillRect ( axisY , // 125) ) ; 125) ) ; 0; o < 10 ; of surface of 1, to 0, 0, 16 , 0, 0, 40 , 40 , 16 , 0, 0, ( 1 ; SCREEN_Y) 1, size 16 , 0, 0, ( 1 ; SCREEN_Y) 0, 0, 0) ) ; black to gray axisY to gray 125 , 125 , 125) ) 125 , 125 , 125) ) SDL_MapRGB( s c r e e n −>f o r m a t , axisY to gray axisY to gray SDL_MapRGB( s c r e e n −>f o r m a t , Setting 16 , ( 1 ; SCREEN_Y) size SDL_MapRGB( s c r e e n −>f o r m a t , NULL, // body 1, (SCREEN_X ; 1 ) size (SDL_HWSURFACE, axisX Setting SDL_FillRect ( scaleBarY , surface SCREEN_Y, (1;1) SCREEN_Y, of SDL_MapRGB( s c r e e n −>f o r m a t , NULL, // size (SDL_HWSURFACE, canvas Setting SDL_FillRect ( scaleBarX , surface body size SDL_MapRGB( s c r e e n −>f o r m a t , Setting NULL, ; of SCREEN_X, (SDL_HWSURFACE, Setting NULL, ; SCREEN_X, surface of Creating NULL, // ) ; Simulation " ) ; surface Creating // o = body = SDL_CreateRGBSurface 0 ,0) ; body (SDL_HWSURFACE, Creating // SDL_HWSURFACE Caption (SDL_HWSURFACE, = SDL_CreateRGBSurface 0 ,0) ; scaleBarY up Creating = SDL_CreateRGBSurface 0 ,0) ; 16 , screen Creating = SDL_CreateRGBSurface 0 ,0) ; font SCREEN_Y, S i m u l a t i o n " , "N Body Setting = SDL_CreateRGBSurface 0, " Consolas " SCREEN_X, SDL_WM_SetCaption ( "N Body ( SDL_INIT_EVENTTHREAD) ; = TTF_OpenFont ( " c o n s o l a . t t f " , 2 5 ) ; // for | // // 16 , ∗ ∗/ stuff SDL_INIT_VIDEO screen 42 10 , only surfaces 36 41 ∗ canvas ∗ scaleBarX SDL_Surface font 40 = screen surfaces 35 39 precision = NULL ; witch TTF_Init ( ) ; 38 on ∗ screen SDL_Surface ∗ int if // printed dataFile SDL_Surface axis be dataFile ; toward 34 37 Thibault Wildi - 3M8 125 , 125 , 125 , 125 , o++) { bodyImg [ o ] 0, 0) ; = SDL_CreateRGBSurface (SDL_HWSURFACE, // Creating body surface of size 3, 3, 16 , 0, 0, (1;1) } S D L _ F i l l R e c t ( bodyImg [ 0 ] , 255) ) ; // S D L _ F i l l R e c t ( bodyImg [ 1 ] , ) ; // // // NULL, NULL, Green S D L _ F i l l R e c t ( bodyImg [ 3 ] , ) ; Light Red S D L _ F i l l R e c t ( bodyImg [ 2 ] , ) ; NULL, NULL, SDL_MapRGB( s c r e e n −>f o r m a t , 0, 255 , Blue SDL_MapRGB( s c r e e n −>f o r m a t , 255 , SDL_MapRGB( s c r e e n −>f o r m a t , SDL_MapRGB( s c r e e n −>f o r m a t , Blue 61 0, 0) 0, 255 , 0) 0, 0, 255) Simulation n corps 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 S D L _ F i l l R e c t ( bodyImg [ 4 ] , 0) ) ; 0) ) ; 102 103 104 105 106 107 108 109 110 111 NULL, Orange // NULL, Yellow S D L _ F i l l R e c t ( bodyImg [ 6 ] , 0) ) ; // NULL, Light S D L _ F i l l R e c t ( bodyImg [ 7 ] , 255) ) ; 255) ) ; Starting dataFile in bin /∗ ∗ int while the = to read read 255 , 255 , SDL_MapRGB( s c r e e n −>f o r m a t , 128 , 255 , 128 , 0, Pink SDL_MapRGB( s c r e e n −>f o r m a t , 255 , 0, NULL, SDL_MapRGB( s c r e e n −>f o r m a t , 0, // dark Pink the data file up 0; = (0 == end to the ∗ ∗/ " rb " ) ; // scale , focus center , and screen feof ( dataFile ) ) check for Opening data 0; ( o < BODY_NB; precision ∗ ∗/ Read until file o++ ∗2 , ) SEEK_CUR) ; ) // Skip // for body { f r e a d (& b o d y P o s i t i o n [ 0 ] if // Set up the maximum ( bodyPosition [0] > , sizeof double ( and minimum ) , 2, dataFile ) ; values max_X) { max_X = if bodyPosition [ 0 ] ; } ( bodyPosition [0] < min_X ) { min_X = if bodyPosition [ 0 ] ; } ( bodyPosition [1] > max_Y) { max_Y = if bodyPosition [ 1 ] ; } ( bodyPosition [1] < min_Y ) { min_Y = bodyPosition [ 1 ] ; } } s t e p s ++; if } ( SCREEN_Y / Checking most (max_Y what little − scale min_Y ) <= to use in SCREEN_X / function meterToPixelRatio Add (max_X of screen − min_X ) ) ratio ( uses scale ) { // a 10% = (SCREEN_Y / (max_Y − min_Y ) ) ∗ 0.9; (SCREEN_X / (max_X − min_X ) ) ∗ 0.9; margin else } { meterToPixelRatio // Add a 10% = margin } /∗ ∗ Setting file coordinates sizeof double dataFile , energy (o = // largest { fseek ( 255 , mode steps for SDL_MapRGB( s c r e e n −>f o r m a t , f o p e n ( dataFileName_BIN , Setting and 128 , SDL_MapRGB( s c r e e n −>f o r m a t , NULL, S D L _ F i l l R e c t ( bodyImg [ 9 ] , /∗ ∗ 255 , Purple // 128) ) ; SDL_MapRGB( s c r e e n −>f o r m a t , Green NULL, // S D L _ F i l l R e c t ( bodyImg [ 8 ] , // 100 101 // S D L _ F i l l R e c t ( bodyImg [ 5 ] , 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 Thibault Wildi - 3M8 center point on screen ∗ ∗/ centerPosition [0] = (max_X + min_X ) / 2 ; centerPosition [1] = (max_Y + min_Y ) / 2 ; 62 Time every Simulation n corps 112 113 114 115 116 117 118 119 120 /∗ ∗ if Setting Thibault Wildi - 3M8 percentage screenPrecision = of poins ∗ c e i l (( steps ( screenPrecision == to 125 126 127 128 0) screenPrecision =1; /∗ ∗ Handling ∗ ∗/ events SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, // Enabling key board while ( true ) { SDL_FillRect ( s c r e e n , // if // Setting screen NULL, to SDL_MapRGB( s c r e e n −>f o r m a t , 137 138 139 140 0, 0, ( v i e w i n g M o d e == STATIC ) If in if { static mode ( printAxis // Printing : == true ) axis { p r i n t A x i s O n S c r e e n ( s c r e e n , axisX , axisY , scaleBarX , scaleBarY , font , meterToPixelRatio , c e n t e r P o s i t i o n ) ; } printDataOnScreen_Static ( dataFile , meterToPixelRatio , Prints trajectories bodies on screenPrecision , font ) ; // screen SDL_Flip ( s c r e e n ) ; Update screen SDL_WaitEvent(& e v e n t ) ; else if } // If if { ( v i e w i n g M o d e == DYNAMIC) in dynamic ( pause == mode : false ) { printDataOnScreen_Dynamic ( d a t a F i l e , m e t e r T o P i x e l R a t i o , c e n t e r P o s i t i o n , s c r e e n , c a n v a s , bodyImg , s c r e e n P r e c i s i o n , f o n t , 141 if printTrajectories ) ; // 142 143 144 145 // Print ( printAxis Printing == body position on screen true ) axis { p r i n t A x i s O n S c r e e n ( s c r e e n , axisX , axisY , scaleBarX , scaleBarY , font , meterToPixelRatio , c e n t e r P o s i t i o n ) ; } SDL_Flip ( s c r e e n ) ; // 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 0) ) ; black // 134 135 136 event repetition c e n t e r P o s i t i o n , s c r e e n , bodyImg , 133 ∗ ∗/ / MAX_PT_ON_SCREEN) ; } 129 130 131 132 printed { SDL_DEFAULT_REPEAT_INTERVAL) ; 121 122 123 124 be BODY_NB) Update screen } SDL_PollEvent (& e v e n t ) ; SDL_Delay ( 3 0 ) ; } switch // ( event . type ) Checking case events { SDL_QUIT : { exit (0) ; break case switch } ; SDL_KEYDOWN: // Handling Keyboard events ( e v e n t . k e y . keysym . sym ) { 63 Simulation n corps case 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 Thibault Wildi - 3M8 / ∗∗< SDLK_KP_PLUS : Case of numpad + { meterToPixelRatio = meterToPixelRatio SDL_FillRect ( canvas , 0, NULL, ∗ , ∗/ Zoom 1.2; SDL_MapRGB( c a n v a s −>f o r m a t , 0, 0) ) ; break case } ; / ∗∗< SDLK_KP_MINUS : Case of numpad { meterToPixelRatio = meterToPixelRatio SDL_FillRect ( canvas , 0, NULL, − ∗ un−Zoom ∗ / 0.8; SDL_MapRGB( c a n v a s −>f o r m a t , 0, 0) ) ; break case } ; / ∗∗< SDLK_UP : Case up arrow , shift upwards ∗/ { centerPosition [1] += SDL_FillRect ( canvas , 0, (SCREEN_Y / NULL, meterToPixelRatio ) /10; SDL_MapRGB( c a n v a s −>f o r m a t , 0, 0) ) ; break case } ; / ∗∗< SDLK_DOWN: ∗/ { centerPosition [1] −= SDL_FillRect ( canvas , 0, Case down (SCREEN_Y / NULL, arrow , s h i f t downwards meterToPixelRatio ) /10; SDL_MapRGB( c a n v a s −>f o r m a t , 0, 0) ) ; break case } leftwards ∗/ ; / ∗∗< SDLK_LEFT : { centerPosition [0] −= SDL_FillRect ( canvas , 0, Case left (SCREEN_Y / NULL, arrow , shift meterToPixelRatio ) /10; SDL_MapRGB( c a n v a s −>f o r m a t , 0, 0) ) ; break case } ; / ∗∗< SDLK_RIGHT : rightwards ∗/ Case right arrow , shift { centerPosition [0] += SDL_FillRect ( canvas , 0, (SCREEN_Y / NULL, meterToPixelRatio ) /10; SDL_MapRGB( c a n v a s −>f o r m a t , 0, 0) ) ; break case } printed ∗/ { ; / ∗∗< SDLK_PERIOD : if ( screenPrecision > Case . , raise Nb Pts 1) { screenPrecision of = floor ( screenPrecision ∗0.9) ; } break case } printed ∗/ ; / ∗∗< SDLK_COMMA: Case , diminish { screenPrecision break case = ceil ( screenPrecision ∗ Nb of Pts 1.1) ; } ; SDLK_s : / ∗∗< Case s , viewing mode static Case d , viewing mode dynamic { viewingMode break case = STATIC ; } ∗/ ; SDLK_d : / ∗∗< { 64 ∗/ Simulation n corps 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 Thibault Wildi - 3M8 viewingMode = DYNAMIC ; rewind ( d a t a F i l e ) ; break case } ; / ∗∗< SDLK_e : Case e , erase ∗/ trajectories { SDL_FillRect ( canvas , 0, NULL, SDL_MapRGB( c a n v a s −>f o r m a t , 0) ) ; break case } ; / ∗∗< SDLK_p : dynamic ∗/ viewing if { ( pause == Case p , toggle pause for false ) { pause = true ; pause = false ; else } { while } prevents key ( event . type != SDL_KEYUP) // This loop repetitions { SDL_WaitEvent(& e v e n t ) ; } break case if } ; / ∗∗< SDLK_a : Case a , toggle axis ∗/ { ( printAxis == false ) { printAxis = true ; printAxis = false ; else } { while } prevents key ( event . type != SDL_KEYUP) // This loop repetitions { SDL_WaitEvent(& e v e n t ) ; } break case if } ; SDLK_t : / ∗∗< Case == false ) t , toggle trajectory // loop ∗/ { ( printTrajectories { printTrajectories = true ; printTrajectories = false ; else } { while } prevents key ( event . type != SDL_KEYUP) This repetitions { SDL_WaitEvent(& e v e n t ) ; } break case } Instant ; SDLK_i : Screen Shot ) ∗/ / ∗∗< Case i , save { SDL_SaveBMP ( s c r e e n ,OUTPUTIMAGE_BMP) ; 65 actual view ( 0, Simulation n corps 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 break case } 310 311 312 313 314 315 316 317 ; / ∗∗< SDLK_r : Case rewind ( d a t a F i l e ) ; SDL_FillRect ( canvas , 0, restart ∗/ dynamic SDL_MapRGB( c a n v a s −>f o r m a t , NULL, break case } ; / ∗∗< SDLK_ESCAPE : Case ESC , quit ∗/ { exit (0) ; break default break } ; : ; } } } f c l o s e ( dataFile ) ; TTF_CloseFont ( f o n t ) ; TTF_Quit ( ) ; SDL_Quit ( ) ; } void double const ∗ int static static static double static double p r i n t D a t a O n S c r e e n _ S t a t i c ( FILE bodyIMG , { focusCenter , screenPrecision , SDL_Rect // Will hold // Will hold Will Will static static static static static static // // // Will double int char Will Will hold hold in pixels txtPosError ; position on number position maximum scale and hold Text in hold total our still on screen ( total points or int text ∗ txtPoints number of ∗ txtSurfError , points colorWhite = ; texts {255 , 255 , 255}; o; up some stuff up = screen ∗ ∗/ focusCenter [ 0 ] boundaries = focusCenter [ 1 ] screenBoundaryMin [ 0 ] = focusCenter [ 0 ] screenBoundaryMin [ 1 ] = focusCenter [ 1 ] 322 323 324 325 rewind ( dataFile_BIN ) ; loadedPtCount // showing color Setting Setting file point and count + SCREEN_Y/ ( 2 ∗ s c a l e ) ; − − SCREEN_X/ ( 2 ∗ s c a l e ) ; SCREEN_Y/ ( 2 ∗ s c a l e ) ; prints to 0 points ∗ ∗/ f e o f ( dataFile_BIN ) ) until the end { fseek ( + SCREEN_X/ ( 2 ∗ s c a l e ) ; 0; file 0 == Read = loaded Reading ( screen center ) on loaded screen SDL_Color Setting while on meters coordinates focus points screenBoundaryMax [ 0 ] /∗ ∗ points loadedPercentage ; loaded SDL_Surface // // of text [ 3 0 ] ; Will // ∗∗ loadedPtCount ; // /∗ ∗ scale , SDL_Surface screenBoundaryMax [ 2 ] , screenBoundaryMin [ 2 ] ; the on , bodyPosition [ 2 ] ; body hole depending position txtPosPts , printing hold double const bodyPrintPosition ; body SDL_Rect // ∗ dataFile_BIN , ∗ screen TTF_Font ∗ f o n t ) SDL_Surface screenBoundaryMax [ 1 ] 328 , 0) ) ; 318 319 320 321 326 327 r { // 309 Thibault Wildi - 3M8 dataFile_BIN , screenPrecision − 1) +2) , to check for sizeof double ( SEEK_CUR) ; 66 ) largest coordinates ∗ ( (BODY_NB∗ 2+2) ∗ ( // Jump to next position 0, Simulation n corps for 329 330 331 332 333 (o = Thibault Wildi - 3M8 0; o < BODY_NB; o++ // < [1] && is 334 335 if [0] in , bodyPosition [ 1 ] the < body ( ( ( screenBoundaryMin [ 0 ] screenBoundaryMax [ 0 ] ) every sizeof double { f r e a d (& b o d y P o s i t i o n ) for && < ) , 2, dataFile_BIN ) ; bodyPosition [ 0 ] screenBoundaryMax [ 1 ] ) ) // bodyPrintPosition . x = printing ∗ scale ) bodyPrintPosition . y = ∗ round ( ( scale ) // Setting 338 339 340 341 342 343 344 345 346 0, screen , 367 368 369 370 371 372 373 // & Printing body screen l o a d e d P t C o u n t ++; } ∗= loadedPtCount BODY_NB; loadedPercentage /∗ ∗ Prints = 100 / ( double ) screenPrecision ; ∗ ∗/ text s p r i n t f ( t e x t , " Loaded PTs : %d / % . 2 f%%" , loadedPtCount , l o a d e d P e r c e n t a g e ) ; txtPoints = TTF_RenderText_Blended ( f o n t , t e x t , c o l o r W h i t e ) ; s p r i n t f ( text , " Error : %.6 f%%" , (THEORETICAL_ENERGY THEORETICAL_ENERGY 100) ; txtSurfError ∗ − SYSTEM_ENERGY) / = TTF_RenderText_Blended ( f o n t , t e x t , c o l o r W h i t e ) ; txtPosPts . y = 10; txtPosPts . x = 10; txtPosError . y = 10; − t x t P o s E r r o r . x = SCREEN_X t x t S u r f E r r o r −>w − 10; S D L _ B l i t S u r f a c e ( t x t P o i n t s , 0 , s c r e e n ,& t x t P o s P t s ) ; S D L _ B l i t S u r f a c e ( t x t S u r f E r r o r , 0 , s c r e e n ,& t x t P o s E r r o r ) ; } void double const ∗ focusCenter S D L _ S u r f a c e ∗∗ bodyIMG , int printTrajectories ) { ∗ dataFile_BIN , ∗ screen p r i n t D a t a O n S c r e e n _ D y n a m i c ( FILE static static static double static double SDL_Rect // Will hold // Will hold Will hole static double static char static static static int depending // Will on hold , SDL_Surface screenPrecision , , double const scale , ∗ canvas SDL_Surface ∗ font TTF_Font , bool bodyPrintPosition ; body SDL_Rect // 366 − } , 365 − the } 347 348 364 up round (( −( b o d y P o s i t i o n [ 1 ] S D L _ B l i t S u r f a c e ( bodyIMG [ o % 1 0 ] , on point + SCREEN_Y/ 2 ; bodyPrintPosition ) ; 362 363 the ( bodyPosition [ 0 ] + SCREEN_X/ 2 ; position focusCenter [ 1 ] ) ) 337 361 bodyPosition If { 336 359 360 bodyPosition < screen focusCenter [ 0 ] ) ) 349 350 351 352 353 354 355 356 357 358 && ( screenBoundaryMin [ 1 ] position in txtPosTime , printing pixels txtPosEnergy , position of text txtPosError ; on screen bodyPosition [ 2 ] ; screenBoundaryMax [ 2 ] , screenBoundaryMin [ 2 ] ; the maximum scale and time , time coordinates focus systemEnergy , and still showing on screen ( center ) system ' s systemError ; total energy ( at time " time ") text [ 3 0 ] ; // Will hold our screen // Will hold // Text text SDL_Color text ∗ txtSurfTime SDL_Surface , ∗ txtSurfEnergy , ∗ txtSurfError surfaces colorWhite = {255 , 255 , 255}; color o; /∗ ∗ Setting up some screenBoundaryMax [ 0 ] // Setting up stuff = screen ∗ ∗/ focusCenter [ 0 ] boundaries screenBoundaryMax [ 1 ] = focusCenter [ 1 ] screenBoundaryMin [ 0 ] = focusCenter [ 0 ] 67 + SCREEN_X/ ( 2 ∗ s c a l e ) ; + SCREEN_Y/ ( 2 ∗ s c a l e ) ; − SCREEN_X/ ( 2 ∗ s c a l e ) ; ; Simulation n corps 374 375 376 377 378 379 Thibault Wildi - 3M8 screenBoundaryMin [ 1 ] /∗ ∗ if In case of = focusCenter [ 1 ] trajectory ( printTrajectories == SDL_FillRect ( canvas , 380 381 382 383 Setting screen ∗ ∗/ mode SDL_MapRGB( c a n v a s −>f o r m a t , NULL, to 0, 0, 0) ) ; black } /∗ ∗ Reading fseek ( − 1) , 384 385 386 387 and prints // ) Jump &t i m e , ( ) , fread ( &s y s t e m E n e r g y , ( o = 0; o < BODY_NB; o++ // every for f r e a d (& b o d y P o s i t i o n if && , bodyPosition [ 1 ] < next 1, dataFile_BIN ) ; ) , position 1, && dataFile_BIN ) ; ) body ( ( ( screenBoundaryMin [ 0 ] screenBoundaryMax [ 0 ] ) (BODY_NB∗ 2+2) ∗ ( s c r e e n P r e c i s i o n to sizeof double { 392 393 points ( SEEK_CUR) ; ( ∗ ∗/ sizeof double ∗ sizeof double sizeof double file dataFile_BIN , fread ( for 388 389 390 391 < ) , 2, dataFile_BIN ) ; bodyPosition [ 0 ] && ( screenBoundaryMin [ 1 ] < bodyPosition [ 0 ] screenBoundaryMax [ 1 ] ) ) scale ) round ( ( + SCREEN_X/ 2 ; // scale ) up the − focusCenter printing − position focusCenter + SCREEN_Y/ 2 ; S D L _ B l i t S u r f a c e ( bodyIMG [ o % 1 0 ] , ) ; 396 397 398 399 400 401 402 403 404 ( bodyPosition [ 0 ] Setting round (( −( b o d y P o s i t i o n [ 1 ] bodyPrintPosition . y = ∗ [1]) ) 395 bodyPrintPosition . x = ∗ [0]) ) < bodyPosition [ 1 ] { 394 0, // canvas , &b o d y P r i n t P o s i t i o n Printing body on screen } } /∗ ∗ if (0 If reaching != end of ∗ ∗/ file f e o f ( dataFile_BIN ) ) { rewind ( dataFile_BIN ) ; SDL_Delay ( 1 0 0 0 ) ; SDL_FillRect ( canvas , // 405 406 407 408 Setting SDL_MapRGB( c a n v a s −>f o r m a t , NULL, screen to 0, 0, 0) ) ; black } /∗ ∗ Print canvas on // /∗ ∗ Printing Prints text systemError = body on screen , ∗ 0) ; screen ∗ ∗/ s p r i n t f ( t e x t , " Time : − systemEnergy ) / 100; %.2 f [ s ] " , time ) ; = TTF_RenderText_Blended ( f o n t , t e x t , c o l o r W h i t e ) ; s p r i n t f ( t e x t , " Energy : txtSurfEnergy %.10 e [ J ] " , systemEnergy ) ; = TTF_RenderText_Blended ( f o n t , t e x t , c o l o r W h i t e ) ; s p r i n t f ( text , " Error : txtSurfError 0, (THEORETICAL_ENERGY THEORETICAL_ENERGY txtSurfTime ∗ ∗/ screen SDL_BlitSurface ( canvas , 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 SCREEN_Y/ ( 2 ∗ s c a l e ) ; false ) { // − %.6 f%%" , s y s t e m E r r o r ) ; = TTF_RenderText_Blended ( f o n t , t e x t , c o l o r W h i t e ) ; txtPosTime . y = 10; txtPosTime . x = 10; txtPosEnergy . y = 10; t x t P o s E n e r g y . x = SCREEN_X − t x t S u r f E n e r g y −>w txtPosError . y = txtPosEnergy . y + txtPosError . x = txtPosEnergy . x ; − 10; t x t S u r f E n e r g y −>h + 10; S D L _ B l i t S u r f a c e ( t x t S u r f T i m e , 0 , s c r e e n ,& t x t P o s T i m e ) ; S D L _ B l i t S u r f a c e ( t x t S u r f E n e r g y , 0 , s c r e e n ,& t x t P o s E n e r g y ) ; S D L _ B l i t S u r f a c e ( t x t S u r f E r r o r , 0 , s c r e e n ,& t x t P o s E r r o r ) ; } void p r i n t A x i s O n S c r e e n ( SDL_Surface SDL_Surface ∗ axisY , ∗ s c r e e n , SDL_Surface ∗ a x i s X , ∗ s c a l e B a r X , SDL_Surface ∗ s c a l e B a r Y SDL_Surface 68 Simulation n corps 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 , { Thibault Wildi - 3M8 ∗ font TTF_Font static static static static static double static static char static int SDL_Rect Will hold axis Will hold Text position double const , axisXPrintPosition , positions SDL_Rect SDL_Rect bar for color Will hold // positions scaleBarYPosition ; in // pixels ∗ txtScale // ; // text ScaleBarSpacing ; between SDL_Color Text axisYPrintPosition ; txtScalePos ; SDL_Surface Separation ∗ focusCenter ) pixels scaleBarXPosition , scales SDL_Surface in double const scale , scales // bars colorWhite = {255 , 255 , 255}; // text [ 3 0 ] ; our // screen text i ; /∗ ∗ Calculating ScaleBarSpacing scale s p r i n t f ( text , " Scale : ScaleBarSpacing /∗ ∗ Setting txtScale up bars ∗ ∗/ separation = pow ( 1 0 , r o u n d ( l o g 1 0 ( (SCREEN_X / = scale ) ) ) ; round ( S c a l e B a r S p a c i n g ∗ s c a l e ) ; axis ∗ ∗/ positions = TTF_RenderText_Blended ( f o n t , t e x t , c o l o r W h i t e ) ; txtScalePos . x = 10; t x t S c a l e P o s . y = SCREEN_Y − t x t S c a l e −>h − 10; axisXPrintPosition . x = 0; axisXPrintPosition . y = round ( f o c u s C e n t e r [ 1 ] axisYPrintPosition . y = 0; axisYPrintPosition . x = r o u n d (− axisXPrintPosition . y scaleBarYPosition . x = axisYPrintPosition . x ( axisXPrintPosition . y < 0) scaleBarXPosition . y = 0; ∗ scale ) ∗ focusCenter [ 0 ] scaleBarXPosition . y = if 10) / %.0 e [m] " , S c a l e B a r S p a c i n g ) ; − − + SCREEN_Y/ 2 ; scale ) + SCREEN_X/ 2 ; 20; 20; { else if } ( a x i s X P r i n t P o s i t i o n . y > SCREEN_Y) { − s c a l e B a r X P o s i t i o n . y = SCREEN_Y if 40; } ( axisYPrintPosition . x < 0) scaleBarYPosition . x = 0; { else if } ( a x i s Y P r i n t P o s i t i o n . x > SCREEN_X) { − s c a l e B a r Y P o s i t i o n . x = SCREEN_X 40; } /∗ ∗ Printing Axis and scales SDL_BlitSurface ( axisX , 0, SDL_BlitSurface ( axisY , 0, SDL_BlitSurface ( t x t S c a l e , for ( i =1 ; bars on Screen ∗ ∗/ screen , &a x i s X P r i n t P o s i t i o n ) ; screen , &a x i s Y P r i n t P o s i t i o n ) ; 0, screen , &t x t S c a l e P o s ) ; s c a l e B a r X P o s i t i o n . x < SCREEN_X ; i ++) { scaleBarXPosition . x = axisYPrintPosition . x + SDL_BlitSurface ( scaleBarX , for 0, screen , i ∗ ScaleBarSpacing ; &s c a l e B a r X P o s i t i o n ) ; } ( i =1 ; scaleBarXPosition . x > 0 ; i ++) { scaleBarXPosition . x = axisYPrintPosition . x SDL_BlitSurface ( scaleBarX , 0, screen , − } for ( i =1 ; s c a l e B a r Y P o s i t i o n . y < SCREEN_Y 69 i ∗ ScaleBarSpacing &s c a l e B a r X P o s i t i o n ) ; ; i ++) ; Simulation n corps 490 491 492 493 494 495 496 497 498 499 Thibault Wildi - 3M8 { scaleBarYPosition . y = axisXPrintPosition . y + SDL_BlitSurface ( scaleBarY , for 0, screen , i ∗ ScaleBarSpacing ; &s c a l e B a r Y P o s i t i o n ) ; } ( i =1 ; scaleBarYPosition . y > 0 ; i ++) { scaleBarYPosition . y = axisXPrintPosition . y SDL_BlitSurface ( scaleBarY , 0, screen , } } Code 7.8 display2d.c 70 − i ∗ ScaleBarSpacing &s c a l e B a r Y P o s i t i o n ) ; ;