dsh-math-calculator 2.1.1 → 2.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 (44) hide show
  1. package/CHANGELOG.md +921 -0
  2. package/README.md +64 -50
  3. package/dsh.plugin.json +2 -2
  4. package/lib/__wtest.txt +1 -0
  5. package/lib/awareness.js +69 -20
  6. package/lib/format.js +522 -13
  7. package/lib/formula-library-em.js +428 -0
  8. package/lib/formulas.js +28 -1
  9. package/lib/guards.js +653 -0
  10. package/lib/help.js +509 -0
  11. package/lib/index.js +1357 -212
  12. package/lib/ops/shared.js +547 -6
  13. package/lib/ops/units-op.js +116 -10
  14. package/lib/ops/workspace-op.js +10 -0
  15. package/lib/playbook.js +32 -7
  16. package/lib/types/format.d.ts +31 -1
  17. package/lib/types/formula-library-em.d.ts +28 -0
  18. package/lib/types/formulas.d.ts +2 -0
  19. package/lib/types/guards.d.ts +255 -0
  20. package/lib/types/help.d.ts +70 -0
  21. package/lib/types/index.d.ts +34 -1
  22. package/lib/types/ops/shared.d.ts +87 -3
  23. package/lib/types/playbook.d.ts +28 -0
  24. package/lib/types/units-data.d.ts +253 -0
  25. package/lib/types/units.d.ts +54 -4
  26. package/lib/types/workspace.d.ts +50 -0
  27. package/lib/units-data.js +1037 -0
  28. package/lib/units.js +82 -117
  29. package/lib/workspace.js +54 -0
  30. package/package.json +1 -1
  31. package/src/awareness.ts +69 -20
  32. package/src/format.ts +520 -12
  33. package/src/formula-library-em.ts +435 -0
  34. package/src/formulas.ts +35 -4
  35. package/src/guards.ts +673 -0
  36. package/src/help.ts +543 -0
  37. package/src/index.ts +1492 -213
  38. package/src/ops/shared.ts +589 -9
  39. package/src/ops/units-op.ts +125 -9
  40. package/src/ops/workspace-op.ts +10 -0
  41. package/src/playbook.ts +32 -7
  42. package/src/units-data.ts +1128 -0
  43. package/src/units.ts +149 -130
  44. package/src/workspace.ts +65 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * 公式库:电学 / 光学 / 核物理与化学计量。
