@theerapat-s28/ts-structural-eng-tools 3.1.0 → 3.2.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.
Files changed (305) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/README.md +284 -127
  3. package/dist/core/errors/fea.error.d.ts +47 -0
  4. package/dist/{esm/core/errors/frame-analysis.error.js → core/errors/fea.error.js} +14 -5
  5. package/dist/core/errors/fea.error.js.map +1 -0
  6. package/dist/core/index.d.ts +4 -2
  7. package/dist/core/index.js +8 -4
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/{core/errors/frame-analysis.error.js → esm/core/errors/fea.error.js} +10 -9
  10. package/dist/esm/core/errors/fea.error.js.map +1 -0
  11. package/dist/esm/core/index.js +3 -1
  12. package/dist/esm/core/index.js.map +1 -1
  13. package/dist/esm/fea/analysis/index.js +6 -0
  14. package/dist/esm/fea/analysis/index.js.map +1 -0
  15. package/dist/esm/fea/{frame → analysis}/mesh/expand-model.js +12 -6
  16. package/dist/esm/fea/analysis/mesh/expand-model.js.map +1 -0
  17. package/dist/esm/fea/{frame → analysis}/post/demand-envelope.js +4 -4
  18. package/dist/esm/fea/analysis/post/demand-envelope.js.map +1 -0
  19. package/dist/esm/fea/analysis/post/envelope.js.map +1 -0
  20. package/dist/esm/fea/{frame → analysis}/post/present-state.js +4 -4
  21. package/dist/esm/fea/analysis/post/present-state.js.map +1 -0
  22. package/dist/esm/fea/{frame/solver/frame-system.js → analysis/solver/analysis-system.js} +17 -90
  23. package/dist/esm/fea/analysis/solver/analysis-system.js.map +1 -0
  24. package/dist/esm/fea/{frame/solver/analyze-frame.js → analysis/solver/analyze.js} +20 -18
  25. package/dist/esm/fea/analysis/solver/analyze.js.map +1 -0
  26. package/dist/esm/fea/{frame → analysis}/solver/combinations.js +8 -8
  27. package/dist/esm/fea/analysis/solver/combinations.js.map +1 -0
  28. package/dist/esm/fea/analysis/solver/distribute-area-loads.js +25 -0
  29. package/dist/esm/fea/analysis/solver/distribute-area-loads.js.map +1 -0
  30. package/dist/esm/fea/analysis/solver/load-case-solver.js +103 -0
  31. package/dist/esm/fea/analysis/solver/load-case-solver.js.map +1 -0
  32. package/dist/esm/fea/analysis/types/model.type.js +2 -0
  33. package/dist/esm/fea/analysis/types/model.type.js.map +1 -0
  34. package/dist/esm/fea/analysis/types/results.type.js +2 -0
  35. package/dist/esm/fea/analysis/types/results.type.js.map +1 -0
  36. package/dist/esm/fea/analysis/validation/validate-model.js +263 -0
  37. package/dist/esm/fea/analysis/validation/validate-model.js.map +1 -0
  38. package/dist/esm/fea/deprecated.js +9 -0
  39. package/dist/esm/fea/deprecated.js.map +1 -0
  40. package/dist/esm/fea/frame/area/{distribute-area-loads.js → area-member-loads.js} +7 -28
  41. package/dist/esm/fea/frame/area/area-member-loads.js.map +1 -0
  42. package/dist/esm/fea/frame/area/edge-members.js.map +1 -1
  43. package/dist/esm/fea/frame/elements/end-releases.js +2 -6
  44. package/dist/esm/fea/frame/elements/end-releases.js.map +1 -1
  45. package/dist/esm/fea/frame/elements/fixed-end-forces.js +5 -5
  46. package/dist/esm/fea/frame/elements/fixed-end-forces.js.map +1 -1
  47. package/dist/esm/fea/frame/elements/static-condensation.js +3 -3
  48. package/dist/esm/fea/frame/elements/static-condensation.js.map +1 -1
  49. package/dist/esm/fea/frame/index.js +1 -4
  50. package/dist/esm/fea/frame/index.js.map +1 -1
  51. package/dist/esm/fea/frame/post/member-diagram.js.map +1 -1
  52. package/dist/esm/fea/frame/solver/{global-assembly.js → member-assembly.js} +2 -10
  53. package/dist/esm/fea/frame/solver/member-assembly.js.map +1 -0
  54. package/dist/esm/fea/frame/solver/member-loads.js +141 -0
  55. package/dist/esm/fea/frame/solver/member-loads.js.map +1 -0
  56. package/dist/esm/fea/frame/solver/system-member.js +66 -0
  57. package/dist/esm/fea/frame/solver/system-member.js.map +1 -0
  58. package/dist/esm/fea/frame/transformation/coordinate-transform.js +3 -3
  59. package/dist/esm/fea/frame/transformation/coordinate-transform.js.map +1 -1
  60. package/dist/esm/fea/frame/validation/validate-members.js +160 -0
  61. package/dist/esm/fea/frame/validation/validate-members.js.map +1 -0
  62. package/dist/esm/fea/frame/validation/validate-null-areas.js +88 -0
  63. package/dist/esm/fea/frame/validation/validate-null-areas.js.map +1 -0
  64. package/dist/esm/fea/index.js +4 -1
  65. package/dist/esm/fea/index.js.map +1 -1
  66. package/dist/esm/fea/shared/index.js +2 -0
  67. package/dist/esm/fea/shared/index.js.map +1 -0
  68. package/dist/esm/fea/shared/solver/auto-restraint.js.map +1 -0
  69. package/dist/esm/fea/{frame → shared}/solver/boundary-conditions.js +5 -1
  70. package/dist/esm/fea/shared/solver/boundary-conditions.js.map +1 -0
  71. package/dist/esm/fea/shared/solver/global-assembly.js +10 -0
  72. package/dist/esm/fea/shared/solver/global-assembly.js.map +1 -0
  73. package/dist/esm/fea/shared/types/model.type.js +2 -0
  74. package/dist/esm/fea/shared/types/model.type.js.map +1 -0
  75. package/dist/esm/fea/shared/validation/issues.js +32 -0
  76. package/dist/esm/fea/shared/validation/issues.js.map +1 -0
  77. package/dist/esm/fea/{frame/elements/shell → shell/elements}/dkq-plate.js +1 -1
  78. package/dist/esm/fea/shell/elements/dkq-plate.js.map +1 -0
  79. package/dist/esm/fea/{frame/elements/shell → shell/elements}/flat-shell.js +1 -1
  80. package/dist/esm/fea/shell/elements/flat-shell.js.map +1 -0
  81. package/dist/esm/fea/{frame/elements/shell → shell/elements}/membrane-quad.js +2 -2
  82. package/dist/esm/fea/shell/elements/membrane-quad.js.map +1 -0
  83. package/dist/esm/fea/shell/elements/quad-geometry.js.map +1 -0
  84. package/dist/esm/fea/shell/elements/rigidity.js.map +1 -0
  85. package/dist/esm/fea/{frame/elements/shell → shell/elements}/shell-axes.js +1 -1
  86. package/dist/esm/fea/shell/elements/shell-axes.js.map +1 -0
  87. package/dist/esm/fea/shell/index.js +2 -0
  88. package/dist/esm/fea/shell/index.js.map +1 -0
  89. package/dist/esm/fea/{frame → shell}/mesh/mesh-shells.js +1 -1
  90. package/dist/esm/fea/shell/mesh/mesh-shells.js.map +1 -0
  91. package/dist/esm/fea/{frame → shell}/post/shell-forces.js +4 -4
  92. package/dist/esm/fea/{frame → shell}/post/shell-forces.js.map +1 -1
  93. package/dist/esm/fea/shell/solver/shell-loads.js +53 -0
  94. package/dist/esm/fea/shell/solver/shell-loads.js.map +1 -0
  95. package/dist/esm/fea/shell/solver/system-shell.js +37 -0
  96. package/dist/esm/fea/shell/solver/system-shell.js.map +1 -0
  97. package/dist/esm/fea/shell/types/model.type.js +2 -0
  98. package/dist/esm/fea/shell/types/model.type.js.map +1 -0
  99. package/dist/esm/fea/shell/types/results.type.js +2 -0
  100. package/dist/esm/fea/shell/types/results.type.js.map +1 -0
  101. package/dist/esm/fea/shell/validation/validate-shells.js +98 -0
  102. package/dist/esm/fea/shell/validation/validate-shells.js.map +1 -0
  103. package/dist/esm/index.js +5 -3
  104. package/dist/esm/index.js.map +1 -1
  105. package/dist/fea/analysis/index.d.ts +8 -0
  106. package/dist/fea/analysis/index.js +14 -0
  107. package/dist/fea/analysis/index.js.map +1 -0
  108. package/dist/fea/{frame → analysis}/mesh/expand-model.d.ts +9 -7
  109. package/dist/fea/{frame → analysis}/mesh/expand-model.js +12 -6
  110. package/dist/fea/analysis/mesh/expand-model.js.map +1 -0
  111. package/dist/fea/{frame → analysis}/post/demand-envelope.d.ts +5 -5
  112. package/dist/fea/{frame → analysis}/post/demand-envelope.js +4 -4
  113. package/dist/fea/analysis/post/demand-envelope.js.map +1 -0
  114. package/dist/fea/{frame → analysis}/post/envelope.d.ts +2 -2
  115. package/dist/fea/analysis/post/envelope.js.map +1 -0
  116. package/dist/fea/{frame → analysis}/post/present-state.d.ts +6 -5
  117. package/dist/fea/{frame → analysis}/post/present-state.js +5 -5
  118. package/dist/fea/analysis/post/present-state.js.map +1 -0
  119. package/dist/fea/analysis/solver/analysis-system.d.ts +46 -0
  120. package/dist/fea/{frame/solver/frame-system.js → analysis/solver/analysis-system.js} +22 -95
  121. package/dist/fea/analysis/solver/analysis-system.js.map +1 -0
  122. package/dist/fea/{frame/solver/analyze-frame.d.ts → analysis/solver/analyze.d.ts} +23 -20
  123. package/dist/fea/{frame/solver/analyze-frame.js → analysis/solver/analyze.js} +25 -23
  124. package/dist/fea/analysis/solver/analyze.js.map +1 -0
  125. package/dist/fea/{frame → analysis}/solver/combinations.d.ts +1 -1
  126. package/dist/fea/{frame → analysis}/solver/combinations.js +8 -8
  127. package/dist/fea/analysis/solver/combinations.js.map +1 -0
  128. package/dist/fea/{frame/area → analysis/solver}/distribute-area-loads.d.ts +6 -15
  129. package/dist/fea/analysis/solver/distribute-area-loads.js +29 -0
  130. package/dist/fea/analysis/solver/distribute-area-loads.js.map +1 -0
  131. package/dist/fea/{frame → analysis}/solver/load-case-solver.d.ts +9 -13
  132. package/dist/fea/analysis/solver/load-case-solver.js +108 -0
  133. package/dist/fea/analysis/solver/load-case-solver.js.map +1 -0
  134. package/dist/fea/analysis/types/model.type.d.ts +80 -0
  135. package/dist/fea/analysis/types/model.type.js +3 -0
  136. package/dist/fea/analysis/types/model.type.js.map +1 -0
  137. package/dist/fea/analysis/types/results.type.d.ts +113 -0
  138. package/dist/fea/analysis/types/results.type.js +3 -0
  139. package/dist/fea/analysis/types/results.type.js.map +1 -0
  140. package/dist/fea/analysis/validation/validate-model.d.ts +12 -0
  141. package/dist/fea/analysis/validation/validate-model.js +267 -0
  142. package/dist/fea/analysis/validation/validate-model.js.map +1 -0
  143. package/dist/fea/deprecated.d.ts +27 -0
  144. package/dist/fea/deprecated.js +12 -0
  145. package/dist/fea/deprecated.js.map +1 -0
  146. package/dist/fea/frame/area/area-member-loads.d.ts +19 -0
  147. package/dist/fea/frame/area/{distribute-area-loads.js → area-member-loads.js} +8 -30
  148. package/dist/fea/frame/area/area-member-loads.js.map +1 -0
  149. package/dist/fea/frame/area/edge-members.d.ts +5 -4
  150. package/dist/fea/frame/area/edge-members.js.map +1 -1
  151. package/dist/fea/frame/elements/end-releases.d.ts +1 -6
  152. package/dist/fea/frame/elements/end-releases.js +4 -8
  153. package/dist/fea/frame/elements/end-releases.js.map +1 -1
  154. package/dist/fea/frame/elements/fixed-end-forces.d.ts +1 -1
  155. package/dist/fea/frame/elements/fixed-end-forces.js +5 -5
  156. package/dist/fea/frame/elements/fixed-end-forces.js.map +1 -1
  157. package/dist/fea/frame/elements/static-condensation.d.ts +1 -1
  158. package/dist/fea/frame/elements/static-condensation.js +3 -3
  159. package/dist/fea/frame/elements/static-condensation.js.map +1 -1
  160. package/dist/fea/frame/index.d.ts +2 -8
  161. package/dist/fea/frame/index.js +2 -10
  162. package/dist/fea/frame/index.js.map +1 -1
  163. package/dist/fea/frame/post/member-diagram.d.ts +3 -3
  164. package/dist/fea/frame/post/member-diagram.js.map +1 -1
  165. package/dist/fea/frame/solver/{global-assembly.d.ts → member-assembly.d.ts} +0 -11
  166. package/dist/fea/frame/solver/{global-assembly.js → member-assembly.js} +4 -13
  167. package/dist/fea/frame/solver/member-assembly.js.map +1 -0
  168. package/dist/fea/frame/solver/member-loads.d.ts +21 -0
  169. package/dist/fea/frame/solver/member-loads.js +146 -0
  170. package/dist/fea/frame/solver/member-loads.js.map +1 -0
  171. package/dist/fea/frame/solver/system-member.d.ts +50 -0
  172. package/dist/fea/frame/solver/system-member.js +70 -0
  173. package/dist/fea/frame/solver/system-member.js.map +1 -0
  174. package/dist/fea/frame/transformation/coordinate-transform.d.ts +1 -1
  175. package/dist/fea/frame/transformation/coordinate-transform.js +3 -3
  176. package/dist/fea/frame/transformation/coordinate-transform.js.map +1 -1
  177. package/dist/fea/frame/types/model.type.d.ts +10 -175
  178. package/dist/fea/frame/types/results.type.d.ts +4 -159
  179. package/dist/fea/frame/validation/validate-members.d.ts +30 -0
  180. package/dist/fea/frame/validation/validate-members.js +165 -0
  181. package/dist/fea/frame/validation/validate-members.js.map +1 -0
  182. package/dist/fea/frame/validation/validate-null-areas.d.ts +38 -0
  183. package/dist/fea/frame/validation/validate-null-areas.js +93 -0
  184. package/dist/fea/frame/validation/validate-null-areas.js.map +1 -0
  185. package/dist/fea/index.d.ts +4 -0
  186. package/dist/fea/index.js +4 -1
  187. package/dist/fea/index.js.map +1 -1
  188. package/dist/fea/shared/index.d.ts +1 -0
  189. package/dist/fea/shared/index.js +3 -0
  190. package/dist/fea/shared/index.js.map +1 -0
  191. package/dist/fea/shared/solver/auto-restraint.js.map +1 -0
  192. package/dist/fea/{frame → shared}/solver/boundary-conditions.d.ts +5 -0
  193. package/dist/fea/{frame → shared}/solver/boundary-conditions.js +7 -3
  194. package/dist/fea/shared/solver/boundary-conditions.js.map +1 -0
  195. package/dist/fea/shared/solver/global-assembly.d.ts +12 -0
  196. package/dist/fea/shared/solver/global-assembly.js +14 -0
  197. package/dist/fea/shared/solver/global-assembly.js.map +1 -0
  198. package/dist/fea/shared/types/model.type.d.ts +34 -0
  199. package/dist/fea/shared/types/model.type.js +3 -0
  200. package/dist/fea/shared/types/model.type.js.map +1 -0
  201. package/dist/fea/shared/validation/issues.d.ts +9 -0
  202. package/dist/fea/shared/validation/issues.js +38 -0
  203. package/dist/fea/shared/validation/issues.js.map +1 -0
  204. package/dist/fea/{frame/elements/shell → shell/elements}/dkq-plate.d.ts +1 -1
  205. package/dist/fea/{frame/elements/shell → shell/elements}/dkq-plate.js +1 -1
  206. package/dist/fea/shell/elements/dkq-plate.js.map +1 -0
  207. package/dist/fea/{frame/elements/shell → shell/elements}/flat-shell.d.ts +1 -1
  208. package/dist/fea/{frame/elements/shell → shell/elements}/flat-shell.js +1 -1
  209. package/dist/fea/shell/elements/flat-shell.js.map +1 -0
  210. package/dist/fea/{frame/elements/shell → shell/elements}/membrane-quad.d.ts +1 -1
  211. package/dist/fea/{frame/elements/shell → shell/elements}/membrane-quad.js +2 -2
  212. package/dist/fea/shell/elements/membrane-quad.js.map +1 -0
  213. package/dist/fea/shell/elements/quad-geometry.js.map +1 -0
  214. package/dist/fea/{frame/elements/shell → shell/elements}/rigidity.d.ts +1 -1
  215. package/dist/fea/shell/elements/rigidity.js.map +1 -0
  216. package/dist/fea/{frame/elements/shell → shell/elements}/shell-axes.d.ts +1 -1
  217. package/dist/fea/{frame/elements/shell → shell/elements}/shell-axes.js +1 -1
  218. package/dist/fea/shell/elements/shell-axes.js.map +1 -0
  219. package/dist/fea/shell/index.d.ts +2 -0
  220. package/dist/fea/shell/index.js +3 -0
  221. package/dist/fea/shell/index.js.map +1 -0
  222. package/dist/fea/{frame → shell}/mesh/mesh-shells.d.ts +7 -6
  223. package/dist/fea/{frame → shell}/mesh/mesh-shells.js +1 -1
  224. package/dist/fea/shell/mesh/mesh-shells.js.map +1 -0
  225. package/dist/fea/{frame → shell}/post/shell-forces.d.ts +1 -1
  226. package/dist/fea/{frame → shell}/post/shell-forces.js +4 -4
  227. package/dist/fea/{frame → shell}/post/shell-forces.js.map +1 -1
  228. package/dist/fea/shell/solver/shell-loads.d.ts +17 -0
  229. package/dist/fea/shell/solver/shell-loads.js +57 -0
  230. package/dist/fea/shell/solver/shell-loads.js.map +1 -0
  231. package/dist/fea/shell/solver/system-shell.d.ts +29 -0
  232. package/dist/fea/shell/solver/system-shell.js +41 -0
  233. package/dist/fea/shell/solver/system-shell.js.map +1 -0
  234. package/dist/fea/shell/types/model.type.d.ts +57 -0
  235. package/dist/fea/shell/types/model.type.js +3 -0
  236. package/dist/fea/shell/types/model.type.js.map +1 -0
  237. package/dist/fea/shell/types/results.type.d.ts +47 -0
  238. package/dist/fea/shell/types/results.type.js +3 -0
  239. package/dist/fea/shell/types/results.type.js.map +1 -0
  240. package/dist/fea/shell/validation/validate-shells.d.ts +21 -0
  241. package/dist/fea/shell/validation/validate-shells.js +103 -0
  242. package/dist/fea/shell/validation/validate-shells.js.map +1 -0
  243. package/dist/index.d.ts +6 -4
  244. package/dist/index.js +13 -6
  245. package/dist/index.js.map +1 -1
  246. package/package.json +1 -1
  247. package/dist/core/errors/frame-analysis.error.d.ts +0 -37
  248. package/dist/core/errors/frame-analysis.error.js.map +0 -1
  249. package/dist/esm/core/errors/frame-analysis.error.js.map +0 -1
  250. package/dist/esm/fea/frame/area/distribute-area-loads.js.map +0 -1
  251. package/dist/esm/fea/frame/elements/shell/dkq-plate.js.map +0 -1
  252. package/dist/esm/fea/frame/elements/shell/flat-shell.js.map +0 -1
  253. package/dist/esm/fea/frame/elements/shell/membrane-quad.js.map +0 -1
  254. package/dist/esm/fea/frame/elements/shell/quad-geometry.js.map +0 -1
  255. package/dist/esm/fea/frame/elements/shell/rigidity.js.map +0 -1
  256. package/dist/esm/fea/frame/elements/shell/shell-axes.js.map +0 -1
  257. package/dist/esm/fea/frame/mesh/expand-model.js.map +0 -1
  258. package/dist/esm/fea/frame/mesh/mesh-shells.js.map +0 -1
  259. package/dist/esm/fea/frame/post/demand-envelope.js.map +0 -1
  260. package/dist/esm/fea/frame/post/envelope.js.map +0 -1
  261. package/dist/esm/fea/frame/post/present-state.js.map +0 -1
  262. package/dist/esm/fea/frame/solver/analyze-frame.js.map +0 -1
  263. package/dist/esm/fea/frame/solver/auto-restraint.js.map +0 -1
  264. package/dist/esm/fea/frame/solver/boundary-conditions.js.map +0 -1
  265. package/dist/esm/fea/frame/solver/combinations.js.map +0 -1
  266. package/dist/esm/fea/frame/solver/frame-system.js.map +0 -1
  267. package/dist/esm/fea/frame/solver/global-assembly.js.map +0 -1
  268. package/dist/esm/fea/frame/solver/load-case-solver.js +0 -255
  269. package/dist/esm/fea/frame/solver/load-case-solver.js.map +0 -1
  270. package/dist/esm/fea/frame/validation/validate-model.js +0 -529
  271. package/dist/esm/fea/frame/validation/validate-model.js.map +0 -1
  272. package/dist/fea/frame/area/distribute-area-loads.js.map +0 -1
  273. package/dist/fea/frame/elements/shell/dkq-plate.js.map +0 -1
  274. package/dist/fea/frame/elements/shell/flat-shell.js.map +0 -1
  275. package/dist/fea/frame/elements/shell/membrane-quad.js.map +0 -1
  276. package/dist/fea/frame/elements/shell/quad-geometry.js.map +0 -1
  277. package/dist/fea/frame/elements/shell/rigidity.js.map +0 -1
  278. package/dist/fea/frame/elements/shell/shell-axes.js.map +0 -1
  279. package/dist/fea/frame/mesh/expand-model.js.map +0 -1
  280. package/dist/fea/frame/mesh/mesh-shells.js.map +0 -1
  281. package/dist/fea/frame/post/demand-envelope.js.map +0 -1
  282. package/dist/fea/frame/post/envelope.js.map +0 -1
  283. package/dist/fea/frame/post/present-state.js.map +0 -1
  284. package/dist/fea/frame/solver/analyze-frame.js.map +0 -1
  285. package/dist/fea/frame/solver/auto-restraint.js.map +0 -1
  286. package/dist/fea/frame/solver/boundary-conditions.js.map +0 -1
  287. package/dist/fea/frame/solver/combinations.js.map +0 -1
  288. package/dist/fea/frame/solver/frame-system.d.ts +0 -89
  289. package/dist/fea/frame/solver/frame-system.js.map +0 -1
  290. package/dist/fea/frame/solver/global-assembly.js.map +0 -1
  291. package/dist/fea/frame/solver/load-case-solver.js +0 -260
  292. package/dist/fea/frame/solver/load-case-solver.js.map +0 -1
  293. package/dist/fea/frame/validation/validate-model.d.ts +0 -19
  294. package/dist/fea/frame/validation/validate-model.js +0 -535
  295. package/dist/fea/frame/validation/validate-model.js.map +0 -1
  296. /package/dist/esm/fea/{frame → analysis}/post/envelope.js +0 -0
  297. /package/dist/esm/fea/{frame → shared}/solver/auto-restraint.js +0 -0
  298. /package/dist/esm/fea/{frame/elements/shell → shell/elements}/quad-geometry.js +0 -0
  299. /package/dist/esm/fea/{frame/elements/shell → shell/elements}/rigidity.js +0 -0
  300. /package/dist/fea/{frame → analysis}/post/envelope.js +0 -0
  301. /package/dist/fea/{frame → shared}/solver/auto-restraint.d.ts +0 -0
  302. /package/dist/fea/{frame → shared}/solver/auto-restraint.js +0 -0
  303. /package/dist/fea/{frame/elements/shell → shell/elements}/quad-geometry.d.ts +0 -0
  304. /package/dist/fea/{frame/elements/shell → shell/elements}/quad-geometry.js +0 -0
  305. /package/dist/fea/{frame/elements/shell → shell/elements}/rigidity.js +0 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,37 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.2.0] — 2026-10-31
10
+
11
+ **The analysis exports lose "Frame" from their names.** A model can be members, shells or both, so `analyzeFrame` is now `analyze`, `FrameModel` is `FeaModel`, `FrameAnalysisError` is `FeaError`, and so on, and a model of shells only, such as a mat foundation, no longer needs a `members` list. Nothing breaks: the old names still work as deprecated aliases until 4.0.0, and every result is identical to 3.1.0.
12
+
13
+ ### Added
14
+
15
+ - **New names for the analysis exports.** "Frame" now means frame members only, as in ETABS. The exports that apply to the whole model, which may have members, shells or both, are renamed:
16
+
17
+ | Before | Now |
18
+ | --- | --- |
19
+ | `analyzeFrame`, `createFrameAnalysis`, `validateFrameModel` | `analyze`, `createAnalysis`, `validateModel` |
20
+ | `AnalyzeFrameOptions`, `FrameAnalysis` | `AnalyzeOptions`, `Analysis` |
21
+ | `FrameModel`, `FrameResult`, `FrameCaseResult`, `FrameEnvelope`, `FrameMesh`, `FrameNode`, `FrameId` | `FeaModel`, `FeaResult`, `FeaCaseResult`, `FeaEnvelope`, `FeaMesh`, `FeaNode`, `FeaId` |
22
+ | `FrameAnalysisError`, `FrameErrors`, `FrameModelIssue`, `FrameErrorDetails` | `FeaError`, `FeaErrors`, `FeaModelIssue`, `FeaErrorDetails` |
23
+
24
+ They are exported from the same places as the old names (the package root, `/fea`, and `/core` for the errors) and behave the same: same inputs, results, and error codes and messages.
25
+ - **`members` is optional in `FeaModel`.** A model of shells only, such as a mat foundation, can leave it out instead of passing `members: []`.
26
+
27
+ ### Changed
28
+
29
+ - The plain `Error` that `memberDemandEnvelope` throws for an id that is not in the result says "is not in the result" instead of "is not in the frame result".
30
+
31
+ ### Deprecated
32
+
33
+ - The names before 3.2.0, listed under **Added**, still work: each is an alias of its new name (the same function, type, error class or object), so `instanceof FrameAnalysisError` still catches every error `analyze` throws. They are marked `@deprecated`, so editors strike them through, and will be removed in 4.0.0. To migrate, rename the imports and the types; nothing else changes.
34
+
35
+ ### Documentation
36
+
37
+ - The FEA reference page is titled **Frame and Shell Analysis** and uses the new names throughout. Its anchors follow them too (`#analyze`, `#create-analysis`, `#validate-model`, `#fea-model`, `#fea-result` and so on), so links to the old anchors such as `#analyze-frame` open the top of the page.
38
+ - README: a second quick start, with a slab meshed into shell elements on edge beams and columns; what changes for a front end when a model has shells (generated nodes, elements, shell forces); the model and result fields at a glance; null areas compared with shells; running in a Web Worker, with measured analysis times; and the shell error codes 326–329.
39
+
9
40
  ## [3.1.0] — 2026-10-31
10
41
 
11
42
  **Engine reorganised, and a guide for upgrading from 2.x.** The analysis engine behind `/fea` is reorganised into separate parts for frame members, shell elements and the analysis that runs them together. Nothing you call changes: every export keeps its name, its import path and its behaviour, and every result is identical to 3.0.0. Coming from 2.x? Read **Upgrading from 2.x** below; it covers this release and 3.0.0 together.
@@ -214,7 +245,8 @@ Maintenance release with no changes to the library code.
214
245
 
215
246
  First version, under the unscoped name `ts-structural-eng-tools`: `rectBeamMomentCapacity` (singly and doubly reinforced rectangular beams per ACI 318-19), steel plate jacketing, and concrete property helpers.
216
247
 
217
- [Unreleased]: https://github.com/theerapat-s28/ts-structural-eng/compare/v3.1.0...HEAD
248
+ [Unreleased]: https://github.com/theerapat-s28/ts-structural-eng/compare/v3.2.0...HEAD
249
+ [3.2.0]: https://github.com/theerapat-s28/ts-structural-eng/compare/v3.1.0...v3.2.0
218
250
  [3.1.0]: https://github.com/theerapat-s28/ts-structural-eng/compare/v3.0.0...v3.1.0
219
251
  [3.0.0]: https://github.com/theerapat-s28/ts-structural-eng/compare/v2.3.0...v3.0.0
220
252
  [2.3.0]: https://github.com/theerapat-s28/ts-structural-eng/compare/v2.2.0...v2.3.0
package/README.md CHANGED
@@ -1,72 +1,61 @@
1
1
  # ts-structural-eng
2
2
 
