Remove código morto, eliminar console.log e deduplicate catálogo de parâmetros
- Remove solve.js (código morto que referenciava globais inexistentes) - Remove console.log de PSO.run() e comentários de debug - Exporta parameterCatalog, modelParameters e getParamDisplayInfo de search.js - index.html passa a importar esses símbolos em vez de redefiní-los Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
125a899b60
commit
eed063f16c
5 changed files with 89 additions and 165 deletions
85
CLAUDE.md
Normal file
85
CLAUDE.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What is BioLab
|
||||
|
||||
BioLab Parameter Explorer is a scientific web application that estimates kinetic parameters for microbial growth and substrate consumption models. Given time-series measurements of biomass (cells, g/L) and residual substrate (g/L), it runs Particle Swarm Optimization (PSO) over seven built-in kinetic models and ranks them by the Akaike Information Criterion (AIC).
|
||||
|
||||
The app is packaged with Apache Cordova and targets Electron (desktop), Android, and Browser.
|
||||
|
||||
## Commands
|
||||
|
||||
### Tests (run from `www/`)
|
||||
|
||||
```sh
|
||||
cd www && node tests/demo.mjs
|
||||
cd www && node tests/synthetic-search.mjs
|
||||
# or both at once:
|
||||
cd www && npm test
|
||||
```
|
||||
|
||||
Tests use Node.js directly — no test runner or build step needed.
|
||||
|
||||
### Run / build (Electron)
|
||||
|
||||
```sh
|
||||
cordova run electron # dev run
|
||||
cordova build electron --release --verbose # release build
|
||||
```
|
||||
|
||||
### Run on Android
|
||||
|
||||
```sh
|
||||
# Set up environment first (or source env.sh):
|
||||
source env.sh
|
||||
# env.sh sets ANDROID_HOME, ANDROID_SDK_ROOT, PATH entries for the SDK,
|
||||
# and isolates Gradle cache to ./.gradle, then calls `cordova run android`.
|
||||
```
|
||||
|
||||
### Serve in browser
|
||||
|
||||
```sh
|
||||
cordova run browser
|
||||
# or just open www/index.html via any static file server
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
www/
|
||||
index.html — single-page app; all UI logic is an inline <script type="module">
|
||||
src/
|
||||
conhecidos.js — pure functions: the seven kinetic µ(S) equations + Pirt
|
||||
runge-kutta.js — RK4 solver (RK4) and point interpolator (RK4getvalue)
|
||||
Objective.js — Objective class: wraps experimental data + ODE, computes normalized SSR
|
||||
PSO.js — PSO class: initializes swarm, runs iterations, exposes pos_best_g / err_best_g
|
||||
search.js — orchestrates everything: builds ODE functions (model × Pirt coupling),
|
||||
runs PSO per model, renders Plotly charts, computes AIC, sorts results
|
||||
rrandom.js — Math.random wrapper used by PSO
|
||||
solve.js — standalone legacy helper (not imported by search.js)
|
||||
tests/
|
||||
demo.mjs — smoke test: PSO converges to a finite error
|
||||
synthetic-search.mjs — accuracy test: PSO recovers known Monod params from synthetic data
|
||||
assets/
|
||||
dados.json — default experimental dataset loaded on page start
|
||||
```
|
||||
|
||||
### Data flow
|
||||
|
||||
1. `index.html` collects experimental data, PSO hyperparameters, and per-model parameter bounds from the form.
|
||||
2. It calls `main(data, { alg, bounds, onProgress })` exported from `search.js`.
|
||||
3. For each of the seven models, `search.js` constructs an ODE function (`<model>Pirt`) that couples a growth-rate formula from `conhecidos.js` with the Pirt substrate consumption equation.
|
||||
4. An `Objective` instance wraps the ODE and experimental data. It normalizes residuals by column mean before summing squared errors — this makes SSR dimensionless and comparable across variables with different scales.
|
||||
5. `PSO` minimizes the objective over the parameter bounds, producing `pos_best_g` (best parameter vector).
|
||||
6. RK4 integrates the ODE at 500 internal steps; `RK4getvalue` interpolates from that solution to exact experimental time points.
|
||||
7. After all models complete, results are sorted by AIC and a comparison table is rendered.
|
||||
|
||||
### Key design constraints
|
||||
|
||||
- The frontend is **no-build**: ES modules loaded directly in the browser; no bundler.
|
||||
- `www/package.json` has `"type": "module"` so Node can run the tests with native ESM imports.
|
||||
- External CDN dependencies: KaTeX (math rendering), Plotly (charts). Both are loaded in `index.html`; `search.js` assumes `Plotly` and `katex` are globals.
|
||||
- The `Objective` constructor detects column order from header keywords (Portuguese and English), so column order in the data table matters only when headers are absent or unrecognized.
|
||||
- `PSO` uses `Math.random` directly — `synthetic-search.mjs` patches it with a deterministic LCG for reproducible test results.
|
||||
- `solve.js` is a legacy standalone function that references globals (`RK4`, `Plotly`, `document`) and is not imported anywhere in the current codebase.
|
||||
113
www/index.html
113
www/index.html
|
|
@ -253,7 +253,7 @@
|
|||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { main } from "./src/search.js";
|
||||
import { main, parameterCatalog, modelParameters, getParamDisplayInfo } from "./src/search.js";
|
||||
|
||||
const measurementUnits = {
|
||||
time: "h",
|
||||
|
|
@ -267,117 +267,6 @@
|
|||
`cells (${measurementUnits.cells})`,
|
||||
];
|
||||
|
||||
const parameterCatalog = {
|
||||
K_S: {
|
||||
latex: "K_S",
|
||||
unitText: "g/L",
|
||||
unitLatex: "\\mathrm{g\\,L^{-1}}",
|
||||
overrides: {
|
||||
contois: {
|
||||
unitText: "g_S/g_X",
|
||||
unitLatex: "\\frac{\\mathrm{g}_{S}}{\\mathrm{g}_{X}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
mu_max: {
|
||||
latex: "\\mu_{max}",
|
||||
unitText: "h⁻¹",
|
||||
unitLatex: "\\mathrm{h^{-1}}",
|
||||
},
|
||||
K_I: {
|
||||
latex: "K_I",
|
||||
unitText: "g/L",
|
||||
unitLatex: "\\mathrm{g\\,L^{-1}}",
|
||||
overrides: {
|
||||
aiba: {
|
||||
unitText: "L/g",
|
||||
unitLatex: "\\mathrm{L\\,g^{-1}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
m_S: {
|
||||
latex: "m_S",
|
||||
unitText: "g_S/(g_X·h)",
|
||||
unitLatex: "\\frac{\\mathrm{g}_{S}}{\\mathrm{g}_{X}\\,\\mathrm{h}}",
|
||||
},
|
||||
Y_XS: {
|
||||
latex: "Y_{XS}",
|
||||
unitText: "g_X/g_S",
|
||||
unitLatex: "\\frac{\\mathrm{g}_{X}}{\\mathrm{g}_{S}}",
|
||||
},
|
||||
T: {
|
||||
latex: "T",
|
||||
unitText: "h",
|
||||
unitLatex: "\\mathrm{h}",
|
||||
},
|
||||
n: {
|
||||
latex: "n",
|
||||
unitText: null,
|
||||
unitLatex: null,
|
||||
},
|
||||
};
|
||||
|
||||
const modelParameters = {
|
||||
aiba: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
{ key: "K_I", bounds: [0.01, 1] },
|
||||
{ key: "m_S", bounds: [0.0015, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
andrews: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
{ key: "K_I", bounds: [5, 150] },
|
||||
{ key: "m_S", bounds: [0.0015, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
bergter: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
{ key: "T", bounds: [5, 80] },
|
||||
{ key: "m_S", bounds: [0.0015, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
contois: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
{ key: "m_S", bounds: [0.0015, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
monod: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
{ key: "m_S", bounds: [0.0015, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
moser: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
{ key: "n", bounds: [0.8, 2.5] },
|
||||
{ key: "m_S", bounds: [0.0015, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
tessier: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.2, 0.9] },
|
||||
{ key: "m_S", bounds: [0.005, 0.05] },
|
||||
{ key: "Y_XS", bounds: [0.3, 0.7] },
|
||||
],
|
||||
};
|
||||
|
||||
function getParamDisplayInfo(paramKey, modelKey) {
|
||||
const baseInfo = parameterCatalog[paramKey];
|
||||
if (!baseInfo) {
|
||||
throw new Error(`Unknown parameter: ${paramKey}`);
|
||||
}
|
||||
const override = baseInfo.overrides?.[modelKey];
|
||||
if (!override) {
|
||||
return baseInfo;
|
||||
}
|
||||
return { ...baseInfo, ...override };
|
||||
}
|
||||
|
||||
const demoData = [
|
||||
dataHeader,
|
||||
[0, 3.0, 0.05],
|
||||
|
|
|
|||
|
|
@ -44,12 +44,7 @@ export class PSO {
|
|||
run(c1, c2, w, iteration) {
|
||||
for (let i = 0; i < iteration; i++) {
|
||||
this.update(c1, c2, w);
|
||||
// console.log(i, "/", iteration, "\t", this.err_best_g);
|
||||
}
|
||||
|
||||
console.log(this.pos_best_g);
|
||||
// console.log(this.err_best_g)
|
||||
// console.table(this.pos)
|
||||
}
|
||||
|
||||
update(c1, c2, w) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from "./conhecidos.js";
|
||||
import "https://cdn.plot.ly/plotly-2.29.1.min.js";
|
||||
|
||||
const parameterCatalog = {
|
||||
export const parameterCatalog = {
|
||||
K_S: {
|
||||
latex: "K_S",
|
||||
unitText: "g/L",
|
||||
|
|
@ -63,7 +63,7 @@ const parameterCatalog = {
|
|||
},
|
||||
};
|
||||
|
||||
const modelParameters = {
|
||||
export const modelParameters = {
|
||||
aiba: [
|
||||
{ key: "K_S", bounds: [0.005, 2] },
|
||||
{ key: "mu_max", bounds: [0.05, 0.9] },
|
||||
|
|
@ -112,7 +112,7 @@ const modelParameters = {
|
|||
],
|
||||
};
|
||||
|
||||
function getParamDisplayInfo(paramKey, modelKey) {
|
||||
export function getParamDisplayInfo(paramKey, modelKey) {
|
||||
const baseInfo = parameterCatalog[paramKey];
|
||||
if (!baseInfo) {
|
||||
throw new Error(`Unknown parameter: ${paramKey}`);
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
function solve(tf, Ks, mu_max, m_S, Y_XS, X0, S0) {
|
||||
let res = 5000;
|
||||
let timeArray = [];
|
||||
for (let i = 0; i <= res; i++) {
|
||||
timeArray[i] = (i * tf) / res;
|
||||
}
|
||||
|
||||
let sol = RK4(model, timeArray, [X0, S0], [Ks, mu_max, m_S, Y_XS]);
|
||||
let cels = [];
|
||||
let subs = [];
|
||||
for (let i = 0; i < sol.length; i++) {
|
||||
cels[i] = sol[i][0];
|
||||
subs[i] = sol[i][1];
|
||||
}
|
||||
|
||||
TESTER = document.getElementById("tester");
|
||||
Plotly.newPlot(
|
||||
TESTER,
|
||||
[
|
||||
{
|
||||
x: timeArray,
|
||||
y: subs,
|
||||
name: "Calculated substrate",
|
||||
line: { color: "#4a90e2" },
|
||||
},
|
||||
{
|
||||
x: timeArray,
|
||||
y: cels,
|
||||
name: "Calculated cells",
|
||||
line: { color: "#50e3c2" },
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, b: 30 },
|
||||
paper_bgcolor: "#f0f4f8",
|
||||
plot_bgcolor: "#f0f4f8",
|
||||
legend: {
|
||||
orientation: "h",
|
||||
yanchor: "top",
|
||||
y: -0.2,
|
||||
font: { size: 10 },
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue