{"id":11711,"date":"2026-02-03T09:08:53","date_gmt":"2026-02-03T08:08:53","guid":{"rendered":"https:\/\/spgoo.org\/?page_id=11711"},"modified":"2026-02-03T09:08:54","modified_gmt":"2026-02-03T08:08:54","slug":"sno-tourbieres-simul1","status":"publish","type":"page","link":"https:\/\/spgoo.org\/?page_id=11711","title":{"rendered":"SNO Tourbi\u00e8res : simul1"},"content":{"rendered":"\n<link rel='stylesheet' id='11707-css'  href='https:\/\/spgoo.org\/wp-content\/uploads\/winp-css-js\/11707.css?ver=1770106020' type='text\/css' media='all' \/>\n\n\n    <div id=\"header\">\r\n        <h1>\ud83c\udf3f Mod\u00e8le 3D de Tourbi\u00e8re &#8211; Flux de Gaz<\/h1>\r\n        <p>Visualisation des \u00e9changes CO\u2082 et CH\u2084 avec l&#8217;atmosph\u00e8re<\/p>\r\n    <\/div>\r\n    \r\n    <div id=\"container\">\r\n        <div id=\"canvas-container\">\r\n            <canvas id=\"canvas\"><\/canvas>\r\n            \r\n            <div id=\"legend\">\r\n                <h4>L\u00e9gende<\/h4>\r\n                <div class=\"legend-item\">\r\n                    <div class=\"legend-color\" style=\"background: #ff6b6b;\"><\/div>\r\n                    <span>CO\u2082 (respiration)<\/span>\r\n                <\/div>\r\n                <div class=\"legend-item\">\r\n                    <div class=\"legend-color\" style=\"background: #4ecdc4;\"><\/div>\r\n                    <span>CH\u2084 (m\u00e9thanogen\u00e8se)<\/span>\r\n                <\/div>\r\n                <div class=\"legend-item\">\r\n                    <div class=\"legend-color\" style=\"background: #4169E1; opacity: 0.6;\"><\/div>\r\n                    <span>Nappe phr\u00e9atique<\/span>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div id=\"info-overlay\">\r\n                <h3>\u2139\ufe0f Guide d&#8217;utilisation<\/h3>\r\n                <ul>\r\n                    <li>\u2022 <strong>Souris<\/strong> : Rotation de la vue<\/li>\r\n                    <li>\u2022 <strong>Sph\u00e8res rouges<\/strong> : CO\u2082<\/li>\r\n                    <li>\u2022 <strong>Sph\u00e8res cyan<\/strong> : CH\u2084<\/li>\r\n                    <li>\u2022 <strong>Zone bleue<\/strong> : Nappe phr\u00e9atique<\/li>\r\n                    <li>\u2022 <strong>Acrotelme<\/strong> : Zone a\u00e9robie (production CO\u2082)<\/li>\r\n                    <li>\u2022 <strong>Catotelme<\/strong> : Zone ana\u00e9robie (production CH\u2084)<\/li>\r\n                <\/ul>\r\n            <\/div>\r\n        <\/div>\r\n        \r\n        <div id=\"controls\">\r\n            <div class=\"control-section\">\r\n                <h3>\u2699\ufe0f Contr\u00f4les<\/h3>\r\n                <button class=\"btn-primary\" id=\"playPauseBtn\">\u23f8 Pause<\/button>\r\n                <button class=\"btn-secondary\" id=\"resetBtn\">\u21bb R\u00e9initialiser<\/button>\r\n                <button class=\"btn-info\" id=\"infoBtn\">\u2139 Afficher l&#8217;aide<\/button>\r\n            <\/div>\r\n            \r\n            <div class=\"control-section\">\r\n                <h3>\ud83c\udf9a\ufe0f Param\u00e8tres<\/h3>\r\n                <div class=\"slider-group\">\r\n                    <label>Nappe phr\u00e9atique: <span id=\"waterTableValue\">15<\/span> cm<\/label>\r\n                    <input type=\"range\" id=\"waterTableSlider\" min=\"5\" max=\"40\" value=\"15\" step=\"1\">\r\n                    <div class=\"slider-labels\">\r\n                        <span>5 cm<\/span>\r\n                        <span>40 cm<\/span>\r\n                    <\/div>\r\n                <\/div>\r\n                \r\n                <div class=\"slider-group\">\r\n                    <label>Temp\u00e9rature: <span id=\"temperatureValue\">15<\/span>\u00b0C<\/label>\r\n                    <input type=\"range\" id=\"temperatureSlider\" min=\"0\" max=\"30\" value=\"15\" step=\"1\">\r\n                    <div class=\"slider-labels\">\r\n                        <span>0\u00b0C<\/span>\r\n                        <span>30\u00b0C<\/span>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"control-section\">\r\n                <h3>\ud83d\udc41\ufe0f Affichage<\/h3>\r\n                <label class=\"checkbox-group\">\r\n                    <input type=\"checkbox\" id=\"showCO2\" checked>\r\n                    <span>Afficher CO\u2082<\/span>\r\n                <\/label>\r\n                <label class=\"checkbox-group\">\r\n                    <input type=\"checkbox\" id=\"showCH4\" checked>\r\n                    <span>Afficher CH\u2084<\/span>\r\n                <\/label>\r\n            <\/div>\r\n            \r\n            <div class=\"control-section\">\r\n                <div id=\"flux-display\">\r\n                    <h3>\ud83d\udcca Flux instantan\u00e9s<\/h3>\r\n                    <div class=\"flux-card\">\r\n                        <div class=\"flux-label\">Flux CO\u2082<\/div>\r\n                        <div class=\"flux-value\" style=\"color: #dc2626;\" id=\"fluxCO2\">120.0<\/div>\r\n                        <div class=\"flux-unit\">mg CO\u2082\/m\u00b2\/h<\/div>\r\n                    <\/div>\r\n                    <div class=\"flux-card\">\r\n                        <div class=\"flux-label\">Flux CH\u2084<\/div>\r\n                        <div class=\"flux-value\" style=\"color: #0891b2;\" id=\"fluxCH4\">45.0<\/div>\r\n                        <div class=\"flux-unit\">mg CH\u2084\/m\u00b2\/h<\/div>\r\n                    <\/div>\r\n                    <div class=\"flux-card\">\r\n                        <div class=\"flux-label\">PRG\u2081\u2080\u2080 \u00e9quivalent<\/div>\r\n                        <div class=\"flux-value\" style=\"color: #ea580c;\" id=\"fluxPRG\">1380<\/div>\r\n                        <div class=\"flux-unit\">mg CO\u2082-eq\/m\u00b2\/h<\/div>\r\n                    <\/div>\r\n                    <div class=\"info-box\">\r\n                        \ud83d\udca1 <strong>Info:<\/strong> Le CH\u2084 a un PRG de 28\u00d7 le CO\u2082 sur 100 ans\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"control-section\">\r\n                <div class=\"process-list\">\r\n                    <h4>Processus actifs:<\/h4>\r\n                    <ul>\r\n                        <li>\u2713 Diffusion mol\u00e9culaire<\/li>\r\n                        <li>\u2713 Respiration a\u00e9robie (CO\u2082)<\/li>\r\n                        <li>\u2713 M\u00e9thanogen\u00e8se (CH\u2084)<\/li>\r\n                        <li>\u2713 Oxydation CH\u2084 (zone a\u00e9robie)<\/li>\r\n                        <li>\u2713 Effet temp\u00e9rature (Q\u2081\u2080)<\/li>\r\n                    <\/ul>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"control-section chart-section\">\r\n                <h3>\ud83d\udcc8 Graphiques<\/h3>\r\n                <div class=\"tabs\">\r\n                    <button class=\"tab active\" data-tab=\"temporal\">Temporel<\/button>\r\n                    <button class=\"tab\" data-tab=\"profile\">Profil<\/button>\r\n                <\/div>\r\n                \r\n                <div id=\"temporal-panel\" class=\"chart-panel active\">\r\n                    <div class=\"chart-container\">\r\n                        <h4>\u00c9volution des flux dans le temps<\/h4>\r\n                        <canvas id=\"fluxChart\"><\/canvas>\r\n                    <\/div>\r\n                    <div class=\"chart-container\">\r\n                        <h4>Cumul journalier (estimation)<\/h4>\r\n                        <canvas id=\"cumulChart\"><\/canvas>\r\n                    <\/div>\r\n                <\/div>\r\n                \r\n                <div id=\"profile-panel\" class=\"chart-panel\">\r\n                    <div class=\"chart-container\">\r\n                        <h4>Profil de concentration CO\u2082<\/h4>\r\n                        <canvas id=\"profileCO2Chart\"><\/canvas>\r\n                    <\/div>\r\n                    <div class=\"chart-container\">\r\n                        <h4>Profil de concentration CH\u2084<\/h4>\r\n                        <canvas id=\"profileCH4Chart\"><\/canvas>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n    \r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/Chart.js\/3.9.1\/chart.min.js\"><\/script>\r\n    <script>\r\n        \/\/ State\r\n        let isPlaying = true;\r\n        let time = 0;\r\n        let waterTable = 0.15;\r\n        let temperature = 15;\r\n        let showCO2 = true;\r\n        let showCH4 = true;\r\n        \r\n        \/\/ Data storage for charts\r\n        const maxDataPoints = 60; \/\/ 60 seconds of data\r\n        const fluxHistory = {\r\n            time: [],\r\n            co2: [],\r\n            ch4: [],\r\n            prg: []\r\n        };\r\n        \r\n        const cumulData = {\r\n            time: [],\r\n            co2: 0,\r\n            ch4: 0,\r\n            co2History: [],\r\n            ch4History: []\r\n        };\r\n        \r\n        \/\/ Three.js setup\r\n        const canvas = document.getElementById('canvas');\r\n        const scene = new THREE.Scene();\r\n        scene.background = new THREE.Color(0x87CEEB);\r\n        \r\n        const camera = new THREE.PerspectiveCamera(60, canvas.clientWidth \/ canvas.clientHeight, 0.1, 1000);\r\n        camera.position.set(3, 2, 3);\r\n        camera.lookAt(0, 0, 0);\r\n        \r\n        const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });\r\n        renderer.setSize(canvas.clientWidth, canvas.clientHeight);\r\n        renderer.shadowMap.enabled = true;\r\n        \r\n        \/\/ Lights\r\n        const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);\r\n        scene.add(ambientLight);\r\n        \r\n        const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);\r\n        dirLight.position.set(5, 10, 5);\r\n        dirLight.castShadow = true;\r\n        scene.add(dirLight);\r\n        \r\n        \/\/ Peatland layers\r\n        const layerConfigs = [\r\n            { color: 0x8B4513, height: 0.3, name: 'Acrotelme' },\r\n            { color: 0x654321, height: 0.5, name: 'Catotelme sup.' },\r\n            { color: 0x3d2817, height: 0.8, name: 'Catotelme prof.' },\r\n            { color: 0x2a1810, height: 0.7, name: 'Tourbe ancienne' }\r\n        ];\r\n        \r\n        let yOffset = 0;\r\n        layerConfigs.forEach((config) => {\r\n            const geometry = new THREE.BoxGeometry(2, config.height, 2);\r\n            const material = new THREE.MeshPhongMaterial({ \r\n                color: config.color,\r\n                transparent: true,\r\n                opacity: 0.85\r\n            });\r\n            const mesh = new THREE.Mesh(geometry, material);\r\n            mesh.position.y = yOffset - config.height \/ 2;\r\n            mesh.castShadow = true;\r\n            mesh.receiveShadow = true;\r\n            scene.add(mesh);\r\n            \r\n            const edges = new THREE.EdgesGeometry(geometry);\r\n            const line = new THREE.LineSegments(edges, \r\n                new THREE.LineBasicMaterial({ color: 0x000000, opacity: 0.3, transparent: true }));\r\n            line.position.copy(mesh.position);\r\n            scene.add(line);\r\n            \r\n            yOffset -= config.height;\r\n        });\r\n        \r\n        \/\/ Water table\r\n        const waterGeometry = new THREE.PlaneGeometry(2.2, 2.2);\r\n        const waterMaterial = new THREE.MeshPhongMaterial({ \r\n            color: 0x4169E1,\r\n            transparent: true,\r\n            opacity: 0.6,\r\n            side: THREE.DoubleSide\r\n        });\r\n        const waterMesh = new THREE.Mesh(waterGeometry, waterMaterial);\r\n        waterMesh.rotation.x = -Math.PI \/ 2;\r\n        scene.add(waterMesh);\r\n        \r\n        \/\/ Particles\r\n        const particlesCO2 = [];\r\n        const particlesCH4 = [];\r\n        \r\n        function createParticle(color, isCO2) {\r\n            const geometry = new THREE.SphereGeometry(0.02, 8, 8);\r\n            const material = new THREE.MeshBasicMaterial({ \r\n                color,\r\n                transparent: true,\r\n                opacity: 0.7\r\n            });\r\n            const particle = new THREE.Mesh(geometry, material);\r\n            \r\n            const x = (Math.random() - 0.5) * 1.8;\r\n            const z = (Math.random() - 0.5) * 1.8;\r\n            const y = -Math.random() * 2.3;\r\n            \r\n            particle.position.set(x, y, z);\r\n            particle.userData = {\r\n                velocity: 0.002 + Math.random() * 0.003,\r\n                baseX: x,\r\n                baseZ: z,\r\n                age: 0,\r\n                maxAge: 100 + Math.random() * 100\r\n            };\r\n            \r\n            scene.add(particle);\r\n            return particle;\r\n        }\r\n        \r\n        for (let i = 0; i < 80; i++) {\r\n            particlesCO2.push(createParticle(0xff6b6b, true));\r\n        }\r\n        for (let i = 0; i < 60; i++) {\r\n            particlesCH4.push(createParticle(0x4ecdc4, false));\r\n        }\r\n        \r\n        \/\/ Arrows\r\n        const arrowsCO2 = [];\r\n        const arrowsCH4 = [];\r\n        \r\n        for (let i = 0; i < 10; i++) {\r\n            const x = (Math.random() - 0.5) * 1.6;\r\n            const z = (Math.random() - 0.5) * 1.6;\r\n            \r\n            const arrowCO2 = new THREE.ArrowHelper(\r\n                new THREE.Vector3(0, 1, 0),\r\n                new THREE.Vector3(x, 0.3, z),\r\n                0.5, 0xff6b6b, 0.1, 0.08\r\n            );\r\n            scene.add(arrowCO2);\r\n            arrowsCO2.push(arrowCO2);\r\n            \r\n            const arrowCH4 = new THREE.ArrowHelper(\r\n                new THREE.Vector3(0, 1, 0),\r\n                new THREE.Vector3(x + 0.1, 0.3, z + 0.1),\r\n                0.4, 0x4ecdc4, 0.1, 0.08\r\n            );\r\n            scene.add(arrowCH4);\r\n            arrowsCH4.push(arrowCH4);\r\n        }\r\n        \r\n        \/\/ Mouse controls\r\n        let mouseX = 0, mouseY = 0;\r\n        let targetRotationX = 0, targetRotationY = 0;\r\n        \r\n        canvas.addEventListener('mousemove', (e) => {\r\n            const rect = canvas.getBoundingClientRect();\r\n            mouseX = ((e.clientX - rect.left) \/ rect.width) * 2 - 1;\r\n            mouseY = -((e.clientY - rect.top) \/ rect.height) * 2 + 1;\r\n            targetRotationX = mouseY * 0.5;\r\n            targetRotationY = mouseX * 0.5;\r\n        });\r\n        \r\n        \/\/ Initialize Charts\r\n        Chart.defaults.font.size = 11;\r\n        Chart.defaults.color = '#374151';\r\n        \r\n        \/\/ Flux temporal chart\r\n        const fluxChart = new Chart(document.getElementById('fluxChart'), {\r\n            type: 'line',\r\n            data: {\r\n                labels: [],\r\n                datasets: [{\r\n                    label: 'CO\u2082 (mg\/m\u00b2\/h)',\r\n                    data: [],\r\n                    borderColor: '#dc2626',\r\n                    backgroundColor: 'rgba(220, 38, 38, 0.1)',\r\n                    tension: 0.4,\r\n                    fill: true\r\n                }, {\r\n                    label: 'CH\u2084 (mg\/m\u00b2\/h)',\r\n                    data: [],\r\n                    borderColor: '#0891b2',\r\n                    backgroundColor: 'rgba(8, 145, 178, 0.1)',\r\n                    tension: 0.4,\r\n                    fill: true\r\n                }]\r\n            },\r\n            options: {\r\n                responsive: true,\r\n                maintainAspectRatio: true,\r\n                interaction: {\r\n                    intersect: false,\r\n                    mode: 'index'\r\n                },\r\n                plugins: {\r\n                    legend: {\r\n                        display: true,\r\n                        position: 'top'\r\n                    }\r\n                },\r\n                scales: {\r\n                    x: {\r\n                        display: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Temps (s)'\r\n                        }\r\n                    },\r\n                    y: {\r\n                        display: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Flux (mg\/m\u00b2\/h)'\r\n                        },\r\n                        beginAtZero: false\r\n                    }\r\n                }\r\n            }\r\n        });\r\n        \r\n        \/\/ Cumulative chart\r\n        const cumulChart = new Chart(document.getElementById('cumulChart'), {\r\n            type: 'bar',\r\n            data: {\r\n                labels: [],\r\n                datasets: [{\r\n                    label: 'CO\u2082 cumul\u00e9 (g\/m\u00b2)',\r\n                    data: [],\r\n                    backgroundColor: 'rgba(220, 38, 38, 0.7)',\r\n                    borderColor: '#dc2626',\r\n                    borderWidth: 1\r\n                }, {\r\n                    label: 'CH\u2084 cumul\u00e9 (g\/m\u00b2)',\r\n                    data: [],\r\n                    backgroundColor: 'rgba(8, 145, 178, 0.7)',\r\n                    borderColor: '#0891b2',\r\n                    borderWidth: 1\r\n                }]\r\n            },\r\n            options: {\r\n                responsive: true,\r\n                maintainAspectRatio: true,\r\n                plugins: {\r\n                    legend: {\r\n                        display: true,\r\n                        position: 'top'\r\n                    }\r\n                },\r\n                scales: {\r\n                    x: {\r\n                        display: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Temps (s)'\r\n                        }\r\n                    },\r\n                    y: {\r\n                        display: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Cumul (g\/m\u00b2)'\r\n                        },\r\n                        beginAtZero: true\r\n                    }\r\n                }\r\n            }\r\n        });\r\n        \r\n        \/\/ Profile CO2 chart\r\n        const profileCO2Chart = new Chart(document.getElementById('profileCO2Chart'), {\r\n            type: 'line',\r\n            data: {\r\n                labels: [],\r\n                datasets: [{\r\n                    label: 'Concentration CO\u2082',\r\n                    data: [],\r\n                    borderColor: '#dc2626',\r\n                    backgroundColor: 'rgba(220, 38, 38, 0.2)',\r\n                    tension: 0.3,\r\n                    fill: true,\r\n                    pointRadius: 4,\r\n                    pointHoverRadius: 6\r\n                }]\r\n            },\r\n            options: {\r\n                responsive: true,\r\n                maintainAspectRatio: true,\r\n                indexAxis: 'y',\r\n                plugins: {\r\n                    legend: {\r\n                        display: false\r\n                    }\r\n                },\r\n                scales: {\r\n                    x: {\r\n                        display: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Concentration relative'\r\n                        },\r\n                        beginAtZero: true\r\n                    },\r\n                    y: {\r\n                        display: true,\r\n                        reverse: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Profondeur (cm)'\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        });\r\n        \r\n        \/\/ Profile CH4 chart\r\n        const profileCH4Chart = new Chart(document.getElementById('profileCH4Chart'), {\r\n            type: 'line',\r\n            data: {\r\n                labels: [],\r\n                datasets: [{\r\n                    label: 'Concentration CH\u2084',\r\n                    data: [],\r\n                    borderColor: '#0891b2',\r\n                    backgroundColor: 'rgba(8, 145, 178, 0.2)',\r\n                    tension: 0.3,\r\n                    fill: true,\r\n                    pointRadius: 4,\r\n                    pointHoverRadius: 6\r\n                }]\r\n            },\r\n            options: {\r\n                responsive: true,\r\n                maintainAspectRatio: true,\r\n                indexAxis: 'y',\r\n                plugins: {\r\n                    legend: {\r\n                        display: false\r\n                    }\r\n                },\r\n                scales: {\r\n                    x: {\r\n                        display: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Concentration relative'\r\n                        },\r\n                        beginAtZero: true\r\n                    },\r\n                    y: {\r\n                        display: true,\r\n                        reverse: true,\r\n                        title: {\r\n                            display: true,\r\n                            text: 'Profondeur (cm)'\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        });\r\n        \r\n        \/\/ Update profile charts\r\n        function updateProfileCharts() {\r\n            const depths = [];\r\n            const co2Concentrations = [];\r\n            const ch4Concentrations = [];\r\n            \r\n            \/\/ Simulate concentration profiles\r\n            const tempFactor = Math.pow(2.5, (temperature - 15) \/ 10);\r\n            \r\n            for (let depth = 0; depth <= 200; depth += 5) {\r\n                depths.push(depth);\r\n                \r\n                const depthM = depth \/ 100;\r\n                \r\n                \/\/ CO2 profile: higher at surface (aerobic respiration)\r\n                const co2Surface = 100 * tempFactor;\r\n                const co2Deep = 30 * tempFactor;\r\n                const co2 = co2Surface * Math.exp(-depthM * 2) + co2Deep;\r\n                co2Concentrations.push(co2);\r\n                \r\n                \/\/ CH4 profile: production below water table\r\n                let ch4;\r\n                if (depthM < waterTable) {\r\n                    \/\/ Above water table: low CH4 (oxidation)\r\n                    ch4 = 5 * Math.exp(-depthM * 10);\r\n                } else {\r\n                    \/\/ Below water table: high CH4 (methanogenesis)\r\n                    const productionDepth = depthM - waterTable;\r\n                    ch4 = 80 * tempFactor * (1 - Math.exp(-productionDepth * 3));\r\n                }\r\n                ch4Concentrations.push(ch4);\r\n            }\r\n            \r\n            profileCO2Chart.data.labels = depths;\r\n            profileCO2Chart.data.datasets[0].data = co2Concentrations;\r\n            profileCO2Chart.update('none');\r\n            \r\n            profileCH4Chart.data.labels = depths;\r\n            profileCH4Chart.data.datasets[0].data = ch4Concentrations;\r\n            profileCH4Chart.update('none');\r\n        }\r\n        \r\n        \/\/ Initial profile update\r\n        updateProfileCharts();\r\n        \r\n        let lastUpdateTime = 0;\r\n        \r\n        \/\/ Animation loop\r\n        function animate() {\r\n            requestAnimationFrame(animate);\r\n            \r\n            if (isPlaying) {\r\n                time += 0.016;\r\n            }\r\n            \r\n            \/\/ Camera rotation\r\n            camera.position.x += (Math.sin(targetRotationY) * 3 - camera.position.x) * 0.05;\r\n            camera.position.z += (Math.cos(targetRotationY) * 3 - camera.position.z) * 0.05;\r\n            camera.position.y += (2 + targetRotationX - camera.position.y) * 0.05;\r\n            camera.lookAt(0, -0.5, 0);\r\n            \r\n            \/\/ Update water table\r\n            waterMesh.position.y = -waterTable;\r\n            \r\n            \/\/ Temperature effect\r\n            const tempFactor = Math.pow(2.5, (temperature - 15) \/ 10);\r\n            \r\n            \/\/ Update CO2 particles\r\n            particlesCO2.forEach(p => {\r\n                p.visible = showCO2;\r\n                if (!showCO2) return;\r\n                \r\n                p.position.y += p.userData.velocity * tempFactor;\r\n                p.userData.age++;\r\n                \r\n                p.position.x = p.userData.baseX + Math.sin(time * 2 + p.userData.age * 0.1) * 0.02;\r\n                p.position.z = p.userData.baseZ + Math.cos(time * 2 + p.userData.age * 0.1) * 0.02;\r\n                \r\n                if (p.position.y > 0.5 || p.userData.age > p.userData.maxAge) {\r\n                    p.position.x = (Math.random() - 0.5) * 1.8;\r\n                    p.position.z = (Math.random() - 0.5) * 1.8;\r\n                    p.position.y = -Math.random() * 2.3;\r\n                    p.userData.age = 0;\r\n                    p.userData.baseX = p.position.x;\r\n                    p.userData.baseZ = p.position.z;\r\n                }\r\n                \r\n                p.material.opacity = 0.7 * Math.max(0, 1 - p.position.y \/ 0.5);\r\n            });\r\n            \r\n            \/\/ Update CH4 particles\r\n            particlesCH4.forEach(p => {\r\n                p.visible = showCH4;\r\n                if (!showCH4) return;\r\n                \r\n                if (p.position.y < -waterTable) {\r\n                    p.userData.velocity = 0.004 + Math.random() * 0.002;\r\n                } else {\r\n                    p.userData.velocity *= 0.7;\r\n                }\r\n                \r\n                p.position.y += p.userData.velocity * tempFactor * 1.2;\r\n                p.userData.age++;\r\n                \r\n                p.position.x = p.userData.baseX + Math.sin(time * 3 + p.userData.age * 0.15) * 0.03;\r\n                p.position.z = p.userData.baseZ + Math.cos(time * 3 + p.userData.age * 0.15) * 0.03;\r\n                \r\n                if (p.position.y > 0.5 || p.userData.age > p.userData.maxAge) {\r\n                    p.position.x = (Math.random() - 0.5) * 1.8;\r\n                    p.position.z = (Math.random() - 0.5) * 1.8;\r\n                    p.position.y = -waterTable - Math.random() * 1.5;\r\n                    p.userData.age = 0;\r\n                    p.userData.baseX = p.position.x;\r\n                    p.userData.baseZ = p.position.z;\r\n                }\r\n                \r\n                p.material.opacity = 0.7 * Math.max(0, 1 - p.position.y \/ 0.5);\r\n            });\r\n            \r\n            \/\/ Animate arrows\r\n            arrowsCO2.forEach((arrow, i) => {\r\n                arrow.visible = showCO2;\r\n                if (showCO2) {\r\n                    arrow.setLength(0.3 + Math.sin(time * 2 + i) * 0.15 * tempFactor);\r\n                }\r\n            });\r\n            \r\n            arrowsCH4.forEach((arrow, i) => {\r\n                arrow.visible = showCH4;\r\n                if (showCH4) {\r\n                    arrow.setLength(0.25 + Math.sin(time * 3 + i * 0.5) * 0.12 * tempFactor);\r\n                }\r\n            });\r\n            \r\n            \/\/ Update flux display\r\n            const fluxCO2 = 120 + Math.sin(time * 0.5) * 30 + (temperature - 15) * 8;\r\n            const fluxCH4 = 45 + Math.sin(time * 0.7) * 15 + (temperature - 15) * 3.5;\r\n            const prg = fluxCO2 + 28 * fluxCH4;\r\n            \r\n            document.getElementById('fluxCO2').textContent = fluxCO2.toFixed(1);\r\n            document.getElementById('fluxCH4').textContent = fluxCH4.toFixed(1);\r\n            document.getElementById('fluxPRG').textContent = prg.toFixed(0);\r\n            \r\n            \/\/ Update charts every second\r\n            if (time - lastUpdateTime >= 1.0) {\r\n                lastUpdateTime = time;\r\n                \r\n                \/\/ Add data to flux history\r\n                const timeLabel = Math.floor(time);\r\n                fluxHistory.time.push(timeLabel);\r\n                fluxHistory.co2.push(fluxCO2);\r\n                fluxHistory.ch4.push(fluxCH4);\r\n                fluxHistory.prg.push(prg);\r\n                \r\n                \/\/ Keep only last maxDataPoints\r\n                if (fluxHistory.time.length > maxDataPoints) {\r\n                    fluxHistory.time.shift();\r\n                    fluxHistory.co2.shift();\r\n                    fluxHistory.ch4.shift();\r\n                    fluxHistory.prg.shift();\r\n                }\r\n                \r\n                \/\/ Update cumulative data (convert mg\/m\u00b2\/h to g\/m\u00b2 per second)\r\n                cumulData.co2 += (fluxCO2 \/ 1000) \/ 3600; \/\/ mg to g, hour to second\r\n                cumulData.ch4 += (fluxCH4 \/ 1000) \/ 3600;\r\n                cumulData.time.push(timeLabel);\r\n                cumulData.co2History.push(cumulData.co2);\r\n                cumulData.ch4History.push(cumulData.ch4);\r\n                \r\n                if (cumulData.time.length > maxDataPoints) {\r\n                    cumulData.time.shift();\r\n                    cumulData.co2History.shift();\r\n                    cumulData.ch4History.shift();\r\n                }\r\n                \r\n                \/\/ Update flux chart\r\n                fluxChart.data.labels = fluxHistory.time;\r\n                fluxChart.data.datasets[0].data = fluxHistory.co2;\r\n                fluxChart.data.datasets[1].data = fluxHistory.ch4;\r\n                fluxChart.update('none');\r\n                \r\n                \/\/ Update cumulative chart\r\n                cumulChart.data.labels = cumulData.time;\r\n                cumulChart.data.datasets[0].data = cumulData.co2History;\r\n                cumulChart.data.datasets[1].data = cumulData.ch4History;\r\n                cumulChart.update('none');\r\n            }\r\n            \r\n            renderer.render(scene, camera);\r\n        }\r\n        \r\n        animate();\r\n        \r\n        \/\/ Event listeners\r\n        document.getElementById('playPauseBtn').addEventListener('click', function() {\r\n            isPlaying = !isPlaying;\r\n            this.textContent = isPlaying ? '\u23f8 Pause' : '\u25b6 Lecture';\r\n        });\r\n        \r\n        document.getElementById('resetBtn').addEventListener('click', () => {\r\n            time = 0;\r\n            lastUpdateTime = 0;\r\n            \r\n            \/\/ Reset flux history\r\n            fluxHistory.time = [];\r\n            fluxHistory.co2 = [];\r\n            fluxHistory.ch4 = [];\r\n            fluxHistory.prg = [];\r\n            \r\n            \/\/ Reset cumulative data\r\n            cumulData.time = [];\r\n            cumulData.co2 = 0;\r\n            cumulData.ch4 = 0;\r\n            cumulData.co2History = [];\r\n            cumulData.ch4History = [];\r\n            \r\n            \/\/ Update charts\r\n            fluxChart.data.labels = [];\r\n            fluxChart.data.datasets[0].data = [];\r\n            fluxChart.data.datasets[1].data = [];\r\n            fluxChart.update();\r\n            \r\n            cumulChart.data.labels = [];\r\n            cumulChart.data.datasets[0].data = [];\r\n            cumulChart.data.datasets[1].data = [];\r\n            cumulChart.update();\r\n        });\r\n        \r\n        document.getElementById('infoBtn').addEventListener('click', function() {\r\n            const overlay = document.getElementById('info-overlay');\r\n            overlay.classList.toggle('visible');\r\n            this.textContent = overlay.classList.contains('visible') ? '\u2139 Masquer l\\'aide' : '\u2139 Afficher l\\'aide';\r\n        });\r\n        \r\n        document.getElementById('waterTableSlider').addEventListener('input', function() {\r\n            waterTable = parseFloat(this.value) \/ 100;\r\n            document.getElementById('waterTableValue').textContent = this.value;\r\n            updateProfileCharts();\r\n        });\r\n        \r\n        document.getElementById('temperatureSlider').addEventListener('input', function() {\r\n            temperature = parseInt(this.value);\r\n            document.getElementById('temperatureValue').textContent = this.value;\r\n            updateProfileCharts();\r\n        });\r\n        \r\n        document.getElementById('showCO2').addEventListener('change', function() {\r\n            showCO2 = this.checked;\r\n        });\r\n        \r\n        document.getElementById('showCH4').addEventListener('change', function() {\r\n            showCH4 = this.checked;\r\n        });\r\n        \r\n        \/\/ Tab switching\r\n        document.querySelectorAll('.tab').forEach(tab => {\r\n            tab.addEventListener('click', function() {\r\n                const targetTab = this.dataset.tab;\r\n                \r\n                \/\/ Update active tab\r\n                document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\r\n                this.classList.add('active');\r\n                \r\n                \/\/ Update active panel\r\n                document.querySelectorAll('.chart-panel').forEach(panel => {\r\n                    panel.classList.remove('active');\r\n                });\r\n                document.getElementById(targetTab + '-panel').classList.add('active');\r\n            });\r\n        });\r\n        \r\n        \/\/ Handle resize\r\n        window.addEventListener('resize', () => {\r\n            camera.aspect = canvas.clientWidth \/ canvas.clientHeight;\r\n            camera.updateProjectionMatrix();\r\n            renderer.setSize(canvas.clientWidth, canvas.clientHeight);\r\n        });\r\n    <\/script>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-11711","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages\/11711","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spgoo.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11711"}],"version-history":[{"count":1,"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages\/11711\/revisions"}],"predecessor-version":[{"id":11712,"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages\/11711\/revisions\/11712"}],"wp:attachment":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11711"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}