ACTIVITE 1 – Méthode d’Euler : un 1ᵉʳ exemple avec le RC série

Chargement de Python... Veuillez patienter.

Dans cette activité nous allons mettre en œuvre la méthode d'Euler qui permet de résoudre numériquement et de manière approchée une équation différentielle du 1er ordre. Nous l'appliquerons à l'exemple du circuit RC série. L'équation différentielle qui pilote ce circuit peut être résolue analytiquement de manière exacte, comme vous le savez. Utiliser la méthode d'Euler sur cet exemple n'est donc pas nécessaire en réalité, mais l'avantage est qu'il s'agit d'un exemple simple dont on connait la solution exacte, ainsi nous pourrons comparer cette dernière avec ce que nous obtiendrons en appliquant l'algorithme d'Euler !

On a \( E=5 \text{ V} \), \( R=1000 \text{ Ω} \) et \(C=100 \text{ nF} \).
Initialement le condensateur est déchargé, on a donc \( u_c(0^-)=0 \). A l'instant \( t=0 \), on ferme l'interrupteur.

Que vaut la tension \( u_c(0^+) \) aux bornes du condensateur juste après avoir fermé l'interrupteur ? Pourquoi ?
Quelle est l'équation différentielle vérifiée par la tension \( u_c \) à partir de cet instant ?

C'est donc cette équation différentielle que nous allons tenter de résoudre numériquement aujourd'hui.

Au bout de combien de temps (en fonction des données du problème) peut-on considérer que le régime permanent est atteint ?