3
+ *
4
+ * ## 为什么单独成批
5
+ *
6
+ * 单位表补齐了 18 个量纲(电流、电压、电阻、磁通密度、照度、活度、
7
+ * 吸收剂量、催化活度……),但**公式库几乎空白**:
8
+ *
9
+ * | 量纲 | 补齐前 |
10
+ * |---|---|
11
+ * | 磁通密度 / 立体角 / 发光强度 / 照度 | 0 条 |
12
+ * | 放射性活度 / 吸收剂量 / 剂量当量 / 催化活度 | 0 条 |
13
+ *
14
+ * 有单位没公式 = 那批单位永远产出不出来。这一批按**该量纲最常被问到的量**
15
+ * 补齐,每条都能独立算出该量纲的一个值。
16
+ *
17
+ * ## 收录判据(与其它批次一致)
18
+ *
19
+ * 能写成闭合式、参数现实可得、不依赖外部数据集。
20
+ * 经验式必须标注适用域;只有在地球成立的,desc 里写明。
21
+ *
22
+ * @module dsh-math-calculator/formula-library-em
23
+ */
24
+ import { CONSTANTS } from './formula-library.js';
25
+ /** 带默认值的参数。 */
26
+ const p = (value, desc) => ({ value, desc });
27
+ /** 必填参数。 */
28
+ const req = (desc) => ({ value: null, desc });
29
+ /**
30
+ * 电学 / 光学 / 核物理 / 化学计量公式。
31
+ */
32
+ export const EM_OPTICAL_NUCLEAR_FORMULAS = [
33
+ // ══════════════════════════════════════════════════════════════════
34
+ // 电磁学 · 基本关系
35
+ // ══════════════════════════════════════════════════════════════════
36
+ {
37
+ id: 'ohm_current',
38
+ cat: 'physics/em',
39
+ name: '欧姆定律(求电流)',
40
+ expr: 'V / R',
41
+ params: { V: req('电压 V'), R: req('电阻 Ω') },
42
+ unit: 'A',
43
+ desc: 'I = V/R。与 ohm(求电压)互为逆式,避免每次自己移项。',
44
+ keys: ['ohm', 'current', '欧姆', '电流'],
45
+ },
46
+ {
47
+ id: 'ohms_law_resistance',
48
+ cat: 'physics/em',
49
+ name: '欧姆定律(求电阻)',
50
+ expr: 'V / I',
51
+ params: { V: req('电压 V'), I: req('电流 A') },
52
+ unit: 'Ω',
53
+ desc: 'R = V/I。',
54
+ keys: ['resistance', '电阻'],
55
+ },
56
+ {
57
+ id: 'electric_power_vi',
58
+ cat: 'physics/em',
59
+ name: '电功率(电压电流式)',
60
+ expr: 'V * I',
61
+ params: { V: req('电压 V'), I: req('电流 A') },
62
+ unit: 'W',
63
+ desc: 'P = VI。与 joule_power(I²R 式)互补:已知电压电流时用这条。',
64
+ keys: ['power', '功率'],
65
+ },
66
+ {
67
+ id: 'resistors_mixed_two',
68
+ cat: 'physics/em',
69
+ name: '两电阻并联后的等效电阻',
70
+ expr: '(R1 * R2) / (R1 + R2)',
71
+ params: { R1: req('电阻1 Ω'), R2: req('电阻2 Ω') },
72
+ unit: 'Ω',
73
+ desc: 'R = R1R2/(R1+R2)。并联结果必小于任一支路,可用来自查。',
74
+ keys: ['parallel', '并联', '分流'],
75
+ },
76
+ {
77
+ id: 'conductance_from_resistance',
78
+ cat: 'physics/em',
79
+ name: '电导(电阻倒数)',
80
+ expr: '1 / R',
81
+ params: { R: req('电阻 Ω') },
82
+ unit: 'S',
83
+ desc: 'G = 1/R。电导与电阻互为倒数,小电阻对应大电导。',
84
+ keys: ['conductance', '电导', '西门子'],
85
+ },
86
+ {
87
+ id: 'charge_from_current_time',
88
+ cat: 'physics/em',
89
+ name: '电荷量(电流×时间)',
90
+ expr: 'I * t',
91
+ params: { I: req('电流 A'), t: req('时间 s') },
92
+ unit: 'C',
93
+ desc: 'Q = It。电池容量「1 Ah = 3600 C」就是这条的常用形式。',
94
+ keys: ['charge', '电荷', '安时'],
95
+ },
96
+ {
97
+ id: 'capacitor_charge_from_cv',
98
+ cat: 'physics/em',
99
+ name: '电容电荷(Q=CU)',
100
+ expr: 'C * V',
101
+ params: { C: req('电容 F'), V: req('电压 V') },
102
+ unit: 'C',
103
+ desc: 'Q = CU。与 capacitor_charge 同义,参数名更直观。',
104
+ keys: ['capacitor', '电容', '电荷'],
105
+ },
106
+ {
107
+ id: 'rc_time_constant_tau',
108
+ cat: 'physics/em',
109
+ name: 'RC 时间常数',
110
+ expr: 'R * C',
111
+ params: { R: req('电阻 Ω'), C: req('电容 F') },
112
+ unit: 's',
113
+ desc: 'τ = RC。经过 1τ 充到 63.2%,5τ 基本充满 —— 电路快慢的判断依据。',
114
+ keys: ['rc', '时间常数', 'tau'],
115
+ },
116
+ {
117
+ id: 'rc_discharge_voltage_tau',
118
+ cat: 'physics/em',
119
+ name: 'RC 放电电压(τ 倍数式)',
120
+ expr: 'V0 * exp(-n)',
121
+ params: { V0: req('初始电压 V'), n: p(1, '经过几个时间常数 τ') },
122
+ unit: 'V',
123
+ desc: 'V = V0·e^(−n),n = t/τ。n=1 剩 37%,n=5 剩 0.67%。',
124
+ keys: ['rc', '放电', '衰减'],
125
+ },
126
+ {
127
+ id: 'inductor_energy_half_li2',
128
+ cat: 'physics/em',
129
+ name: '电感储能',
130
+ expr: '0.5 * L * I^2',
131
+ params: { L: req('电感 H'), I: req('电流 A') },
132
+ unit: 'J',
133
+ desc: 'E = ½LI²。与电容储能 ½CV² 对称 —— 电感储能与电流平方成正比。',
134
+ keys: ['inductor', '电感', '储能'],
135
+ },
136
+ {
137
+ id: 'lc_resonance_freq_hz',
138
+ cat: 'physics/em',
139
+ name: 'LC 谐振频率',
140
+ expr: '1 / (2 * pi * sqrt(L * C))',
141
+ params: { L: req('电感 H'), C: req('电容 F') },
142
+ unit: 'Hz',
143
+ desc: 'f = 1/(2π√(LC))。',
144
+ keys: ['resonance', '谐振', 'lc'],
145
+ },
146
+ // ══════════════════════════════════════════════════════════════════
147
+ // 磁学(补齐 magnetic_flux_density 的空白)
148
+ // ══════════════════════════════════════════════════════════════════
149
+ {
150
+ id: 'magnetic_field_wire',
151
+ cat: 'physics/em',
152
+ name: '直导线磁场(安培环路)',
153
+ expr: 'mu0 * I / (2 * pi * r)',
154
+ params: { I: req('电流 A'), r: req('到导线距离 m'), mu0: p(CONSTANTS.mu0, '真空磁导率') },
155
+ unit: 'T',
156
+ desc: 'B = μ0I/(2πr)。距离翻倍磁场减半 —— 这是「磁通密度 T」最基础的产出式。',
157
+ keys: ['magnetic field', '磁场', '导线'],
158
+ },
159
+ {
160
+ id: 'magnetic_field_solenoid',
161
+ cat: 'physics/em',
162
+ name: '螺线管内部磁场',
163
+ expr: 'mu0 * N * I / L',
164
+ params: { N: req('匝数'), I: req('电流 A'), L: req('螺线管长度 m'), mu0: p(CONSTANTS.mu0, '真空磁导率') },
165
+ unit: 'T',
166
+ desc: 'B = μ0NI/L(忽略螺线管端部效应,L ≫ 直径 时成立)。',
167
+ keys: ['solenoid', '螺线管', '线圈'],
168
+ },
169
+ {
170
+ id: 'magnetic_flux_from_bt',
171
+ cat: 'physics/em',
172
+ name: '磁通量(B×面积)',
173
+ expr: 'B * A * cos(theta)',
174
+ params: { B: req('磁通密度 T'), A: req('面积 m²'), theta: p(0, '法线夹角 rad') },
175
+ unit: 'Wb',
176
+ desc: 'Φ = BA·cosθ。θ=0 时通量最大,θ=90° 时为零(磁感线平行于面)。',
177
+ keys: ['flux', '磁通量', '韦伯'],
178
+ },
179
+ {
180
+ id: 'ampere_force_two_wires',
181
+ cat: 'physics/em',
182
+ name: '两平行导线间的作用力',
183
+ expr: 'mu0 * I1 * I2 * L / (2 * pi * d)',
184
+ params: { I1: req('电流1 A'), I2: req('电流2 A'), L: req('导线长度 m'), d: req('间距 m'), mu0: p(CONSTANTS.mu0, '真空磁导率') },
185
+ unit: 'N',
186
+ desc: 'F = μ0I1I2L/(2πd)。同向相吸、反向相斥。',
187
+ keys: ['ampere', '安培力', '导线'],
188
+ },
189
+ {
190
+ id: 'faraday_emf_from_flux_change',
191
+ cat: 'physics/em',
192
+ name: '法拉第电磁感应(通量变化率式)',
193
+ expr: 'N * (B2 - B1) * A / t',
194
+ params: { N: req('匝数'), B2: req('末磁通密度 T'), B1: req('初磁通密度 T'), A: req('面积 m²'), t: req('时间 s') },
195
+ unit: 'V',
196
+ desc: 'E = N·Δ(BA)/Δt。与 faraday_emf(速度式)是同一现象的两条写法。',
197
+ keys: ['faraday', '感应', '法拉第'],
198
+ },
199
+ {
200
+ id: 'inductance_solenoid',
201
+ cat: 'physics/em',
202
+ name: '螺线管电感',
203
+ expr: 'mu0 * N^2 * A / L',
204
+ params: { N: req('匝数'), A: req('截面积 m²'), L: req('长度 m'), mu0: p(CONSTANTS.mu0, '真空磁导率') },
205
+ unit: 'H',
206
+ desc: 'L = μ0N²A/L。匝数翻倍电感变四倍(N² 关系)。',
207
+ keys: ['inductance', '电感', '螺线管'],
208
+ },
209
+ {
210
+ id: 'transformer_current_ratio',
211
+ cat: 'physics/em',
212
+ name: '变压器电流比',
213
+ expr: 'I1 * N1 / N2',
214
+ params: { I1: req('初级电流 A'), N1: req('初级匝数'), N2: req('次级匝数') },
215
+ unit: 'A',
216
+ desc: 'I2 = I1·N1/N2。升压则降流,功率守恒(理想变压器)。',
217
+ keys: ['transformer', '变压器', '电流'],
218
+ },
219
+ {
220
+ id: 'power_factor_real',
221
+ cat: 'physics/em',
222
+ name: '有功功率(含功率因数)',
223
+ expr: 'V * I * cos(phi)',
224
+ params: { V: req('电压 V'), I: req('电流 A'), phi: p(0, '功率因数角 rad') },
225
+ unit: 'W',
226
+ desc: 'P = VI·cosφ。φ=0 时是纯阻性(cosφ=1);感性负载 cosφ<1,视在功率大于有功。',
227
+ keys: ['power factor', '功率因数', '有功'],
228
+ },
229
+ // ══════════════════════════════════════════════════════════════════
230
+ // 光学(补齐立体角 / 光通量 / 照度 / 发光强度)
231
+ // ══════════════════════════════════════════════════════════════════
232
+ {
233
+ id: 'solid_angle_cone',
234
+ cat: 'physics/optics',
235
+ name: '圆锥立体角',
236
+ expr: '2 * pi * (1 - cos(theta))',
237
+ params: { theta: req('半顶角 rad') },
238
+ unit: 'sr',
239
+ desc: 'Ω = 2π(1−cosθ)。半顶角 90° 时得 2π(半空间),180° 时得 4π(整球)。',
240
+ keys: ['solid angle', '立体角', '圆锥'],
241
+ },
242
+ {
243
+ id: 'illuminance_inverse_square',
244
+ cat: 'physics/optics',
245
+ name: '照度(距离平方反比)',
246
+ expr: 'I / r^2',
247
+ params: { I: req('发光强度 cd'), r: req('距离 m') },
248
+ unit: 'lx',
249
+ desc: 'E = I/r²。点光源的照度随距离平方衰减 —— 「离灯近一倍,亮四倍」。',
250
+ keys: ['illuminance', '照度', '照度衰减'],
251
+ },
252
+ {
253
+ id: 'luminous_flux_from_intensity_angle',
254
+ cat: 'physics/optics',
255
+ name: '光通量(发光强度×立体角)',
256
+ expr: 'I * omega',
257
+ params: { I: req('发光强度 cd'), omega: req('立体角 sr') },
258
+ unit: 'lm',
259
+ desc: 'Φ = I·Ω。整球 4π 时,1 cd 的点光源给出 4π lm ≈ 12.57 lm。',
260
+ keys: ['luminous flux', '光通量', '流明'],
261
+ },
262
+ {
263
+ id: 'luminance_required',
264
+ cat: 'physics/optics',
265
+ name: '房间所需光通量',
266
+ expr: 'E * A',
267
+ params: { E: req('目标照度 lx'), A: req('面积 m²') },
268
+ unit: 'lm',
269
+ desc: 'Φ = E·A。已知「每平米要多少勒克斯」就能算总光通量,再除光效得瓦数。',
270
+ keys: ['lighting', '照明', '光通量'],
271
+ },
272
+ {
273
+ id: 'lamp_power_from_flux',
274
+ cat: 'physics/optics',
275
+ name: '灯具功率(按光效)',
276
+ expr: 'phi / efficacy',
277
+ params: { phi: req('光通量 lm'), efficacy: p(100, '光效 lm/W(LED 约 100,白炽约 12)') },
278
+ unit: 'W',
279
+ desc: 'P = Φ/η。光效 η 是这类的关键参数:LED 100—150 lm/W,荧光灯 60—90,白炽灯 12—15。',
280
+ keys: ['lamp', '灯具', '光效'],
281
+ },
282
+ {
283
+ id: 'illuminance_from_flux_area',
284
+ cat: 'physics/optics',
285
+ name: '照度(光通量/面积)',
286
+ expr: 'phi / A',
287
+ params: { phi: req('光通量 lm'), A: req('受照面积 m²') },
288
+ unit: 'lx',
289
+ desc: 'E = Φ/A。',
290
+ keys: ['illuminance', '照度', '光通量'],
291
+ },
292
+ // ══════════════════════════════════════════════════════════════════
293
+ // 核物理(补齐放射性活度 / 吸收剂量 / 剂量当量)
294
+ // ══════════════════════════════════════════════════════════════════
295
+ {
296
+ id: 'decay_constant_from_halflife',
297
+ cat: 'physics/nuclear',
298
+ name: '衰变常数(由半衰期)',
299
+ expr: 'log(2) / T',
300
+ params: { T: req('半衰期 s') },
301
+ unit: '1/s',
302
+ desc: 'λ = ln2 / T½。放射性活度、衰减比例都从这条出发。(公式表达式用 log:mathjs 的 log 即自然对数,ln 只在 evaluate 层做归一化,公式库不经那层。)',
303
+ keys: ['decay', '衰变', '半衰期'],
304
+ },
305
+ {
306
+ id: 'activity_from_atoms',
307
+ cat: 'physics/nuclear',
308
+ name: '放射性活度(原子数×衰变常数)',
309
+ expr: 'N * log(2) / T',
310
+ params: { N: req('当前放射性原子数'), T: req('半衰期 s') },
311
+ unit: 'Bq',
312
+ desc: 'A = Nλ = N·ln2/T½。活度是「每秒衰变多少次」,与原子总数成正比。',
313
+ keys: ['activity', '活度', '贝可'],
314
+ },
315
+ {
316
+ id: 'activity_from_mass',
317
+ cat: 'physics/nuclear',
318
+ name: '放射性活度(由质量衡量)',
319
+ expr: '(m / M) * NA * log(2) / T',
320
+ params: { m: req('放射性核素质量 g'), M: req('摩尔质量 g/mol'), T: req('半衰期 s'), NA: p(CONSTANTS.NA, '阿伏伽德罗常数') },
321
+ unit: 'Bq',
322
+ desc: 'A = (m/M)·NA·ln2/T½。这是「1 克镭-226 约 3.7×10¹⁰ Bq」的来源,也就是居里的定义。',
323
+ keys: ['activity', '活度', '居里', '质量'],
324
+ },
325
+ {
326
+ id: 'radiation_absorbed_dose',
327
+ cat: 'physics/nuclear',
328
+ name: '吸收剂量',
329
+ expr: 'E / m',
330
+ params: { E: req('沉积能量 J'), m: req('受照质量 kg') },
331
+ unit: 'Gy',
332
+ desc: 'D = E/m。1 Gy = 1 J/kg —— 与比热容同量纲,但含义是辐射沉积。',
333
+ keys: ['dose', '吸收剂量', '戈瑞'],
334
+ },
335
+ {
336
+ id: 'dose_equivalent_from_dose',
337
+ cat: 'physics/nuclear',
338
+ name: '剂量当量(吸收剂量×权重因子)',
339
+ expr: 'D * Q',
340
+ params: { D: req('吸收剂量 Gy'), Q: p(1, '辐射权重因:X/γ=1、β=1、中子≈10、α=20') },
341
+ unit: 'Sv',
342
+ desc: 'H = D·Q。同样 1 Gy,α 粒子造成的生物损伤是 X 射线的 20 倍 —— 这就是 Sv 与 Gy 分开的原因。',
343
+ keys: ['dose equivalent', '剂量当量', '希沃特'],
344
+ },
345
+ {
346
+ id: 'radiation_dose_rate',
347
+ cat: 'physics/nuclear',
348
+ name: '辐射剂量率',
349
+ expr: 'A * E_per_decay * t_exp / m',
350
+ params: { A: req('源活度 Bq'), E_per_decay: req('每次衰变沉积能量 J'), t_exp: req('暴露时间 s'), m: req('受照质量 kg') },
351
+ unit: 'Gy',
352
+ desc: 'D = A·E_d·t/m。活度与累积剂量的桥:同样的源,暴露时间翻倍剂量翻倍。',
353
+ keys: ['dose rate', '剂量率', '辐照'],
354
+ },
355
+ {
356
+ id: 'radioactive_remaining_fraction',
357
+ cat: 'physics/nuclear',
358
+ name: '剩余比例(经过若干半衰期)',
359
+ expr: '0.5^n',
360
+ params: { n: req('经过几个半衰期') },
361
+ unit: '无量纲',
362
+ desc: 'N/N₀ = 0.5ⁿ。n=1 剩一半,n=10 剩 0.1%,n=30 剩十亿分之一。',
363
+ keys: ['half-life', '半衰期', '衰减'],
364
+ },
365
+ {
366
+ id: 'shielding_halfvalue_layers',
367
+ cat: 'physics/nuclear',
368
+ name: '屏蔽所需半值层数',
369
+ expr: 'log(I0 / I) / log(2)',
370
+ params: { I0: req('入射强度'), I: req('目标强度') },
371
+ unit: '层',
372
+ desc: 'n = log(I₀/I)/log2。每穿一层半值层衰减一半 —— 计算防护厚度的标准做法。',
373
+ keys: ['shielding', '屏蔽', '防护'],
374
+ },
375
+ // ══════════════════════════════════════════════════════════════════
376
+ // 化学计量(补齐物质的量 / 催化活度)
377
+ // ══════════════════════════════════════════════════════════════════
378
+ {
379
+ id: 'moles_from_mass',
380
+ cat: 'chemistry/mole',
381
+ name: '物质的量(质量/摩尔质量)',
382
+ expr: 'm / M',
383
+ params: { m: req('质量 g'), M: req('摩尔质量 g/mol') },
384
+ unit: 'mol',
385
+ desc: 'n = m/M。化学计量最基础的一条:18 g 水 = 1 mol。',
386
+ keys: ['mole', '摩尔', '物质的量'],
387
+ },
388
+ {
389
+ id: 'mass_from_moles_compound',
390
+ cat: 'chemistry/mole',
391
+ name: '质量(物质的量×摩尔质量)',
392
+ expr: 'n * M',
393
+ params: { n: req('物质的量 mol'), M: req('摩尔质量 g/mol') },
394
+ unit: 'g',
395
+ desc: 'm = nM。与 moles_from_mass 互为逆式。',
396
+ keys: ['mass', '质量', '摩尔'],
397
+ },
398
+ {
399
+ id: 'catalyst_rate_from_units',
400
+ cat: 'chemistry/kinetics',
401
+ name: '酶活(单位 U 换算成 kat)',
402
+ expr: 'U * 1.6666666666666667e-8',
403
+ params: { U: req('酶活 U(µmol/min)') },
404
+ unit: 'kat',
405
+ desc: '1 U = 1 µmol/min ≈ 1.667×10⁻⁸ kat。实验报告多用 U,SI 用 kat。',
406
+ keys: ['enzyme', '酶活', '催化'],
407
+ },
408
+ {
409
+ id: 'enzyme_activity_from_rata',
410
+ cat: 'chemistry/kinetics',
411
+ name: '催化活度(底物消耗速率)',
412
+ expr: 'delta_n / t',
413
+ params: { delta_n: req('底物消耗量 mol'), t: req('时间 s') },
414
+ unit: 'kat',
415
+ desc: 'kat = Δn/Δt,每秒转化多少摩尔底物。',
416
+ keys: ['catalytic', '催化活度', '开特'],
417
+ },
418
+ {
419
+ id: 'molar_concentration_mol_per_l',
420
+ cat: 'chemistry/solution',
421
+ name: '摩尔浓度(mol/L)',
422
+ expr: 'n / V',
423
+ params: { n: req('溶质物质的量 mol'), V: req('溶液体积 L') },
424
+ unit: 'mol/L',
425
+ desc: 'c = n/V。生物与化学里「mmol/L」是临床化验的标准写法。',
426
+ keys: ['concentration', '浓度', '摩尔'],
427
+ },
428
+ ];
package/lib/formulas.js CHANGED
@@ -18,6 +18,7 @@ import { STORY2_FORMULAS } from './formula-library-story2.js';
18
18
  import { POWER_FORMULAS } from './formula-library-power.js';
