Девлог #16 — Створення конструктора мостів: МСЕ в браузері

Жодної зовнішньої бібліотеки, жодного WebAssembly, жодного сервера — лише JavaScript, що складає глобальну матрицю жорсткості, розв'язує її методом Гаусса та рендерить напруження кольором у реальному часі. Ось як це працює насправді.

Що таке метод скінченних елементів?

МСЕ розбиває суцільну конструкцію на прості елементи. Для 2D-ферми (тип, що використовується в мостах) кожен елемент — це стрижень, який може сприймати лише осьове навантаження — розтягування чи стискання. Аналіз визначає, наскільки деформується кожен стрижень під зовнішнім навантаженням, а потім обчислює внутрішнє напруження.

Для ферми з $n$ вузлами система рівнянь має вигляд: $\mathbf{K} \cdot \mathbf{u} = \mathbf{f}$, де $\mathbf{K}$ — глобальна матриця жорсткості ($2n \times 2n$), $\mathbf{u}$ — вектор переміщень, а $\mathbf{f}$ — вектор прикладених сил.

Крок 1: Побудова матриці жорсткості елемента

Кожен стрижень ферми додає локальну матрицю жорсткості 4×4. Вона залежить від кута орієнтації стрижня $\theta$, площі його поперечного перерізу $A$, модуля Юнга $E$ та довжини $L$:

function elementStiffness(nodeA, nodeB, E, A) { const dx = nodeB.x - nodeA.x; const dy = nodeB.y - nodeA.y; const L = Math.hypot(dx, dy); const c = dx / L; // cos θ const s = dy / L; // sin θ const k = E * A / L; // Локальна матриця 4×4 [ux_i, uy_i, ux_j, uy_j] return [ [ c*c, c*s, -c*c, -c*s], [ c*s, s*s, -c*s, -s*s], [-c*c, -c*s, c*c, c*s], [-c*s, -s*s, c*s, s*s], ].map(row => row.map(v => v * k)); }

Крок 2: Складання глобальної матриці жорсткості

Матриця 4×4 кожного елемента "штампується" у відповідні позиції глобальної матриці розміром $2n \times 2n$. Вузол $i$ займає ступені вільності $[2i, 2i+1]$ (x та y):

function assembleK(nodes, elements, E, A) { const n = nodes.length; const dof = n * 2; const K = Array.from({ length: dof }, () => new Float64Array(dof)); for (const el of elements) { const ke = elementStiffness(nodes[el.i], nodes[el.j], E, A); const idx = [el.i*2, el.i*2+1, el.j*2, el.j*2+1]; for (let r = 0; r < 4; r++) for (let c = 0; c < 4; c++) K[idx[r]][idx[c]] += ke[r][c]; } return K; }

Крок 3: Застосування граничних умов

Закріплені вузли (опори) мають відоме переміщення (нульове). Ми закладаємо це, обнуляючи рядок і стовпець для кожного закріпленого ступеня вільності та встановлюючи діагональ у 1:

for (const dof of fixedDofs) { for (let i = 0; i < K.length; i++) { K[dof][i] = 0; K[i][dof] = 0; } K[dof][dof] = 1; f[dof] = 0; // немає переміщення на опорі }

Крок 4: Розв'язувач методом Гаусса

Із застосованими граничними умовами розв'язуємо $\mathbf{K} \cdot \mathbf{u} = \mathbf{f}$ методом Гаусса з частковим вибором провідного елемента. Для типового моста (50 вузлів) це виконується менш ніж за 1 мс у JavaScript:

function gaussianElimination(A, b) { const n = b.length; // Пряме виключення з частковим вибором провідного елемента for (let col = 0; col < n; col++) { // Знаходимо рядок з максимальним абсолютним значенням у цьому стовпці let maxRow = col; for (let r = col+1; r < n; r++) if (Math.abs(A[r][col]) > Math.abs(A[maxRow][col])) maxRow = r; [A[col], A[maxRow]] = [A[maxRow], A[col]]; [b[col], b[maxRow]] = [b[maxRow], b[col]]; for (let r = col+1; r < n; r++) { const factor = A[r][col] / A[col][col]; b[r] -= factor * b[col]; for (let c = col; c < n; c++) A[r][c] -= factor * A[col][c]; } } // Зворотна підстановка const x = new Float64Array(n); for (let i = n-1; i >= 0; i--) { x[i] = b[i]; for (let j = i+1; j < n; j++) x[i] -= A[i][j] * x[j]; x[i] /= A[i][i]; } return x; }

Крок 5: Обчислення напруження в елементах

Знаючи переміщення $\mathbf{u}$, осьову деформацію кожного стрижня обчислюємо як: $\varepsilon = (du_x \cdot \cos\theta + du_y \cdot \sin\theta) / L$, а напруження — як $\sigma = E \cdot \varepsilon$. Додатне значення = розтягування (стрижень витягується), від'ємне = стискання.

function memberStress(el, nodes, u, E) { const { i, j } = el; const dx = nodes[j].x - nodes[i].x; const dy = nodes[j].y - nodes[i].y; const L = Math.hypot(dx, dy); const c = dx / L, s = dy / L; const du = (u[j*2] - u[i*2]) * c + (u[j*2+1] - u[i*2+1]) * s; return E * du / L; // σ = E·ε }

Крок 6: Кольорове кодування напружень у Three.js

Значення напруження кожного стрижня перетворюється на колір: синій (максимальне стискання) → білий (нульове навантаження) → червоний (максимальне розтягування).

0
+
СтисканняНульРозтягування
function stressToColor(sigma, maxSigma) { const t = clamp(sigma / maxSigma, -1, 1); if (t < 0) return new THREE.Color().lerpColors(white, blue, -t); else return new THREE.Color().lerpColors(white, red, t); }

Візуалізація деформації (масштаб 50×)

Реальні прогини сталевих мостів під навантаженням вимірюються міліметрами — вони непомітні у звичайному масштабі. Ми застосовуємо настроюваний коефіцієнт посилення (за замовчуванням 50×) до переміщень вузлів, щоб деформація була видимою без спотворення геометрії. Стрижень залишається з'єднаним з (масштабованими) зміщеними вузлами:

const DEFORM_SCALE = 50; nodes.forEach((node, i) => { node.displayX = node.x + u[i*2] * DEFORM_SCALE; node.displayY = node.y + u[i*2+1] * DEFORM_SCALE; });

Готові типи мостів (пресети)

Ферма Уоррена
Діагональні елементи, що чергуються, під кутом 60°. Ефективне використання матеріалу, рівномірний розподіл напружень.
Найрівномірніший розподіл напружень
Ферма Пратта
Вертикальні елементи працюють на стискання, діагональні — на розтягування. Перевага для великих прольотів.
Чудово для великих навантажень у центрі
Ферма Гау
Протилежність фермі Пратта — діагоналі працюють на стискання. Менш ефективна для сталі, але підходить для дерева.
Класична форма XIX століття

Примітка щодо точності: Наша реалізація обробляє лише 2D-елементи ферми. Реальні мости потребують 3D-балкових елементів (Ейлера-Бернуллі) для врахування згинальних моментів. Для навчальної мети — демонстрації складання матриці жорсткості та розв'язання МСЕ — 2D-аналіз ферми є точним і швидким у браузері.

Спробуйте зараз

Відкрийте Конструктор мостів, оберіть пресет, прикладіть навантаження в різних вузлах і спостерігайте, як кольорове кодування напружень і деформація оновлюються в реальному часі. Панель коефіцієнта запасу міцності стає червоною, коли будь-який елемент перевищує межу текучості.