dsh-math-calculator 2.3.3 → 2.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +180 -0
- package/dsh.plugin.json +1 -1
- package/lib/formula-library-process.js +375 -0
- package/lib/formulas.js +4 -0
- package/lib/guards.js +30 -2
- package/lib/help.js +513 -156
- package/lib/index.js +57 -11
- package/lib/types/formula-library-process.d.ts +45 -0
- package/lib/types/help.d.ts +15 -1
- package/package.json +1 -1
- package/src/formula-library-process.ts +393 -0
- package/src/formulas.ts +3 -0
- package/src/guards.ts +28 -1
- package/src/help.ts +527 -158
- package/src/index.ts +57 -9
package/lib/index.js
CHANGED
|
@@ -3425,7 +3425,7 @@ export function runMathTool(rawArguments) {
|
|
|
3425
3425
|
if (hintNote)
|
|
3426
3426
|
result.notice = hintNote;
|
|
3427
3427
|
/** 调低计算精度导致结果被截 —— 那种「看起来正常、末位是假的」情况。 */
|
|
3428
|
-
const truncNote = checkPrecisionTruncation(requestedPrecision, precision, result.result);
|
|
3428
|
+
const truncNote = checkPrecisionTruncation(requestedPrecision, precision, result.result, input);
|
|
3429
3429
|
if (truncNote) {
|
|
3430
3430
|
result.notice = result.notice ? result.notice + '\n' + truncNote : truncNote;
|
|
3431
3431
|
}
|
|
@@ -4143,7 +4143,7 @@ let ctxRef = null;
|
|
|
4143
4143
|
* @param text - 已格式化的结果文本
|
|
4144
4144
|
* @returns 提示文本;不需要时返回空串
|
|
4145
4145
|
*/
|
|
4146
|
-
function checkPrecisionTruncation(requested, effective, text) {
|
|
4146
|
+
function checkPrecisionTruncation(requested, effective, text, source) {
|
|
4147
4147
|
/**
|
|
4148
4148
|
* 判据:调用方显式给的精度**低于默认 64** 才算「调低了」,
|
|
4149
4149
|
* 然后看结果是否真的装不下。
|
|
@@ -4153,17 +4153,63 @@ function checkPrecisionTruncation(requested, effective, text) {
|
|
|
4153
4153
|
*/
|
|
4154
4154
|
if (requested === undefined || requested >= DEFAULT_PRECISION)
|
|
4155
4155
|
return '';
|
|
4156
|
-
/**
|
|
4156
|
+
/**
|
|
4157
|
+
* ## 两个判据,缺一不可(本轮补的是第二个)
|
|
4158
|
+
*
|
|
4159
|
+
* ① **结果装不下**:整数位数 > precision,末几位被截。
|
|
4160
|
+
* ② **表达式本身超出精度**:即使结果看起来很小或干脆是 0。
|
|
4161
|
+
*
|
|
4162
|
+
* ★ ② 是本轮实测抓到的漏洞(外部反馈文档 2.3):
|
|
4163
|
+
*
|
|
4164
|
+
* ```
|
|
4165
|
+
* (10^20+1)-(10^20), precision=20 → 0 ok=true, notice 空
|
|
4166
|
+
* ```
|
|
4167
|
+
*
|
|
4168
|
+
* 结果被截成了 **0** —— 而 0 只有 1 位,判据① 立刻放行。
|
|
4169
|
+
* 于是「两个 20 位数相减、结果被算成 0」这种最危险的失真,
|
|
4170
|
+
* 恰恰因为结果太小而绕过了检查。
|
|
4171
|
+
*
|
|
4172
|
+
* 这类「结果越离谱、越容易逃过检查」的结构必须堵掉,
|
|
4173
|
+
* 所以改成**看输入而不只看输出**:表达式里出现 10^n(n 超过 precision)
|
|
4174
|
+
* 或长整数串时,无论结果多小都提示。
|
|
4175
|
+
*/
|
|
4157
4176
|
const numeric = text.trim().match(/^-?([0-9]+)(?:\.([0-9]+))?$/);
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4177
|
+
const integerDigits = numeric ? numeric[1].replace(/^0+(?=.)/, '').length : 0;
|
|
4178
|
+
const resultOverflows = numeric !== null && integerDigits > effective;
|
|
4179
|
+
/**
|
|
4180
|
+
* 表达式里是否有超出 precision 的大数。
|
|
4181
|
+
*
|
|
4182
|
+
* 两种形态:
|
|
4183
|
+
* · 幂字面量 `10^20`、`1e20`
|
|
4184
|
+
* · 长整数串(连续 16 位以上数字)
|
|
4185
|
+
*
|
|
4186
|
+
* 阈值用 effective(而不是固定值):precision=20 时 `10^20` 是边界,
|
|
4187
|
+
* `10^21` 才必然丢位;而 precision=8 时 `10^9` 就丢。
|
|
4188
|
+
*/
|
|
4189
|
+
/**
|
|
4190
|
+
* 临界点是 `power >= effective`,不是 `>`。
|
|
4191
|
+
*
|
|
4192
|
+
* 因为 `10^20` 是「1 后跟 20 个零」= **21 位**数字,
|
|
4193
|
+
* precision=20 只能表示 20 位有效数字,装不下它。
|
|
4194
|
+
* 所以 precision=20 时 `10^20` 已在边界上,`10^20+1` 里的 +1 必然被吃掉。
|
|
4195
|
+
*
|
|
4196
|
+
* 第一版写成 `>` 时,`(10^20+1)-(10^20)` 恰好卡在边界外,漏掉了。
|
|
4197
|
+
*/
|
|
4198
|
+
const powerMatch = source.match(/10\s*\^\s*(\d+)/);
|
|
4199
|
+
const powerBeyond = powerMatch ? Number(powerMatch[1]) >= effective : false;
|
|
4200
|
+
/** 科学记数法字面量(`1e20`)同样算大数。 */
|
|
4201
|
+
const expMatch = source.match(/[0-9][0-9.]*[eE]\s*(\d+)/);
|
|
4202
|
+
const expBeyond = expMatch ? Number(expMatch[1]) >= effective : false;
|
|
4203
|
+
const longDigits = /(^|[^0-9.eE+-])[0-9]{16,}(?![0-9])/.test(source);
|
|
4204
|
+
const sourceOverflows = powerBeyond || expBeyond || longDigits;
|
|
4205
|
+
if (!resultOverflows && !sourceOverflows)
|
|
4162
4206
|
return '';
|
|
4163
|
-
|
|
4164
|
-
'
|
|
4165
|
-
|
|
4166
|
-
'
|
|
4207
|
+
const reason = resultOverflows
|
|
4208
|
+
? '本次结果有 ' + integerDigits + ' 位有效数字,precision=' + effective +
|
|
4209
|
+
' 只能精确表示 ' + effective + ' 位——末 ' + (integerDigits - effective) + ' 位可能已失真。'
|
|
4210
|
+
: '表达式里出现了超出 precision=' + effective + ' 能表示的大数,结果可能已失真' +
|
|
4211
|
+
(numeric !== null ? '(本次结果 ' + text.trim() + ')' : '') + '。';
|
|
4212
|
+
return reason + '需要精确值时请用 precision:64(默认),只想让输出短些请用 displayPrecision。';
|
|
4167
4213
|
}
|
|
4168
4214
|
/**
|
|
4169
4215
|
* 批量调公式(`formulas: [...]`)。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公式库第十四批:**工艺级工程公式**(v2.3.5)。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么单独成批
|
|
5
|
+
*
|
|
6
|
+
* 外部实测反馈(《工程公式缺口与公式库扩展建议》,2026-11-08)的核心判断:
|
|
7
|
+
*
|
|
8
|
+
* > 「公式库不是数量少(499 条),而是『工艺级工程公式』稀薄 ——
|
|
9
|
+
* > 它能算『灌溉要多少水』,算不了『掺 10% 砂的陶坯在 1100℃ 会不会裂』。」
|
|
10
|
+
*
|
|
11
|
+
* 实测分布证实了这点:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* physics 187 条 ← 极充实,解释「世界为什么这样」
|
|
15
|
+
* engineering 21 条 ← 全是顶层物理式(梁柱/热机/管流/泵)
|
|
16
|
+
* 机械 0 命中 | 发酵 0 命中 | 窑 0 命中
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* 也就是说:**已有的 187 条物理规律往下接一层就够用,缺的是那座桥。**
|
|
20
|
+
*
|
|
21
|
+
* ## 这批补的链
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
24
|
+
* 能源链 kiln_heat_balance → kiln_firing_time → wall_heat_loss
|
|
25
|
+
* 材料链 metallurgy_yield → case_depth → gear_bending_strength
|
|
26
|
+
* 传动链 gear_geometry / pendulum_clock_drift / gravity_storage_discharge
|
|
27
|
+
* 农业链 growing_degree_days → leaching_residue → fermentation_time → syrup_concentration
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* ## 一个「自洽性检查」公式的特别价值
|
|
31
|
+
*
|
|
32
|
+
* `gravity_storage_discharge` 不是为了算新数,而是**检查设定自不自洽**:
|
|
33
|
+
* 625J、8W、12 分钟各自都「看着合理」,只有放进同一个式子才暴露出差了数量级。
|
|
34
|
+
* 这类「把散落的好看数字拼起来才出错」的场景,正是公式库最该覆盖的。
|
|
35
|
+
*
|
|
36
|
+
* ## 可信度
|
|
37
|
+
*
|
|
38
|
+
* 本批多为 `phenomenological`(形式对、系数需标定)。
|
|
39
|
+
* 每个 desc 都给了典型区间与「换条件要重标定」的说明。
|
|
40
|
+
*
|
|
41
|
+
* @module dsh-math-calculator/formula-library-process
|
|
42
|
+
*/
|
|
43
|
+
import type { FormulaDef } from './formula-library.js';
|
|
44
|
+
/** 工艺级工程公式 14 条。 */
|
|
45
|
+
export declare const PROCESS_FORMULAS: FormulaDef[];
|
package/lib/types/help.d.ts
CHANGED
|
@@ -96,7 +96,21 @@ export interface HelpTopic {
|
|
|
96
96
|
* 各节注册表。**顺序即目录顺序**,而顺序决定「AI 会不会翻到它」。
|
|
97
97
|
*
|
|
98
98
|
* 排前面的是高频或高风险节:`evaluate` 最常用、`world` 体量最大。
|
|
99
|
-
*
|
|
99
|
+
*
|
|
100
|
+
* ## v2.3.4 从 14 节扩到 26 节
|
|
101
|
+
*
|
|
102
|
+
* 不是为了塞更多内容,而是**把「按需付费」做彻底**:
|
|
103
|
+
*
|
|
104
|
+
* ```
|
|
105
|
+
* 原 domains 8707 字符 九个互不相关的领域压在一起
|
|
106
|
+
* 原 recipe 5371 字符 三条场景配方 + 通用手法 + 错误码
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* 查「财政赤字」要读梁柱挠度与 Péclet 数,查「帝国会不会崩」要先读 2834 字符的材料链 ——
|
|
110
|
+
* 这不合理。拆开后单次查询从 8707 降到 2840(最大节),典型任务约 2400。
|
|
111
|
+
*
|
|
112
|
+
* ★ 固定成本(schema 6212 字符)**一分没涨**:扩容全部发生在按需层。
|
|
113
|
+
* 目录从 1083 涨到 1284 —— 多出的 201 字符换来 12 个可独立查询的节。
|
|
100
114
|
*/
|
|
101
115
|
export declare const HELP_TOPICS: HelpTopic[];
|
|
102
116
|
/**
|
package/package.json
CHANGED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公式库第十四批:**工艺级工程公式**(v2.3.5)。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么单独成批
|
|
5
|
+
*
|
|
6
|
+
* 外部实测反馈(《工程公式缺口与公式库扩展建议》,2026-11-08)的核心判断:
|
|
7
|
+
*
|
|
8
|
+
* > 「公式库不是数量少(499 条),而是『工艺级工程公式』稀薄 ——
|
|
9
|
+
* > 它能算『灌溉要多少水』,算不了『掺 10% 砂的陶坯在 1100℃ 会不会裂』。」
|
|
10
|
+
*
|
|
11
|
+
* 实测分布证实了这点:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* physics 187 条 ← 极充实,解释「世界为什么这样」
|
|
15
|
+
* engineering 21 条 ← 全是顶层物理式(梁柱/热机/管流/泵)
|
|
16
|
+
* 机械 0 命中 | 发酵 0 命中 | 窑 0 命中
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* 也就是说:**已有的 187 条物理规律往下接一层就够用,缺的是那座桥。**
|
|
20
|
+
*
|
|
21
|
+
* ## 这批补的链
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
24
|
+
* 能源链 kiln_heat_balance → kiln_firing_time → wall_heat_loss
|
|
25
|
+
* 材料链 metallurgy_yield → case_depth → gear_bending_strength
|
|
26
|
+
* 传动链 gear_geometry / pendulum_clock_drift / gravity_storage_discharge
|
|
27
|
+
* 农业链 growing_degree_days → leaching_residue → fermentation_time → syrup_concentration
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* ## 一个「自洽性检查」公式的特别价值
|
|
31
|
+
*
|
|
32
|
+
* `gravity_storage_discharge` 不是为了算新数,而是**检查设定自不自洽**:
|
|
33
|
+
* 625J、8W、12 分钟各自都「看着合理」,只有放进同一个式子才暴露出差了数量级。
|
|
34
|
+
* 这类「把散落的好看数字拼起来才出错」的场景,正是公式库最该覆盖的。
|
|
35
|
+
*
|
|
36
|
+
* ## 可信度
|
|
37
|
+
*
|
|
38
|
+
* 本批多为 `phenomenological`(形式对、系数需标定)。
|
|
39
|
+
* 每个 desc 都给了典型区间与「换条件要重标定」的说明。
|
|
40
|
+
*
|
|
41
|
+
* @module dsh-math-calculator/formula-library-process
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import type { FormulaDef } from './formula-library.js'
|
|
45
|
+
|
|
46
|
+
const p = (value: number, desc: string) => ({ value, desc })
|
|
47
|
+
const req = (desc: string) => ({ value: null, desc })
|
|
48
|
+
|
|
49
|
+
/** 工艺级工程公式 14 条。 */
|
|
50
|
+
export const PROCESS_FORMULAS: FormulaDef[] = [
|
|
51
|
+
{
|
|
52
|
+
id: 'kiln_heat_balance',
|
|
53
|
+
cat: 'engineering/process',
|
|
54
|
+
name: '窑炉热平衡(燃料热量去哪了)',
|
|
55
|
+
expr: 'ware_mass * ware_specific_heat * delta_T / 1000 * (1 + shell_loss_ratio)',
|
|
56
|
+
params: {
|
|
57
|
+
ware_mass: req('装坯质量 kg'),
|
|
58
|
+
ware_specific_heat: p(0.9, '坯体比热 kJ/(kg·K)(陶 0.8~1.1、瓷 0.9~1.3)'),
|
|
59
|
+
delta_T: req('升温幅度 K(室温→烧成温度)'),
|
|
60
|
+
shell_loss_ratio: p(12, '窑体蓄热+散热相对坯体之比(间歇窑 8~20,连续窑 3~6)'),
|
|
61
|
+
},
|
|
62
|
+
unit: 'MJ(估算的一次烧成总耗热)',
|
|
63
|
+
desc:
|
|
64
|
+
'「单窑耗煤 Xkg」这类经验数可以靠这条验算:Q = m·c·ΔT·(1+损比)。' +
|
|
65
|
+
'★ 这是全库第一缺口 —— 有了它,耗煤量、窑衬寿命、窑型升级幅度全部可算。' +
|
|
66
|
+
'★ 实测对照:100kg 坯 20→1100℃ 本身只需 97MJ,而实际耗煤往往对应 8~15 倍,' +
|
|
67
|
+
' 差的那部分正是窑体蓄热、窑壁散热、烟气带走。' +
|
|
68
|
+
'★ 省燃料的关键在 shell_loss_ratio:间歇窑 12 是常态,' +
|
|
69
|
+
' 改连续窑/余热回收能压到 3~6 —— 这就是「窑型升级 +40%~+300%」的来源。' +
|
|
70
|
+
'⚠ 只算一次烧成的显热,不含保温维持与不完全燃烧。',
|
|
71
|
+
keys: ['窑炉', '热平衡', '耗煤', '窑体蓄热', '烧成能耗', 'kiln'],
|
|
72
|
+
frequency: 'high',
|
|
73
|
+
layer: 'phenomenological',
|
|
74
|
+
domains: ['engineering', 'materials'],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'kiln_firing_time',
|
|
78
|
+
cat: 'engineering/process',
|
|
79
|
+
name: '烧成时间(升温速率受热应力限制)',
|
|
80
|
+
/**
|
|
81
|
+
* ★ 不要再除以 60 —— `heating_rate` 的单位已经是 **K/h**,
|
|
82
|
+
* 所以 ΔT/(K/h) 直接得小时。
|
|
83
|
+
*
|
|
84
|
+
* 我第一版写成 `delta_T / heating_rate / 60`,把一个 5.4 小时的窑
|
|
85
|
+
* 算成了 0.09——那看着像「5 分钟」而不是「5.4 小时」,
|
|
86
|
+
* 而单位栏还写着 h,属于**单位与数值互相矛盾**。
|
|
87
|
+
* 这类错误比纯粹的算术错更难发现,因为数值本身「看着像个小时间」。
|
|
88
|
+
*/
|
|
89
|
+
expr: 'delta_T / heating_rate',
|
|
90
|
+
params: {
|
|
91
|
+
delta_T: req('升温幅度 K'),
|
|
92
|
+
heating_rate: p(200, '安全升温速率 K/h(薄壁 150~300、厚壁 60~120)'),
|
|
93
|
+
},
|
|
94
|
+
unit: 'h(到达烧成温度所需时间)',
|
|
95
|
+
desc:
|
|
96
|
+
't = ΔT / 升温速率。**升温速率不是随便定的,它被热应力上限卡住。**' +
|
|
97
|
+
'★ 这就是「排潮 2h → 氧化 2.5h → 高温 2h → 保温 1h」里那些 2h 的来源:' +
|
|
98
|
+
' 不是工艺偏保守,而是厚壁件温差过大会直接开裂。' +
|
|
99
|
+
'★ 实测对照:室温→1100℃ 取 200K/h → 5.5h 爬温 + 各阶段保温,与常见「一窑一天」对得上。' +
|
|
100
|
+
'★ 关键推论:薄壁能快、厚壁必须慢 —— 壁厚翻倍,允许升温率大致降一半,工期翻倍。' +
|
|
101
|
+
'⚠ 150~300 / 60~120 是陶与瓷的常见区间;具体依热膨胀系数与抗张强度。',
|
|
102
|
+
keys: ['烧成', '升温速率', '开裂', '烧成曲线', '工期', 'firing'],
|
|
103
|
+
frequency: 'high',
|
|
104
|
+
layer: 'phenomenological',
|
|
105
|
+
domains: ['engineering', 'materials'],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'wall_heat_loss',
|
|
109
|
+
cat: 'engineering/process',
|
|
110
|
+
name: '围护结构稳态热损(R 值法)',
|
|
111
|
+
expr: 'total_area * delta_T / r_value + ventilation_loss',
|
|
112
|
+
params: {
|
|
113
|
+
total_area: req('围护总面积 m²(墙+屋顶+地板)'),
|
|
114
|
+
delta_T: req('内外温差 K'),
|
|
115
|
+
r_value: p(2, '总热阻 R = Σ(δ/k) m²·K/W(单层砖墙 0.5,多层保温 9+)'),
|
|
116
|
+
ventilation_loss: p(0, '通风换气热损 W(可单列)'),
|
|
117
|
+
},
|
|
118
|
+
unit: 'W(维持室内温度所需加热功率)',
|
|
119
|
+
desc:
|
|
120
|
+
'Q = A·ΔT/R + 通风。建筑围护结构的标准算法,与工业换热器的 LMTD 完全是两回事。' +
|
|
121
|
+
'★ 关键:用总热阻 R 而不是导热系数 k,因为多层墙要逐层累加 δ/k。' +
|
|
122
|
+
'★ 实测对照:64㎡、R=9.34、ΔT=25℃ → 171W;加通风 50W 得 221W,与实测 282W 同量级' +
|
|
123
|
+
' (差异来自缝隙与热桥 —— 实际总是比计算大)。' +
|
|
124
|
+
'★ 反推用途:知道实测耗热 282W,可反推真实 R 值,检验保温做够了没有。' +
|
|
125
|
+
'⚠ R 值是灵魂:单层砖墙约 0.5,加 10cm 岩棉可到 3~4,多层真空/反射箔可达 9+。',
|
|
126
|
+
keys: ['热损', '保温', 'R值', '围护', '建筑传热', 'insulation'],
|
|
127
|
+
frequency: 'high',
|
|
128
|
+
layer: 'derived',
|
|
129
|
+
domains: ['engineering'],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'metallurgy_yield',
|
|
133
|
+
cat: 'engineering/process',
|
|
134
|
+
name: '冶金收得率(矿石 → 金属)',
|
|
135
|
+
expr: 'ore_mass * grade * recovery',
|
|
136
|
+
params: {
|
|
137
|
+
ore_mass: req('矿石量 kg'),
|
|
138
|
+
grade: req('品位 0—1(赤铁 0.5~0.6、孔雀石 0.55~0.57)'),
|
|
139
|
+
recovery: p(0.85, '回收率(土法 0.6~0.8、改进 0.85~0.95)'),
|
|
140
|
+
},
|
|
141
|
+
unit: 'kg(产出金属量)',
|
|
142
|
+
desc:
|
|
143
|
+
'm = m_矿 × 品位 × 回收率。一眼就能看出「年产 5kg」与「年产 15-20kg」差在哪。' +
|
|
144
|
+
'★ 实测对照:15kg 矿 × 0.3 × 0.9 = 4.05kg 生铁;5kg 孔雀石 × 0.57 × 0.8 = 2.28kg 精铜。' +
|
|
145
|
+
'★ 反推最实用:已知「两炉年产 5kg」,反推每炉投多少矿 → 5/(2×0.35×0.7) ≈ 10kg 矿/炉。' +
|
|
146
|
+
' 数字自相矛盾时,这条能把矛盾定位到具体环节。' +
|
|
147
|
+
'★ 三因子各自可调:品位靠选矿、回收率靠炉型、矿量靠开采 ——' +
|
|
148
|
+
' 外推产能时先看哪个因子先到天花板,那才是真瓶颈。',
|
|
149
|
+
keys: ['冶金', '收得率', '品位', '回收率', '矿石', '炼铁'],
|
|
150
|
+
frequency: 'high',
|
|
151
|
+
layer: 'phenomenological',
|
|
152
|
+
domains: ['engineering', 'materials'],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'case_depth',
|
|
156
|
+
cat: 'engineering/process',
|
|
157
|
+
name: '渗碳层深(扩散控制)',
|
|
158
|
+
expr: '2 * sqrt(D0 * exp(-activation_energy / (R * temperature)) * hold_time)',
|
|
159
|
+
params: {
|
|
160
|
+
D0: p(2e-5, '扩散常数 D₀ m²/s(碳在奥氏体铁中约 2e-5)'),
|
|
161
|
+
activation_energy: p(1.5e5, '激活能 J/mol(碳在 γ-Fe 约 1.3~1.6e5)'),
|
|
162
|
+
R: p(8.314462618, '气体常数 J/(mol·K)'),
|
|
163
|
+
temperature: req('渗碳温度 K(900℃ = 1173K)'),
|
|
164
|
+
hold_time: req('保温时间 s(3h = 10800)'),
|
|
165
|
+
},
|
|
166
|
+
unit: 'm(渗碳层深度)',
|
|
167
|
+
desc:
|
|
168
|
+
'x ≈ 2√(D·t),D = D₀·exp(−Q/RT)。表面硬化层的标准算法。' +
|
|
169
|
+
'★ 实测对照:900℃ 保温 3h → D≈4.2e-12 m²/s、x≈0.42mm,与「表面硬化零点几毫米」吻合。' +
|
|
170
|
+
'★ 关键推论:温度是平方级杠杆,时间是线性。' +
|
|
171
|
+
' 900→950℃ 只差 50 度,D 约翻倍,层深涨 √2 倍;而保温 3h→5h 只涨 √(5/3)≈1.29 倍。' +
|
|
172
|
+
' 所以「渗层不够就多烧一会儿」效率很低,提温才是真手段 —— 但要防过热与晶粒长大。' +
|
|
173
|
+
'⚠ D₀ 与 Q 依钢种与碳势而变,给的是低碳钢 900-950℃ 的典型区间。',
|
|
174
|
+
keys: ['渗碳', '扩散', '表面硬化', '层深', 'carburize'],
|
|
175
|
+
frequency: 'medium',
|
|
176
|
+
layer: 'derived',
|
|
177
|
+
domains: ['engineering', 'materials'],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 'gear_bending_strength',
|
|
181
|
+
cat: 'engineering/process',
|
|
182
|
+
name: '齿轮齿根弯曲强度(模数选型)',
|
|
183
|
+
expr: 'tangential_force / (face_width * gear_module * form_factor)',
|
|
184
|
+
params: {
|
|
185
|
+
tangential_force: req('圆周力 F_t N(F_t = 2T/d)'),
|
|
186
|
+
face_width: req('齿宽 b m'),
|
|
187
|
+
gear_module: req('模数 m m'),
|
|
188
|
+
form_factor: p(0.32, '齿形系数 Y(20° 标准齿 0.30~0.34)'),
|
|
189
|
+
},
|
|
190
|
+
unit: 'Pa(齿根弯曲应力;与许用弯曲应力比即判安全)',
|
|
191
|
+
desc:
|
|
192
|
+
'σ = F_t/(b·m·Y)。齿轮传动的强度判据,也是模数选型的依据。' +
|
|
193
|
+
'★ 这条能验算「模数 4 比 2.5 厚约 60%,足以承受 30kg 重锤」这种断言:' +
|
|
194
|
+
' m=4、z=30 → 分度圆 120mm;30kg 在 r=60mm → F_t≈294N;' +
|
|
195
|
+
' 齿宽 30mm、Y=0.32 → σ ≈ 7.7 MPa,远低于铸铁许用 30~60MPa。' +
|
|
196
|
+
'★ 结论是「模数 4 绰绰有余」,而模数 2.5 时应力涨到约 12 MPa —— 仍够。' +
|
|
197
|
+
' 所以那句断言是对的,但只有算出这两个数才知道它对。' +
|
|
198
|
+
'⚠ 简化式(未含动载系数、应力集中、齿面接触疲劳)。单件低速手工齿轮够用。',
|
|
199
|
+
keys: ['齿轮', '齿根', '弯曲强度', '模数', '选型'],
|
|
200
|
+
frequency: 'high',
|
|
201
|
+
layer: 'phenomenological',
|
|
202
|
+
domains: ['engineering'],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 'gear_geometry',
|
|
206
|
+
cat: 'engineering/process',
|
|
207
|
+
name: '齿轮几何(分度圆与传动比)',
|
|
208
|
+
expr: 'gear_module * teeth',
|
|
209
|
+
params: {
|
|
210
|
+
gear_module: req('模数 m mm'),
|
|
211
|
+
teeth: req('齿数 z'),
|
|
212
|
+
},
|
|
213
|
+
unit: 'mm(分度圆直径 d)',
|
|
214
|
+
desc:
|
|
215
|
+
'd = m·z。齿轮的一切尺寸都从这两个数派生:外径 d+2m、齿高 2.25m、中心距 (d₁+d₂)/2。' +
|
|
216
|
+
'★ 传动比 i = z_从/z_主(同模数)。所以「1:12 减速」可反推齿数组合:' +
|
|
217
|
+
' 单级 30:360,或两级 30:180 + 20:40 —— 单级占空间、多级效率低但紧凑,' +
|
|
218
|
+
' 这就是设计取舍的量化形式。' +
|
|
219
|
+
'★ 手工加工的约束:模数决定刀具规格,所以一个作坊只用一两种模数,' +
|
|
220
|
+
' 而齿数可自由组合 —— 「统一模数 + 自由齿数」正是钟表业的传统做法。',
|
|
221
|
+
keys: ['齿轮', '模数', '齿数', '分度圆', '传动比'],
|
|
222
|
+
frequency: 'high',
|
|
223
|
+
layer: 'derived',
|
|
224
|
+
domains: ['engineering'],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: 'pendulum_clock_drift',
|
|
228
|
+
cat: 'engineering/process',
|
|
229
|
+
name: '摆钟走时精度(温度引起的快慢)',
|
|
230
|
+
expr: '86400 * (-0.5 * length_change_ratio + 0.5 * gravity_change_ratio)',
|
|
231
|
+
params: {
|
|
232
|
+
length_change_ratio: req('摆长相对变化 ΔL/L(热膨胀 α·ΔT;钢 α≈1.2e-5/K)'),
|
|
233
|
+
gravity_change_ratio: p(0, '重力相对变化 Δg/g(同地可忽略,高原/异星要算)'),
|
|
234
|
+
},
|
|
235
|
+
unit: 's/天(正为走快,负为走慢)',
|
|
236
|
+
desc:
|
|
237
|
+
'T = 2π√(L/g),取对数微分得 ΔT/T = −½·ΔL/L + ½·Δg/g,乘 86400 得每天偏差。' +
|
|
238
|
+
'★ 实测对照:钢摆 α=1.2e-5、冬夏温差 20℃ → ΔL/L=2.4e-4 → 每天慢约 10.4 秒。' +
|
|
239
|
+
' 这就是「摆钟冬夏要重新校」的定量原因,也是补偿摆(双金属)要解决的问题。' +
|
|
240
|
+
'★ 重要推论:周期只与摆长的**平方根**有关,所以摆长误差的影响被压掉一半;' +
|
|
241
|
+
' 摆长差 0.1% 只让每天差 43 秒 —— 天文观测完全够用来校准。' +
|
|
242
|
+
'★ 反过来最有用:**异星设定**里 g 变了多少,钟就快慢多少,这条直接给数。' +
|
|
243
|
+
'⚠ 补偿摆能压到每天 1~2 秒;真空+恒温的精密摆钟可到每天 0.01 秒。',
|
|
244
|
+
keys: ['摆钟', '钟表', '走时', '精度', '热膨胀'],
|
|
245
|
+
frequency: 'medium',
|
|
246
|
+
layer: 'derived',
|
|
247
|
+
domains: ['engineering'],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'gravity_storage_discharge',
|
|
251
|
+
cat: 'engineering/process',
|
|
252
|
+
name: '重力储能放电时长(自洽性检查)',
|
|
253
|
+
expr: 'mass * 9.80665 * height / power',
|
|
254
|
+
params: {
|
|
255
|
+
mass: req('配重质量 kg'),
|
|
256
|
+
height: req('落差 m'),
|
|
257
|
+
power: req('放电功率 W'),
|
|
258
|
+
},
|
|
259
|
+
unit: 's(能维持该功率的时长)',
|
|
260
|
+
desc:
|
|
261
|
+
't = E/P = m·g·h/P。**最重要的用途是检查设定自不自洽,而不是算新数。**' +
|
|
262
|
+
'★ 实测对照(外部反馈的原案例):25kg 配重 × 2.5m = 625J;' +
|
|
263
|
+
' 若设定说「6~8W 持续 12~15 分钟」,625J ÷ 8W = 78 秒 —— 差一个数量级。' +
|
|
264
|
+
'★ 这种错误极难自察:625J、8W、12 分钟各自都「看着合理」,' +
|
|
265
|
+
' 只有把三个数放进同一个式子才暴露。这正是能量守恒检查的价值。' +
|
|
266
|
+
'★ 反推用途:要维持 8W 共 12 分钟需 5760J → 需 235kg×2.5m 或 25kg×23.5m。' +
|
|
267
|
+
' 于是「要么加配重十倍、要么加高十倍」—— 设计方向一下就明确了。',
|
|
268
|
+
keys: ['重力储能', '配重', '放电', '储能时长', '自洽检查'],
|
|
269
|
+
frequency: 'high',
|
|
270
|
+
layer: 'fundamental',
|
|
271
|
+
domains: ['engineering', 'physics'],
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: 'water_turbine_output',
|
|
275
|
+
cat: 'engineering/process',
|
|
276
|
+
name: '水轮输出(功率/扭矩/转速)',
|
|
277
|
+
expr: 'density * 9.80665 * flow * head * efficiency',
|
|
278
|
+
params: {
|
|
279
|
+
density: p(1000, '水密度 kg/m³'),
|
|
280
|
+
flow: req('流量 m³/s'),
|
|
281
|
+
head: req('有效水头 m'),
|
|
282
|
+
efficiency: p(0.7, '总效率(木制水轮 0.4~0.6、改进 0.7~0.85)'),
|
|
283
|
+
},
|
|
284
|
+
unit: 'W(轴输出功率)',
|
|
285
|
+
desc:
|
|
286
|
+
'P = ρgQHη。与水轮机的「泵」公式同形但方向相反 —— 泵用电换水头,水轮用水头换功。' +
|
|
287
|
+
'★ 实测对照:溪流 0.01m³/s、水头 3m、η=0.6 → 176W;小河 0.1m³/s、水头 6m → 3.5kW。' +
|
|
288
|
+
'★ 扭矩 T = P/ω,转速 n = 60v/(πD):' +
|
|
289
|
+
' 低水头大流量的水轮转速慢、扭矩大,**这正是它能直接带动锻锤的原因**;' +
|
|
290
|
+
' 而带动锯需要高转速,得加增速传动 —— 于是「传动比」成了必需项而不是选择项。' +
|
|
291
|
+
'★ 效率 0.4→0.85 就是「木轮改铁轮 + 导流」的全部收益,提升 2 倍。',
|
|
292
|
+
keys: ['水轮', '水车', '水力', '功率', '扭矩'],
|
|
293
|
+
frequency: 'high',
|
|
294
|
+
layer: 'derived',
|
|
295
|
+
domains: ['engineering'],
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: 'gdd_accumulated',
|
|
299
|
+
cat: 'engineering/process',
|
|
300
|
+
name: '累计生长度日(一季能攒多少积温)',
|
|
301
|
+
expr: 'excess_daily_temp * days',
|
|
302
|
+
params: {
|
|
303
|
+
excess_daily_temp: p(10, '日均温超出基点部分 K(日均 20℃、基点 10℃ → 10)'),
|
|
304
|
+
days: req('生育期天数(无霜期 180~210)'),
|
|
305
|
+
},
|
|
306
|
+
unit: '℃·d(生长度日 GDD)',
|
|
307
|
+
desc:
|
|
308
|
+
'GDD = Σ max(0, T_日均 − T_基点)。**四季节奏的数学骨架。**' +
|
|
309
|
+
'★ 价值在于「把时间换算成热量」:同样 200 天生育期,' +
|
|
310
|
+
' 暖地日均积 15 度、冷地 8 度 → GDD 差近 2 倍 → 能种的作物完全不同。' +
|
|
311
|
+
'★ 实测用途:给定「无霜期 180~210 天」可反推这一季能攒多少积温,' +
|
|
312
|
+
' 再对照作物需求(玉米 2500~2800、小麦 1800~2200、水稻 3000+)判断能不能熟。' +
|
|
313
|
+
'★ 这也是「春播秋收冬藏」必须按日历走的定量理由 —— 积温不够就熟不了。' +
|
|
314
|
+
'⚠ 基点温度依作物而变(小麦 0~5℃、玉米 10℃、水稻 10~12℃)。',
|
|
315
|
+
keys: ['生长度日', 'GDD', '积温', '物候', '无霜期', '农业'],
|
|
316
|
+
frequency: 'medium',
|
|
317
|
+
layer: 'phenomenological',
|
|
318
|
+
domains: ['agriculture', 'biology'],
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: 'leaching_residue',
|
|
322
|
+
cat: 'engineering/process',
|
|
323
|
+
name: '浸提残留(换水去单宁/苦涩)',
|
|
324
|
+
expr: 'initial_concentration * residual_ratio^changes',
|
|
325
|
+
params: {
|
|
326
|
+
initial_concentration: req('初始浓度(如单宁含量)'),
|
|
327
|
+
residual_ratio: p(0.6, '每次换水后的残留比(0.5~0.7,依切块大小与水温)'),
|
|
328
|
+
changes: req('换水次数'),
|
|
329
|
+
},
|
|
330
|
+
unit: '与初始浓度同单位',
|
|
331
|
+
desc:
|
|
332
|
+
'C_n = C₀·rⁿ。橡子、蕨根、苦杏仁去毒去涩的标准算法。' +
|
|
333
|
+
'★ 实测对照:r=0.6 换 4 次 → 0.1296,残留 13%;换 5 次 7.8%、换 6 次 4.7% —— 指数衰减。' +
|
|
334
|
+
'★ 关键推论:**换水次数比每次泡多久更重要。**' +
|
|
335
|
+
' 泡一夜再换 4 次(0.13)远好于泡 4 天不换(几乎不衰减)——' +
|
|
336
|
+
' 因为扩散平衡后浓度不再下降,必须靠换水把平衡点推下去。' +
|
|
337
|
+
'★ 这也是「手工程序值得公式化」的最好例子:' +
|
|
338
|
+
' 设定里写「需浸泡去单宁」但没给次数,而次数直接决定能不能吃。',
|
|
339
|
+
keys: ['浸提', '去单宁', '浸泡', '换水', '脱毒', 'leaching'],
|
|
340
|
+
frequency: 'medium',
|
|
341
|
+
layer: 'derived',
|
|
342
|
+
domains: ['agriculture', 'chemistry'],
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: 'fermentation_time',
|
|
346
|
+
cat: 'engineering/process',
|
|
347
|
+
name: '发酵时间(达到目标残留糖)',
|
|
348
|
+
expr: '-(log(target_residual / initial_sugar)) / rate_constant',
|
|
349
|
+
params: {
|
|
350
|
+
initial_sugar: req('初始糖浓度'),
|
|
351
|
+
target_residual: req('目标残留糖浓度'),
|
|
352
|
+
rate_constant: p(0.07, '速率常数 k 1/day(酿酒 0.05~0.09、酿醋 0.02~0.04)'),
|
|
353
|
+
},
|
|
354
|
+
unit: 'day(达到目标残留糖所需天数)',
|
|
355
|
+
desc:
|
|
356
|
+
'S(t) = S₀·e^(−kt),解出 t = −ln(S_残/S₀)/k。酿酒、酿醋、沤肥的时间估算。' +
|
|
357
|
+
'★ 实测对照:降到 5%、k=0.07 → t = −ln(0.05)/0.07 ≈ 43 天;' +
|
|
358
|
+
' 而 k=0.04(低温)需 75 天 —— **这就是「温度低一度要延长几天」的定量答案。**' +
|
|
359
|
+
'★ 关键推论:发酵时间对温度极敏感(k 随温度按 Q₁₀ 律变,见 q10_temperature)。' +
|
|
360
|
+
' 所以「酿酒 2~4 周、酿醋 6~8 周」不是随便写的,它对应两个不同的 k。' +
|
|
361
|
+
'★ 串联用途:先用 q10_temperature 由温度得 k,再代入本式得工期 —— 这是「换季要改工艺」的可算理由。' +
|
|
362
|
+
'⚠ 简化的一级动力学;实际发酵有滞后期与产物抑制,工业上要用 Monod + 抑制项。',
|
|
363
|
+
keys: ['发酵', '酿酒', '酿醋', '沤肥', '时间', 'fermentation'],
|
|
364
|
+
frequency: 'medium',
|
|
365
|
+
layer: 'derived',
|
|
366
|
+
domains: ['agriculture', 'biology', 'chemistry'],
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
id: 'syrup_concentration',
|
|
370
|
+
cat: 'engineering/process',
|
|
371
|
+
name: '熬煮浓缩(糖浆得率)',
|
|
372
|
+
expr: 'juice_volume * initial_brix / final_brix',
|
|
373
|
+
params: {
|
|
374
|
+
juice_volume: req('原汁体积 L'),
|
|
375
|
+
initial_brix: req('初始糖度 Brix(%)'),
|
|
376
|
+
final_brix: req('成品糖度 Brix(%)'),
|
|
377
|
+
},
|
|
378
|
+
unit: 'L(成品体积)',
|
|
379
|
+
desc:
|
|
380
|
+
'V_f = V₀·Brix₀/Brix_f。糖量守恒的浓缩换算。' +
|
|
381
|
+
'★ 实测对照:40L 枫汁 Brix=2.5、成品 66 → 40×2.5/66 = 1.52L。' +
|
|
382
|
+
' 设定里「40L 产 1L 糖浆」对应成品 Brix 更高或含水更多,量级一致。' +
|
|
383
|
+
'★ 关键推论:**浓缩比只由糖度比决定**,与锅的大小、火的大小都无关。' +
|
|
384
|
+
' 所以「熬多久」只影响风味与色泽,不影响得率 —— 得率只看两个度数。' +
|
|
385
|
+
'★ 反过来最实用:知道要熬到多少度,就能预估这一锅能出多少,' +
|
|
386
|
+
' 这也是判断「值不值得熬」的依据(燃料成本 vs 成品收益)。' +
|
|
387
|
+
'⚠ Brix 是质量百分比糖度;若用糖度计读数,需按温度修正。',
|
|
388
|
+
keys: ['熬煮', '浓缩', '糖浆', 'Brix', '糖度', '得率'],
|
|
389
|
+
frequency: 'medium',
|
|
390
|
+
layer: 'derived',
|
|
391
|
+
domains: ['agriculture', 'chemistry'],
|
|
392
|
+
}
|
|
393
|
+
]
|
package/src/formulas.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { ENGINEERING_FORMULAS } from './formula-library-engineering.js'
|
|
|
29
29
|
import { BIOECON_FORMULAS } from './formula-library-bioecon.js'
|
|
30
30
|
import { MACRO_MATERIAL_FORMULAS } from './formula-library-macro-materials.js'
|
|
31
31
|
import { BIO_GENERAL_FORMULAS } from './formula-library-biol-general.js'
|
|
32
|
+
import { PROCESS_FORMULAS } from './formula-library-process.js'
|
|
32
33
|
import { getActiveConstants } from './constants-graph.js'
|
|
33
34
|
import { loadGlobalFormulas } from './formula-pack.js'
|
|
34
35
|
import { findUnit, convert } from './units.js'
|
|
@@ -52,6 +53,7 @@ export const FORMULAS: FormulaDef[] = [
|
|
|
52
53
|
...BIOECON_FORMULAS,
|
|
53
54
|
...MACRO_MATERIAL_FORMULAS,
|
|
54
55
|
...BIO_GENERAL_FORMULAS,
|
|
56
|
+
...PROCESS_FORMULAS,
|
|
55
57
|
]
|
|
56
58
|
|
|
57
59
|
/** id 索引(后批次覆盖同名 id 时以后者为准,便于后续修订)。 */
|
|
@@ -80,6 +82,7 @@ for (const formula of ENGINEERING_FORMULAS) FORMULA_INDEX.set(formula.id, formul
|
|
|
80
82
|
for (const formula of BIOECON_FORMULAS) FORMULA_INDEX.set(formula.id, formula)
|
|
81
83
|
for (const formula of MACRO_MATERIAL_FORMULAS) FORMULA_INDEX.set(formula.id, formula)
|
|
82
84
|
for (const formula of BIO_GENERAL_FORMULAS) FORMULA_INDEX.set(formula.id, formula)
|
|
85
|
+
for (const formula of PROCESS_FORMULAS) FORMULA_INDEX.set(formula.id, formula)
|
|
83
86
|
|
|
84
87
|
/**
|
|
85
88
|
* 把全局公式包并入内置公式(幂等:已存在的 id 跳过)。
|
package/src/guards.ts
CHANGED
|
@@ -85,7 +85,34 @@ export function magnitudeOfLabel(label: string): { word: string; power: number }
|
|
|
85
85
|
*/
|
|
86
86
|
let body = text
|
|
87
87
|
const bracketPair = body.match(/[((\[【]\s*(.+?)\s*[))\]】]$/)
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* ## 括号前还有描述文字时,括号内是**输入条件**,不是结果单位
|
|
90
|
+
*
|
|
91
|
+
* 实测反馈(2026-11-08 第二份):
|
|
92
|
+
*
|
|
93
|
+
* ```
|
|
94
|
+
* label:"煤储量/年耗 可支撑年数(2万吨)" expr:"20000/1.364" → 14662.75
|
|
95
|
+
* → ⚠ label 含「万」,而结果 14662 本身已达 10⁴;若按字面读成「万」则为 1.47e8
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* 而作者的真实意思是「**2 万吨煤**能支撑多少年」—— 括号里是那个煤量的口径说明,
|
|
99
|
+
* 不是「结果单位是万吨」。这条警告纯属噪声,而且会把真正的量纲问题盖过去。
|
|
100
|
+
*
|
|
101
|
+
* 判据:成对括号**在末尾**,但括号**前面还有非空白文字**,且那段文字里有
|
|
102
|
+
* 中文描述(不是纯数字/符号),则括号内是条件描述,直接放过。
|
|
103
|
+
*
|
|
104
|
+
* 保留的正当情形:`年发电量(亿)` —— 括号前是「年发电量」这个**量名**,
|
|
105
|
+
* 括号里是它的单位。所以还要排除「括号前那段是纯量名(不含数字、不含斜杠)」…
|
|
106
|
+
* 太细了,实测两类写法都出现,所以这里取一个更稳的折衷:
|
|
107
|
+
* **括号内含数字 + 量级词 + 单位词**(如 `2万吨`、`300万件`)→ 判为条件;
|
|
108
|
+
* **括号内只有量级词 + 单位词**(如 `亿`、`万吨`)→ 仍按结果单位处理。
|
|
109
|
+
*/
|
|
110
|
+
if (bracketPair) {
|
|
111
|
+
const inner = bracketPair[1].trim()
|
|
112
|
+
const hasQuantity = /^[0-9][0-9,.]*\s*/.test(inner)
|
|
113
|
+
if (!hasQuantity) body = inner
|
|
114
|
+
else return null
|
|
115
|
+
}
|
|
89
116
|
|
|
90
117
|
/**
|
|
91
118
|
* ## 判据:量级词必须处在「单位位置」
|