Spaces:
Running on Zero
Running on Zero
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cyclic Voltammetry Parameter Extraction & Physical Model Fitting</title> | |
| <link rel="stylesheet" href="style.css"> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.plot.ly/plotly-2.27.0.min.js" charset="utf-8"></script> | |
| </head> | |
| <body> | |
| <!-- Top Academic Navigation Bar --> | |
| <nav class="top-nav"> | |
| <div class="nav-container"> | |
| <div class="nav-brand"> | |
| <span class="brand-title">Cyclic Voltammetry Analysis</span> | |
| <span class="brand-subtitle">Physical Model & Non-Linear JAX Parameter Extraction</span> | |
| </div> | |
| <div class="nav-links"> | |
| <div class="engine-badge" id="engine-status-badge"> | |
| <span class="status-dot online" id="engine-dot"></span> | |
| <span class="engine-text" id="engine-label">JAX Optimization Engine Ready</span> | |
| </div> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Main Fluid Layout Container --> | |
| <main class="app-container"> | |
| <div class="content-grid"> | |
| <!-- Left Sidebar: Parameters & Data Configuration --> | |
| <aside class="controls-column"> | |
| <section class="panel control-panel"> | |
| <header class="panel-header"> | |
| <h2>Experiment Configuration</h2> | |
| </header> | |
| <div class="panel-body"> | |
| <form id="cv-form" onsubmit="window.handleFormSubmit && window.handleFormSubmit(event);"> | |
| <!-- File Upload Area --> | |
| <div class="file-upload-wrapper" id="drop-zone" | |
| ondragover="event.preventDefault(); this.classList.add('dragover');" | |
| ondragleave="this.classList.remove('dragover');" | |
| ondrop="event.preventDefault(); this.classList.remove('dragover'); window.handleCSVDrop && window.handleCSVDrop(event);"> | |
| <label class="section-label">Voltammetry Data File (.csv)</label> | |
| <div class="file-upload-box" onclick="document.getElementById('csv-file-input').click();"> | |
| <div class="file-icon">📄</div> | |
| <span class="btn-file-select">Choose CSV Data</span> | |
| <span class="drag-hint">or drop file here</span> | |
| </div> | |
| <div class="file-status-row"> | |
| <span id="file-name-display">No file selected</span> | |
| </div> | |
| <input type="file" id="csv-file-input" class="hidden-file-input" accept=".csv,.txt,.dat" onchange="window.handleCSVFileChange && window.handleCSVFileChange(this);" /> | |
| </div> | |
| <!-- Generalized 2-Column Selectors --> | |
| <fieldset class="form-section"> | |
| <legend>Data Columns</legend> | |
| <div class="input-grid"> | |
| <div class="form-group"> | |
| <label for="pot_col">Potential (<i>V</i>) Column</label> | |
| <select id="pot_col" name="pot_col" class="academic-dropdown" onchange="window.updateLivePreviewFromColumns && window.updateLivePreviewFromColumns();"> | |
| <option value="0">Column 0</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="cur_col">Current (<i>I</i>) Column</label> | |
| <select id="cur_col" name="cur_col" class="academic-dropdown" onchange="window.updateLivePreviewFromColumns && window.updateLivePreviewFromColumns();"> | |
| <option value="1">Column 1</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="column-meta-bar" id="column-meta-bar"> | |
| <span id="detected-columns-text">Upload a file to map columns</span> | |
| </div> | |
| <!-- Live Data Stats Preview --> | |
| <div class="col-stats-preview" id="col-stats-preview"> | |
| <div class="stat-pill"> | |
| <span class="stat-k">V Range:</span> | |
| <span class="stat-v" id="stat-v-range">-</span> | |
| </div> | |
| <div class="stat-pill"> | |
| <span class="stat-k">I Range:</span> | |
| <span class="stat-v" id="stat-i-range">-</span> | |
| </div> | |
| <div class="stat-pill"> | |
| <span class="stat-k">Data Points:</span> | |
| <span class="stat-v" id="stat-points-count">-</span> | |
| </div> | |
| </div> | |
| </fieldset> | |
| <!-- Physical & Experimental Parameters --> | |
| <fieldset class="form-section"> | |
| <legend>Physical & Electrochemical Parameters</legend> | |
| <div class="input-grid"> | |
| <div class="form-group"> | |
| <label for="scan_rate">Scan Rate <i>v</i> (V/s)</label> | |
| <input type="number" step="any" id="scan_rate" name="scan_rate" value="0.010" required /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="film_thickness">Film Thickness <i>L</i> (cm)</label> | |
| <input type="number" step="any" id="film_thickness" name="film_thickness" value="0.0001" required /> | |
| </div> | |
| </div> | |
| <div class="input-grid" style="margin-top: 0.65rem;"> | |
| <div class="form-group"> | |
| <label for="v_min">Potential Range <i>V</i><sub>min</sub> (V)</label> | |
| <input type="number" step="any" id="v_min" name="v_min" value="-1.0" required /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="v_max">Potential Range <i>V</i><sub>max</sub> (V)</label> | |
| <input type="number" step="any" id="v_max" name="v_max" value="1.0" required /> | |
| </div> | |
| </div> | |
| </fieldset> | |
| <!-- Advanced Computational Hyperparameters (Collapsible) --> | |
| <div class="advanced-section"> | |
| <button type="button" class="advanced-toggle" id="advanced-toggle" onclick="window.toggleAdvanced && window.toggleAdvanced();" aria-expanded="false"> | |
| <span>Solver Hyperparameters & JAX Grid</span> | |
| <span class="toggle-icon" id="toggle-icon">▼</span> | |
| </button> | |
| <div class="advanced-content hidden" id="advanced-content"> | |
| <fieldset class="form-section sub-section"> | |
| <div class="input-grid"> | |
| <div class="form-group"> | |
| <label for="num_peaks">DOS Sub-Bands (<i>N</i>)</label> | |
| <input type="number" id="num_peaks" name="num_peaks" value="50" min="5" max="200" /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="num_terms">Fourier PDE Terms</label> | |
| <input type="number" id="num_terms" name="num_terms" value="50" min="10" max="200" /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="skip_factor">Downsample Factor</label> | |
| <input type="number" id="skip_factor" name="skip_factor" value="5" min="1" max="50" /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="max_iter">L-BFGS-B Iterations</label> | |
| <input type="number" id="max_iter" name="max_iter" value="100" min="10" max="500" /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="tol_ftol">Tolerance (<i>f_tol</i>)</label> | |
| <input type="number" step="any" id="tol_ftol" name="tol_ftol" value="1e-8" /> | |
| </div> | |
| <div class="form-group"> | |
| <label for="loss_weight_const">Weight Constant</label> | |
| <input type="number" step="any" id="loss_weight_const" name="loss_weight_const" value="1.0" /> | |
| </div> | |
| </div> | |
| </fieldset> | |
| </div> | |
| </div> | |
| <!-- Execution Button --> | |
| <button type="submit" id="submit-btn" class="btn-primary"> | |
| Execute Optimization | |
| </button> | |
| </form> | |
| </div> | |
| </section> | |
| </aside> | |
| <!-- Right Main Area: Primary Plots & Diagnostics --> | |
| <section class="visualizations-column"> | |
| <!-- Status & Primary Voltammogram Canvas --> | |
| <div class="panel main-chart-panel"> | |
| <header class="panel-header status-header-row"> | |
| <div class="status-left"> | |
| <div class="spinner hidden" id="status-spinner"></div> | |
| <div> | |
| <h2 id="status-stage">System Ready</h2> | |
| <p id="status-details">Upload experimental voltammogram CSV data to begin parameter extraction.</p> | |
| </div> | |
| </div> | |
| </header> | |
| <div class="panel-body chart-panel-body"> | |
| <div class="chart-wrapper-large"> | |
| <div id="live-chart"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Extracted Parameters & Derived Spectra --> | |
| <div id="results-panel" class="results-container hidden"> | |
| <!-- Physical Constants & Optimization Summary --> | |
| <div class="panel"> | |
| <header class="panel-header results-header-row"> | |
| <h3>Extracted Physical Parameters</h3> | |
| <div class="export-actions"> | |
| <button type="button" class="btn-export" onclick="window.exportResultsJson && window.exportResultsJson();"> | |
| Export JSON | |
| </button> | |
| <button type="button" class="btn-export" onclick="window.exportResultsCsv && window.exportResultsCsv();"> | |
| Export CSV Curves | |
| </button> | |
| </div> | |
| </header> | |
| <div class="panel-body"> | |
| <div class="stats-grid" id="params-output"></div> | |
| </div> | |
| </div> | |
| <!-- Secondary Physical Diagnostics: DOS(V) & D(V) --> | |
| <div class="secondary-charts-grid"> | |
| <div class="panel"> | |
| <header class="panel-header"> | |
| <h3>Density of States DOS(V)</h3> | |
| </header> | |
| <div class="panel-body chart-panel-body"> | |
| <div class="chart-wrapper-diagnostic"> | |
| <div id="dos-chart"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="panel"> | |
| <header class="panel-header"> | |
| <h3>Diffusivity Profile D(V)</h3> | |
| </header> | |
| <div class="panel-body chart-panel-body"> | |
| <div class="chart-wrapper-diagnostic"> | |
| <div id="diffusivity-chart"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </div> | |
| </main> | |
| <!-- Bootstrap Trigger to ensure JS runs regardless of iframe or innerHTML injection --> | |
| <img src="data:image/svg+xml;utf8,<svg/>" style="display:none" onload="window.__initCVApp && window.__initCVApp();" onerror="window.__initCVApp && window.__initCVApp();" /> | |
| <script src="app.js"></script> | |
| </body> | |
| </html> |