@theerapat-s28/ts-structural-eng-tools 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -1
- package/README.md +8 -5
- package/dist/core/constants/frame.constant.d.ts +15 -0
- package/dist/core/constants/frame.constant.js +16 -1
- package/dist/core/constants/frame.constant.js.map +1 -1
- package/dist/core/errors/frame-analysis.error.d.ts +1 -0
- package/dist/core/errors/frame-analysis.error.js +18 -2
- package/dist/core/errors/frame-analysis.error.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/esm/core/constants/frame.constant.js +15 -0
- package/dist/esm/core/constants/frame.constant.js.map +1 -1
- package/dist/esm/core/errors/frame-analysis.error.js +18 -2
- package/dist/esm/core/errors/frame-analysis.error.js.map +1 -1
- package/dist/esm/core/index.js +1 -1
- package/dist/esm/core/index.js.map +1 -1
- package/dist/esm/fea/frame/area/distribute-area-loads.js +10 -5
- package/dist/esm/fea/frame/area/distribute-area-loads.js.map +1 -1
- package/dist/esm/fea/frame/elements/shell/dkq-plate.js +109 -0
- package/dist/esm/fea/frame/elements/shell/dkq-plate.js.map +1 -0
- package/dist/esm/fea/frame/elements/shell/flat-shell.js +111 -0
- package/dist/esm/fea/frame/elements/shell/flat-shell.js.map +1 -0
- package/dist/esm/fea/frame/elements/shell/membrane-quad.js +107 -0
- package/dist/esm/fea/frame/elements/shell/membrane-quad.js.map +1 -0
- package/dist/esm/fea/frame/elements/shell/quad-geometry.js +127 -0
- package/dist/esm/fea/frame/elements/shell/quad-geometry.js.map +1 -0
- package/dist/esm/fea/frame/elements/shell/rigidity.js +19 -0
- package/dist/esm/fea/frame/elements/shell/rigidity.js.map +1 -0
- package/dist/esm/fea/frame/elements/shell/shell-axes.js +97 -0
- package/dist/esm/fea/frame/elements/shell/shell-axes.js.map +1 -0
- package/dist/esm/fea/frame/mesh/expand-model.js +129 -0
- package/dist/esm/fea/frame/mesh/expand-model.js.map +1 -0
- package/dist/esm/fea/frame/mesh/mesh-shells.js +239 -0
- package/dist/esm/fea/frame/mesh/mesh-shells.js.map +1 -0
- package/dist/esm/fea/frame/post/envelope.js +38 -1
- package/dist/esm/fea/frame/post/envelope.js.map +1 -1
- package/dist/esm/fea/frame/post/member-diagram.js +51 -2
- package/dist/esm/fea/frame/post/member-diagram.js.map +1 -1
- package/dist/esm/fea/frame/post/present-state.js +34 -16
- package/dist/esm/fea/frame/post/present-state.js.map +1 -1
- package/dist/esm/fea/frame/post/shell-forces.js +111 -0
- package/dist/esm/fea/frame/post/shell-forces.js.map +1 -0
- package/dist/esm/fea/frame/solver/analyze-frame.js +27 -7
- package/dist/esm/fea/frame/solver/analyze-frame.js.map +1 -1
- package/dist/esm/fea/frame/solver/auto-restraint.js +58 -10
- package/dist/esm/fea/frame/solver/auto-restraint.js.map +1 -1
- package/dist/esm/fea/frame/solver/frame-system.js +148 -40
- package/dist/esm/fea/frame/solver/frame-system.js.map +1 -1
- package/dist/esm/fea/frame/solver/global-assembly.js +5 -3
- package/dist/esm/fea/frame/solver/global-assembly.js.map +1 -1
- package/dist/esm/fea/frame/solver/load-case-solver.js +111 -11
- package/dist/esm/fea/frame/solver/load-case-solver.js.map +1 -1
- package/dist/esm/fea/frame/validation/validate-model.js +133 -3
- package/dist/esm/fea/frame/validation/validate-model.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/fea/frame/area/distribute-area-loads.d.ts +3 -2
- package/dist/fea/frame/area/distribute-area-loads.js +10 -5
- package/dist/fea/frame/area/distribute-area-loads.js.map +1 -1
- package/dist/fea/frame/elements/shell/dkq-plate.d.ts +50 -0
- package/dist/fea/frame/elements/shell/dkq-plate.js +115 -0
- package/dist/fea/frame/elements/shell/dkq-plate.js.map +1 -0
- package/dist/fea/frame/elements/shell/flat-shell.d.ts +63 -0
- package/dist/fea/frame/elements/shell/flat-shell.js +118 -0
- package/dist/fea/frame/elements/shell/flat-shell.js.map +1 -0
- package/dist/fea/frame/elements/shell/membrane-quad.d.ts +61 -0
- package/dist/fea/frame/elements/shell/membrane-quad.js +113 -0
- package/dist/fea/frame/elements/shell/membrane-quad.js.map +1 -0
- package/dist/fea/frame/elements/shell/quad-geometry.d.ts +71 -0
- package/dist/fea/frame/elements/shell/quad-geometry.js +136 -0
- package/dist/fea/frame/elements/shell/quad-geometry.js.map +1 -0
- package/dist/fea/frame/elements/shell/rigidity.d.ts +14 -0
- package/dist/fea/frame/elements/shell/rigidity.js +23 -0
- package/dist/fea/frame/elements/shell/rigidity.js.map +1 -0
- package/dist/fea/frame/elements/shell/shell-axes.d.ts +43 -0
- package/dist/fea/frame/elements/shell/shell-axes.js +102 -0
- package/dist/fea/frame/elements/shell/shell-axes.js.map +1 -0
- package/dist/fea/frame/index.d.ts +2 -2
- package/dist/fea/frame/mesh/expand-model.d.ts +29 -0
- package/dist/fea/frame/mesh/expand-model.js +133 -0
- package/dist/fea/frame/mesh/expand-model.js.map +1 -0
- package/dist/fea/frame/mesh/mesh-shells.d.ts +38 -0
- package/dist/fea/frame/mesh/mesh-shells.js +244 -0
- package/dist/fea/frame/mesh/mesh-shells.js.map +1 -0
- package/dist/fea/frame/post/envelope.js +38 -1
- package/dist/fea/frame/post/envelope.js.map +1 -1
- package/dist/fea/frame/post/member-diagram.d.ts +21 -1
- package/dist/fea/frame/post/member-diagram.js +53 -3
- package/dist/fea/frame/post/member-diagram.js.map +1 -1
- package/dist/fea/frame/post/present-state.d.ts +3 -2
- package/dist/fea/frame/post/present-state.js +33 -15
- package/dist/fea/frame/post/present-state.js.map +1 -1
- package/dist/fea/frame/post/shell-forces.d.ts +27 -0
- package/dist/fea/frame/post/shell-forces.js +116 -0
- package/dist/fea/frame/post/shell-forces.js.map +1 -0
- package/dist/fea/frame/solver/analyze-frame.d.ts +8 -4
- package/dist/fea/frame/solver/analyze-frame.js +27 -7
- package/dist/fea/frame/solver/analyze-frame.js.map +1 -1
- package/dist/fea/frame/solver/auto-restraint.d.ts +23 -10
- package/dist/fea/frame/solver/auto-restraint.js +60 -11
- package/dist/fea/frame/solver/auto-restraint.js.map +1 -1
- package/dist/fea/frame/solver/frame-system.d.ts +47 -8
- package/dist/fea/frame/solver/frame-system.js +146 -38
- package/dist/fea/frame/solver/frame-system.js.map +1 -1
- package/dist/fea/frame/solver/global-assembly.d.ts +11 -0
- package/dist/fea/frame/solver/global-assembly.js +7 -4
- package/dist/fea/frame/solver/global-assembly.js.map +1 -1
- package/dist/fea/frame/solver/load-case-solver.d.ts +11 -1
- package/dist/fea/frame/solver/load-case-solver.js +109 -9
- package/dist/fea/frame/solver/load-case-solver.js.map +1 -1
- package/dist/fea/frame/types/model.type.d.ts +95 -3
- package/dist/fea/frame/types/results.type.d.ts +61 -1
- package/dist/fea/frame/validation/validate-model.d.ts +3 -2
- package/dist/fea/frame/validation/validate-model.js +133 -3
- package/dist/fea/frame/validation/validate-model.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { zeros } from "../../../../utils/matrix.js";
|
|
2
|
+
import { dkqPlateStiffness } from "./dkq-plate.js";
|
|
3
|
+
import { membraneQuadStiffness } from "./membrane-quad.js";
|
|
4
|
+
import { shellAxes } from "./shell-axes.js";
|
|
5
|
+
/** Positions of the membrane DOFs [u, v, θz] and the plate DOFs [w, θx, θy] among a node's six. */
|
|
6
|
+
const MEMBRANE_DOFS = [0, 1, 5];
|
|
7
|
+
const PLATE_DOFS = [2, 3, 4];
|
|
8
|
+
/**
|
|
9
|
+
* Local stiffness of a flat four-node shell: the drilling membrane (`membraneQuadStiffness`) on
|
|
10
|
+
* u₁, u₂, r₃ and the DKQ plate (`dkqPlateStiffness`) on u₃, r₁, r₂ of every corner. For a flat
|
|
11
|
+
* element of uniform section the two do not couple. `"membrane"` and `"plate"` keep only their
|
|
12
|
+
* own part, leaving the other DOFs without stiffness.
|
|
13
|
+
*
|
|
14
|
+
* @param corners - The four corners in the element plane (mm), counter-clockwise
|
|
15
|
+
* @param properties - Material, thickness, type and stiffness modifiers
|
|
16
|
+
* @returns The 24×24 stiffness over `[u₁ u₂ u₃ r₁ r₂ r₃]` per corner (N and mm)
|
|
17
|
+
*/
|
|
18
|
+
export const flatShellLocalStiffness = (corners, properties) => {
|
|
19
|
+
const { E, nu, t, type } = properties;
|
|
20
|
+
const k = zeros(24, 24);
|
|
21
|
+
const scatter = (part, dofs) => {
|
|
22
|
+
for (let a = 0; a < 12; a++) {
|
|
23
|
+
const i = 6 * Math.floor(a / 3) + dofs[a % 3];
|
|
24
|
+
for (let b = 0; b < 12; b++)
|
|
25
|
+
k[i][6 * Math.floor(b / 3) + dofs[b % 3]] += part[a][b];
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
if (type !== "plate") {
|
|
29
|
+
const membrane = { E, nu, t, modifiers: properties.membraneModifiers };
|
|
30
|
+
scatter(membraneQuadStiffness(corners, membrane), MEMBRANE_DOFS);
|
|
31
|
+
}
|
|
32
|
+
if (type !== "membrane") {
|
|
33
|
+
const plate = { E, nu, t, modifiers: properties.bendingModifiers };
|
|
34
|
+
scatter(dkqPlateStiffness(corners, plate), PLATE_DOFS);
|
|
35
|
+
}
|
|
36
|
+
return k;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Per corner, the 6×6 transformation from the global DOFs at the real node to the local DOFs at
|
|
40
|
+
* its projection on the mean plane: the direction cosines λ on translations and rotations, plus
|
|
41
|
+
* a rigid link across the offset h along local 3, `u_proj = u + θ × (−h·e₃)`, which adds
|
|
42
|
+
* `−h·r₂` to u₁ and `+h·r₁` to u₂. For a flat element it is just λ.
|
|
43
|
+
*/
|
|
44
|
+
export const shellNodeTransforms = ({ lambda, offsets }) => offsets.map((h) => {
|
|
45
|
+
const T = zeros(6, 6);
|
|
46
|
+
for (let i = 0; i < 3; i++) {
|
|
47
|
+
for (let j = 0; j < 3; j++) {
|
|
48
|
+
T[i][j] = lambda[i][j];
|
|
49
|
+
T[3 + i][3 + j] = lambda[i][j];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (let j = 0; j < 3; j++) {
|
|
53
|
+
T[0][3 + j] = -h * lambda[1][j];
|
|
54
|
+
T[1][3 + j] = h * lambda[0][j];
|
|
55
|
+
}
|
|
56
|
+
return T;
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Transforms a 24×24 local shell stiffness to global DOFs block by block,
|
|
60
|
+
* `k_ab = T_aᵀ·k_local,ab·T_b`, without forming the 24×24 transformation.
|
|
61
|
+
*
|
|
62
|
+
* @param kLocal - Stiffness over the local DOFs of the projected corners
|
|
63
|
+
* @param transforms - The four corner transformations from `shellNodeTransforms`
|
|
64
|
+
*/
|
|
65
|
+
export const shellGlobalStiffness = (kLocal, transforms) => {
|
|
66
|
+
const k = zeros(24, 24);
|
|
67
|
+
for (let a = 0; a < 4; a++) {
|
|
68
|
+
for (let b = 0; b < 4; b++) {
|
|
69
|
+
const Ta = transforms[a];
|
|
70
|
+
const Tb = transforms[b];
|
|
71
|
+
// block·T_b
|
|
72
|
+
const right = zeros(6, 6);
|
|
73
|
+
for (let i = 0; i < 6; i++) {
|
|
74
|
+
for (let m = 0; m < 6; m++) {
|
|
75
|
+
const v = kLocal[6 * a + i][6 * b + m];
|
|
76
|
+
if (v === 0)
|
|
77
|
+
continue;
|
|
78
|
+
for (let j = 0; j < 6; j++)
|
|
79
|
+
right[i][j] += v * Tb[m][j];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// T_aᵀ·(block·T_b)
|
|
83
|
+
for (let i = 0; i < 6; i++) {
|
|
84
|
+
for (let m = 0; m < 6; m++) {
|
|
85
|
+
const v = Ta[m][i];
|
|
86
|
+
if (v === 0)
|
|
87
|
+
continue;
|
|
88
|
+
for (let j = 0; j < 6; j++)
|
|
89
|
+
k[6 * a + i][6 * b + j] += v * right[m][j];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return k;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* A flat four-node shell element (drilling membrane plus DKQ plate) built on the mean plane of
|
|
98
|
+
* its corners, linked rigidly to the real corners when they are not coplanar.
|
|
99
|
+
*
|
|
100
|
+
* @param points - The four corners in global axes (mm), in order around the element; local 3
|
|
101
|
+
* follows the right-hand rule around them
|
|
102
|
+
* @param properties - Material, thickness, type and stiffness modifiers
|
|
103
|
+
* @param angle - Rotation of local 1 and 2 about local 3 in rad (default 0)
|
|
104
|
+
*/
|
|
105
|
+
export const flatShellElement = (points, properties, angle = 0) => {
|
|
106
|
+
const axes = shellAxes(points, angle);
|
|
107
|
+
const transforms = shellNodeTransforms(axes);
|
|
108
|
+
const kLocal = flatShellLocalStiffness(axes.corners, properties);
|
|
109
|
+
return { axes, transforms, kLocal, kGlobal: shellGlobalStiffness(kLocal, transforms) };
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=flat-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flat-shell.js","sourceRoot":"","sources":["../../../../../../src/fea/frame/elements/shell/flat-shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { DRILLING_PENALTY_RATIO } from "../../../../core/constants/frame.constant.js";
|
|
2
|
+
import { zeros } from "../../../../utils/matrix.js";
|
|
3
|
+
import { bilinearShape, cartesianDerivatives, GAUSS_3X3, midsideShape, quadJacobian, } from "./quad-geometry.js";
|
|
4
|
+
import { isotropicRigidity } from "./rigidity.js";
|
|
5
|
+
/**
|
|
6
|
+
* Strain–displacement rows of the four-node membrane with drilling rotations at (ξ, η).
|
|
7
|
+
*
|
|
8
|
+
* Displacements are bilinear plus Allman-type edge terms: for the edge from corner i to corner
|
|
9
|
+
* j (counter-clockwise) with mid-side function N_k,
|
|
10
|
+
* `u += N_k·(y_j − y_i)/8·(θ_j − θ_i)` and `v += N_k·(x_i − x_j)/8·(θ_j − θ_i)`, a quadratic
|
|
11
|
+
* bulge along the edge's outward normal that matches the difference of the corner rotations.
|
|
12
|
+
*
|
|
13
|
+
* @param corners - The four corners in the element plane (mm), counter-clockwise
|
|
14
|
+
* @param xi - Natural coordinate ξ
|
|
15
|
+
* @param eta - Natural coordinate η
|
|
16
|
+
*/
|
|
17
|
+
export const membraneStrainRows = (corners, xi, eta) => {
|
|
18
|
+
const jacobian = quadJacobian(corners, xi, eta);
|
|
19
|
+
const corner = bilinearShape(xi, eta);
|
|
20
|
+
const cornerD = cartesianDerivatives(jacobian, corner);
|
|
21
|
+
const edgeD = cartesianDerivatives(jacobian, midsideShape(xi, eta));
|
|
22
|
+
// u,x u,y v,x v,y over the 12 DOFs
|
|
23
|
+
const ux = new Array(12).fill(0);
|
|
24
|
+
const uy = new Array(12).fill(0);
|
|
25
|
+
const vx = new Array(12).fill(0);
|
|
26
|
+
const vy = new Array(12).fill(0);
|
|
27
|
+
const rotation = new Array(12).fill(0);
|
|
28
|
+
for (let i = 0; i < 4; i++) {
|
|
29
|
+
ux[3 * i] = cornerD.dX[i];
|
|
30
|
+
uy[3 * i] = cornerD.dY[i];
|
|
31
|
+
vx[3 * i + 1] = cornerD.dX[i];
|
|
32
|
+
vy[3 * i + 1] = cornerD.dY[i];
|
|
33
|
+
rotation[3 * i + 2] = corner.N[i];
|
|
34
|
+
}
|
|
35
|
+
for (let k = 0; k < 4; k++) {
|
|
36
|
+
const i = k;
|
|
37
|
+
const j = (k + 1) % 4;
|
|
38
|
+
// (y_j − y_i, x_i − x_j) is the edge length times its outward normal
|
|
39
|
+
const cu = (corners[j][1] - corners[i][1]) / 8;
|
|
40
|
+
const cv = (corners[i][0] - corners[j][0]) / 8;
|
|
41
|
+
for (const [node, sign] of [
|
|
42
|
+
[j, 1],
|
|
43
|
+
[i, -1],
|
|
44
|
+
]) {
|
|
45
|
+
const theta = 3 * node + 2;
|
|
46
|
+
ux[theta] += sign * cu * edgeD.dX[k];
|
|
47
|
+
uy[theta] += sign * cu * edgeD.dY[k];
|
|
48
|
+
vx[theta] += sign * cv * edgeD.dX[k];
|
|
49
|
+
vy[theta] += sign * cv * edgeD.dY[k];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (let a = 0; a < 12; a++)
|
|
53
|
+
rotation[a] -= 0.5 * (vx[a] - uy[a]);
|
|
54
|
+
return { B: [ux, vy, uy.map((v, a) => v + vx[a])], rotation, detJ: jacobian.detJ };
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Plane-stress membrane rigidity `E·t/(1 − ν²)·[[1, ν, 0], [ν, 1, 0], [0, 0, (1 − ν)/2]]`
|
|
58
|
+
* (N/mm), with the stiffness modifiers applied (see `isotropicRigidity`).
|
|
59
|
+
*/
|
|
60
|
+
export const membraneRigidity = ({ E, nu, t, modifiers }) => isotropicRigidity((E * t) / (1 - nu * nu), nu, modifiers);
|
|
61
|
+
/**
|
|
62
|
+
* Local stiffness of the four-node membrane with drilling rotations (after Ibrahimbegovic,
|
|
63
|
+
* Taylor & Wilson 1990): bilinear displacements plus Allman-type edge terms, and the
|
|
64
|
+
* Hughes–Brezzi penalty `½·γ·t·∫(θ − ω)² dA` with `γ = penaltyRatio · G`.
|
|
65
|
+
*
|
|
66
|
+
* - The strain energy is integrated with 3×3 Gauss points. With 2×2 the edge terms leave a
|
|
67
|
+
* spurious zero-energy mode in rectangular elements (alternating corner rotations whose
|
|
68
|
+
* normal strains vanish exactly at the 2×2 points).
|
|
69
|
+
* - The penalty is integrated at the centre (one point). With 2×2 it over-constrains distorted
|
|
70
|
+
* elements and they lock as γ grows; at the centre it only ties the element's mean rotation
|
|
71
|
+
* to the material, so results hardly change with γ.
|
|
72
|
+
* - Stiffness modifiers scale the rigidity only; the penalty keeps the unmodified G.
|
|
73
|
+
*
|
|
74
|
+
* The DOFs are `[u₁ v₁ θ₁ … u₄ v₄ θ₄]`: in-plane displacements along the element's x and y and
|
|
75
|
+
* the rotation about its normal, right-handed, at corners listed counter-clockwise about that
|
|
76
|
+
* normal. The quad must be convex (det J > 0 throughout), which validation guarantees.
|
|
77
|
+
*
|
|
78
|
+
* @param corners - The four corners in the element plane (mm)
|
|
79
|
+
* @param properties - E (MPa), ν and thickness t (mm)
|
|
80
|
+
* @param penaltyRatio - γ / G (default `DRILLING_PENALTY_RATIO`)
|
|
81
|
+
* @returns The symmetric 12×12 stiffness in N and mm (N/mm, N, N·mm/rad); exactly three
|
|
82
|
+
* zero-energy modes, the in-plane rigid-body motions
|
|
83
|
+
*/
|
|
84
|
+
export const membraneQuadStiffness = (corners, properties, penaltyRatio = DRILLING_PENALTY_RATIO) => {
|
|
85
|
+
const D = membraneRigidity(properties);
|
|
86
|
+
const k = zeros(12, 12);
|
|
87
|
+
for (const { xi, eta, weight } of GAUSS_3X3) {
|
|
88
|
+
const { B, detJ } = membraneStrainRows(corners, xi, eta);
|
|
89
|
+
const scale = detJ * weight;
|
|
90
|
+
// Bᵀ·D·B via D·B
|
|
91
|
+
const DB = D.map((row) => B[0].map((_, a) => row[0] * B[0][a] + row[1] * B[1][a] + row[2] * B[2][a]));
|
|
92
|
+
for (let a = 0; a < 12; a++) {
|
|
93
|
+
for (let b = 0; b < 12; b++) {
|
|
94
|
+
k[a][b] += (B[0][a] * DB[0][b] + B[1][a] * DB[1][b] + B[2][a] * DB[2][b]) * scale;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// one-point rule at the centre: weight 4
|
|
99
|
+
const { rotation, detJ } = membraneStrainRows(corners, 0, 0);
|
|
100
|
+
const penalty = ((penaltyRatio * properties.E * properties.t) / (2 * (1 + properties.nu))) * detJ * 4;
|
|
101
|
+
for (let a = 0; a < 12; a++) {
|
|
102
|
+
for (let b = 0; b < 12; b++)
|
|
103
|
+
k[a][b] += penalty * rotation[a] * rotation[b];
|
|
104
|
+
}
|
|
105
|
+
return k;
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=membrane-quad.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membrane-quad.js","sourceRoot":"","sources":["../../../../../../src/fea/frame/elements/shell/membrane-quad.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/** Natural coordinates (ξ, η) of the four corners, counter-clockwise about the normal. */
|
|
2
|
+
const CORNER_XI = [-1, 1, 1, -1];
|
|
3
|
+
const CORNER_ETA = [-1, -1, 1, 1];
|
|
4
|
+
const GAUSS = 1 / Math.sqrt(3);
|
|
5
|
+
/**
|
|
6
|
+
* 2×2 Gauss rule over the parent square −1 ≤ ξ, η ≤ 1: points (±1/√3, ±1/√3), weight 1 each.
|
|
7
|
+
* Exact for polynomials up to cubic in each of ξ and η.
|
|
8
|
+
*/
|
|
9
|
+
export const GAUSS_2X2 = [
|
|
10
|
+
{ xi: -GAUSS, eta: -GAUSS, weight: 1 },
|
|
11
|
+
{ xi: GAUSS, eta: -GAUSS, weight: 1 },
|
|
12
|
+
{ xi: GAUSS, eta: GAUSS, weight: 1 },
|
|
13
|
+
{ xi: -GAUSS, eta: GAUSS, weight: 1 },
|
|
14
|
+
];
|
|
15
|
+
const GAUSS_3 = Math.sqrt(0.6);
|
|
16
|
+
const GAUSS_3_POINTS = [
|
|
17
|
+
{ at: -GAUSS_3, weight: 5 / 9 },
|
|
18
|
+
{ at: 0, weight: 8 / 9 },
|
|
19
|
+
{ at: GAUSS_3, weight: 5 / 9 },
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* 3×3 Gauss rule over the parent square: points 0 and ±√0.6 in each direction, weights the
|
|
23
|
+
* products of 5/9, 8/9, 5/9. Exact for polynomials up to quintic in each of ξ and η.
|
|
24
|
+
*/
|
|
25
|
+
export const GAUSS_3X3 = GAUSS_3_POINTS.flatMap((p) => GAUSS_3_POINTS.map((q) => ({ xi: p.at, eta: q.at, weight: p.weight * q.weight })));
|
|
26
|
+
/** Bilinear corner functions `Nᵢ = ¼(1 + ξᵢξ)(1 + ηᵢη)`, i = 1…4. */
|
|
27
|
+
export const bilinearShape = (xi, eta) => {
|
|
28
|
+
const N = [];
|
|
29
|
+
const dXi = [];
|
|
30
|
+
const dEta = [];
|
|
31
|
+
for (let i = 0; i < 4; i++) {
|
|
32
|
+
const a = CORNER_XI[i];
|
|
33
|
+
const b = CORNER_ETA[i];
|
|
34
|
+
N.push(0.25 * (1 + a * xi) * (1 + b * eta));
|
|
35
|
+
dXi.push(0.25 * a * (1 + b * eta));
|
|
36
|
+
dEta.push(0.25 * b * (1 + a * xi));
|
|
37
|
+
}
|
|
38
|
+
return { N, dXi, dEta };
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Mid-side functions N₅…N₈, one per edge 1→2, 2→3, 3→4, 4→1 (index k = edge from corner k to
|
|
42
|
+
* corner k + 1): 1 at the edge's midpoint and 0 at every corner and every other midpoint.
|
|
43
|
+
*/
|
|
44
|
+
export const midsideShape = (xi, eta) => ({
|
|
45
|
+
N: [
|
|
46
|
+
0.5 * (1 - xi * xi) * (1 - eta),
|
|
47
|
+
0.5 * (1 + xi) * (1 - eta * eta),
|
|
48
|
+
0.5 * (1 - xi * xi) * (1 + eta),
|
|
49
|
+
0.5 * (1 - xi) * (1 - eta * eta),
|
|
50
|
+
],
|
|
51
|
+
dXi: [-xi * (1 - eta), 0.5 * (1 - eta * eta), -xi * (1 + eta), -0.5 * (1 - eta * eta)],
|
|
52
|
+
dEta: [-0.5 * (1 - xi * xi), -(1 + xi) * eta, 0.5 * (1 - xi * xi), -(1 - xi) * eta],
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* 8-node serendipity functions: the corner functions
|
|
56
|
+
* `Nᵢ = ¼(1 + ξᵢξ)(1 + ηᵢη)(ξᵢξ + ηᵢη − 1)`, i = 1…4, followed by the mid-side functions
|
|
57
|
+
* N₅…N₈ of `midsideShape`. Each is 1 at its own node and 0 at the other seven.
|
|
58
|
+
*/
|
|
59
|
+
export const serendipityShape = (xi, eta) => {
|
|
60
|
+
const N = [];
|
|
61
|
+
const dXi = [];
|
|
62
|
+
const dEta = [];
|
|
63
|
+
for (let i = 0; i < 4; i++) {
|
|
64
|
+
const a = CORNER_XI[i];
|
|
65
|
+
const b = CORNER_ETA[i];
|
|
66
|
+
N.push(0.25 * (1 + a * xi) * (1 + b * eta) * (a * xi + b * eta - 1));
|
|
67
|
+
dXi.push(0.25 * a * (1 + b * eta) * (2 * a * xi + b * eta));
|
|
68
|
+
dEta.push(0.25 * b * (1 + a * xi) * (a * xi + 2 * b * eta));
|
|
69
|
+
}
|
|
70
|
+
const mid = midsideShape(xi, eta);
|
|
71
|
+
return { N: [...N, ...mid.N], dXi: [...dXi, ...mid.dXi], dEta: [...dEta, ...mid.dEta] };
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Jacobian of the bilinear map from the parent square to a quadrilateral.
|
|
75
|
+
*
|
|
76
|
+
* @param corners - The four corners in the element plane (mm), counter-clockwise
|
|
77
|
+
* @param xi - Natural coordinate ξ
|
|
78
|
+
* @param eta - Natural coordinate η
|
|
79
|
+
* @returns `det J` and `J⁻¹`; `det J > 0` everywhere for a convex, counter-clockwise quad
|
|
80
|
+
*/
|
|
81
|
+
export const quadJacobian = (corners, xi, eta) => {
|
|
82
|
+
const { dXi, dEta } = bilinearShape(xi, eta);
|
|
83
|
+
let xXi = 0;
|
|
84
|
+
let yXi = 0;
|
|
85
|
+
let xEta = 0;
|
|
86
|
+
let yEta = 0;
|
|
87
|
+
for (let i = 0; i < 4; i++) {
|
|
88
|
+
xXi += dXi[i] * corners[i][0];
|
|
89
|
+
yXi += dXi[i] * corners[i][1];
|
|
90
|
+
xEta += dEta[i] * corners[i][0];
|
|
91
|
+
yEta += dEta[i] * corners[i][1];
|
|
92
|
+
}
|
|
93
|
+
const detJ = xXi * yEta - yXi * xEta;
|
|
94
|
+
return {
|
|
95
|
+
detJ,
|
|
96
|
+
inverse: [
|
|
97
|
+
[yEta / detJ, -yXi / detJ],
|
|
98
|
+
[-xEta / detJ, xXi / detJ],
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Converts ξ, η derivatives of shape functions to x, y derivatives: `[N,x; N,y] = J⁻¹·[N,ξ; N,η]`.
|
|
104
|
+
*
|
|
105
|
+
* @returns `dX[i]` and `dY[i]` for every function i of `shape`
|
|
106
|
+
*/
|
|
107
|
+
export const cartesianDerivatives = ({ inverse }, shape) => ({
|
|
108
|
+
dX: shape.dXi.map((d, i) => inverse[0][0] * d + inverse[0][1] * shape.dEta[i]),
|
|
109
|
+
dY: shape.dXi.map((d, i) => inverse[1][0] * d + inverse[1][1] * shape.dEta[i]),
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* Integrals `∫ Nᵢ dA` of the bilinear corner functions over a quad (2×2 Gauss, exact): the
|
|
113
|
+
* share of a uniform surface load each corner takes. They sum to the quad's area; each is
|
|
114
|
+
* A/4 for a parallelogram.
|
|
115
|
+
*
|
|
116
|
+
* @param corners - The four corners in the element plane (mm), counter-clockwise
|
|
117
|
+
* @returns Four areas in mm²
|
|
118
|
+
*/
|
|
119
|
+
export const cornerAreas = (corners) => {
|
|
120
|
+
const areas = [0, 0, 0, 0];
|
|
121
|
+
for (const { xi, eta, weight } of GAUSS_2X2) {
|
|
122
|
+
const { detJ } = quadJacobian(corners, xi, eta);
|
|
123
|
+
bilinearShape(xi, eta).N.forEach((n, i) => (areas[i] += n * detJ * weight));
|
|
124
|
+
}
|
|
125
|
+
return areas;
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=quad-geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quad-geometry.js","sourceRoot":"","sources":["../../../../../../src/fea/frame/elements/shell/quad-geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isotropic rigidity `c·[[1, ν, 0], [ν, 1, 0], [0, 0, (1 − ν)/2]]` with stiffness modifiers applied
|
|
3
|
+
* as `S·D·S`, `S = diag(√f₁₁, √f₂₂, √f₁₂)`: D₁₁, D₂₂ and D₃₃ scale by their own modifier and the
|
|
4
|
+
* coupling D₁₂ by √(f₁₁·f₂₂), so D stays symmetric and positive definite and equal modifiers
|
|
5
|
+
* scale it uniformly.
|
|
6
|
+
*
|
|
7
|
+
* @param c - E·t/(1 − ν²) for a membrane, E·t³/(12(1 − ν²)) for a plate
|
|
8
|
+
* @param nu - Poisson's ratio
|
|
9
|
+
* @param modifiers - Positive modifiers [f₁₁, f₂₂, f₁₂] (default all 1)
|
|
10
|
+
*/
|
|
11
|
+
export const isotropicRigidity = (c, nu, modifiers = [1, 1, 1]) => {
|
|
12
|
+
const [f11, f22, f12] = modifiers;
|
|
13
|
+
return [
|
|
14
|
+
[c * f11, c * nu * Math.sqrt(f11 * f22), 0],
|
|
15
|
+
[c * nu * Math.sqrt(f11 * f22), c * f22, 0],
|
|
16
|
+
[0, 0, ((c * (1 - nu)) / 2) * f12],
|
|
17
|
+
];
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=rigidity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rigidity.js","sourceRoot":"","sources":["../../../../../../src/fea/frame/elements/shell/rigidity.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,CAAS,EACT,EAAU,EACV,YAA+B,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAChC,EAAE;IACV,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;IAClC,OAAO;QACL,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;QAC3C,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;KACnC,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { AREA_GEOMETRY_TOLERANCE, SHELL_WARPING_LIMIT, VERTICAL_MEMBER_TOLERANCE, } from "../../../../core/constants/frame.constant.js";
|
|
2
|
+
import { quadJacobian } from "./quad-geometry.js";
|
|
3
|
+
const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
4
|
+
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
5
|
+
const cross = (a, b) => [
|
|
6
|
+
a[1] * b[2] - a[2] * b[1],
|
|
7
|
+
a[2] * b[0] - a[0] * b[2],
|
|
8
|
+
a[0] * b[1] - a[1] * b[0],
|
|
9
|
+
];
|
|
10
|
+
const unit = (a) => {
|
|
11
|
+
const n = Math.hypot(a[0], a[1], a[2]);
|
|
12
|
+
return [a[0] / n, a[1] / n, a[2] / n];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Local axes of a four-node shell element, ETABS-style, and its corners on the mean plane.
|
|
16
|
+
*
|
|
17
|
+
* - Local 3 is the unit normal `n ∥ d₁ × d₂` of the diagonals `d₁ = x₃ − x₁`, `d₂ = x₄ − x₂`: the
|
|
18
|
+
* right-hand rule around the nodes, as for null areas (for four nodes Newell's vector area is
|
|
19
|
+
* exactly `½ d₁ × d₂`).
|
|
20
|
+
* - Non-horizontal element: local 2 is +Z projected onto the plane (upward on a wall) and
|
|
21
|
+
* local 1 = 2 × 3 is horizontal.
|
|
22
|
+
* - Horizontal element (the normal's horizontal part below the vertical-member tolerance):
|
|
23
|
+
* local 1 = +X and local 2 = 3 × 1, so local 2 = +Y when local 3 = +Z.
|
|
24
|
+
* - `angle` then rotates local 1 and 2 about local 3 by the right-hand rule.
|
|
25
|
+
*
|
|
26
|
+
* The mean plane passes through the centroid with normal n; a warped element's corners lie
|
|
27
|
+
* alternately h above and below it.
|
|
28
|
+
*
|
|
29
|
+
* @param points - The four corners in global axes (mm), in order around the element
|
|
30
|
+
* @param angle - Rotation of local 1 and 2 about local 3 in rad (default 0)
|
|
31
|
+
*/
|
|
32
|
+
export const shellAxes = (points, angle = 0) => {
|
|
33
|
+
const e3 = unit(cross(sub(points[2], points[0]), sub(points[3], points[1])));
|
|
34
|
+
let e1;
|
|
35
|
+
let e2;
|
|
36
|
+
if (Math.hypot(e3[0], e3[1]) < VERTICAL_MEMBER_TOLERANCE) {
|
|
37
|
+
e1 = [1, 0, 0];
|
|
38
|
+
e2 = cross(e3, e1);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
e2 = unit([-e3[2] * e3[0], -e3[2] * e3[1], 1 - e3[2] * e3[2]]); // Z − (Z·n)·n
|
|
42
|
+
e1 = cross(e2, e3);
|
|
43
|
+
}
|
|
44
|
+
if (angle !== 0) {
|
|
45
|
+
const c = Math.cos(angle);
|
|
46
|
+
const s = Math.sin(angle);
|
|
47
|
+
const r1 = [c * e1[0] + s * e2[0], c * e1[1] + s * e2[1], c * e1[2] + s * e2[2]];
|
|
48
|
+
const r2 = [-s * e1[0] + c * e2[0], -s * e1[1] + c * e2[1], -s * e1[2] + c * e2[2]];
|
|
49
|
+
e1 = r1;
|
|
50
|
+
e2 = r2;
|
|
51
|
+
}
|
|
52
|
+
const origin = [0, 1, 2].map((k) => (points[0][k] + points[1][k] + points[2][k] + points[3][k]) / 4);
|
|
53
|
+
const relative = points.map((p) => sub(p, origin));
|
|
54
|
+
return {
|
|
55
|
+
lambda: [e1, e2, e3],
|
|
56
|
+
origin,
|
|
57
|
+
corners: relative.map((r) => [dot(r, e1), dot(r, e2)]),
|
|
58
|
+
offsets: relative.map((r) => dot(r, e3)),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Whether four corners make a valid shell element: no repeated corner, a non-degenerate
|
|
63
|
+
* convex quadrilateral in order around it (det J > 0 at every corner of the bilinear map), and
|
|
64
|
+
* no corner farther than `SHELL_WARPING_LIMIT · √A` from the mean plane.
|
|
65
|
+
*/
|
|
66
|
+
export const isValidShellQuad = (points) => {
|
|
67
|
+
const size = Math.max(...points.map((p, i) => {
|
|
68
|
+
const q = points[(i + 1) % 4];
|
|
69
|
+
return Math.hypot(q[0] - p[0], q[1] - p[1], q[2] - p[2]);
|
|
70
|
+
}));
|
|
71
|
+
const tolerance = AREA_GEOMETRY_TOLERANCE * size;
|
|
72
|
+
for (let i = 0; i < 4; i++) {
|
|
73
|
+
for (let j = i + 1; j < 4; j++) {
|
|
74
|
+
const [p, q] = [points[i], points[j]];
|
|
75
|
+
if (!(Math.hypot(q[0] - p[0], q[1] - p[1], q[2] - p[2]) > tolerance))
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const d1 = [0, 1, 2].map((k) => points[2][k] - points[0][k]);
|
|
80
|
+
const d2 = [0, 1, 2].map((k) => points[3][k] - points[1][k]);
|
|
81
|
+
const area = Math.hypot(d1[1] * d2[2] - d1[2] * d2[1], d1[2] * d2[0] - d1[0] * d2[2], d1[0] * d2[1] - d1[1] * d2[0]) / 2;
|
|
82
|
+
if (!(area > tolerance * size))
|
|
83
|
+
return false;
|
|
84
|
+
const axes = shellAxes(points);
|
|
85
|
+
for (const [xi, eta] of [
|
|
86
|
+
[-1, -1],
|
|
87
|
+
[1, -1],
|
|
88
|
+
[1, 1],
|
|
89
|
+
[-1, 1],
|
|
90
|
+
]) {
|
|
91
|
+
if (!(quadJacobian(axes.corners, xi, eta).detJ > (AREA_GEOMETRY_TOLERANCE * area) / 4)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return Math.max(...axes.offsets.map(Math.abs)) <= SHELL_WARPING_LIMIT * Math.sqrt(area);
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=shell-axes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-axes.js","sourceRoot":"","sources":["../../../../../../src/fea/frame/elements/shell/shell-axes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { AREA_GEOMETRY_TOLERANCE } from "../../../core/constants/frame.constant.js";
|
|
2
|
+
import { shellAxes } from "../elements/shell/shell-axes.js";
|
|
3
|
+
import { meshShells, pointOnSegment } from "./mesh-shells.js";
|
|
4
|
+
const DOF_KEYS = ["ux", "uy", "uz", "rx", "ry", "rz"];
|
|
5
|
+
/**
|
|
6
|
+
* Restraints of every node on each line restraint's segment (ends included), merged with the
|
|
7
|
+
* point restraints: a DOF fixed by either is fixed.
|
|
8
|
+
*/
|
|
9
|
+
const spreadLineRestraints = (model, nodes) => {
|
|
10
|
+
const lines = model.lineRestraints ?? [];
|
|
11
|
+
if (lines.length === 0)
|
|
12
|
+
return model.restraints;
|
|
13
|
+
const position = new Map(nodes.map((n) => [n.id, [n.x, n.y, n.z]]));
|
|
14
|
+
const merged = new Map(model.restraints.map((r) => [r.nodeId, { ...r }]));
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const [P, Q] = line.nodeIds.map((id) => position.get(id));
|
|
17
|
+
for (const node of nodes) {
|
|
18
|
+
const on = line.nodeIds.includes(node.id) ||
|
|
19
|
+
pointOnSegment([node.x, node.y, node.z], P, Q, true) !== null;
|
|
20
|
+
if (!on)
|
|
21
|
+
continue;
|
|
22
|
+
const restraint = merged.get(node.id) ?? { nodeId: node.id };
|
|
23
|
+
for (const key of DOF_KEYS)
|
|
24
|
+
if (line[key] === true)
|
|
25
|
+
restraint[key] = true;
|
|
26
|
+
merged.set(node.id, restraint);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return [...merged.values()];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Warnings for member nodes lying inside a shell area but not on its mesh: such a node is not
|
|
33
|
+
* connected to the shell.
|
|
34
|
+
*/
|
|
35
|
+
const disconnectedNodes = (model, meshNodes) => {
|
|
36
|
+
const warnings = [];
|
|
37
|
+
const shells = model.shells ?? [];
|
|
38
|
+
const position = new Map(model.nodes.map((n) => [n.id, [n.x, n.y, n.z]]));
|
|
39
|
+
const memberNodes = new Set(model.members.flatMap((m) => [m.startNodeId, m.endNodeId]));
|
|
40
|
+
for (const shell of shells) {
|
|
41
|
+
const points = shell.nodeIds.map((id) => position.get(id));
|
|
42
|
+
const axes = shellAxes(points);
|
|
43
|
+
const size = Math.max(...axes.corners.map(([x, y]) => Math.hypot(x, y)));
|
|
44
|
+
const tolerance = AREA_GEOMETRY_TOLERANCE * size;
|
|
45
|
+
for (const id of memberNodes) {
|
|
46
|
+
if (meshNodes.has(id))
|
|
47
|
+
continue;
|
|
48
|
+
const p = position.get(id);
|
|
49
|
+
const r = [p[0] - axes.origin[0], p[1] - axes.origin[1], p[2] - axes.origin[2]];
|
|
50
|
+
const [e1, e2, e3] = axes.lambda;
|
|
51
|
+
const z = r[0] * e3[0] + r[1] * e3[1] + r[2] * e3[2];
|
|
52
|
+
if (Math.abs(z) > Math.max(tolerance, ...axes.offsets.map(Math.abs)))
|
|
53
|
+
continue;
|
|
54
|
+
const x = r[0] * e1[0] + r[1] * e1[1] + r[2] * e1[2];
|
|
55
|
+
const y = r[0] * e2[0] + r[1] * e2[1] + r[2] * e2[2];
|
|
56
|
+
const inside = axes.corners.every(([ax, ay], k) => {
|
|
57
|
+
const [bx, by] = axes.corners[(k + 1) % 4];
|
|
58
|
+
return (bx - ax) * (y - ay) - (by - ay) * (x - ax) > tolerance * size;
|
|
59
|
+
});
|
|
60
|
+
if (inside) {
|
|
61
|
+
warnings.push({
|
|
62
|
+
reference: "Shell area meshing",
|
|
63
|
+
message: `Node "${String(id)}" lies inside shell area "${String(shell.id)}" but not on its mesh, so its members are not connected to the shell; divide the area through the node.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return warnings;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The generated nodes lying strictly inside each member (a beam along a meshed edge), so the
|
|
72
|
+
* member can be split there and share them with the shells.
|
|
73
|
+
*/
|
|
74
|
+
const splitMembers = (model, generated) => {
|
|
75
|
+
if (generated.length === 0)
|
|
76
|
+
return model.members;
|
|
77
|
+
const position = new Map(model.nodes.map((n) => [n.id, [n.x, n.y, n.z]]));
|
|
78
|
+
return model.members.map((member) => {
|
|
79
|
+
const [P, Q] = [position.get(member.startNodeId), position.get(member.endNodeId)];
|
|
80
|
+
const lo = [0, 1, 2].map((k) => Math.min(P[k], Q[k]));
|
|
81
|
+
const hi = [0, 1, 2].map((k) => Math.max(P[k], Q[k]));
|
|
82
|
+
const slack = AREA_GEOMETRY_TOLERANCE * Math.hypot(Q[0] - P[0], Q[1] - P[1], Q[2] - P[2]);
|
|
83
|
+
const through = [];
|
|
84
|
+
for (const node of generated) {
|
|
85
|
+
const x = [node.x, node.y, node.z];
|
|
86
|
+
if (x.some((v, k) => v < lo[k] - slack || v > hi[k] + slack))
|
|
87
|
+
continue;
|
|
88
|
+
const t = pointOnSegment(x, P, Q);
|
|
89
|
+
if (t !== null)
|
|
90
|
+
through.push({ id: node.id, t });
|
|
91
|
+
}
|
|
92
|
+
return through.length === 0
|
|
93
|
+
? member
|
|
94
|
+
: { ...member, through: through.sort((a, b) => a.t - b.t).map((h) => h.id) };
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Meshes a valid model's shell areas, spreads its line restraints and marks the members that
|
|
99
|
+
* generated nodes split (see `meshShells`).
|
|
100
|
+
* Without shells and line restraints the model is returned as it is.
|
|
101
|
+
*/
|
|
102
|
+
export const expandModel = (model) => {
|
|
103
|
+
const shells = model.shells ?? [];
|
|
104
|
+
if (shells.length === 0) {
|
|
105
|
+
return {
|
|
106
|
+
model: { ...model, shells: [], restraints: spreadLineRestraints(model, model.nodes) },
|
|
107
|
+
mesh: undefined,
|
|
108
|
+
warnings: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const meshed = meshShells(model.nodes, shells);
|
|
112
|
+
const nodes = [...model.nodes, ...meshed.nodes];
|
|
113
|
+
const meshNodes = new Set(meshed.elements.flatMap((e) => e.nodeIds));
|
|
114
|
+
return {
|
|
115
|
+
model: {
|
|
116
|
+
...model,
|
|
117
|
+
nodes,
|
|
118
|
+
members: splitMembers(model, meshed.nodes),
|
|
119
|
+
shells: meshed.elements,
|
|
120
|
+
restraints: spreadLineRestraints(model, nodes),
|
|
121
|
+
},
|
|
122
|
+
mesh: {
|
|
123
|
+
nodes: meshed.nodes,
|
|
124
|
+
shells: meshed.elements.map(({ id, areaId, nodeIds }) => ({ id, areaId, nodeIds })),
|
|
125
|
+
},
|
|
126
|
+
warnings: disconnectedNodes(model, meshNodes),
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=expand-model.js.map
|