@jjlmoya/utils-science 1.44.0 → 1.45.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 (30) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +3 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/exoplanet-biosignature-detector/bibliography.astro +14 -0
  8. package/src/tool/exoplanet-biosignature-detector/bibliography.ts +16 -0
  9. package/src/tool/exoplanet-biosignature-detector/component.astro +351 -0
  10. package/src/tool/exoplanet-biosignature-detector/entry.ts +26 -0
  11. package/src/tool/exoplanet-biosignature-detector/exoplanet-biosignature-detector.css +516 -0
  12. package/src/tool/exoplanet-biosignature-detector/i18n/de.ts +196 -0
  13. package/src/tool/exoplanet-biosignature-detector/i18n/en.ts +196 -0
  14. package/src/tool/exoplanet-biosignature-detector/i18n/es.ts +196 -0
  15. package/src/tool/exoplanet-biosignature-detector/i18n/fr.ts +196 -0
  16. package/src/tool/exoplanet-biosignature-detector/i18n/id.ts +196 -0
  17. package/src/tool/exoplanet-biosignature-detector/i18n/it.ts +196 -0
  18. package/src/tool/exoplanet-biosignature-detector/i18n/ja.ts +196 -0
  19. package/src/tool/exoplanet-biosignature-detector/i18n/ko.ts +196 -0
  20. package/src/tool/exoplanet-biosignature-detector/i18n/nl.ts +196 -0
  21. package/src/tool/exoplanet-biosignature-detector/i18n/pl.ts +149 -0
  22. package/src/tool/exoplanet-biosignature-detector/i18n/pt.ts +149 -0
  23. package/src/tool/exoplanet-biosignature-detector/i18n/ru.ts +149 -0
  24. package/src/tool/exoplanet-biosignature-detector/i18n/sv.ts +149 -0
  25. package/src/tool/exoplanet-biosignature-detector/i18n/tr.ts +149 -0
  26. package/src/tool/exoplanet-biosignature-detector/i18n/zh.ts +196 -0
  27. package/src/tool/exoplanet-biosignature-detector/index.ts +11 -0
  28. package/src/tool/exoplanet-biosignature-detector/logic.ts +169 -0
  29. package/src/tool/exoplanet-biosignature-detector/seo.astro +15 -0
  30. package/src/tools.ts +2 -0
