International Essays |
Combien de temps faut-il pour faire bouillir un œuf ? Modélisation MEF avec Wolfram Language
Combien de temps faut-il pour faire bouillir un œuf ? Modélisation MEF avec Wolfram Language
15 septembre 2025
Ricardo López, stagiaire développeur d’applications physiques, algorithmes R&D ; Gay Wilson, conservateur de données alimentaires, contenu scientifique Wolfram|Alpha ; Oliver Ruebenkoenig, développeur senior de noyaux, algorithmes R&D
Il est temps de répondre à la question que tout amateur de petit-déjeuner se pose : « Combien de temps faut-il pour faire bouillir un œuf ? » Bien que ça paraisse très simple, il suffit de plonger un œuf dans l’eau bouillante et d’attendre. Il serait imprudent de dire qu’un œuf dur est la seule manière de savourer un délicieux apport en protéines. Nous pouvons utiliser la méthode des éléments finis (MEF), pour simuler les conditions d’un œuf dans l’eau et déterminer la température et la durée idéales pour obtenir l’œuf parfait, en évaluant les variations de température à l’intérieur même de l’œuf. Nous pouvons ainsi prédire le temps nécessaire pour atteindre différentes consistances, comme un jaune coulant ou un jaune friable et complètement pris.
La modélisation des systèmes physiques complexes commence souvent par leur décomposition en parties gérables. C’est précisément ce que fait la méthode des éléments finis (MEF) : elle divise une structure compliquée en éléments plus petits et plus simples, résout localement les équations régissantes, puis assemble les résultats afin de saisir le comportement de l’ensemble du système.
Cette approche permet de simuler et d’analyser des scénarios réels sans le coût ni l’effort liés aux prototypes physiques. Ici, nous allons présenter le flux de travail de l’analyse par éléments finis dans Wolfram Language et parcourir un exemple simple qui couvre les principes de base et met en évidence les problèmes courants pouvant survenir au cours du processus.
Notre objectif est de fournir une compréhension générale de la manière de résoudre numériquement les équations aux dérivées partielles (EDP) à l’aide de MEF, ainsi qu’une ressource pratique que vous pouvez utiliser pour construire vos propres modèles.
Pourquoi utiliser les éléments finis ?
Pourquoi utiliser les éléments finis ?
De nombreuses EDP (y compris des équations classiques comme l’équation de Poisson ou les équations de Schrödinger) n’admettent pas toujours de solution analytique, en particulier si la région dans laquelle on cherche à résoudre l’équation est relativement compliquée. La MEF est une technique précieuse pour résoudre numériquement les EDP pour au moins deux raisons : premièrement, il est possible de résoudre des EDP sur des régions de forme arbitraire. Deuxièmement, on peut résoudre de nombreux types d’équations différentielles, allant de l’équation de Laplace aux équations de Navier–Stokes.
Outils nécessaires pour l’analyse par éléments finis
Outils nécessaires pour l’analyse par éléments finis
◼
Une région qui est discrétisée en un maillage. Ce maillage est constitué de nombreuses petites sous-régions simples appelées éléments. L’idée clé de la méthode des éléments finis (MEF) est de résoudre une version simplifiée de l’EDP à l’intérieur de chaque élément, puis de combiner ces solutions locales en une solution globale qui approxime le comportement de l’EDP d’origine sur toute la région.
◼
L’EDP spécifique que nous souhaitons résoudre.
◼
Les conditions aux limites, qui relient l’équation aux dérivées partielles au monde qui est extérieur à la région où la solution est en cours de calcul.
Dans Wolfram Language, la MEF est implémentée à l’aide des fonctions , et . Nous allons utiliser NDSolveValue dans notre scénario d’œuf à la coque.
Version 1 : commencer un flux de travail
Version 1 : commencer un flux de travail
Nous allons construire notre modèle étape par étape, en passant par plusieurs versions et en augmentant progressivement la complexité du modèle. Nous commencerons par un modèle simple, en gardant à l’esprit les tailles des objets et les unités.
La première étape consiste à définir la géométrie ou la région de la structure. Comme un œuf possède une symétrie axiale, nous supposerons que l’analyse d’une coupe de l’œuf est suffisante pour décrire son comportement. Pour la MEF, il est nécessaire de discrétiser la région en un maillage, et comme un maillage bidimensionnel n’a pas besoin d’autant d’éléments pour approcher la géométrie qu’un maillage tridimensionnel, notre calcul prendra moins de temps. C’est une raison importante pour utiliser un modèle axisymétrique.
Un œuf moyen peut mesurer environ 5 cm de diamètre et 2,5 cm de rayon. Comme il est recommandé d’utiliser les unités SI pour les paramètres du modèle, nous allons définir le rayon de l’œuf comme étant de 0,025 m. À ce stade, nous choisissons volontairement d’ignorer toute différence entre la matière du jaune et celle du blanc d’œuf. Notre objectif est de rendre cette première version du modèle aussi simple que possible.
Chargez le paquet « Finite Element » :
In[]:=
Needs["NDSolve`FEM`"]
Nous allons également définir à 0, ce qui limite le nombre de résultats précédents stockés dans la session à zéro, économisant ainsi de la mémoire de calcul à un coût minimal.
Définissez $HistoryLength à 0 :
In[]:=
$HistoryLength=0;
Créez une géométrie simple d’un œuf :
In[]:=
radius=0.025;Ω=RegionUnion[Circle[{0,0},radius,{-π/2,π/2}],Line[{{0,radius},{0,-radius}}]]
Out[]=
De plus, nous utilisons ToElementMesh pour générer un maillage par éléments finis par défaut à partir de la géométrie que nous venons de créer.
Générez le maillage d’éléments :
In[]:=
mesh=ToElementMesh[Ω]
Out[]=
ElementMesh[{{0.,0.025},{-0.025,0.025}},{TriangleElement[<492>]}]
Visualisez le maillage par éléments finis :
In[]:=
mesh["Wireframe"]
Out[]=
Pour modéliser des EDP dans Wolfram Language, nous utilisons ce que l’on appelle des composantes EDP, qui sont des éléments de base nous aidant à spécifier les EDP. Il s’agit de fonctions qui prennent en entrée des variables et des paramètres et qui renvoient un opérateur EDP pouvant ensuite être utilisé avec NDSolveValue. Vous pouvez accéder à la liste de toutes les composantes EDP pour n’importe quel domaine spécifique. Pour étudier l’évolution de la température à l’intérieur de l’œuf, nous allons utiliser afin de modéliser le transfert de chaleur au cours du temps.
La solution que nous cherchons, la variable dépendante, est la température (kelvin). Dans notre configuration à symétrie cylindrique, nous avons deux variables indépendantes spatiales : r est la coordonnée radiale (distance par rapport à l’axe central) et z est la coordonnée axiale (distance le long de l’axe du cylindre). Toutes deux sont exprimées en mètres. Comme la cuisson d’un œuf dépend du temps, nous aurons également besoin de la variable de temps t (secondes).
T
Définissez les variables dépendantes et indépendantes du modèle :
In[]:=
vars={T[t,r,z],t,{r,z}};
Nous avons également besoin des paramètres. Pour notre première version simplifiée, nous allons seulement spécifier la symétrie de la région comme étant axisymétrique, ce qui nous donnera les bons termes pour la tranche d’œuf sur laquelle nous nous concentrons. HeatTransferPDEComponent remplira le reste des paramètres nécessaires avec des valeurs par défaut, et cela convient pour l’instant.
Définissez les paramètres :
In[]:=
pars=<|"RegionSymmetry"->"Axisymmetric"|>;
Ensuite, définissez l’opérateur EDP. Ici, est l’opérateur EDP : c’est simplement le membre de gauche de l’équation de la chaleur. Ainsi, lorsque nous écrivons , nous écrivons en réalité l’équation de la chaleur elle-même.
op
op0
Configurez l’opérateur EDP :
In[]:=
op=HeatTransferPDEComponent[vars,pars]
Out[]=
-,0.T[t,r,z]+·{{-1,0},{0,-1}}.T[t,r,z]+[t,r,z]
1
r
∇
{r,z}
∇
{r,z}
∇
{r,z}
(1,0,0)
T
In[]:=
op==0
Out[]=
-,0.T[t,r,z]+·{{-1,0},{0,-1}}.T[t,r,z]+[t,r,z]0
1
r
∇
{r,z}
∇
{r,z}
∇
{r,z}
(1,0,0)
T
Conditions aux limites
Conditions aux limites
Les conditions initiales et aux limites sont des éléments importants de la modélisation des EDP, car elles contiennent des informations sur la physique sous-jacente du processus en cours. Les conditions aux limites spécifient le comportement de la solution sur les limites. Dans ce cas, une condition aux limites importante concerne l’extérieur de l’œuf qui est soumis à la température de l’eau bouillante environnante. La condition initiale correspond à la température de départ de l’œuf, qu’il ait été réfrigéré ou conservé à température ambiante.
En supposant que l’œuf soit à température ambiante, nous utiliserons 20 °C comme condition initiale à l’instant 0, puis nous le convertirons dans l’unité de base du SI, le kelvin.
Définissez la température initiale en kelvins :
Définissez une condition initiale pour la température à l’intérieur de l’œuf :
Il est important de rappeler que les conditions aux limites et les conditions initiales doivent être cohérentes. On pourrait être tenté de simplement définir une température de 100 °C à l'extérieur de la région, représentant le contact avec l'eau bouillante. Bien que ça ne soit pas entièrement faux, ça pose un problème. D'après notre condition initiale, tout le domaine a une température initiale de 20 °C, même à la limite externe. Ainsi, définir la limite extérieure à 100 °C contredit cela.
Pour éviter ça dans notre scénario, nous utilisons une fonction de transition douce qui commence à la température initiale et atteint rapidement la température d’ébullition de 100 °C. Ceci modélise effectivement l’action de plonger l’œuf dans l’eau bouillante, ce qui constitue un processus en soi.
Plus précisément, nous définissons la condition aux limites à l'aide d'une fonction par morceaux qui vaut 1 pour les temps supérieurs à un dixième (1/10) de seconde. Pour des temps plus courts, elle augmente progressivement, selon une fonction cosinus. Tracer la fonction rend ce comportement plus clair. Cela modélise la température à la limite qui augmente rapidement, mais de manière progressive, au cours du premier dixième de seconde après que l'œuf a été immergé dans l'eau bouillante.
Spécifiez la température de la limite en kelvins :
Spécifiez le comportement temporel de la condition aux limites :
Visualisez l’évolution temporelle de la condition aux limites :
Mais nous allons faire quelque chose de différent qui sera utile dans les versions ultérieures de ce modèle.
Visualisez le marqueur d’élément ponctuel :
Notre visualisation du maillage affiche les éléments ponctuels et les marqueurs d’éléments (voir le tutoriel sur la visualisation du maillage des éléments pour en savoir plus). Nous allons en profiter pour définir la condition aux limites.
Un avantage de l’utilisation de la fonction HeatTemperatureCondition est qu’elle prendra en compte les paramètres que nous avons définis.
Spécifiez la condition aux limites en kelvins :
Spécifiez la condition de symétrie :
Remarquez que ceci évalue à une valeur nulle de Neumann. Si vous avez besoin d’un rappel sur les valeurs de Neumann, consultez ces sources dans la documentation :
Une valeur nulle de Neumann, en termes simples, indique que la dérivée de la température, prise selon la normale à l’axe de symétrie, est nulle. De plus, une valeur nulle de Neumann est la valeur par défaut si aucune n’est spécifiée. Par conséquent, nous allons l’omettre dans les prochaines versions de ce modèle. Mais il est utile de garder cela à l’esprit.
Enfin, nous définissons la durée de la simulation. Comme il s’agit d’une première version simple et que nous avons utilisé les paramètres par défaut pour notre PDE, nous choisissons une durée d’une seconde, que nous modifierons plus tard.
Définissez la durée de la simulation :
Pour obtenir notre solution, nous utilisons NDSolveValue.
Calculez la solution et stockez-la dans une variable :
Examinez la solution :
Nous obtenons une fonction d’interpolation qui représente notre solution que nous pouvons ensuite tracer pour visualiser la solution.
Appeler “ValuesOnGrid” donne les valeurs de la fonction à chaque coordonnée du maillage. Ensuite, nous pouvons extraire les valeurs minimale et maximale de la solution.
Pour faciliter l’interprétation, transformons les températures en degrés Celsius, mais les paramètres utilisés dans le modèle devront toujours être définis en kelvins. Définissons donc un décalage pour convertir plus facilement entre degrés Celsius et kelvin.
Définissez un décalage :
Le tracé semble montrer que toute la région atteint une température de 100 °C en seulement la moitié du temps de simulation (0,5 seconde). Pour le vérifier, créons une animation de l’évolution de la température au cours du temps.
Pour gagner du temps plus tard, nous allons créer dès maintenant une fonction auxiliaire pour les tracés.
Construisez une fonction auxiliaire pour créer des tracés de contours de la température à différents moments :
Nous avons créé une fonction de la variable time, qui nous donne le tracé pour un instant précis. Nous pouvons ensuite appliquer cette fonction à une liste de moments.
Définissez une version de la fonction d’assistance avec DensityPlot :
Créez un certain nombre d’images de tracés de contours :
Pixelisez les images pour alléger le notebook (cette étape est facultative) :
Animez les images :
L’animation montre que toute la région commence à la température initiale et chauffe presque instantanément jusqu’à 100 °C (la température de l’eau), ce qui n’a pas beaucoup de sens. Mais nous ne nous attendions pas à ce que ce soit notre solution finale. Pour l’instant, nous savons qu’il n’y a aucune erreur ni aucun avertissement dans notre simulation. Cette étape visait à lancer notre flux de travail, et c’est bien cela qui compte à ce stade du modèle.
Version 2 : un exemple de ce qui peut mal tourner
Version 2 : un exemple de ce qui peut mal tourner
Maintenant que notre flux de travail est établi, nous commençons cette nouvelle version en définissant des paramètres plus réalistes à la place de ceux que HeatTransferPDEComponent (la fonction utilisée pour générer l’équation aux dérivées partielles) fournit par défaut.
L’équation de la chaleur comporte trois grandeurs physiques qui dépendent du type de matière modélisé : la masse volumique, la capacité calorifique et la conductivité thermique :
Nous utiliserons les estimations des valeurs moyennes pour le blanc d’œuf et le jaune d’œuf afin de définir les paramètres de la matière :
Examinez les paramètres :
Définissez une durée d’arrêt réaliste pour la simulation à 10 minutes :
Régénérez les équations avec les nouveaux paramètres :
Comme nous l’avons fait précédemment, résolvez l’équation aux dérivées partielles :
Jusqu’ici, tout va bien !
Vérifions les valeurs minimale et maximale de la solution en degrés Celsius :
C’est clairement incorrect. Une température négative ici n’a aucun sens. Traçons la solution à l’instant précis où cette température négative est atteinte.
Trouvez la position de la valeur minimale dans les données de la solution :
En appelant “Coordinates” sur la solution et en prenant la première partie, on obtient les instants de temps utilisés pendant la résolution :
Trouvez l’étape temporelle à laquelle la valeur minimale est enregistrée :
Dans notre variable badPosition, qui est {{18, 386}}, le premier nombre de la paire représente l’indice du moment où notre valeur négative apparaît. Nous pouvons extraire le temps problématique en prenant la dix-huitième position dans les intervalles de temps :
Nous pouvons maintenant visualiser la solution à cet instant précis.
Visualisez la solution à l’instant où les valeurs sont extrêmes :
Ici, il est clair qu’au début de la simulation, il y a beaucoup d’oscillations près de la limite, et c’est la raison pour laquelle nous observons des températures extrêmes proches de –2 °C. Le tracé 3D affiche des dépassements et des sous-dépassements.
Zoomez sur le tracé de la solution et visualisez le maillage d’éléments finis sous-jacent :
Je vous encourage à consulter la documentation d’ElementMeshProjection pour en savoir plus sur la façon de visualiser un maillage 2D en 3D.
Il est maintenant clair que les dépassements négatifs se produisent à quelques éléments seulement de la limite. Mais qu’est-ce qui pourrait provoquer ces dépassements positifs et négatifs ?
Obtenez la longueur minimale d’arête du maillage en\b mètres :
La distance entre les éléments est d’environ 1 mm, ce qui signifie que les premiers éléments proches de la limite ont une longueur de côté d’environ 1 mm. D’autre part, étant donné que notre température initiale est de 20 °C et que la température à la limite est de 100 °C, la différence de température entre la limite et le reste de la région est d’environ 80 °C.
Essayer de résoudre ce changement brusque d’environ 80 °C à la limite avec des éléments de cette taille n’est pas suffisant. C’est probablement la raison pour laquelle nous obtenons des températures bien inférieures à la température initiale, alors qu’il n’y a aucune source de dissipation thermique. Par conséquent, le maillage doit être affiné.
Si nous affinons tout le maillage, ça résoudra probablement notre problème, mais cet affinage n’est pas nécessaire au centre de l’œuf. Affiner tout le maillage maintenant ralentira la simulation, donc il est préférable d’affiner uniquement le maillage près de la limite (la coquille de l’œuf).
Créez une fonction de distance signée pour l’ensemble de la région de l’œuf :
Nous pouvons visualiser la fonction de distance signée et observer comment elle décroît linéairement lorsqu’elle atteint la limite. Ce résultat est logique, car la distance par rapport à la coquille, sur la coquille, est, comme on pouvait s’y attendre, nulle.
Visualisez la fonction de raffinement sur la géométrie maillée :
Créez la fonction de raffinement du maillage :
Ensuite, nous définissons l’option MeshRefinementFunction dans ToElementMesh sur notre refinementFunction.
Redéfinissez le maillage :
Résolvez l’EDP et surveillez la progression :
Examinez l’intervalle de température de la nouvelle solution trouvée :
Super ! Maintenant, nous obtenons une plage de températures à laquelle nous nous attendrions. Il y aura bien sûr une petite erreur numérique intrinsèque à la méthode, c’est-à-dire que la plage ne sera pas exactement {20, 100}. Mais c’est encourageant.
Nous pouvons continuer à créer notre animation comme d’habitude, en appelant la fonction d’aide que nous avons définie avec notre nouvelle solution, en tramant les images et en les animant ensuite.
Créez les cadres pour la solution :
Tramez les images :
Animez les images :
On peut voir que le problème de dépassement et de sous-dépassement est résolu. Ça démontre à quel point il est important de vérifier que nos solutions ont du sens et comment nous pouvons modifier le maillage si nécessaire.
À ce stade, nous comprenons bien comment la température se répartit lors de l’ébullition de l’œuf. Calculons la température au centre de l’œuf après six minutes de cuisson.
Le résultat est de 41,6°C. Cette valeur ne représente probablement pas la température réelle du jaune d’œuf dans un véritable œuf, car nous faisons actuellement trop d’hypothèses. Cependant, c’est une bonne référence pour l’instant.
Dans cette version, nous avons utilisé un modèle de base qui ne tenait pas compte de l’existence de deux régions distinctes : le jaune d’œuf et le blanc d’œuf. Nous aborderons cela dans la prochaine version.
Version 3 : représentation de plusieurs régions de matière
Version 3 : représentation de plusieurs régions de matière
Créez une géométrie à plusieurs matières :
Ici, nous définissons des marqueurs de région de matière afin de différencier plus tard les paramètres des deux régions. Ça rendra notre code plus clair, comme nous allons le voir.
Spécifiez les marqueurs de région de matière :
Nous utilisons l’option “RegionMarker” pour distinguer les deux régions en attribuant un marqueur différent à chaque sous-région. L’option est donnée sous forme d’une liste de listes, où chaque liste interne contient un point à l’intérieur de la sous-région et son marqueur de région associé.
Créez un maillage avec des marqueurs de matière :
Nous pouvons en faire une visualisation avec différentes couleurs, en spécifiant “MeshElementStyle” dans les options de “Wireframe”.
Visualisez le maillage à plusieurs matières :
Visualisez les marqueurs d’élément ponctuel :
Puisque nous avons modifié la région, les marqueurs d’éléments pour la limite ont également changé. Par conséquent, nous pouvons redéfinir la condition aux limites pour les marqueurs 2 et 5.
Gardez à l’esprit que ce sont des marqueurs d’éléments pour la limite. Ils sont différents des marqueurs de région que nous avons définis précédemment. Ne confondez pas les deux.
Régénérez la condition aux limites :
Après avoir visualisé les marqueurs d’éléments ponctuels, nous redéfinissons le maillage en spécifiant la fonction d’affinage du maillage comme précédemment.
Créez un maillage avec des marqueurs de matière et un raffinement du maillage :
Visualisez le maillage raffiné à plusieurs matières :
Excellent résultat ! Mais avant de résoudre le problème, nous allons redéfinir les paramètres pour chaque matière, en utilisant des estimations des valeurs moyennes pour la masse volumique, la conductivité thermique et la capacité thermique des jaunes d’œufs et des blancs d’œufs, en nous basant sur les résultats de « densité, capacité thermique et conductivité thermique des ovoproduits liquides ».
Nous pouvons alors régénérer et résoudre l’EDP comme nous l’avons fait précédemment.
Régénérez l’équation aux dérivées partielles :
Résolvez l’EDP :
Examinez la plage de températures de la nouvelle solution :
La plage de températures obtenue est raisonnable. Visualisons la solution.
Créez les cadres pour la solution :
Tramez les images :
Animez les images :
Ici, nous observons un chauffage homogène de l’œuf, similaire à notre dernière version avec une seule région. Cependant, cette version est plus précise car nous disposons d’un meilleur modèle de la structure réelle de l’œuf.
Dans cette solution, le résultat de température de 38 °C semble encore trop bas pour le temps donné. En fait, cette température est même inférieure au résultat précédent d’environ 42 °C.
Nous avons besoin d’une méthode pour savoir quelles parties de l’œuf sont cuites après un certain temps.
Version 4 : affiner davantage le modèle
Version 4 : affiner davantage le modèle
Dans cette version, nous allons affiner les données utilisées pour les grandeurs physiques impliquées dans le modèle. Nous prédirons également si l’œuf est cuit en comparant les résultats avec les températures de dénaturation du jaune et du blanc d’œuf (c’est-à-dire les températures auxquelles les protéines de l’œuf commencent à se déplier et à se solidifier).
Pour affiner les grandeurs physiques (masse volumique, capacité thermique et conductivité thermique), nous nous appuierons sur les données fournies par ces deux études :
Dans la version précédente, nous avions de bonnes estimations des grandeurs physiques pour le jaune d’œuf et le blanc d’œuf. Mais en réalité, ces grandeurs peuvent changer au cours du temps lorsque l’œuf chauffe. Un modèle plus précis est celui qui prend en compte la manière dont ces grandeurs varient en fonction de la température.
En utilisant les données issues des études mentionnées précédemment, nous allons établir des paires de températures et la grandeur physique correspondante (en unités de base SI). Notre objectif est de modéliser chaque grandeur physique (masse volumique, conductivité thermique, capacité thermique) en fonction de la température.
Commençons par la masse volumique. Remarquez qu’il existe une définition des données de masse volumique à la fois pour le jaune d’œuf et le blanc d’œuf.
Commençons par la masse volumique. Remarquez qu’il existe une définition des données de masse volumique à la fois pour le jaune d’œuf et le blanc d’œuf.
Préparez les données de mesure pour la masse volumique du jaune d’œuf et du blanc d’œuf :
L’option “ExtrapolationHandler” est utilisée pour gérer les points qui se trouvent en dehors de l’intervalle de nos données disponibles. Nos données ne sont pas parfaites et ne couvrent pas toute la plage de températures. Comme des propriétés telles que la densité varient presque linéairement, extrapoler à partir des données existantes fournit une approximation raisonnablement précise.
Visualisez les données mesurées et les fonctions interpolées :
Nous pouvons observer la façon dont la densité diminue lorsque la température augmente. Bien que nous ne disposions pas de données de température au-delà de 335 kelvins, nous avons néanmoins une extrapolation raisonnable à partir des données.
Maintenant, nous répétons le même processus pour la conductivité.
Préparez les données de mesure pour la conductivité thermique du jaune d’œuf et du blanc d’œuf :
Créez une InterpolatingFunction pour les données de conductivité thermique :
Visualisez les données mesurées et les fonctions interpolées :
On peut voir comment la conductivité augmente légèrement avec la température et, encore une fois, nous avons une extrapolation raisonnable des données.
Pour la capacité calorifique spécifique, nous adoptons une approche légèrement différente. Nous disposons de moins de points de données, et la qualité de ces points n’est pas aussi bonne que pour les deux autres grandeurs. L’approche optimale consiste à effectuer un ajustement linéaire des données, ce qui nous donnera une fonction linéaire s’ajustant au mieux aux données.
Préparez les données de mesure pour la capacité calorifique du jaune d’œuf et du blanc d’œuf :
Pour explorer plus en détail le fonctionnement de la fonction, consultez la documentation de LinearModelFit.
Visualisez les données mesurées et la fonction linéaire qui leur correspond :
Le tracé présente une approximation raisonnable des données ainsi qu’une bonne extrapolation aux températures plus élevées.
Maintenant que nous avons les fonctions pour les grandeurs physiques, il ne nous reste plus qu’à les spécifier en tant que fonctions par morceaux, comme nous l’avons fait précédemment :
Régénérez l’équation aux dérivées partielles :
Une considération importante : la densité de masse, la conductivité thermique et la capacité calorifique sont maintenant des fonctions de la température T. En même temps, T est la variable dépendante pour laquelle on cherche une solution. Cette interdépendance implique que les coefficients dans l’équation varient avec la solution elle-même, rendant l’EDP non linéaire. Les modèles non linéaires nécessitent généralement plus de temps et d’effort de calcul pour être résolus. C’est pourquoi il est souvent judicieux de commencer avec un maillage peu raffiné lors de la mise en place du modèle. Une fois que vous obtenez une solution raisonnable, vous pouvez passer à un maillage raffiné pour une meilleure précision. Cependant, dans ce cas, nous allons directement utiliser le maillage raffiné.
Nous résolvons maintenant l’équation aux dérivées partielles, surveillons la progression et mesurons le temps de calcul ainsi que la mémoire utilisée (environ trois minutes sur un ordinateur portable standard).
Résolvez l’EDP :
Cette augmentation du temps de calcul est attendue pour la plupart des modèles non linéaires.
Examinez la plage de températures de la nouvelle solution trouvée :
Avant de visualiser notre solution, nous allons définir les températures de dénaturation du jaune d’œuf et du blanc d’œuf. Ça nous donnera une bonne idée pour savoir si les deux régions de l’œuf sont cuites à un moment donné. Ces températures proviennent de l’étude publiée dans la revue Journal of Food Measurement and Characterization citée précédemment.
Définissez la température de dénaturation du blanc d’œuf :
Définissez la température de dénaturation du jaune d’œuf :
Nous pouvons visualiser les températures de dénaturation en même temps que l’animation de notre solution. La meilleure façon de le faire est d’utiliser un tracé de contours avec une ligne de contour qui indique le point de la région où la température de dénaturation est atteinte.
Je préfère le tracé de densité, donc ici nous définissons une nouvelle fonction appelée TemperatureDenatureDensityPlot qui appelle la fonction d’assistance TemperatureDensityPlot définie précédemment. Nous utilisons Show pour afficher le tracé comme précédemment, mais avec les contours pour les températures de dénaturation du jaune d’œuf et du blanc d’œuf. Les contours sont tracés avec un ContourPlot supplémentaire.
Créez une fonction auxiliaire pour générer un tracé de densité de la répartition de la température et visualiser les températures de dénaturation :
Créez les cadres pour la solution :
Tramez les images :
Animez les images :
La température de dénaturation du blanc d’œuf est indiquée par la ligne verte en pointillés, et la température de dénaturation du jaune d’œuf est indiquée par la ligne magenta en pointillés. Le tracé montre la diffusion de la chaleur à l’intérieur de l’œuf, comme on pourrait s’y attendre.
À la marque des six minutes, nous pouvons constater que tout le blanc d’œuf a dépassé sa température de dénaturation. C’est un résultat prometteur pour notre modèle.
Cependant, si l’on observe au repère de 10 minutes, toute la région dépasse la température de dénaturation du blanc d’œuf, tandis qu’une partie du jaune d’œuf n’a pas dépassé la température de dénaturation du jaune, ce qui indique que le jaune n’est pas suffisamment cuit, même après 10 minutes.
Les œufs durs cuisent généralement pendant 10 à 12 minutes, donc 10 minutes devraient donner un jaune d’œuf complètement cuit. Par conséquent, notre modèle n’est pas encore complet, puisqu’il indique qu’après 10 minutes, le jaune d’œuf n’est pas entièrement cuit.
Examinez la température de dénaturation du jaune :
Dans les versions 2 et 3 du modèle, nous avons obtenu des températures de 38 °C et 42 °C, respectivement. Ici, dans la version 4, nous avons environ 40 °C pour la température au centre de l’œuf à la sixième minute. Comparée à la température de dénaturation du jaune, ça semble encore trop bas. Nous devons améliorer ça dans la prochaine version.
Version 5 : construction d’une géométrie réaliste pour un œuf
Version 5 : construction d’une géométrie réaliste pour un œuf
Lorsque le modèle ne nous donne pas la réponse attendue, il peut être judicieux de l’affiner davantage. Une façon de procéder consiste à examiner les hypothèses que nous avons posées. Une hypothèse importante est que l’œuf a une forme circulaire, ce qui, bien sûr, n’est pas le cas en réalité. Dans cette version, nous allons créer une géométrie plus réaliste pour l’œuf.
Nous pouvons toujours supposer que la géométrie du jaune d’œuf est bien modélisée par un cercle. Mais nous pouvons approximer la géométrie de la coquille d’œuf d’une manière plus réaliste.
Pour cela, nous allons utiliser une équation mathématique qui approxime la géométrie de la coquille d’œuf, d’après cette référence.
Créez une fonction d’assistance pour calculer les coordonnées de la forme d’une géométrie de l’œuf :
Manipulez les paramètres pour afficher différentes formes :
Inspectez la valeur du rayon :
Nous allons appeler la nouvelle fonction eggShapePoints pour un œuf qui mesure 5 cm de haut et 4 cm de large.
Créez des coordonnées pour la géométrie de l’œuf en spécifiant les paramètres :
Créez une courbe spline :
Nous pouvons alors utiliser RegionUnion, en joignant un demi-cercle à notre courbe de coquille d’œuf, pour créer l’ossature de notre région. C’est exactement ce que nous avons fait précédemment, mais avec la courbe spline pour la coquille d’œuf.
Créez une géométrie de l’œuf avec une sous-région :
Ensuite, nous créons le maillage avec les marqueurs de région, comme nous l’avons fait précédemment.
Créez un maillage avec des marqueurs de matière :
Visualisez le marqueur d’élément ponctuel :
Comme nous pouvons le voir, les marqueurs pour l’extérieur de l’œuf sont toujours 2 et 5, donc nous n’avons pas besoin de redéfinir la condition aux limites.
Examinez la condition aux limites :
Examinez la fonction refinementFunction :
Nous sommes déjà à un bon stade, mais nous avons un léger problème. Pour notre version précédente, nous avons créé une fonction d’affinage basée sur le fait que la région avait une forme circulaire. Nous devons créer une nouvelle fonction d’affinage pour notre nouvelle région :
Nous pouvons affiner les éléments en fonction de leur distance radiale à la coquille qui est représentée par une ligne verte sur l’image. Le point rouge représente un élément et il possède une distance radiale à l’axe de symétrie (la ligne bleue) ainsi qu’une distance radiale à la coquille.
Nous souhaitons définir une fonction de raffinement basée sur la distance en vert. Plus la distance par rapport à la coquille est grande, plus le raffinement est faible ; plus la distance par rapport à la coquille est faible, plus le raffinement est élevé. De plus, pour obtenir la distance de la ligne verte, nous soustrayons la distance de la ligne bleue à partir de la distance de l’axe de symétrie jusqu’à la coquille de l’œuf (indiquée en magenta) :
Tout d’abord, nous définissons une fonction qui renvoie la valeur de la coordonnée cylindrique r pour un z donné dans la courbe de la coquille d’œuf.
Faites une interpolation des coordonnées de la coquille :
Nous inversons simplement l’ordre des points dans les coordonnées de la coquille : on remplace {r, z} par {z, r} ; puis, on effectue une interpolation à partir de ça. Ici, rShell est une fonction d’interpolation qui prend z et renvoie r.
Tracez la fonction qui donne la fonction rShell :
Le tracé de cette fonction est ce à quoi nous nous attendrions : la coordonnée r de la coquille d’œuf en fonction de z.
Ensuite, la seule autre chose qu’il nous reste à faire est de calculer la distance entre l’axe et la coquille moins la valeur absolue de la coordonnée r pour chaque point. Cela nous donnera la distance de chaque point à la coquille (affichée en vert) :
Définissez une fonction pour la distance radiale par rapport à la coquille et tracez-la :
Le tracé montre que la distance de chaque point par rapport à la coquille diminue presque linéairement, comme on pourrait s’y attendre.
Tracez la distance au cube par rapport à la coquille :
Lorsque nous traçons la distance au cube, les valeurs diminuent plus rapidement à mesure que la distance par rapport à la coquille devient plus petite.
Gardez à l’esprit que la recherche du comportement correct pour la fonction de raffinement est un processus qui repose sur des essais et des erreurs.
Définissez la fonction de raffinement :
Ici, nous avons défini la refinementFunction de sorte qu’un élément soit raffiné si sa surface est supérieure à la distance au cube par rapport à la coquille, avec un certain décalage pour éviter un raffinement excessif. En d’autres termes, la taille des éléments est proportionnelle à la distance au cube par rapport à la coquille. Plus on est proche de la coquille, plus les éléments sont petits.
Visualisez le maillage à plusieurs matières :
Comme nous pouvons le voir, les éléments proches du centre de l’œuf ne sont pas du tout affinés, mais nous obtenons un raffinement très fin près de la coquille de l’œuf.
Nous pouvons continuer à résoudre l’EDP comme précédemment, en mesurant le temps et la mémoire utilisés. Ce calcul d’un modèle non linéaire avec ce maillage prend environ six minutes pour se terminer (tout comme les œufs que j’aime manger ). Gardez à l’esprit le temps de calcul lors du développement de vos propres modèles. C’est pour ça que Monitor est utile, et qu’il est judicieux d’utiliser d’abord un maillage non raffiné.
Résolvez l’EDP, surveillez la progression et mesurez le temps de calcul ainsi que la mémoire utilisée :
Examinez la plage de températures de la nouvelle solution trouvée :
Encore une fois, nous obtenons une plage de températures raisonnable, donc nous pouvons poursuivre avec l’animation.
Créez les cadres pour la solution :
Tramez les images :
Animez les images :
Deux points importants : premièrement, à six minutes, le jaune d’œuf n’est toujours pas cuit, ce à quoi on s’attendait. Deuxièmement, à dix minutes, toute la région a dépassé les températures de dénaturation du blanc et du jaune d’œuf, donc on peut supposer que l’œuf est entièrement cuit à ce stade. Le modèle reflète désormais un délai réaliste pour cuire l’œuf. Super !
Au bout de six minutes, nous obtenons une température beaucoup plus réaliste, proche de 60 °C au centre de l’œuf, comparée à notre version précédente du modèle, dans laquelle le centre n’atteignait qu’environ 40 °C. Nous pouvons en conclure que la géométrie joue un rôle crucial dans la dynamique de chauffage de l’œuf.
Version 6 : utilisation du modèle
Version 6 : utilisation du modèle
Maintenant que notre modèle produit des résultats satisfaisants, nous pouvons l’utiliser pour faire des prédictions.
Précédemment, notre œuf a été plongé dans de l’eau bouillante alors qu’il était à température ambiante. Modélisons maintenant un cas plus réaliste dans lequel l’œuf est sorti directement du réfrigérateur, en supposant que sa température initiale est de 8 °C. Pour modéliser cela, nous devons modifier nos conditions initiales et nos conditions aux limites. En particulier, la condition aux limites doit commencer à notre nouvelle température initiale et monter rapidement à 100 °C.
Tout d'abord, nous définissons la température initiale à 8 °C et nous la convertissons en kelvins.
Définissez la température initiale de l’œuf :
Ensuite, nous définissons notre nouvelle condition initiale.
Définissez une condition initiale pour la température à l’intérieur de l’œuf :
Il est également nécessaire de modifier la fonction de condition aux limites qui commence désormais à partir de la nouvelle température initiale.
Spécifiez le comportement temporel de la condition aux limites :
Visualisez le comportement temporel de la condition aux limites :
Régénérez la condition aux limites :
Ensuite, nous résolvons l’EDP comme précédemment. Notez qu’ici, nous stockons notre solution dans une nouvelle variable appelée solutionFridge, afin de pouvoir la comparer avec notre ancienne solution.
Résolvez l’EDP, surveillez la progression et mesurez le temps de calcul ainsi que la mémoire utilisée :
Examinez la plage de températures de la solution nouvellement trouvée :
Nous obtenons une plage qui est raisonnable pour notre œuf réfrigéré.
Nous pouvons tracer la solution de la même manière qu’auparavant.
Créez les cadres pour la solution :
Tramez les images :
Animez les images :
À six minutes, le jaune d’œuf n’est toujours pas cuit, tout comme dans notre dernière version. À dix minutes, l’œuf semble être entièrement cuit. Il est difficile de voir simplement en regardant l’animation s’il existe des différences significatives entre l’œuf réfrigéré et celui à température ambiante.
Une bonne option consiste à tracer la température pour la ligne qui passe par le centre du jaune d’œuf jusqu’à la coquille pour les deux solutions : l’œuf réfrigéré et celui à température ambiante. Un tracé plus simple pourrait révéler des aspects plus subtils qui sont difficiles à observer dans le tracé de densité.
Nous traçons la température pour les valeurs de r qui se trouvent sur cette ligne. Nous avons besoin de la valeur de r pour la limite droite. Avec notre fonction rShell que nous avons définie précédemment, nous pouvons obtenir la valeur de la coordonnée r pour la valeur de z égale à 0,005 qui correspond au centre du jaune d’œuf.
Obtenez la valeur de r pour la ligne :
Nous connaissons maintenant la valeur de r pour la limite droite de la ligne qui nous intéresse. Ensuite, nous voulons savoir comment la température augmente pour cette ligne, spécifiquement pour les valeurs de r comprises entre 0 et 0,0179. Nous pouvons créer un tracé simple, par exemple à six minutes, pour ces valeurs de r.
Tracez la solution le long de la ligne radiale depuis le centre du jaune jusqu’à l’extérieur de l’œuf après six minutes :
On peut voir que la température à six minutes est plus basse pour l’œuf sorti directement du réfrigérateur que pour l’œuf à température ambiante, pour toutes les valeurs de r. En particulier, on observe une différence d’environ 5°C au centre de l’œuf.
Comme nous souhaitons prédire si l’œuf est cuit, nous avons également besoin d’un moyen pour visualiser la température de dénaturation. Une façon de le faire consiste à tracer une ligne verticale qui indique la valeur de r à laquelle l’œuf atteint la température de dénaturation.
Obtenez la valeur de r pour laquelle la solution est égale à la température de dénaturation :
Nous constatons que la température de dénaturation du jaune d’œuf prélevé dans le réfrigérateur est atteinte à un rayon d’environ 10 mm (1 cm) depuis le centre. Nous pouvons représenter cela par une ligne sur notre tracé.
Tracez la solution le long de la ligne radiale partant du centre du jaune jusqu’à l’extérieur de l’œuf :
En plus du jaune d’œuf, nous avons également besoin de la température de dénaturation du blanc d’œuf pour l’œuf à température ambiante et pour l’œuf réfrigéré. Au lieu de faire cela manuellement, nous pouvons définir une fonction auxiliaire.
Cette fonction prend la solution ainsi que la valeur de la température de dénaturation qui nous intéresse, puis utilise FindRoot pour déterminer le rayon, le long de la courbe tracée, auquel la température de dénaturation se produit. Enfin, elle renvoie une ligne verticale que nous pouvons afficher sur le tracé.
Écrivez une fonction d’aide pour créer une ligne représentant la température de dénaturation d’une solution :
Ensuite, nous créons une fonction pour le tracé réel où nous utilisons Show pour afficher le tracé ainsi que les lignes correspondant aux températures de dénaturation, en appelant notre fonction d’aide denatureLine et aussi une ligne indiquant le rayon du jaune d’œuf.
Créez une fonction d’aide pour tracer les positions de dénaturation pour les deux solutions à différents moments :
En observant le tracé à la coche des six minutes, nous remarquons deux choses. Premièrement, il est clair que la dénaturation du blanc d’œuf a déjà atteint le centre de l’œuf. Deuxièmement, la température de dénaturation du jaune s’est rapprochée beaucoup plus du centre dans le cas de l’œuf à température ambiante, comparé à l’œuf réfrigéré.
Qu’est-ce que ça signifie ? Pour l’œuf à température ambiante, le jaune commence tout juste à cuire. Mais pour l’œuf réfrigéré, le jaune est encore coulant : la température de dénaturation a à peine atteint le bord du jaune.
Par conséquent, si nous voulons un jaune coulant, nous avons deux options :
1
.Utilisez un œuf réfrigéré et sortez-le de l’eau bouillante exactement à six minutes
2
.Utilisez un œuf à température ambiante et sortez-le de l’eau quelques secondes plus tôt
Si nous faisons le même tracé, mais pour huit minutes, nous remarquons quelque chose d’intéressant. Aux environs de huit minutes, l’œuf entier à température ambiante a dépassé la température de dénaturation du jaune, ce qui signifie qu’il a également dépassé la température de dénaturation du blanc d’œuf. Mais il reste encore des parties du jaune d’œuf réfrigéré qui n’ont pas encore atteint cette température.
Qu’est-ce que ça signifie ? Si vous souhaitez un jaune d’œuf bien cuit et que vous utilisez un œuf directement sorti du réfrigérateur, laissez-le quelques secondes de plus dans l’eau bouillante après les huit minutes. Si vous utilisez un œuf à température ambiante, et que vous avez faim et n’avez pas envie d’attendre, huit minutes pourraient suffire.
Tout se résume à ceci
Tout se résume à ceci
Nous avons commencé par créer une première version simple, quelque chose simplement pour lancer notre flux de travail. Si nous passons directement à la programmation d’un modèle complexe, nous risquons beaucoup plus de faire des erreurs. Pire encore, ces erreurs peuvent être difficiles à repérer. Si les résultats ne semblent pas corrects, il n’est pas toujours évident de savoir s’il y a un problème avec le modèle lui-même ou si nous avons simplement fait une erreur dans le code.
En commençant simplement et en ajoutant progressivement de la complexité, étape par étape, nous réduisons le risque d’introduire des bogues. Nous acquérons également une bien meilleure compréhension du comportement de notre modèle.
Comme nous l’avons vu, le saut soudain de température entre l’extérieur et l’intérieur de l’œuf provoquait des problèmes numériques dans l’une de nos premières versions du modèle. Pour résoudre ce problème, nous avons raffiné le maillage précisément à la limite à l’aide d’une fonction de raffinement. Cette méthode est bien meilleure que de raffiner l’ensemble du maillage, car ça ralentirait considérablement notre calcul.
Pour finir, nous avons utilisé des données expérimentales de deux manières. Premièrement, nous avons utilisé les données expérimentales pour construire notre modèle en définissant les paramètres dans l’équation de la chaleur. Deuxièmement, nous avons utilisé les données pour comparer nos résultats avec les temps de cuisson réels de vrais œufs.
Nous pouvons conclure en quelques points :
◼
Tout d’abord, la création d’une version simple est une bonne idée.
◼
L’augmentation progressive la complexité du modèle peut faciliter les choses.
◼
Des transitions brusques ou des discontinuités dans les variables de solution peuvent provoquer une instabilité numérique ou une perte de précision. Utilisez un raffinement local du maillage ou un lissage dans ces régions.
◼
Il est utile de disposer d’un moyen pour comparer nos résultats avec des données réelles.
Enfin, si vous avez trouvé cela intéressant et que vous souhaitez mettre en œuvre vos propres modèles d’équations aux dérivées partielles, vous pouvez consulter cette présentation de PDEModels dans la documentation.