fix speed and ETA

This commit is contained in:
2025-12-21 09:19:40 +01:00
parent cfb031aee6
commit abaab3d89b
3 changed files with 57 additions and 50 deletions

View File

@ -23,7 +23,7 @@ This tool implements cell matching algorithms based on research findings about l
> **Internal resistance matching for parallel-connected lithium-ion cells and impacts on battery pack cycle life** > **Internal resistance matching for parallel-connected lithium-ion cells and impacts on battery pack cycle life**
> >
> Shi et al., Journal of Power Sources (2013) > Wang et al., Journal of Power Sources (2013)
> DOI: [10.1016/j.jpowsour.2013.11.064](https://doi.org/10.1016/j.jpowsour.2013.11.064) > DOI: [10.1016/j.jpowsour.2013.11.064](https://doi.org/10.1016/j.jpowsour.2013.11.064)
Key findings: Key findings:
@ -60,14 +60,6 @@ Each cell requires:
- **Capacity**: Measured capacity in mAh - **Capacity**: Measured capacity in mAh
- **Internal Resistance** (optional): Measured IR in mΩ - **Internal Resistance** (optional): Measured IR in mΩ
### Algorithm Selection
| Algorithm | Best For | Speed |
|-----------|----------|-------|
| Genetic Algorithm | Most cases, large pools | Fast |
| Simulated Annealing | Avoiding local optima | Medium |
| Exhaustive | Small configs (<8 cells) | Slow |
### Matching Weights ### Matching Weights
- **Capacity Weight**: Importance of matching parallel group capacities - **Capacity Weight**: Importance of matching parallel group capacities

View File

@ -368,11 +368,7 @@ function updateProgress(progress) {
DOM.progressSpeed.textContent = `${formatNumber(Math.round(progress.iterationsPerSecond))}/s`; DOM.progressSpeed.textContent = `${formatNumber(Math.round(progress.iterationsPerSecond))}/s`;
} }
if (progress.eta > 0 && progress.eta < Infinity) {
DOM.progressEta.textContent = formatDuration(progress.eta); DOM.progressEta.textContent = formatDuration(progress.eta);
} else if (progress.iteration >= progress.maxIterations * 0.9) {
DOM.progressEta.textContent = 'Almost done...';
}
if (progress.currentBest && progress.currentBest.config) { if (progress.currentBest && progress.currentBest.config) {
renderLivePreview(progress.currentBest.config); renderLivePreview(progress.currentBest.config);

View File

@ -102,36 +102,51 @@ function calculateScore(configuration, capacityWeight = 0.7, irWeight = 0.3) {
class StatsTracker { class StatsTracker {
constructor() { constructor() {
this.startTime = Date.now(); this.startTime = Date.now();
this.iterationTimes = []; this.lastProgressTime = this.startTime;
this.lastIterationTime = this.startTime; this.lastProgressIteration = 0;
this.windowSize = 100; // Rolling window for time estimates this.speedHistory = [];
} this.windowSize = 5; // Rolling window for speed averaging
this.lastEta = null;
recordIteration() { this.lastEtaUpdate = 0;
const now = Date.now();
const iterTime = now - this.lastIterationTime;
this.lastIterationTime = now;
this.iterationTimes.push(iterTime);
if (this.iterationTimes.length > this.windowSize) {
this.iterationTimes.shift();
}
} }
getStats(currentIteration, maxIterations) { getStats(currentIteration, maxIterations) {
const elapsedTime = Date.now() - this.startTime; const now = Date.now();
const avgIterTime = this.iterationTimes.length > 0 const elapsedTime = now - this.startTime;
? this.iterationTimes.reduce((a, b) => a + b, 0) / this.iterationTimes.length
// Calculate speed based on iterations since last progress update
const iterationsDelta = currentIteration - this.lastProgressIteration;
const timeDelta = now - this.lastProgressTime;
if (timeDelta > 0 && iterationsDelta > 0) {
const currentSpeed = (iterationsDelta / timeDelta) * 1000; // iterations per second
this.speedHistory.push(currentSpeed);
if (this.speedHistory.length > this.windowSize) {
this.speedHistory.shift();
}
}
this.lastProgressTime = now;
this.lastProgressIteration = currentIteration;
// Average speed from history
const avgSpeed = this.speedHistory.length > 0
? this.speedHistory.reduce((a, b) => a + b, 0) / this.speedHistory.length
: 0; : 0;
const remainingIterations = maxIterations - currentIteration; const remainingIterations = maxIterations - currentIteration;
const eta = avgIterTime * remainingIterations; const eta = avgSpeed > 0 ? (remainingIterations / avgSpeed) * 1000 : 0;
// Only update ETA every 500ms to avoid flickering
if (now - this.lastEtaUpdate > 500 || this.lastEta === null) {
this.lastEta = eta;
this.lastEtaUpdate = now;
}
return { return {
elapsedTime, elapsedTime,
avgIterTime, eta: this.lastEta,
eta, iterationsPerSecond: avgSpeed
iterationsPerSecond: avgIterTime > 0 ? 1000 / avgIterTime : 0
}; };
} }
} }
@ -249,10 +264,13 @@ class ExhaustiveSearch {
} }
iteration++; iteration++;
this.stats.recordIteration();
// Check frequently for stop and send progress updates every 100 iterations // Check for stop every 100 iterations, but only send progress updates every 200ms
if (iteration % 100 === 0) { if (iteration % 100 === 0) {
const now = Date.now();
const timeSinceLastProgress = now - this.stats.lastProgressTime;
if (timeSinceLastProgress >= 200) {
const stats = this.stats.getStats(iteration, Math.min(totalCombinations, this.maxIterations)); const stats = this.stats.getStats(iteration, Math.min(totalCombinations, this.maxIterations));
self.postMessage({ self.postMessage({
@ -268,6 +286,7 @@ class ExhaustiveSearch {
} }
}); });
} }
}
if (iteration >= this.maxIterations) { if (iteration >= this.maxIterations) {
return this.returnBestResult(iteration, totalCombinations); return this.returnBestResult(iteration, totalCombinations);