• Définir les constantes nécessaires avec leur valeur dans le Système International d'unités : E, R et C.
• Définir également la durée totale tf sur laquelle on veut résoudre, en fonction de R et C (voir question précédente).
• Définir un nombre N de divisions de cette intervalle de temps pour appliquer la méthode d'Euler (on pourra par exemple choisir \(N=10\) pour commencer).
• Définir alors le pas de calcul dt en fonction de tf et N (ne pas donner de valeur numérique, écrire le calcul en fonction de tf et N, pour pouvoir ajuster les valeurs de tf ou de N facilement dans la suite de l'activité).

Maintenant, nous allons créer des listes (ou plutôt des "tableaux numpy") pour y stocker les valeurs de temps, de tensions et de dérivées.
• Définir un tableau (liste) numpy t contenant les \(N\) instants \(t_i\) régulièrement espacés entre 0 et tf.[Note]
• Définir un tableau numpy Uc contenant pour l'instant \(N\) zéros. On le remplira par la suite : il contiendra les valeurs de la tension \(u_c(t_i)\) aux différents instants \(t_i\) de calcul.[Note]
• Définir un autre tableau numpy dUc_dt contenant lui aussi pour l'instant \(N\) zéros. On le remplira par la suite : il contiendra les valeurs de la dérivée \( \frac{\text{d}u_c}{\text{d}t}(t_i) \) aux différents instants \(t_i\) de calcul[Note].

Pour résoudre numériquement l'équation différentielle, nous utilisons la méthode d'Euler qui approxime la dérivée par :\( \frac{\text{d}f}{\text{d}t} \approx \frac{f(t+\text{d}t) - f(t)}{\text{d}t} \)

D'après l'équation différentielle du circuit, que vaut la dérivée \( \frac{\text{d}u_c}{\text{d}t} \) en fonction de \( u_c \) et des paramètres \( E \), \( R \) et \( C \) ?
\frac{du_c}{dt}=

• Initialisation : fixer dans Uc[0] la valeur de la condition initiale connue \( u_c(0) \).
• Dans une boucle[Note], on calcule les valeurs suivantes de \( u_c \) de proche en proche :
- calculer dUc_dt[i] en fonction de Uc[i] conformément à l'équation différentielle à résoudre,
- puis calculer Uc[i+1] en fonction de Uc[i] suivant la méthode d'Euler.

Il ne reste plus qu'à tracer le graphe des Uc en ordonnées et des t en abscisses, pour visualiser la solution approchée ainsi obtenue !

Puisque l'équation différentielle sur laquelle on travaille est linéaire, il existe une solution analytique exacte. Il serait donc intéressant de comparer la solution approchée obtenue avec la solution exacte !

Quelle est la solution analytique exacte de cette équation différentielle ?
u_c(t)=

• Dans la cellule Python précédente, ajouter une ligne pour calculer UcExacte en fonction de t, E, R et C.[Note]
Puis, dans le même graphe, superposer cette solution exacte en ajoutant la ligne plt.plot(t,UcExacte).
• Exécuter à nouveau le programme en choisissant des nombres d'itérations N différents. Commenter.

Comparer la résolution numérique approchée par la méthode d'Euler avec la solution analytique exacte.
terminal = false packages = ["numpy", "matplotlib"] [[fetch]] files = [] import sys import io import traceback from js import document, console, window from pyodide.ffi import create_proxy import base64 # Configuration matplotlib pour PyScript import matplotlib matplotlib.use('Agg') # Backend non-interactif pour PyScript import matplotlib.pyplot as plt # Contexte global partagé entre toutes les cellules global_context = {'plt': plt, 'matplotlib': matplotlib} def capture_output(func, *args, **kwargs): """Capture la sortie d'une fonction""" old_stdout = sys.stdout old_stderr = sys.stderr stdout_capture = io.StringIO() stderr_capture = io.StringIO() sys.stdout = stdout_capture sys.stderr = stderr_capture try: result = func(*args, **kwargs) stdout_output = stdout_capture.getvalue() stderr_output = stderr_capture.getvalue() return result, stdout_output, stderr_output finally: sys.stdout = old_stdout sys.stderr = old_stderr def handle_matplotlib_figures(output_element): """Gère l'affichage des figures matplotlib""" current_figures = plt.get_fignums() if current_figures: # Créer ou récupérer le conteneur des figures figures_container = output_element.querySelector('.figures-container') if not figures_container: figures_container = document.createElement('div') figures_container.className = 'figures-container' figures_container.style.cssText = ''' display: flex; flex-wrap: wrap; gap: 15px; margin: 10px 0; justify-content: center; ''' output_element.appendChild(figures_container) for fig_num in current_figures: fig = plt.figure(fig_num) # Convertir la figure en image base64 buf = io.BytesIO() fig.savefig(buf, format='png', bbox_inches='tight', dpi=150) buf.seek(0) img_data = base64.b64encode(buf.read()).decode() buf.close() # Créer un conteneur pour la figure avec bouton de suppression figure_wrapper = document.createElement('div') figure_wrapper.className = 'figure-wrapper' figure_wrapper.style.cssText = ''' position: relative; display: inline-block; border: 1px solid #ddd; border-radius: 8px; padding: 10px; background: white; box-shadow: 0 2px 5px rgba(0,0,0,0.1); margin: 5px; ''' # Créer l'élément img pour afficher la figure img_element = document.createElement('img') img_element.src = f'data:image/png;base64,{img_data}' img_element.className = 'matplotlib-figure' img_element.style.cssText = ''' max-width: 300px; max-height: 250px; height: auto; display: block; border-radius: 4px; ''' # Créer le bouton de suppression delete_btn = document.createElement('button') delete_btn.innerHTML = '×' delete_btn.className = 'figure-delete-btn' delete_btn.style.cssText = ''' position: absolute; top: 5px; right: 5px; width: 25px; height: 25px; border: none; border-radius: 50%; background: rgba(220, 53, 69, 0.8); color: white; font-size: 16px; font-weight: bold; cursor: pointer; display: flex; align-items: center; justify-content: center; line-height: 1; z-index: 10; ''' delete_btn.title = 'Supprimer cette figure' # Ajouter l'événement de suppression def create_delete_handler(wrapper): def delete_figure(event): wrapper.remove() return delete_figure delete_btn.onclick = create_delete_handler(figure_wrapper) # Assembler la figure figure_wrapper.appendChild(img_element) figure_wrapper.appendChild(delete_btn) figures_container.appendChild(figure_wrapper) # Fermer toutes les figures pour éviter les accumulations plt.close('all') return True return False def execute_python_code(code, cell_id): """Exécute le code Python et affiche le résultat""" output_element = document.querySelector(f'#output-{cell_id}') loading_element = document.querySelector(f'#loading-{cell_id}') # Afficher le loading loading_element.style.display = 'block' # Vérifier s'il y a déjà des figures figures_container = output_element.querySelector('.figures-container') if figures_container: # S'il y a des figures, effacer seulement le contenu texte text_outputs = output_element.querySelectorAll('.text-output') for text_output in text_outputs: text_output.remove() # Réinitialiser le style de base sans effacer les figures output_element.className = 'output' # Vider seulement le texte direct, pas les éléments enfants nodes_to_remove = [] for node in output_element.childNodes: if node.nodeType == 3: # Text node nodes_to_remove.append(node) for node in nodes_to_remove: node.remove() else: # Pas de figures, effacer tout output_element.innerHTML = '' output_element.className = 'output' try: def run_code(): # Exécuter le code dans le contexte global exec(code, global_context) # Si la dernière ligne est une expression (pas un statement), l'évaluer et retourner le résultat lines = code.strip().split('\n') if lines: last_line = lines[-1].strip() # Vérifier que ce n'est pas un statement Python commun if (last_line and not last_line.startswith((' ', '\t')) and not last_line.endswith(':') and not last_line.startswith(('print(', 'import ', 'from ', 'def ', 'class ', 'if ', 'for ', 'while ', 'try:', 'except', 'with ', 'assert ', 'del ', 'pass', 'break', 'continue', 'return', 'yield', 'raise', 'global ', 'nonlocal ', 'plt.', 'fig')) and '=' not in last_line.split('#')[0]): # Éviter les assignments try: return eval(last_line, global_context) except: pass return None result, stdout_output, stderr_output = capture_output(run_code) # Gérer l'affichage des figures matplotlib (s'ajoute aux existantes) has_figures = handle_matplotlib_figures(output_element) # Construire la sortie texte output = "" if stdout_output: output += stdout_output if stderr_output: output += stderr_output if result is not None: output += str(result) if output: # S'il y a déjà des figures, ajouter le texte à la suite if has_figures: text_div = document.createElement('div') text_div.className = 'text-output' text_div.textContent = output output_element.appendChild(text_div) else: output_element.textContent = output elif not has_figures: output_element.textContent = "✓ Code exécuté avec succès" output_element.className = 'output success' except Exception as e: error_output = f"❌ Erreur:\n{str(e)}\n\n{traceback.format_exc()}" output_element.textContent = error_output output_element.className = 'output error' finally: loading_element.style.display = 'none' # Exposer les fonctions à JavaScript window.execute_python_code = create_proxy(execute_python_code) # Signaler que PyScript est prêt console.log("PyScript ready!") try: # Activer l'interface immédiatement status_element = document.querySelector('#pyscript-status') if status_element: status_element.innerHTML = 'Python est prêt ! Vous pouvez maintenant exécuter votre code.' status_element.className = 'pyscript-status pyscript-ready' # Activer tous les boutons d'exécution run_buttons = document.querySelectorAll('.btn-run') for button in run_buttons: button.disabled = False # Notifier JavaScript window.pyScriptIsReady = True except Exception as e: console.log(f"Error during PyScript initialization: {e}")