Buckets:
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Simulation Dynamique - Équation des Ondes</title> | |
| <style> | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| margin: 0; | |
| padding: 0; | |
| background: #f5f7fa; | |
| color: #333; | |
| line-height: 1.6; | |
| } | |
| /* Login Modal Styles */ | |
| #loginModal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0,0,0,0.8); | |
| z-index: 1000; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| #loginModal > div { | |
| background: rgba(255, 255, 255, 0.95); | |
| padding: 40px; | |
| border-radius: 20px; | |
| box-shadow: 0 15px 35px rgba(0, 0, 0, 0.3); | |
| text-align: center; | |
| max-width: 400px; | |
| width: 90%; | |
| } | |
| .header { | |
| background: linear-gradient(135deg, #FF416C 0%, #FF4B2B 100%); | |
| color: white; | |
| padding: 40px 20px; | |
| text-align: center; | |
| } | |
| .container { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| padding: 40px 20px; | |
| } | |
| .card { | |
| background: white; | |
| border-radius: 15px; | |
| padding: 30px; | |
| margin-bottom: 30px; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.05); | |
| } | |
| h1 { margin: 0; font-size: 2.5em; } | |
| h2 { color: #FF4B2B; border-bottom: 2px solid #eee; padding-bottom: 10px; margin-top: 0; } | |
| h3 { color: #444; margin-top: 25px; } | |
| .math-block { | |
| background: #f8f9fa; | |
| padding: 15px; | |
| border-left: 4px solid #FF416C; | |
| font-family: 'Courier New', monospace; | |
| margin: 15px 0; | |
| overflow-x: auto; | |
| font-size: 1.1em; | |
| } | |
| .simulation-container { | |
| text-align: center; | |
| margin: 30px 0; | |
| background: black; | |
| padding: 20px; | |
| border-radius: 10px; | |
| } | |
| .simulation-container img { | |
| max-width: 100%; | |
| border-radius: 4px; | |
| box-shadow: 0 0 20px rgba(255, 75, 43, 0.3); | |
| } | |
| .back-btn { | |
| display: inline-block; | |
| background: rgba(255,255,255,0.2); | |
| color: white; | |
| text-decoration: none; | |
| padding: 10px 20px; | |
| border-radius: 20px; | |
| margin-bottom: 20px; | |
| transition: background 0.3s; | |
| } | |
| .back-btn:hover { background: rgba(255,255,255,0.3); } | |
| .params-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .param-box { | |
| background: #f0f2f5; | |
| padding: 15px; | |
| border-radius: 8px; | |
| text-align: center; | |
| } | |
| .param-value { | |
| font-size: 1.5em; | |
| font-weight: bold; | |
| color: #FF4B2B; | |
| } | |
| .param-label { | |
| font-size: 0.9em; | |
| color: #666; | |
| } | |
| </style> | |
| <!-- MathJax --> | |
| <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> | |
| <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <div class="container" style="padding: 0;"> | |
| <a href="index.html" class="back-btn">← Retour à l'accueil</a> | |
| <h1>Simulation Dynamique</h1> | |
| <p>Propagation d'ondes 1D par Éléments Finis</p> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <!-- 1. La Simulation --> | |
| <div class="card"> | |
| <h2>Visualisation de l'Onde</h2> | |
| <p>Voici le résultat de la simulation d'une barre élastique 1D soumise à une perturbation initiale de type Gaussienne. Les extrémités sont fixées (Conditions de Dirichlet homogènes).</p> | |
| <div class="simulation-container"> | |
| <img src="bar_simulation.gif" alt="Simulation Onde 1D"> | |
| </div> | |
| <div class="params-grid"> | |
| <div class="param-box"> | |
| <div class="param-value">50</div> | |
| <div class="param-label">Éléments (Quadratiques)</div> | |
| </div> | |
| <div class="param-box"> | |
| <div class="param-value">2000</div> | |
| <div class="param-label">Pas de temps</div> | |
| </div> | |
| <div class="param-box"> | |
| <div class="param-value">1.0</div> | |
| <div class="param-label">Vitesse d'onde (c)</div> | |
| </div> | |
| <div class="param-box"> | |
| <div class="param-value">0.1</div> | |
| <div class="param-label">CFL (Stabilité)</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 2. Modèle Mathématique --> | |
| <div class="card"> | |
| <h2>Modèle Mathématique</h2> | |
| <p>Nous résolvons l'équation des ondes 1D (équation hyperbolique) :</p> | |
| <div class="math-block"> | |
| $$ \frac{\partial^2 u}{\partial t^2} = c^2 \frac{\partial^2 u}{\partial x^2} $$ | |
| </div> | |
| <h3>Discrétisation Spatiale (FEM)</h3> | |
| <p>On utilise la méthode des éléments finis avec des fonctions de base de Lagrange quadratiques (\(P_2\)). Cela conduit au système semi-discret :</p> | |
| <div class="math-block"> | |
| $$ M \ddot{U} + K U = 0 $$ | |
| </div> | |
| <p>Où \(M\) est la matrice de masse et \(K\) la matrice de rigidité.</p> | |
| <h3>Discrétisation Temporelle</h3> | |
| <p>On utilise un schéma de différences finies centré (explicite) pour la dérivée seconde en temps :</p> | |
| <div class="math-block"> | |
| $$ \ddot{U}^n \approx \frac{U^{n+1} - 2U^n + U^{n-1}}{\Delta t^2} $$ | |
| </div> | |
| <p>Ce qui donne le schéma de mise à jour :</p> | |
| <div class="math-block"> | |
| $$ M U^{n+1} = 2M U^n - M U^{n-1} - \Delta t^2 K U^n $$ | |
| </div> | |
| </div> | |
| <!-- 3. Code Python --> | |
| <div class="card"> | |
| <h2>Implémentation Python</h2> | |
| <p>Le code utilise notre bibliothèque FEM maison <code>fe_appprox1D.py</code> pour l'assemblage des matrices.</p> | |
| <pre style="background: #2d2d2d; color: #ccc; padding: 15px; border-radius: 5px; overflow-x: auto;"><code># Assemblage des matrices | |
| K, _ = assemble(nodes, elements, phi, None, symbolic=False, matrix_function=element_stiffness_matrix) | |
| M, _ = assemble(nodes, elements, phi, None, symbolic=False, matrix_function=element_matrix) | |
| # Boucle temporelle | |
| for n in range(Nt): | |
| # Calcul du second membre (RHS) | |
| b = 2 * M @ u - M @ u_prev - dt**2 * (c**2) * (K @ u) | |
| # Résolution du système linéaire M * u_next = b | |
| # (Note: Avec mass lumping, M serait diagonale -> inversion triviale) | |
| u_next[free_dofs] = np.linalg.solve(M_free, b[free_dofs])</code></pre> | |
| </div> | |
| </div> | |
| <!-- Login Modal --> | |
| <div id="loginModal"> | |
| <div> | |
| <h2 style="color: #333; margin-bottom: 10px;">🔐 Accès Administrateur</h2> | |
| <p style="color: #666; margin-bottom: 30px;">Entrez le code secret pour accéder aux fonctionnalités administrateur</p> | |
| <form id="modalLoginForm"> | |
| <input type="password" id="modalAdminCode" placeholder="Code secret" required style="width: 100%; padding: 15px; border: 2px solid #ddd; border-radius: 10px; font-size: 1.1em; box-sizing: border-box; margin-bottom: 20px;"> | |
| <button type="submit" style="background: linear-gradient(135deg, #FF416C 0%, #FF4B2B 100%); color: white; border: none; padding: 15px 30px; border-radius: 25px; font-size: 1.1em; cursor: pointer; width: 100%;">Se connecter</button> | |
| </form> | |
| <div id="modalMessage" style="margin-top: 15px;"></div> | |
| <button onclick="closeLoginModal()" style="background: none; border: none; color: #666; cursor: pointer; margin-top: 15px; text-decoration: underline;">Annuler</button> | |
| </div> | |
| </div> | |
| <script> | |
| // Fonction pour vérifier l'authentification admin | |
| function checkAdminAuth() { | |
| const isLoggedIn = localStorage.getItem('adminLoggedIn') === 'true'; | |
| const loginTime = localStorage.getItem('adminLoginTime'); | |
| const now = Date.now(); | |
| const oneHour = 60 * 60 * 1000; // 1 heure en millisecondes | |
| // Vérifier si la session a expiré (1 heure) | |
| if (isLoggedIn && loginTime && (now - parseInt(loginTime)) < oneHour) { | |
| return true; | |
| } else { | |
| // Nettoyer les données expirées | |
| localStorage.removeItem('adminLoggedIn'); | |
| localStorage.removeItem('adminLoginTime'); | |
| return false; | |
| } | |
| } | |
| // Fonction pour afficher le modal de connexion | |
| function showLoginModal() { | |
| document.getElementById('loginModal').style.display = 'flex'; | |
| document.getElementById('modalAdminCode').focus(); | |
| } | |
| // Fonction pour fermer le modal | |
| function closeLoginModal() { | |
| document.getElementById('loginModal').style.display = 'none'; | |
| document.getElementById('modalMessage').innerHTML = ''; | |
| document.getElementById('modalAdminCode').value = ''; | |
| } | |
| // Gestionnaire pour le formulaire du modal | |
| document.getElementById('modalLoginForm').addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const code = document.getElementById('modalAdminCode').value; | |
| const messageDiv = document.getElementById('modalMessage'); | |
| if (code === '180201') { | |
| localStorage.setItem('adminLoggedIn', 'true'); | |
| localStorage.setItem('adminLoginTime', Date.now()); | |
| messageDiv.innerHTML = '<div style="color: #2ed573;">✓ Connexion réussie !</div>'; | |
| setTimeout(() => { | |
| closeLoginModal(); | |
| // Recharger la page pour mettre à jour l'état admin | |
| location.reload(); | |
| }, 1000); | |
| } else { | |
| messageDiv.innerHTML = '<div style="color: #ff4757;">✗ Code incorrect. Veuillez réessayer.</div>'; | |
| document.getElementById('modalAdminCode').value = ''; | |
| document.getElementById('modalAdminCode').focus(); | |
| } | |
| }); | |
| // Fermer le modal en cliquant en dehors | |
| document.getElementById('loginModal').addEventListener('click', function(e) { | |
| if (e.target === this) { | |
| closeLoginModal(); | |
| } | |
| }); | |
| // Vérifier au chargement de la page | |
| window.addEventListener('load', function() { | |
| if (!checkAdminAuth()) { | |
| // Si pas connecté, afficher le modal après un court délai | |
| setTimeout(() => { | |
| showLoginModal(); | |
| }, 500); | |
| } | |
| }); | |
| // Ajouter un bouton admin dans le header si connecté | |
| if (checkAdminAuth()) { | |
| const header = document.querySelector('.header'); | |
| if (header) { | |
| const adminBtn = document.createElement('div'); | |
| adminBtn.style.textAlign = 'right'; | |
| adminBtn.style.marginBottom = '10px'; | |
| adminBtn.innerHTML = '<a href="#" onclick="logoutAdmin()" style="color: white; text-decoration: none; font-weight: bold; background: rgba(255,255,255,0.2); padding: 5px 10px; border-radius: 15px;">🚪 Déconnexion Admin</a>'; | |
| header.insertBefore(adminBtn, header.firstChild.nextSibling); | |
| } | |
| } | |
| // Fonction de déconnexion | |
| function logoutAdmin() { | |
| localStorage.removeItem('adminLoggedIn'); | |
| localStorage.removeItem('adminLoginTime'); | |
| location.reload(); | |
| } | |
| </script> | |
| </body> | |
| </html> | |
Xet Storage Details
- Size:
- 12.4 kB
- Xet hash:
- 9b8b7cc2ade11075b9547c32c63365603d0fea7daa07e7a7274a918246d353bf
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.