19
19
  import { BASIC_FORMULAS } from './formula-library-basic.js';
20
20
  import { GENERIC_FORMULAS } from './formula-library-generic.js';
21
+ import { EM_OPTICAL_NUCLEAR_FORMULAS } from './formula-library-em.js';
21
22
  import { getActiveConstants } from './constants-graph.js';
22
23
  import { loadGlobalFormulas } from './formula-pack.js';
23
24
  /**
@@ -33,6 +34,7 @@ export const FORMULAS = [
33
34
  ...POWER_FORMULAS,
34
35
  ...BASIC_FORMULAS,
35
36
  ...GENERIC_FORMULAS,
37
+ ...EM_OPTICAL_NUCLEAR_FORMULAS,
36
38
  ];
37
39
  /** id 索引(后批次覆盖同名 id 时以后者为准,便于后续修订)。 */
38
40
  export const FORMULA_INDEX = new Map();
@@ -116,6 +118,31 @@ export function resolveParams(formula, overrides, extraConstants = {}) {
116
118
  const missing = [];
117
119
  // 当前生效的世界常数(由 operation=constants 设定)
118
120
  const active = getActiveConstants();
121
+ /**
122
+ * ── 收集「传了但用不上」的参数名 ──
123
+ *
124
+ * 实测反馈:`overrides` 的 key 与公式参数名不匹配时**静默忽略**,
125
+ * 于是结果「合理但错误」。报告原话:
126
+ *
127
+ * 「本会话中焦土能量账曾因参数拼写问题导致一次『236 倍缺口』误判」
128
+ *
129
+ * 这类错最隐蔽:公式照常出数,只是那个没生效的参数用了默认值。
130
+ * 所以要把它明确回给调用方,而不是悄悄丢掉。
131
+ *
132
+ * 判定:既不在公式 params 里、也不是常数表里的名字 —— 后者是**合法**的
133
+ * (公式可以引用 g / c / G 这类世界常数),不能算错拼。
134
+ */
135
+ const unknownKeys = [];
136
+ const declaredNames = new Set(Object.keys(formula.params));
137
+ for (const key of Object.keys(overrides ?? {})) {
138
+ if (declaredNames.has(key))
139
+ continue;
140
+ if (Object.prototype.hasOwnProperty.call(active, key))
141
+ continue;
142
+ if (Object.prototype.hasOwnProperty.call(extraConstants, key))
143
+ continue;
144
+ unknownKeys.push(key);
145
+ }
119
146
  for (const [name, spec] of Object.entries(formula.params)) {
120
147
  const arrayLike = isArrayParam(spec.desc);
121
148
  // 1. 调用方显式覆盖(最高优先级)
@@ -157,7 +184,7 @@ export function resolveParams(formula, overrides, extraConstants = {}) {
157
184
  // 5. 必填但没给
158
185
  missing.push(name);
159
186
  }
160
- return { values, overridden, missing };
187
+ return { values, overridden, missing, unknownKeys };
161
188
  }
162
189
  /**
163
190
  * 把参数表拼成 mathjs 可用的 scope。