3
- **📖 [API Reference — theerapat-s28.github.io/ts-structural-eng](https://theerapat-s28.github.io/ts-structural-eng/)**
3
+ **📖 [API Reference — theerapat-s28.github.io/ts-structural-eng](https://theerapat-s28.github.io/ts-structural-eng/)** · [npm](https://www.npmjs.com/package/@theerapat-s28/ts-structural-eng-tools) · [Changelog](CHANGELOG.md)
4
4
 
5
- A headless structural analysis and design engine for TypeScript: 3D frame analysis by the direct stiffness method, reinforced-concrete beam design per ACI 318-19, and strengthening methods. It is built to be installed by front-end applications as their calculation core.
5
+ A headless structural analysis and design engine for TypeScript: 3D analysis of frames and shells — beams, columns, braces, walls, slabs and cores — by the direct stiffness method, reinforced-concrete beam design per ACI 318-19, and strengthening methods. It is built to be installed by front-end applications as their calculation core.
6
6
 
7
7
  ## Scope: an engine, not an application
8
8
 
9
9
  The library does the structural engineering and nothing else. A front-end application installs it, builds a model from its own UI, calls the engine, and draws the results.
10
10
 
11
- | The engine (this package) | The front-end application |
12
- | --------------------------------------------------------------------------- | ------------------------------------------------------------- |
13
- | Model validation with located errors (`details` name the node, member, field…) | Input forms, drawing tools, highlighting the reported problems |
14
- | Analysis: load cases, combinations, envelopes, member diagrams | Rendering the model, deformed shapes and diagrams |
15
- | Design checks with code-clause warnings and `RCDesignError` / `FrameAnalysisError` | Display of results, member colours, view and display options |
16
- | Typed inputs and results in SI units | Unit display, project files, undo history, other UI state |
11
+ | The engine (this package) | The front-end application |
12
+ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
13
+ | Model validation with located errors (`details` name the node, member, shell, field…) | Input forms, drawing tools, highlighting the reported problems |
14
+ | Analysis: load cases, combinations, envelopes, member diagrams, shell forces | Rendering the model, the mesh, deformed shapes, diagrams and contours |
15
+ | Design checks with code-clause warnings and `RCDesignError` / `FeaError` | Display of results, member colours, view and display options |
16
+ | Typed inputs and results in SI units | Unit display, project files, undo history, other UI state |
17
17
 
18
18
  What this means in practice:
19
19
 
20
20
  - **No UI, no rendering, no presentation data.** Inputs and results carry structural quantities only; no colours, labels or view state.
21
- - **Plain JSON in, plain JSON out.** Models and results are serialisable, so the engine runs in a Web Worker, in the browser or in Node.
21
+ - **Plain JSON in, plain JSON out.** Models and results are serialisable, so the engine runs in a Web Worker, in the browser or in Node.js.
22
22
  - **No runtime dependencies** and no DOM or Node-only APIs.
23
- - **ESM and CommonJS builds** with `.d.ts` types, tree-shakeable (`"sideEffects": false`), split into subpath exports (`/fea`, `/rc`, `/strengthening`, `/core`, `/utils`).
23
+ - **ESM and CommonJS builds** with `.d.ts` types, tree-shakeable (`"sideEffects": false`), split into subpath exports.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pnpm add @theerapat-s28/ts-structural-eng-tools
29
+ # or: npm install @theerapat-s28/ts-structural-eng-tools
30
+ ```
31
+
32
+ Import from a module's subpath — `/fea`, `/rc`, `/strengthening`, `/core` or `/utils` — so a front end loads only what it uses, or from the package root, which re-exports every function, type and constant of every module.
33
+
34
+ **Upgrading from 2.x?** Nothing was removed, frame-only models give the same results, and the exports renamed in 3.2.0 keep their old names as deprecated aliases. Results gain a few fields, and models with shells bring new error codes and generated mesh nodes; [Upgrading from 2.x](CHANGELOG.md#upgrading-from-2x) in the changelog lists what a front end may need to handle.
24
35
 
25
36
  ## Documentation
26
37
 
27
38
  Full API reference, with parameter tables, worked examples, warnings and error codes:
28
39
 
29
40
  - **[API reference](https://theerapat-s28.github.io/ts-structural-eng/)** — landing page
41
+ - [FEA — Frame and shell analysis](https://theerapat-s28.github.io/ts-structural-eng/fea/) — `@theerapat-s28/ts-structural-eng-tools/fea`
30
42
  - [RC Beam Design](https://theerapat-s28.github.io/ts-structural-eng/rc/) — `@theerapat-s28/ts-structural-eng-tools/rc`
31
43
  - [Strengthening](https://theerapat-s28.github.io/ts-structural-eng/strengthening/) — `@theerapat-s28/ts-structural-eng-tools/strengthening`
32
- - [FEA — Frame Analysis](https://theerapat-s28.github.io/ts-structural-eng/fea/) — `@theerapat-s28/ts-structural-eng-tools/fea`
33
44
  - [Shell element theory notes](https://theerapat-s28.github.io/ts-structural-eng/shell-element.html) — how the shell element's stiffness is formed, with its benchmarks
34
45
 
35
46
  The pages are served from [`html-docs/`](https://github.com/theerapat-s28/ts-structural-eng/tree/main/html-docs) via GitHub Pages.
36
47
 
37
- ## Quick summary
38
-
39
- - Package: `@theerapat-s28/ts-structural-eng-tools`
40
- - Entry: `src/index.ts`
41
- - Build output: `dist/` (CommonJS + `.d.ts` files) and `dist/esm/` (ES modules)
42
-
43
- ## Prerequisites
44
-
45
- - Node.js (v18+ recommended)
46
- - pnpm
47
-
48
- ## Install
49
-
50
- Use the published package:
51
-
52
- ```bash
53
- pnpm add @theerapat-s28/ts-structural-eng-tools
54
- # or: npm install @theerapat-s28/ts-structural-eng-tools
55
- ```
56
-
57
- Or install this repository's dependencies to work on the library itself:
48
+ ## Quick start
58
49
 
59
- ```bash
60
- pnpm install
61
- ```
50
+ Two models, one with members only and one that adds a slab as shell elements. The values in the comments are what the engine returns for these inputs.
62
51
 
63
- ## Quick start
52
+ ### Frame only: an RC beam from analysis to design check
64
53
 
65
54
  Analyse a simply supported RC beam under self-weight and live load, envelope the design demands, and check them against the section's flexural capacity:
66
55
 
67
56
  ```ts
68
- import { analyzeFrame, memberDemandEnvelope } from "@theerapat-s28/ts-structural-eng-tools/fea";
69
- import type { FrameModel } from "@theerapat-s28/ts-structural-eng-tools/fea";
57
+ import { analyze, memberDemandEnvelope } from "@theerapat-s28/ts-structural-eng-tools/fea";
58
+ import type { FeaModel } from "@theerapat-s28/ts-structural-eng-tools/fea";
70
59
  import {
71
60
  rectBeamMomentCapacity,
72
61
  rectBeamToSectionProperties,
@@ -76,7 +65,7 @@ import {
76
65
  const beam = { Es: 200_000, fc_: 28, fy: 420, As: 1800, b: 300, h: 600, d: 540 };
77
66
  const { sectionProperties } = rectBeamToSectionProperties(beam, { member: "beam" });
78
67
 
79
- const model: FrameModel = {
68
+ const model: FeaModel = {
80
69
  nodes: [
81
70
  { id: "A", x: 0, y: 0, z: 0 },
82
71
  { id: "B", x: 6000, y: 0, z: 0 }, // mm
@@ -102,7 +91,13 @@ const model: FrameModel = {
102
91
  ],
103
92
  };
104
93
 
105
- const result = analyzeFrame(model);
94
+ const result = analyze(model);
95
+
96
+ // Diagrams along every member, ready for plotting
97
+ const { stations } = result.combinations["1.2D+1.6L"].memberDiagrams[0];
98
+ stations.find((s) => s.x === 3000); // M3 → 131.328 (kN·m), uz → -10.47731 (mm) at mid-span
99
+
100
+ // Design demands enveloped over the combinations, then the capacity check
106
101
  const demand = memberDemandEnvelope(result, "AB");
107
102
  // demand.MuPositive → 131.328 (kN·m), demand.Vu → 87.552 (kN), governed by "1.2D+1.6L"
108
103
 
@@ -110,22 +105,243 @@ const { phiMn, warnings } = rectBeamMomentCapacity(beam);
110
105
  // phiMn → 331.39 (kN·m), warnings → []
111
106
  ```
112
107
 
113
- Each module is also its own subpath export (`/fea`, `/rc`, `/strengthening`, `/core`, `/utils`), so a front end can import only what it uses. The package root re-exports the most-used functions and types from every module.
108
+ `rectBeamToSectionProperties` gives the frame section of an RC rectangle with the ACI 318-19 6.6.3.1.1 cracked inertia (0.35 Ig for a beam) and a unit weight, so `selfWeight: 1` loads the beam with its own weight.
109
+
110
+ ### Frame with shells: a slab on edge beams and columns
111
+
112
+ A 6 × 5 m, 200 mm slab on four edge beams and four columns. The slab is one shell area; the engine meshes it, splits the edge beams at the mesh nodes so they share them with the slab, and reports the slab's forces per element:
113
+
114
+ ```ts
115
+ import { analyze, memberDemandEnvelope } from "@theerapat-s28/ts-structural-eng-tools/fea";
116
+ import type { FeaModel } from "@theerapat-s28/ts-structural-eng-tools/fea";
117
+ import {
118
+ concreteElasticModulus,
119
+ rectBeamToSectionProperties,
120
+ } from "@theerapat-s28/ts-structural-eng-tools/rc";
121
+
122
+ const fc_ = 28; // MPa
123
+ const beam = rectBeamToSectionProperties({ fc_, b: 300, h: 600 }, { member: "beam" });
124
+ const column = rectBeamToSectionProperties({ fc_, b: 400, h: 400 }, { member: "column" });
125
+ const fixed = { ux: true, uy: true, uz: true, rx: true, ry: true, rz: true };
126
+
127
+ const model: FeaModel = {
128
+ nodes: [
129
+ { id: "A", x: 0, y: 0, z: 3500 }, // slab corners, mm
130
+ { id: "B", x: 6000, y: 0, z: 3500 },
131
+ { id: "C", x: 6000, y: 5000, z: 3500 },
132
+ { id: "D", x: 0, y: 5000, z: 3500 },
133
+ { id: "A0", x: 0, y: 0, z: 0 }, // column bases
134
+ { id: "B0", x: 6000, y: 0, z: 0 },
135
+ { id: "C0", x: 6000, y: 5000, z: 0 },
136
+ { id: "D0", x: 0, y: 5000, z: 0 },
137
+ ],
138
+ members: [
139
+ ...["A", "B", "C", "D"].map((id) => ({
140
+ id: `col${id}`,
141
+ startNodeId: `${id}0`,
142
+ endNodeId: id,
143
+ section: column.sectionProperties,
144
+ })),
145
+ { id: "AB", startNodeId: "A", endNodeId: "B", section: beam.sectionProperties }, // edge beams
146
+ { id: "BC", startNodeId: "B", endNodeId: "C", section: beam.sectionProperties },
147
+ { id: "CD", startNodeId: "C", endNodeId: "D", section: beam.sectionProperties },
148
+ { id: "DA", startNodeId: "D", endNodeId: "A", section: beam.sectionProperties },
149
+ ],
150
+ restraints: ["A0", "B0", "C0", "D0"].map((nodeId) => ({ nodeId, ...fixed })),
151
+ shells: [
152
+ {
153
+ id: "slab",
154
+ nodeIds: ["A", "B", "C", "D"], // counter-clockwise seen from above, so local 3 = +Z
155
+ section: { thickness: 200, E: concreteElasticModulus(fc_), nu: 0.2, unitWeight: 24 },
156
+ mesh: { maxSize: 500 }, // the engine divides it into 12 × 10 elements
157
+ },
158
+ ],
159
+ loadCases: [
160
+ { id: "D", type: "dead", selfWeight: 1 },
161
+ { id: "L", type: "live" },
162
+ ],
163
+ areaLoads: [{ loadCase: "L", areaId: "slab", direction: "Z", q: -3 }], // kN/m²
164
+ combinations: [
165
+ {
166
+ id: "1.2D+1.6L",
167
+ terms: [
168
+ { case: "D", factor: 1.2 },
169
+ { case: "L", factor: 1.6 },
170
+ ],
171
+ },
172
+ ],
173
+ };
174
+
175
+ const result = analyze(model);
176
+ const mesh = result.mesh!; // present whenever the model has shells
177
+
178
+ // Draw the mesh: 120 elements, each with its four corner nodes
179
+ const xyz = new Map([...model.nodes, ...mesh.nodes].map((n) => [n.id, n]));
180
+ const quads = mesh.shells.map((element) => element.nodeIds.map((id) => xyz.get(id)!));
181
+ // mesh.shells[0] → { id: "slab:0,0", areaId: "slab", nodeIds: ["A", "A~B:1", "slab:1,1", "A~D:1"] }
114
182
 
115
- ## Scripts
183
+ // Slab moments at the element corners, in the slab's local axes (local 1 = +X here)
184
+ const combo = result.combinations["1.2D+1.6L"];
185
+ const M11 = combo.shellForces.flatMap((element) => element.corners.map((c) => c.M11));
186
+ // Math.max(...M11) → 18.849 (kN·m/m), sagging at the slab centre
187
+ // Math.min(...M11) → -15.4157 (kN·m/m), hogging at the corners over the columns
116
188
 
117
- | Command | Description |
118
- | ---------------------- | ----------------------------------------------- |
119
- | `pnpm run dev` | Run the example file with `ts-node` |
120
- | `pnpm run build` | Compile TypeScript into `dist/` |
121
- | `pnpm run lint` | Lint source files with ESLint |
122
- | `pnpm run lint:fix` | Auto-fix lint issues |
123
- | `pnpm run format` | Format code with Prettier |
124
- | `pnpm run format:check`| Check formatting without writing |
125
- | `pnpm run test` | Run unit tests with Vitest |
126
- | `pnpm run test:watch` | Run tests in watch mode |
189
+ // Node results include the generated nodes; "slab:6,5" is the slab centre
190
+ combo.nodalDisplacements.find((d) => d.nodeId === "slab:6,5"); // uz → -4.99265 (mm)
127
191
 
128
- ## Project layout
192
+ // The edge beams are split at the mesh nodes, but report under their own ids
193
+ memberDemandEnvelope(result, "AB"); // MuPositive → 45.8202, MuNegative → 37.2024 (kN·m), Vu → 56.5957 (kN)
194
+
195
+ combo.reactions.reduce((sum, r) => sum + r.fz, 0); // → 495.36 (kN), the total factored load
196
+ ```
197
+
198
+ What changes for the front end when a model has shells:
199
+
200
+ - **Generated nodes.** `result.mesh.nodes` holds the nodes the engine generated, and `nodalDisplacements` and `reactions` list them after your own nodes. Their ids are strings even when yours are numbers: `"<areaId>:<i>,<j>"` inside an area, `"<a>~<b>:<k>"` along an edge between your nodes `a` and `b`.
201
+ - **Elements.** `result.mesh.shells` lists every element with its four nodes. `shellForces` and `envelope.shells` have one entry per element, named by `shellId`, with `areaId` linking it back to your area. An area that is not meshed is one element with the area's own id.
202
+ - **Shell forces** come at each element's corners, in its local axes: membrane forces `F11`, `F22`, `F12` (kN/m), moments `M11`, `M22`, `M12` (kN·m/m, positive with the −3 face in tension, i.e. sagging when local 3 is up), transverse shears `V13`, `V23` (kN/m) and top and bottom face stresses `S11Top` … `S12Bot` (MPa). Elements meeting at a node give different values there, as usual for shell results; average them for a smooth contour.
203
+ - **Members** keep their own ids in every result, even where a meshed edge splits them, and `calculationDetails.memberCount` counts your members.
204
+
205
+ ### Running in a Web Worker
206
+
207
+ Model and result are plain JSON, so post the model to a worker, run `analyze` there, and post the result back; the page stays responsive on large models. For big results, analyse with `{ diagrams: "extremes" }`, which keeps every diagram's extremes (so envelopes and `memberDemandEnvelope` still work) but drops the stations, and draw a member's full diagram on demand with `createAnalysis(model).memberDiagram(resultId, memberId)`. The reference has a [worker example](https://theerapat-s28.github.io/ts-structural-eng/fea/#frontend).
208
+
209
+ On a desktop PC (Intel Core i5-13500, Node.js 24):
210
+
211
+ | Model | Analysis time |
212
+ | --- | --- |
213
+ | Frame building: 880 nodes, 2 163 members, 3 load cases and 3 combinations | 1.7 s; with `diagrams: "extremes"` the result is 3.8× smaller (15 MB of JSON instead of 57 MB) |
214
+ | Slab on beams and columns: 4 × 4 bays, 2 304 shell elements, 14 556 DOFs | 1.7 s |
215
+
216
+ ## API
217
+
218
+ Summarised below; see the [full API reference](https://theerapat-s28.github.io/ts-structural-eng/) for parameter tables, units, warnings and error codes.
219
+
220
+ ### FEA — Frame and shell analysis
221
+
222
+ Linear-elastic 3D analysis by the direct stiffness method, from `@theerapat-s28/ts-structural-eng-tools/fea`. Global axes X, Y, Z (Z up); members and shells use ETABS-style local axes 1, 2, 3. Inputs are in mm, MPa, kN, kN·m and kN/m²; results are in kN, kN·m, mm and rad, and shell forces in kN/m, kN·m/m and MPa, as listed in `result.units`.
223
+
224
+ **Renamed in 3.2.0.** A model may have no frame members at all, e.g. a mat foundation, so the exports that apply to the whole model no longer say "frame": `analyzeFrame` → `analyze`, `createFrameAnalysis` → `createAnalysis`, `validateFrameModel` → `validateModel`, `AnalyzeFrameOptions` → `AnalyzeOptions`, `FrameAnalysis` → `Analysis`, and `Frame` → `Fea` in `FeaModel`, `FeaResult`, `FeaCaseResult`, `FeaEnvelope`, `FeaMesh`, `FeaNode`, `FeaId`, `FeaModelIssue`, `FeaErrorDetails`, `FeaError` and `FeaErrors`. The old names still work as deprecated aliases and are removed in 4.0.0.
225
+
226
+ #### Functions
227
+
228
+ - **`analyze(model, options?)`** — Validates the model, analyses every load case and forms every combination by superposition. The stiffness is factorised once for all cases, and combination diagrams are rebuilt from the combined forces, so their extremes are exact rather than sums of case extremes. `options.stations` sets the stations per member diagram (default 21); `options.diagrams: "extremes"` keeps only each diagram's extremes. Returns a `FeaResult`.
229
+ - **`createAnalysis(model, options?)`** — The same analysis, keeping the solution: returns `{ result, memberDiagram(resultId, memberId, stations?) }` to compute a member's full diagram on demand.
230
+ - **`validateModel(model)`** — Checks a model without analysing it and returns every problem, each with `details` (`nodeId`, `memberId`, `areaId`, `shellId`, `edge`, `loadCaseId`, `combinationId`, `field`, `dof`) for highlighting in a UI. `analyze` runs it first and throws the first problem as a `FeaError` whose `issues` lists all of them.
231
+ - **`distributeAreaLoads(model)`** — Returns `{ memberLoads, warnings }`: the member loads that the model's null areas send to their edge members, exactly what `analyze` adds, for checking or previewing. Loads on shells act on the shells and are not included.
232
+ - **`memberDemandEnvelope(result, memberId, options?)`** — Reduces a member's diagrams to `MuPositive`, `MuNegative`, `Vu` (optionally from distance `d` off the supports) and `Nu`, enveloped over all combinations (or the `combinations` / `cases` you select), with the `governing` combination of each, for the RC capacity functions.
233
+ - **`rectBeamToSectionProperties(section, options?)`** (from `rc`) — Converts a rectangular RC section into `SectionProperties` (`E`, `G`, `A`, `I22`, `I33`, `J`, `unitWeight`) with the ACI 318-19 6.6.3.1.1 inertia modifiers (0.35 beams, 0.70 columns) and a unit weight for self-weight (default 24 kN/m³, common practice rather than an ACI value).
234
+ - **`beam3dLocalStiffness(E, G, A, I22, I33, J, L)`** — The 12×12 local stiffness matrix exactly as in the reference "Complete Beam Stiffness Matrix". Its bending rotations R2 and R3 follow the opposite sign to the right-hand rule; **`beam3dLocalStiffnessRightHanded`** returns the right-handed form that `analyze` uses.
235
+ - **`memberAxes(start, end, angle?)`** — The ETABS-style local axes of a member as a direction-cosine matrix.
236
+
237
+ #### The model
238
+
239
+ A `FeaModel` is plain data. Every load names its `loadCase`, so loads are flat top-level lists:
240
+
241
+ | Field | What it holds |
242
+ | --- | --- |
243
+ | `nodes` | `{ id, x, y, z }` in mm; ids are strings or numbers |
244
+ | `members` | `{ id, startNodeId, endNodeId, section, angle?, releases? }`, with `section` a `SectionProperties`; omit for a model of shells only |
245
+ | `restraints` | Per node, each DOF `ux` … `rz` fixed (`true`) or a spring (kN/mm, kN·m/rad) |
246
+ | `lineRestraints` | `{ nodeIds: [a, b], ux? … rz? }`: fixes those DOFs at every node on the line from `a` to `b`, generated mesh nodes included, e.g. a wall base |
247
+ | `loadCases` | `{ id, name?, type?, selfWeight? }`; `selfWeight: 1` applies the full self-weight of members and shells |
248
+ | `nodalLoads` | `{ loadCase, nodeId, fx? … mz? }` in kN and kN·m |
249
+ | `memberLoads` | Uniform (`"udl"`), `"trapezoidal"` and `"point"` loads in local (`"1"`–`"3"`) or global (`"X"`–`"Z"`) directions, and `"torque"` about the member axis; uniform and trapezoidal loads in a global direction can be per unit projected length (`projected: true`), e.g. snow on a sloped rafter |
250
+ | `areas` | Null areas, `{ id, nodeIds, distribution, supportEdges? }` |
251
+ | `shells` | Shell areas, `{ id, nodeIds, type?, section, angle?, modifiers?, mesh? }` |
252
+ | `areaLoads` | `{ loadCase, areaId, direction, q, projected? }` in kN/m², on a null area or a shell; `direction` is the area's normal `"3"` or a global axis |
253
+ | `combinations` | `{ id, name?, terms }`, each term `{ case, factor }` or `{ combination, factor }`; nested combinations are expanded |
254
+
255
+ #### Members
256
+
257
+ End releases on any of the six end DOFs, full (`true`) or partial fixity (a spring stiffness in series with the member end), with the deformation across each release reported in `releaseDeformations`. Supports are fixed or springs. Joint rotations that no member resists, as at pin-jointed truss joints, are held automatically and listed in `calculationDetails.autoRestrainedDofs`.
258
+
259
+ #### Null areas or shells
260
+
261
+ Both are areas that take `areaLoads`, and they share one id namespace.
262
+
263
+ - **A null area** adds no stiffness: it only sends its loads to the members along its edges. It has 3 or 4 nodes forming a planar convex polygon. `"twoWay"` sends each point to its nearest edge (the corner-bisector split, any convex shape); `"oneWay"` spans strips between the listed `supportEdges`, parallel to the unsupported edges, half to each end; `"cantilever"` (rectangles) puts `q·c` and a torque `q·c²/2` on its one support edge. Edges split by intermediate nodes are cut at them. Use it for slabs whose stiffness you leave out of the model.
264
+ - **A shell area** has its own stiffness and shares nodes with members and other shells; use it for walls, cores, transfer slabs and mats. It is a four-node flat shell: a drilling-rotation membrane, so a beam framing into a wall in its plane transfers its moment, and a thin (DKQ) plate. `type` is `"shell"` (both parts, the default), `"membrane"` or `"plate"`. `section` is `{ thickness, E, nu, unitWeight? }`, and `modifiers` scale the membrane (`f11`, `f22`, `f12`) and bending (`m11`, `m22`, `m12`) stiffness and the self-weight (`weight`), e.g. for the cracked-section values of ACI 318-19 Table 6.6.3.1.1. Slightly warped quads are allowed, up to `SHELL_WARPING_LIMIT` (0.1·√A off the plane). Local 3 follows the right-hand rule around `nodeIds`, local 2 points up on walls and local 1 is +X on horizontal shells; `angle` rotates them.
265
+ - **Meshing.** Give a shell `mesh: { maxSize }` (mm) or `mesh: { divisions: [n1, n2] }` and the engine divides it into elements. Neighbouring areas share their mesh nodes, including where an area edge meets another at a node; model nodes on an area edge become mesh nodes; and members along a meshed edge are split there internally. Fix supports along a meshed edge with `lineRestraints`, which reach the generated nodes.
266
+
267
+ #### The result
268
+
269
+ | Field | What it holds |
270
+ | --- | --- |
271
+ | `cases`, `combinations` | Results keyed by id, each with `nodalDisplacements`, `reactions`, `memberEndForces`, `releaseDeformations`, `memberDiagrams` (`N`, `V2`, `V3`, `T`, `M2`, `M3` and deflections `u1`–`u3` local, `ux`–`uz` global, along each member), `shellForces`, an `equilibriumResidual` check and `warnings` |
272
+ | `envelope` | Max and min of every result over the combinations, with the governing combination; absent without combinations |
273
+ | `mesh` | The generated nodes and every shell element with its nodes; absent without shells |
274
+ | `calculationDetails` | Node (generated included), member, shell-element, DOF, load case and combination counts, `profileSize` and `autoRestrainedDofs` |
275
+ | `units`, `warnings` | The unit of each kind of result, and the warnings of every case and combination |
276
+
277
+ Invalid models and unstable structures throw `FeaError`, described under [Errors and warnings](#errors-and-warnings-from-core).
278
+
279
+ ### RC Beam Design
280
+
281
+ - **`rectBeamMomentCapacity(section)`** — Computes φMn for a rectangular RC beam (singly or doubly reinforced). Returns `phiMn` in kN·m with calculation details and ACI 318-19 warnings. `calculationDetails` reports `c`, `a`, `beta1`, `d`, `As` and `ro` for either section type, plus `d_`, `As_`, `fs_` and `ro_` when compression steel is present.
282
+ - **`rectBeamBarLayout(input)`** — Lays bar groups out into the minimum number of layers that satisfy ACI 318-19 25.2.1 / 25.2.2 spacing, and returns the resulting `d` and `d_` ready to feed into `rectBeamMomentCapacity`. `topBars` is optional; omit it for a section with no top reinforcement.
283
+ - **`concreteShearCapacity(section)`** — Computes φVc, the concrete contribution to shear capacity (ACI 318-19, 22.5.5.1).
284
+ - **`stirrupShearCapacity(section)`** — Computes φVs, the stirrup contribution to shear capacity (ACI 318-19, 22.5.10.5.3).
285
+ - **`checkStirrupRequirement(input)`** — Determines whether stirrups are required, and the required Av/s ratio and max spacing (ACI 318-19, 9.6.3.1, 9.6.3.3, 9.7.6.2.2).
286
+
287
+ ### Strengthening
288
+
289
+ - **`calculateSteelJacketedBeamMomentCapacity(section, jacketedProperties)`** — Computes φMn for a beam strengthened with steel plate jacketing, as a `before` / `after` pair. The bottom plate is added to the tension steel and the top plate to the compression steel, which turns an originally singly reinforced section into a doubly reinforced one.
290
+ - **`plateInterfaceShearFlow(input)`** — Computes the shear flow `q = V·Q/I` (N/mm) to transfer across each concrete-to-plate interface, from a cracked (default) or uncracked elastic transformed section. Returns a per-plate `top` / `bottom` result, `null` where a plate is absent.
291
+ - **`boltShearCapacity(bolt)`** — Computes φVsa for one anchor bolt from the steel strength (ACI 318-19, 17.7.1.2b, φ per Table 17.5.3). Concrete breakout and pryout are not evaluated and must be checked separately.
292
+ - **`plateInterfaceBoltRequirement(input)`** — Computes the required bolt pitch and count at each interface, taking the larger of the shear-flow requirement (`s = n·φVbolt / q`) and the count needed to develop the full plate yield force, and reports which one governs.
293
+
294
+ Both plates are independently optional: a plate counts as present only when its width and thickness are both positive, so the same functions cover a bottom-only jacket and a top-and-bottom jacket. Use `hasTopPlate(props)` / `hasBottomPlate(props)` to test for presence.
295
+
296
+ Plates bolted to the sides of the web strengthen the beam in shear instead. ACI 318-19 has no provisions for steel side plates, so two independent models are offered and both return a `before` / `after` pair of `phiVn` (kN) for a continuous plate or discrete strips, with the plate contribution limited by the bolt anchorage and by the ACI 318-19 22.5.1.2 web-crushing cap. Use `hasSidePlate(props)` to test for presence.
297
+
298
+ - **`sidePlateShearCapacityByWebYielding(section, plates)`** — Takes each plate as a supplementary web reaching the von Mises shear yield stress `0.6·fy` (AISC 360 G2.1), and warns on plate slenderness beyond `2.24·√(Es/fy)`.
299
+ - **`sidePlateShearCapacityByTensionTie(section, plates)`** — Takes each plate as a tension tie crossing the diagonal crack, per the bonded shear reinforcement model of ACI 440.2R-17 §11.4 with steel in place of FRP: `ffe = min(fy, Es·0.004)` and `psi_f = 0.85`. Supports inclined strips.
300
+
301
+ ACI 440.2R-17 11.4.2 (spacing, which defers to the ACI 318 limits) and 11.4.3 (`Vs + Vf ≤ 0.66·√f'c·bw·d`) are verified against the guide; the subsections carrying the 0.004 strain cap and `psi_f` are still cited at §11.4 only. See the citation-status note at the top of `src/strengthening/rc-beam-side-plate-shear.ts`.
302
+ - **`compareSidePlateShearCapacity(section, plates)`** — Runs both models and reports the lower `phiVn` as `governing`; the intended entry point for a conservative design capacity.
303
+
304
+ ### Concrete properties (from `rc`)
305
+
306
+ - **`concreteBeta(fc_)`** — ACI 318 β₁ factor
307
+ - **`concreteElasticModulus(fc_)`** — Ec = 4700√f'c (MPa)
308
+ - **`psiToMpa(psi)`** — Unit conversion
309
+
310
+ ### Errors and warnings (from `core`)
311
+
312
+ A design that is valid but breaks a code provision returns normally with an entry in `warnings`, citing the clause (e.g. `"ACI318-19, 9.6.1.2"`). A calculation whose assumptions break down throws instead, never returning a silently wrong result:
313
+
314
+ - **`RCDesignError`** — `code` and `message` from `Errors`: 1XX for RC beam design (e.g. 102, tensile steel not yielding; 105, `Vs` above the 22.5.1.2 limit), 2XX for strengthening and plate connections (201–206).
315
+ - **`FeaError`** — `code` and `message` from `FeaErrors`, `details` locating the problem, and `issues` listing every problem `validateModel` found. Codes 301–317 cover the model, members, loads, null areas and combinations (304 is an unstable structure); 326–329 cover shells: 326 invalid geometry, 327 invalid section, 328 a translation that nothing resists at a node connected only to membranes or plates, 329 an area that cannot be meshed. Match on `code`, not on `message`.
316
+
317
+ `core` also exports the shared input types, the type guards `isSinglyReinforced`, `hasTopPlate`, `hasBottomPlate` and `hasSidePlate`, and the engineering constants (φ factors, ultimate concrete strain, inertia modifiers, frame-analysis tolerances, `SHELL_WARPING_LIMIT`).
318
+
319
+ ### Utilities (from `utils`)
320
+
321
+ Low-level helpers the engine is built on, exported for reuse: `solveQuadratic`, `roundToDecimalPlaces`, `mergeWarnings`, dense matrix helpers (`zeros`, `identity`, `transpose`, `matMul`, `matVec`), Cholesky and general linear solvers (`choleskyFactor`, `choleskySolveFactored`, `choleskySolve`, `solveLinearSystem`), and the skyline solver (`createSkyline`, `skylineAdd`, `skylineGet`, `skylineSize`, `skylineFactor`, `skylineSolve`, `reverseCuthillMcKee`).
322
+
323
+ ## Development
324
+
325
+ To work on the library itself you need a current Node.js LTS (22 or later) and pnpm. Install the dependencies with:
326
+
327
+ ```bash
328
+ pnpm install
329
+ ```
330
+
331
+ ### Scripts
332
+
333
+ | Command | Description |
334
+ | ----------------------- | ------------------------------------------------------------- |
335
+ | `pnpm run dev` | Run [`examples/basic-usage.ts`](examples/basic-usage.ts) against the sources with `ts-node` |
336
+ | `pnpm run build` | Compile `src/` into `dist/` (CommonJS and `.d.ts`) and `dist/esm/` (ES modules) |
337
+ | `pnpm run lint` | Lint source files with ESLint |
338
+ | `pnpm run lint:fix` | Auto-fix lint issues |
339
+ | `pnpm run format` | Format code with Prettier |
340
+ | `pnpm run format:check` | Check formatting without writing |
341
+ | `pnpm run test` | Run unit tests with Vitest |
342
+ | `pnpm run test:watch` | Run tests in watch mode |
343
+
344
+ ### Project layout
129
345
 
130
346
  ```
131
347
  src/
@@ -143,6 +359,7 @@ src/
143
359
  │ └── rc-beam-side-plate-shear.ts # Shear capacity before/after bolted side plates (two models)
144
360
  ├── fea/ # Finite-element analysis
145
361
  │ ├── index.ts
362
+ │ ├── deprecated.ts # Names before 3.2.0 (analyzeFrame, FrameModel, …), aliases until 4.0.0
146
363
  │ ├── shared/ # The base every part builds on
147
364
  │ │ ├── index.ts
148
365
  │ │ ├── types/ # Nodes, restraints, area loads
@@ -168,14 +385,14 @@ src/
168
385
  │ └── analysis/ # The whole model, members and shells together (direct stiffness method)
169
386
  │ ├── index.ts
170
387
  │ ├── mesh/ # The analysed model: shell mesh, line restraints, member splitting at mesh nodes
171
- │ ├── solver/ # System build + skyline factorisation, per-case solve, combinations, analyzeFrame, distributeAreaLoads
172
- │ ├── validation/ # validateFrameModel: every input problem, with its location
388
+ │ ├── solver/ # System build + skyline factorisation, per-case solve, combinations, analyze, distributeAreaLoads
389
+ │ ├── validation/ # validateModel: every input problem, with its location
173
390
  │ ├── post/ # Result presentation, envelopes, design demands
174
- │ └── types/ # FrameModel (load cases, combinations, nodal loads, line restraints) and FrameResult
391
+ │ └── types/ # FeaModel (load cases, combinations, nodal loads, line restraints) and FeaResult
175
392
  ├── core/ # Shared infrastructure
176
393
  │ ├── index.ts
177
394
  │ ├── constants/ # RC design constants (ACI 318) and frame-analysis tolerances
178
- │ ├── errors/ # RCDesignError and FrameAnalysisError with their coded entries
395
+ │ ├── errors/ # RCDesignError and FeaError with their coded entries
179
396
  │ └── types/ # Shared types: RC sections, plate jacketing, section properties, warnings, units
180
397
  └── utils/ # Helper utilities
181
398
  ├── index.ts
@@ -191,22 +408,23 @@ tests/
191
408
  ├── fea/frame/ # Tests for members and null areas
192
409
  ├── fea/shell/ # Tests for shell elements, meshing and shell results
193
410
  ├── fea/shared/ # Tests for element assembly and automatic restraint
194
- ├── fea/analysis/ # Tests for analyzeFrame, combinations, validation, errors and RC integration
411
+ ├── fea/analysis/ # Tests for analyze, combinations, validation, errors and RC integration
195
412
  └── utils/ # Tests for utilities
196
413
  html-docs/ # HTML API reference, deployed to GitHub Pages
197
414
  ├── DESIGN.md # Style guide for the doc pages
198
415
  ├── index.html # Landing page
199
416
  ├── rc/index.html
200
417
  ├── strengthening/index.html
201
- └── fea/index.html
418
+ ├── fea/index.html
419
+ └── shell-element.html # Shell element theory notes
202
420
  ROADMAP.md # FEA roadmap and implementation plan (not published)
203
421
  .agents/skills/update-version/ # AI agent skill: bump the version and roll the changelog
204
422
  .claude/skills/npm-publish/ # AI agent skill: release checklist for publishing to npm
205
423
  ```
206
424
 
207
- ## TypeScript path aliases
425
+ ### TypeScript path aliases
208
426
 
209
- This project uses `baseUrl` + `paths` for clean imports:
427
+ The sources import each other through `baseUrl` + `paths` aliases:
210
428
 
211
429
  ```jsonc
212
430
  "paths": {
@@ -219,78 +437,17 @@ This project uses `baseUrl` + `paths` for clean imports:
219
437
  }
220
438
  ```
221
439
 
222
- For runtime resolution, `tsconfig-paths` is registered in the `dev` script.
223
-
224
- ## API
225
-
226
- Summarised below; see the [full API reference](https://theerapat-s28.github.io/ts-structural-eng/) for parameter tables, units, warnings and error codes.
227
-
228
- ### RC Beam Design
229
-
230
- - **`rectBeamMomentCapacity(section)`** — Computes φMn for a rectangular RC beam (singly or doubly reinforced). Returns `phiMn` in kN·m with calculation details and ACI 318-19 warnings. `calculationDetails` reports `c`, `a`, `beta1`, `d`, `As` and `ro` for either section type, plus `d_`, `As_`, `fs_` and `ro_` when compression steel is present.
231
- - **`rectBeamBarLayout(input)`** — Lays bar groups out into the minimum number of layers that satisfy ACI 318-19 25.2.1 / 25.2.2 spacing, and returns the resulting `d` and `d_` ready to feed into `rectBeamMomentCapacity`. `topBars` is optional; omit it for a section with no top reinforcement.
232
- - **`concreteShearCapacity(section)`** — Computes φVc, the concrete contribution to shear capacity (ACI 318-19, 22.5.5.1).
233
- - **`stirrupShearCapacity(section)`** — Computes φVs, the stirrup contribution to shear capacity (ACI 318-19, 22.5.10.5.3).
234
- - **`checkStirrupRequirement(input)`** — Determines whether stirrups are required, and the required Av/s ratio and max spacing (ACI 318-19, 9.6.3.1, 9.6.3.3, 9.7.6.2.2).
235
-
236
- ### Strengthening
237
-
238
- - **`calculateSteelJacketedBeamMomentCapacity(section, jacketedProperties)`** — Computes φMn for a beam strengthened with steel plate jacketing, as a `before` / `after` pair. The bottom plate is added to the tension steel and the top plate to the compression steel, which turns an originally singly reinforced section into a doubly reinforced one.
239
- - **`plateInterfaceShearFlow(input)`** — Computes the shear flow `q = V·Q/I` (N/mm) to transfer across each concrete-to-plate interface, from a cracked (default) or uncracked elastic transformed section. Returns a per-plate `top` / `bottom` result, `null` where a plate is absent.
240
- - **`boltShearCapacity(bolt)`** — Computes φVsa for one anchor bolt from the steel strength (ACI 318-19, 17.7.1.2b, φ per Table 17.5.3). Concrete breakout and pryout are not evaluated and must be checked separately.
241
- - **`plateInterfaceBoltRequirement(input)`** — Computes the required bolt pitch and count at each interface, taking the larger of the shear-flow requirement (`s = n·φVbolt / q`) and the count needed to develop the full plate yield force, and reports which one governs.
242
-
243
- Both plates are independently optional: a plate counts as present only when its width and thickness are both positive, so the same functions cover a bottom-only jacket and a top-and-bottom jacket. Use `hasTopPlate(props)` / `hasBottomPlate(props)` to test for presence.
244
-
245
- Plates bolted to the sides of the web strengthen the beam in shear instead. ACI 318-19 has no provisions for steel side plates, so two independent models are offered and both return a `before` / `after` pair of `phiVn` (kN) for a continuous plate or discrete strips, with the plate contribution limited by the bolt anchorage and by the ACI 318-19 22.5.1.2 web-crushing cap. Use `hasSidePlate(props)` to test for presence.
246
-
247
- - **`sidePlateShearCapacityByWebYielding(section, plates)`** — Takes each plate as a supplementary web reaching the von Mises shear yield stress `0.6·fy` (AISC 360 G2.1), and warns on plate slenderness beyond `2.24·√(Es/fy)`.
248
- - **`sidePlateShearCapacityByTensionTie(section, plates)`** — Takes each plate as a tension tie crossing the diagonal crack, per the bonded shear reinforcement model of ACI 440.2R-17 §11.4 with steel in place of FRP: `ffe = min(fy, Es·0.004)` and `psi_f = 0.85`. Supports inclined strips.
249
-
250
- ACI 440.2R-17 11.4.2 (spacing, which defers to the ACI 318 limits) and 11.4.3 (`Vs + Vf ≤ 0.66·√f'c·bw·d`) are verified against the guide; the subsections carrying the 0.004 strain cap and `psi_f` are still cited at §11.4 only. See the citation-status note at the top of `src/strengthening/rc-beam-side-plate-shear.ts`.
251
- - **`compareSidePlateShearCapacity(section, plates)`** — Runs both models and reports the lower `phiVn` as `governing`; the intended entry point for a conservative design capacity.
252
-
253
- ### FEA — Frame Analysis
254
-
255
- Linear-elastic 3D frame analysis by the direct stiffness method, imported from `@theerapat-s28/ts-structural-eng-tools/fea`. Global axes X, Y, Z (Z up) and ETABS-style local axes 1, 2, 3; inputs in mm, MPa, kN and kN·m, results in kN, kN·m, mm and rad.
256
-
257
- - **`analyzeFrame(model, options?)`** — Analyses every load case in `model.loadCases` (dead, superimposed dead, live, wind…, each with an optional `selfWeight` multiplier) and forms every combination in `model.combinations` by superposition. The loads are flat top-level lists, `model.nodalLoads` and `model.memberLoads` (uniform, trapezoidal and point loads in local or global directions, and torques about the member axis; uniform and trapezoidal loads in a global direction can be given per unit projected length with `projected: true`, e.g. snow on a sloped rafter), each load tagged with its `loadCase`, so large models stay readable. Area loads (`model.areaLoads`, kN/m²) on null areas (`model.areas`) are first distributed to the members along each area's edges — see `distributeAreaLoads`. Shell areas (`model.shells`, `{ id, nodeIds, type?, section: { thickness, E, nu, unitWeight? }, angle?, modifiers? }`) are four-node flat shell elements with their own stiffness — a drilling-rotation membrane and a thin (DKQ) plate, as `"shell"`, `"membrane"` or `"plate"` — for walls, cores and slabs; they share nodes with members, take `areaLoads` naming their id and self-weight as corner forces, and accept cracked-section modifiers (`f11`…`m12`). Give an area `mesh: { maxSize }` (or `{ divisions }`) and the engine meshes it: neighbouring areas, including T-junctions, share their mesh nodes, model nodes on an area edge become mesh nodes, and members along meshed edges are split internally while their results stay under their own id; `model.lineRestraints` (`{ nodeIds: [a, b], ux?…rz? }`) fix every node on a line, generated nodes included, and `result.mesh` lists the generated nodes and elements. A translation that nothing resists at a node connected only to membranes or plates throws 328, an area that cannot be meshed 329. Each case and combination reports `shellForces` — per shell and corner, membrane forces `F11`, `F22`, `F12` (kN/m), moments `M11`, `M22`, `M12` (kN·m/m, sagging positive), shears `V13`, `V23` and top/bottom stresses — and the envelope has them per corner. A combination is a list of factored terms `{ case, factor }` or `{ combination, factor }`; factors are entered by the user, and nested combinations are expanded. Supports end releases — full (`true`) or partial fixity (a spring stiffness in series with the member end) on any of the six end DOFs, with the deformation across each release reported in `releaseDeformations` — and fixed or spring supports. Joint rotations that no member resists, as at pin-jointed truss joints, are held automatically and listed in `calculationDetails.autoRestrainedDofs`. The stiffness matrix is factorised once for all cases. Returns `cases` and `combinations` keyed by id — each with displacements, reactions, member end forces, member diagrams along each member (forces `N`, `V2`, `V3`, `T`, `M2`, `M3` and deflections `u1`–`u3` local / `ux`–`uz` global, ready for plotting) and an equilibrium check — and an `envelope` of max/min over the combinations with the governing combination. Combination diagrams are rebuilt exactly, not summed from case extremes. The stiffness is stored in skyline (banded) form with nodes in reverse Cuthill–McKee order, so a building of ~850 nodes runs in about 1.5 s. Model and result are plain JSON, so it runs in a Web Worker. Invalid models, load cases or combinations and mechanisms throw `FrameAnalysisError` (codes 301–317 and 326–329) with `details` locating the problem.
258
- - **`createFrameAnalysis(model, options?)`** — Same analysis, keeping the solution: returns `{ result, memberDiagram(resultId, memberId) }` so full member diagrams can be computed on demand. Combine with `options.diagrams: "extremes"`, which keeps diagram extremes (and the envelope) but drops the station arrays, for results about 4× smaller on large models.
259
- - **`validateFrameModel(model)`** — Checks a model without analysing it and returns every problem, each with `details` (`nodeId`, `memberId`, `areaId`, `shellId`, `edge`, `loadCaseId`, `combinationId`, `field`, `dof`) for highlighting in a UI. `analyzeFrame` runs it first and throws the first problem as a `FrameAnalysisError` whose `issues` lists all of them; non-finite numbers, negative springs, invalid end-release values and unknown option values are rejected (314).
260
- - **`distributeAreaLoads(model)`** — Returns `{ memberLoads, warnings }`: the member loads that the model's null areas send to their edge members, exactly what `analyzeFrame` adds, for checking or previewing (loads on shell areas act on the shells and are not included). A null area (`{ id, nodeIds, distribution, supportEdges? }`) has 3 or 4 nodes forming a planar convex polygon and adds no stiffness. `"twoWay"` sends each point to its nearest edge (the corner-bisector split, any convex shape); `"oneWay"` spans strips between the listed `supportEdges`, parallel to the unsupported edges, half to each end; `"cantilever"` (rectangles) puts `q·c` and a torque `q·c²/2` on its one support edge. Area loads (`{ loadCase, areaId, direction: "3" | "X" | "Y" | "Z", q, projected? }`) are per unit true area, `"3"` being the area's normal; with `projected: true` and a global direction, `q` is per unit projected area (e.g. per plan area on a ramp) and is converted to true area before distribution. Edges split by intermediate nodes are cut at them. Area problems throw 315 (geometry), 316 (an edge without members) or 317 (supports that do not fit the distribution).
261
- - **`memberDemandEnvelope(result, memberId, options?)`** — Reduces a member's diagrams to `MuPositive`, `MuNegative`, `Vu` (optionally at distance `d` from the supports) and `Nu`, enveloped over all combinations (or the `combinations` / `cases` you select), with the `governing` combination of each, for the RC capacity functions.
262
- - **`rectBeamToSectionProperties(section, options?)`** (from `rc`) — Converts a rectangular RC section into `SectionProperties` (`E`, `G`, `A`, `I22`, `I33`, `J`, `unitWeight`) with the ACI 318-19 6.6.3.1.1 inertia modifiers (0.35 beams, 0.70 columns) and a unit weight for self-weight (default 24 kN/m³, common practice rather than an ACI value).
263
- - **`beam3dLocalStiffness(E, G, A, I22, I33, J, L)`** — The 12×12 local stiffness matrix exactly as in the reference "Complete Beam Stiffness Matrix". Its bending rotations R2 and R3 follow the opposite sign to the right-hand rule; **`beam3dLocalStiffnessRightHanded`** returns the right-handed form that `analyzeFrame` uses.
264
- - **`memberAxes(start, end, angle?)`** — The ETABS-style local axes of a member as a direction-cosine matrix.
265
-
266
- ### Concrete properties (from `rc`)
267
-
268
- - **`concreteBeta(fc_)`** — ACI 318 β₁ factor
269
- - **`concreteElasticModulus(fc_)`** — Ec = 4700√f'c (MPa)
270
- - **`psiToMpa(psi)`** — Unit conversion
271
-
272
- ### Errors and warnings (from `core`)
273
-
274
- A design that is valid but breaks a code provision returns normally with an entry in `warnings`, citing the clause (e.g. `"ACI318-19, 9.6.1.2"`). A calculation whose assumptions break down throws instead, never returning a silently wrong result:
275
-
276
- - **`RCDesignError`** — `code` and `message` from `Errors`: 1XX for RC beam design (e.g. 102, tensile steel not yielding; 105, `Vs` above the 22.5.1.2 limit), 2XX for strengthening and plate connections (201–206).
277
- - **`FrameAnalysisError`** — `code` from `FrameErrors` (301–317), `details` locating the problem, and `issues` listing every problem `validateFrameModel` found.
278
-
279
- `core` also exports the shared input types, the type guards `isSinglyReinforced`, `hasTopPlate`, `hasBottomPlate` and `hasSidePlate`, and the engineering constants (φ factors, ultimate concrete strain, inertia modifiers, frame-analysis tolerances).
280
-
281
- ### Utilities (from `utils`)
282
-
283
- Low-level helpers the engine is built on, exported for reuse: `solveQuadratic`, `roundToDecimalPlaces`, `mergeWarnings`, dense matrix helpers (`zeros`, `identity`, `transpose`, `matMul`, `matVec`), Cholesky and general linear solvers (`choleskyFactor`, `choleskySolveFactored`, `choleskySolve`, `solveLinearSystem`), and the skyline solver (`createSkyline`, `skylineAdd`, `skylineGet`, `skylineSize`, `skylineFactor`, `skylineSolve`, `reverseCuthillMcKee`).
440
+ They are duplicated in `vitest.config.ts`, registered at runtime by `tsconfig-paths` in the `dev` script, and rewritten into relative paths by `tsc-alias` when building.
284
441
 
285
- ## Build
442
+ ### Build
286
443
 
287
444
  ```bash
288
445
  pnpm run build
289
446
  ```
290
447
 
291
- The build emits a CommonJS build in `dist/` (for `require`, with the `.d.ts` types) and an ES-module build in `dist/esm/` (for `import` and bundlers such as Vite); `package.json` `exports` routes each subpath to the right one, and `"sideEffects": false` lets bundlers drop unused code. `pnpm run dev` runs [`examples/basic-usage.ts`](examples/basic-usage.ts) against the sources without building.
448
+ The build emits a CommonJS build in `dist/` (for `require`, with the `.d.ts` types) and an ES-module build in `dist/esm/` (for `import` and bundlers such as Vite); `package.json` `exports` routes each subpath to the right one, and `"sideEffects": false` lets bundlers drop unused code. Only `dist/`, `README.md`, `CHANGELOG.md` and `LICENSE` are published.
292
449
 
293
- ## Tests
450
+ ### Tests
294
451
 
295
452
  ```bash
296
453
  pnpm run test # all tests
@@ -299,7 +456,7 @@ pnpm vitest run tests/rc/rc-beam-design.test.ts # one file
299
456
 
300
457
  Tests live in `tests/`, mirroring `src/`, and check results against hand-calculated or textbook values.
301
458
 
302
- ## Releasing
459
+ ### Releasing
303
460
 
304
461
  Releases follow semantic versioning and are published to npm with tags `vX.Y.Z`. What changed in each version is in [CHANGELOG.md](CHANGELOG.md). Preparing a version (changelog, version number, checks) is described in [`.agents/skills/update-version/SKILL.md`](.agents/skills/update-version/SKILL.md), and publishing (release commit and tag, dry run, publish, push tags) in [`.claude/skills/npm-publish/SKILL.md`](.claude/skills/npm-publish/SKILL.md).
305
462