@@ -0,0 +1,196 @@
1
+ import { bibliography } from '../bibliography';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+
4
+ const slug = 'exoplanet-biosignature-detector';
5
+ const title = '系外行星生物信号探测器';
6
+ const description = '在模拟的系外行星透射光谱中对齐氧气、甲烷和臭氧的吸收线。评估生物宜居性、可能的技术信号污染以及来自光谱证据的误报风险。';
7
+
8
+ const howTo = [
9
+ {
10
+ name: '选择主恒星',
11
+ text: '选择一颗K型矮星、类日恒星或M型矮星。恒星的类型会影响误报风险,因为紫外辐射、耀斑和大气光化学过程会改变对氧气和臭氧的判断。',
12
+ },
13
+ {
14
+ name: '对齐光谱线',
15
+ text: '调整氧气、甲烷和臭氧的控制滑块,使观测到的吸收标记与模拟光谱中的参考波段对齐。',
16
+ },
17
+ {
18
+ name: '设置噪声与大气环境',
19
+ text: '增大仪器噪声可以直观地看到置信度如何下降。调整水蒸气和二氧化碳含量,模拟更湿润、更干燥、更稠密或光化学特征更可疑的大气环境。',
20
+ },
21
+ {
22
+ name: '解读宜居性指标',
23
+ text: '对比生物指数、技术指数、误报风险和宜居性读数。一个强有力的结果需要多种气体处于非平衡状态,而不是仅仅依赖某一条孤立的光谱线。',
24
+ },
25
+ ];
26
+
27
+ const faq = [
28
+ {
29
+ question: '什么是系外行星光谱学中的生物信号?',
30
+ answer: '生物信号是一种可通过遥感探测到的特征,其来源可能是生命活动。在大气光谱中,常见的候选包括氧气、臭氧、甲烷、水蒸气,以及那些本应发生反应消失、却似乎有某种过程在持续补充的气体组合。',
31
+ },
32
+ {
33
+ question: '为什么氧气加甲烷的组合值得关注?',
34
+ answer: '氧气和甲烷在化学上会相互反应。如果两者都以可观的水平同时存在,说明大气可能处于化学非平衡状态,这比单独检测到任何一种气体都更有研究价值。模拟器会对这一组合赋予更高的权重。',
35
+ },
36
+ {
37
+ question: '氧气会不会是误报?',
38
+ answer: '会的。氧气可以在没有生命的情况下通过水逃逸、光解作用、微弱的火山汇或异常的恒星紫外环境积累。这就是为什么该工具会报告误报风险,而不是把氧气当作确凿证据。',
39
+ },
40
+ {
41
+ question: '为什么要把臭氧和氧气分开考虑?',
42
+ answer: '臭氧是氧气通过光化学作用产生的,具有显著的光谱特征。在直接观测氧气波段较困难时,臭氧可以作为氧气的间接指示物,但仍需结合大气环境综合判断。',
43
+ },
44
+ {
45
+ question: '技术指数是什么意思?',
46
+ answer: '技术指数是一种探索性的预警评分,用于标记那些大气光谱看起来过于活跃、嘈杂或化学特征异常,以至于不应排除人工源的情况。它并不构成探测声明。',
47
+ },
48
+ {
49
+ question: '这是一个真正的系外行星反演模型吗?',
50
+ answer: '不是。它是一个教学模拟器,将核心光谱学理念转化为交互式评分模型。真正的反演需要借助辐射传输、恒星污染校正、云模型、仪器系统误差和贝叶斯推断。',
51
+ },
52
+ ];
53
+
54
+ export const content: ToolLocaleContent = {
55
+ slug,
56
+ title,
57
+ description,
58
+ ui: {
59
+ visualization: '系外行星透射光谱可视化',
60
+ starClass: '主恒星',
61
+ starK: '安静K型矮星',
62
+ starSun: '类日G型星',
63
+ starM: '活跃M型矮星',
64
+ noise: '仪器噪声',
65
+ oxygenLine: '氧气标记',
66
+ methaneLine: '甲烷标记',
67
+ ozoneLine: '臭氧标记',
68
+ waterVapor: '水蒸气环境',
69
+ carbonDioxide: 'CO₂环境',
70
+ habitabilityIndex: '宜居性指数',
71
+ biologicalIndex: '生物指数',
72
+ technologicalIndex: '技术指数',
73
+ falsePositiveRisk: '误报风险',
74
+ oxygen: 'O₂',
75
+ methane: 'CH₄',
76
+ ozone: 'O₃',
77
+ oxygenShort: 'O₂',
78
+ methaneShort: 'CH₄',
79
+ ozoneShort: 'O₃',
80
+ conclusion: '结论',
81
+ dataLive: '数据实时合成',
82
+ percentUnit: '{value}%',
83
+ nanometerUnit: '{value} nm',
84
+ pendingValue: '...',
85
+ statusReady: '调整光谱标记以评估大气证据。',
86
+ verdictWeak: '光谱较弱:当前谱线尚不足以支持宜居解读。',
87
+ verdictPromising: '光谱有希望:多个生物信号标记已部分对齐。',
88
+ verdictStrong: '光谱很强:氧气、甲烷和臭氧形成了连贯的非平衡信号。',
89
+ verdictAmbiguous: '信号有趣但存疑:恒星或大气的误报因素仍然偏高。',
90
+ },
91
+ seo: [
92
+ {
93
+ type: 'title',
94
+ text: '系外行星生物信号探测器',
95
+ level: 2,
96
+ },
97
+ {
98
+ type: 'paragraph',
99
+ html: '系外行星生物信号探测器是一个交互式光谱学模拟器,用于探索生命相关气体如何出现在透过遥远大气层的光线中。该工具不会把单一分子当作是非题来处理,而是引导你对齐氧气、甲烷和臭氧的吸收特征,然后综合噪声、水蒸气、二氧化碳以及主恒星行为给出评分。',
100
+ },
101
+ {
102
+ type: 'paragraph',
103
+ html: '这正是生物信号科学的核心难题:氧气可能令人兴奋,甲烷可能令人兴奋,臭氧也可能令人兴奋,但没有哪一种是单独成立的证据。最有说服力的案例需要多种气体结合、物理环境佐证,以及对非生物解释的仔细排查。该模拟器以紧凑的实验室风格界面,让这些权衡变得清晰可见。',
104
+ },
105
+ {
106
+ type: 'title',
107
+ text: '透射光谱如何揭示大气气体成分',
108
+ level: 3,
109
+ },
110
+ {
111
+ type: 'paragraph',
112
+ html: '当一颗行星从其恒星前方经过时,极少量的星光会穿过行星大气层到达望远镜。不同分子会吸收特定波长的光,在测量光谱中留下或窄或宽的凹陷。通过对比观测到的凹陷与实验室标定的谱线位置,天文学家可以推断哪些气体可能存在,不过云层、雾霾、温度、恒星黑子和仪器噪声都会增加反演的复杂度。',
113
+ },
114
+ {
115
+ type: 'title',
116
+ text: '氧气、甲烷和臭氧的联合信号',
117
+ level: 3,
118
+ },
119
+ {
120
+ type: 'list',
121
+ items: [
122
+ '<strong>氧气 O₂:</strong>在地球上,丰富的氧气由光合作用维持,但在某些行星演化历史中,非生物过程也可能积累氧气。',
123
+ '<strong>甲烷 CH₄:</strong>甲烷可能来自生物活动、地质作用或撞击事件。当它与氧化性气体一同被发现时,其暗示意义会更强。',
124
+ '<strong>臭氧 O₃:</strong>臭氧是氧气的光化学产物,在某些光谱波段更容易被探测到,但它终究只是一个间接线索。',
125
+ '<strong>水和CO₂的环境背景:</strong>水是宜居性的支撑,二氧化碳则有助于理解温室状态、光化学过程以及误报情景。',
126
+ ],
127
+ },
128
+ {
129
+ type: 'title',
130
+ text: '为什么误报问题至关重要',
131
+ level: 3,
132
+ },
133
+ {
134
+ type: 'paragraph',
135
+ html: '高氧气得分并不自动意味着生命活动的存在。水逃逸可能留下残余的氧气,恒星的紫外辐射可能分解分子,低水平的火山还原气体输出也可能让氧气逐渐积累。活跃的M型矮星尤其棘手,因为耀斑和光化学作用会重塑大气成分。当噪声、恒星类型或大气环境使得生物解释的可靠性降低时,误报读数就会上升。',
136
+ },
137
+ {
138
+ type: 'table',
139
+ headers: ['信号模式', '解读', '注意事项'],
140
+ rows: [
141
+ ['仅有 O₂', '可能存在富氧大气', '水逃逸或异常光化学作用可导致非生物成因'],
142
+ ['仅有 CH₄', '可能为还原性大气或活跃地质活动', '仅凭甲烷不足以构成强有力的生命证据'],
143
+ ['O₂ 加 CH₄', '化学非平衡,值得进一步研究', '需要稳健的反演与污染排查'],
144
+ ['O₂ 加 O₃ 加水', '更具连贯性的类地环境', '云层和恒星活动仍需考虑'],
145
+ ],
146
+ },
147
+ {
148
+ type: 'title',
149
+ text: '生物与技术宜居性指数',
150
+ level: 3,
151
+ },
152
+ {
153
+ type: 'paragraph',
154
+ html: '生物指数侧重化学非平衡性,尤其是氧气与甲烷的共存,然后叠加臭氧屏蔽和水环境因素。技术指数则有意保持探索性:它标记那些大气看起来过于活跃、嘈杂或化学特征异常的场景;在讲述简单的生命故事之前,应先考虑人为污染、有意为之的大气改造或观测假象。',
155
+ },
156
+ {
157
+ type: 'paragraph',
158
+ html: '请将本工具作为推理辅助手段而非结论生成器使用。真正的生物信号评估需要比较多种大气模型,纳入恒星光谱,校正望远镜系统误差,估计云层不透明度,并计算不确定性。本工具的教育价值在于它强制你遵循同样的严谨路径:对齐证据,检验环境背景,始终警惕误报。',
159
+ },
160
+ ],
161
+ faq,
162
+ bibliography,
163
+ howTo,
164
+ schemas: [
165
+ {
166
+ '@context': 'https://schema.org',
167
+ '@type': 'SoftwareApplication',
168
+ name: title,
169
+ description,
170
+ applicationCategory: 'ScientificApplication',
171
+ operatingSystem: 'Any',
172
+ },
173
+ {
174
+ '@context': 'https://schema.org',
175
+ '@type': 'FAQPage',
176
+ mainEntity: faq.map((item) => ({
177
+ '@type': 'Question',
178
+ name: item.question,
179
+ acceptedAnswer: {
180
+ '@type': 'Answer',
181
+ text: item.answer,
182
+ },
183
+ })),
184
+ },
185
+ {
186
+ '@context': 'https://schema.org',
187
+ '@type': 'HowTo',
188
+ name: title,
189
+ step: howTo.map((step) => ({
190
+ '@type': 'HowToStep',
191
+ name: step.name,
192
+ text: step.text,
193
+ })),
194
+ },
195
+ ],
196
+ };
@@ -0,0 +1,11 @@
1
+ import { exoplanetBiosignatureDetector } from './entry';
2
+ import type { ToolDefinition } from '../../types';
3
+
4
+ export * from './entry';
5
+
6
+ export const EXOPLANET_BIOSIGNATURE_DETECTOR_TOOL: ToolDefinition = {
7
+ entry: exoplanetBiosignatureDetector,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,169 @@
1
+ export type MoleculeId = 'oxygen' | 'methane' | 'ozone' | 'water' | 'carbon-dioxide';
2
+ export type StarClass = 'm-dwarf' | 'sun-like' | 'k-dwarf';
3
+
4
+ export interface SpectralLine {
5
+ wavelengthNm: number;
6
+ depth: number;
7
+ }
8
+
9
+ export interface MoleculeSignature {
10
+ id: MoleculeId;
11
+ name: string;
12
+ kind: 'biosignature' | 'context';
13
+ lines: SpectralLine[];
14
+ weight: number;
15
+ }
16
+
17
+ export interface BiosignatureInput {
18
+ oxygenAlignmentNm: number;
19
+ methaneAlignmentNm: number;
20
+ ozoneAlignmentNm: number;
21
+ noisePercent: number;
22
+ stellarClass: StarClass;
23
+ waterVaporPercent: number;
24
+ carbonDioxidePercent: number;
25
+ }
26
+
27
+ export interface MoleculeScore {
28
+ id: MoleculeId;
29
+ name: string;
30
+ alignmentNm: number;
31
+ matchScore: number;
32
+ confidence: number;
33
+ }
34
+
35
+ export interface BiosignatureResult {
36
+ biologicalIndex: number;
37
+ technologicalIndex: number;
38
+ habitabilityIndex: number;
39
+ falsePositiveRisk: number;
40
+ evidence: MoleculeScore[];
41
+ verdict: 'weak' | 'promising' | 'strong' | 'ambiguous';
42
+ }
43
+
44
+ const OXYGEN_SIGNATURE: MoleculeSignature = {
45
+ id: 'oxygen',
46
+ name: 'Oxygen O2',
47
+ kind: 'biosignature',
48
+ weight: 0.34,
49
+ lines: [
50
+ { wavelengthNm: 687, depth: 0.42 },
51
+ { wavelengthNm: 760, depth: 0.92 },
52
+ ],
53
+ };
54
+
55
+ const METHANE_SIGNATURE: MoleculeSignature = {
56
+ id: 'methane',
57
+ name: 'Methane CH4',
58
+ kind: 'biosignature',
59
+ weight: 0.3,
60
+ lines: [
61
+ { wavelengthNm: 890, depth: 0.46 },
62
+ { wavelengthNm: 1660, depth: 0.82 },
63
+ ],
64
+ };
65
+
66
+ const OZONE_SIGNATURE: MoleculeSignature = {
67
+ id: 'ozone',
68
+ name: 'Ozone O3',
69
+ kind: 'biosignature',
70
+ weight: 0.25,
71
+ lines: [
72
+ { wavelengthNm: 255, depth: 0.72 },
73
+ { wavelengthNm: 960, depth: 0.5 },
74
+ ],
75
+ };
76
+
77
+ export const MOLECULE_SIGNATURES: MoleculeSignature[] = [
78
+ OXYGEN_SIGNATURE,
79
+ METHANE_SIGNATURE,
80
+ OZONE_SIGNATURE,
81
+ {
82
+ id: 'water',
83
+ name: 'Water vapor H2O',
84
+ kind: 'context',
85
+ weight: 0.07,
86
+ lines: [
87
+ { wavelengthNm: 720, depth: 0.38 },
88
+ { wavelengthNm: 940, depth: 0.66 },
89
+ ],
90
+ },
91
+ {
92
+ id: 'carbon-dioxide',
93
+ name: 'Carbon dioxide CO2',
94
+ kind: 'context',
95
+ weight: 0.04,
96
+ lines: [
97
+ { wavelengthNm: 1570, depth: 0.5 },
98
+ { wavelengthNm: 2000, depth: 0.72 },
99
+ ],
100
+ },
101
+ ];
102
+
103
+ const STAR_FALSE_POSITIVE_RISK: Record<StarClass, number> = {
104
+ 'm-dwarf': 24,
105
+ 'sun-like': 11,
106
+ 'k-dwarf': 8,
107
+ };
108
+
109
+ const STAR_TECH_MULTIPLIER: Record<StarClass, number> = {
110
+ 'm-dwarf': 1.2,
111
+ 'sun-like': 1,
112
+ 'k-dwarf': 0.88,
113
+ };
114
+
115
+ function clamp(value: number, min: number, max: number): number {
116
+ return Math.min(max, Math.max(min, value));
117
+ }
118
+
119
+ function moleculeScore(signature: MoleculeSignature, alignmentNm: number, noisePercent: number): MoleculeScore {
120
+ const toleranceNm = 24 + (noisePercent * 0.42);
121
+ const weightedMatch = signature.lines.reduce((sum, line) => {
122
+ const offset = Math.abs(alignmentNm - line.wavelengthNm);
123
+ const lineMatch = Math.max(0, 1 - (offset / toleranceNm));
124
+ return sum + (lineMatch * line.depth);
125
+ }, 0);
126
+ const lineDepth = signature.lines.reduce((sum, line) => sum + line.depth, 0);
127
+ const matchScore = clamp((weightedMatch / lineDepth) * 100, 0, 100);
128
+ const confidence = clamp(matchScore - (noisePercent * 0.55), 0, 100);
129
+
130
+ return {
131
+ id: signature.id,
132
+ name: signature.name,
133
+ alignmentNm,
134
+ matchScore,
135
+ confidence,
136
+ };
137
+ }
138
+
139
+ function verdictFromScores(biologicalIndex: number, technologicalIndex: number, falsePositiveRisk: number): BiosignatureResult['verdict'] {
140
+ if (falsePositiveRisk > 48 && biologicalIndex > 52) return 'ambiguous';
141
+ if (biologicalIndex > 72 && technologicalIndex < 62) return 'strong';
142
+ if (biologicalIndex > 45) return 'promising';
143
+ return 'weak';
144
+ }
145
+
146
+ export function analyzeBiosignatures(input: BiosignatureInput): BiosignatureResult {
147
+ const noisePercent = clamp(input.noisePercent, 0, 80);
148
+ const oxygen = moleculeScore(OXYGEN_SIGNATURE, input.oxygenAlignmentNm, noisePercent);
149
+ const methane = moleculeScore(METHANE_SIGNATURE, input.methaneAlignmentNm, noisePercent);
150
+ const ozone = moleculeScore(OZONE_SIGNATURE, input.ozoneAlignmentNm, noisePercent);
151
+ const waterContext = clamp(input.waterVaporPercent, 0, 100) / 100;
152
+ const co2Context = clamp(input.carbonDioxidePercent, 0, 100) / 100;
153
+ const disequilibrium = Math.sqrt((oxygen.confidence / 100) * (methane.confidence / 100)) * 100;
154
+ const ozoneShield = ozone.confidence * 0.7;
155
+ const contextBonus = (waterContext * 12) - (Math.abs(co2Context - 0.38) * 18);
156
+ const biologicalIndex = clamp((disequilibrium * 0.48) + (ozoneShield * 0.34) + (oxygen.confidence * 0.12) + contextBonus, 0, 100);
157
+ const technologicalIndex = clamp(((methane.confidence * 0.25) + (oxygen.confidence * 0.2) + (ozone.confidence * 0.12) + (noisePercent * 0.45)) * STAR_TECH_MULTIPLIER[input.stellarClass], 0, 100);
158
+ const falsePositiveRisk = clamp(STAR_FALSE_POSITIVE_RISK[input.stellarClass] + (noisePercent * 0.38) + (co2Context * 18) - (waterContext * 10), 0, 100);
159
+ const habitabilityIndex = clamp((biologicalIndex * 0.72) + (waterContext * 18) + ((100 - falsePositiveRisk) * 0.1), 0, 100);
160
+
161
+ return {
162
+ biologicalIndex,
163
+ technologicalIndex,
164
+ habitabilityIndex,
165
+ falsePositiveRisk,
166
+ evidence: [oxygen, methane, ozone],
167
+ verdict: verdictFromScores(biologicalIndex, technologicalIndex, falsePositiveRisk),
168
+ };
169
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { exoplanetBiosignatureDetector } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await exoplanetBiosignatureDetector.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
package/src/tools.ts CHANGED
@@ -26,6 +26,7 @@ import { CRYSTAL_LATTICE_STRUCTURE_FINDER_TOOL } from './tool/crystal-lattice-st
26
26
  import { FERMI_PARADOX_FILTER_LAB_TOOL } from './tool/fermi-paradox-filter-lab/index';
27
27
  import { EPIDEMIC_SIR_SIMULATOR_TOOL } from './tool/epidemic-sir-simulator/index';
28
28
  import { BLACK_HOLE_EVENT_HORIZON_SIMULATOR_TOOL } from './tool/black-hole-event-horizon-simulator/index';
29
+ import { EXOPLANET_BIOSIGNATURE_DETECTOR_TOOL } from './tool/exoplanet-biosignature-detector/index';
29
30
 
30
31
  export const ALL_TOOLS: ToolDefinition[] = [
31
32
  COLONY_COUNTER_TOOL,
@@ -54,4 +55,5 @@ export const ALL_TOOLS: ToolDefinition[] = [
54
55
  FERMI_PARADOX_FILTER_LAB_TOOL,
55
56
  EPIDEMIC_SIR_SIMULATOR_TOOL,
56
57
  BLACK_HOLE_EVENT_HORIZON_SIMULATOR_TOOL,
58
+ EXOPLANET_BIOSIGNATURE_DETECTOR_TOOL,
57
59
  ];