@jjlmoya/utils-science 1.41.0 → 1.43.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 (164) hide show
  1. package/package.json +1 -1
  2. package/scripts/crystal-lattice-catalog-translations.mjs +459 -0
  3. package/scripts/sync-crystal-lattice-i18n.mjs +50 -0
  4. package/src/category/index.ts +3 -1
  5. package/src/entries.ts +5 -1
  6. package/src/index.ts +2 -0
  7. package/src/tests/diacritics_density.test.ts +140 -0
  8. package/src/tests/inverted_punctuation.test.ts +84 -0
  9. package/src/tests/locale_completeness.test.ts +2 -2
  10. package/src/tests/script_density.test.ts +94 -0
  11. package/src/tests/tool_validation.test.ts +2 -3
  12. package/src/tool/conway-life-rule-lab/i18n/de.ts +62 -5
  13. package/src/tool/conway-life-rule-lab/i18n/es.ts +62 -5
  14. package/src/tool/conway-life-rule-lab/i18n/fr.ts +62 -5
  15. package/src/tool/conway-life-rule-lab/i18n/id.ts +102 -45
  16. package/src/tool/conway-life-rule-lab/i18n/it.ts +62 -5
  17. package/src/tool/conway-life-rule-lab/i18n/ja.ts +62 -5
  18. package/src/tool/conway-life-rule-lab/i18n/ko.ts +62 -5
  19. package/src/tool/conway-life-rule-lab/i18n/nl.ts +102 -45
  20. package/src/tool/conway-life-rule-lab/i18n/pl.ts +62 -5
  21. package/src/tool/conway-life-rule-lab/i18n/pt.ts +62 -5
  22. package/src/tool/conway-life-rule-lab/i18n/ru.ts +62 -5
  23. package/src/tool/conway-life-rule-lab/i18n/sv.ts +62 -5
  24. package/src/tool/conway-life-rule-lab/i18n/tr.ts +62 -5
  25. package/src/tool/conway-life-rule-lab/i18n/zh.ts +62 -5
  26. package/src/tool/cosmic-inflation/i18n/es.ts +5 -5
  27. package/src/tool/cosmic-inflation/i18n/it.ts +51 -51
  28. package/src/tool/cosmic-inflation/i18n/ru.ts +42 -150
  29. package/src/tool/crystal-lattice-structure-finder/component.astro +120 -39
  30. package/src/tool/crystal-lattice-structure-finder/crystal-lattice-structure-finder.css +204 -56
  31. package/src/tool/crystal-lattice-structure-finder/data.ts +107 -133
  32. package/src/tool/crystal-lattice-structure-finder/i18n/de.ts +39 -0
  33. package/src/tool/crystal-lattice-structure-finder/i18n/en.ts +45 -6
  34. package/src/tool/crystal-lattice-structure-finder/i18n/es.ts +39 -0
  35. package/src/tool/crystal-lattice-structure-finder/i18n/fr.ts +39 -0
  36. package/src/tool/crystal-lattice-structure-finder/i18n/id.ts +39 -0
  37. package/src/tool/crystal-lattice-structure-finder/i18n/it.ts +39 -0
  38. package/src/tool/crystal-lattice-structure-finder/i18n/ja.ts +39 -0
  39. package/src/tool/crystal-lattice-structure-finder/i18n/ko.ts +39 -0
  40. package/src/tool/crystal-lattice-structure-finder/i18n/nl.ts +39 -0
  41. package/src/tool/crystal-lattice-structure-finder/i18n/pl.ts +39 -0
  42. package/src/tool/crystal-lattice-structure-finder/i18n/pt.ts +39 -0
  43. package/src/tool/crystal-lattice-structure-finder/i18n/ru.ts +39 -0
  44. package/src/tool/crystal-lattice-structure-finder/i18n/sv.ts +39 -0
  45. package/src/tool/crystal-lattice-structure-finder/i18n/tr.ts +39 -0
  46. package/src/tool/crystal-lattice-structure-finder/i18n/zh.ts +39 -0
  47. package/src/tool/crystal-lattice-structure-finder/logic.ts +7 -3
  48. package/src/tool/crystal-lattice-structure-finder/structures/README.md +26 -0
  49. package/src/tool/crystal-lattice-structure-finder/structures/body-centered-cubic.ts +19 -0
  50. package/src/tool/crystal-lattice-structure-finder/structures/cesium-chloride.ts +19 -0
  51. package/src/tool/crystal-lattice-structure-finder/structures/diamond-cubic.ts +23 -0
  52. package/src/tool/crystal-lattice-structure-finder/structures/face-centered-cubic.ts +16 -0
  53. package/src/tool/crystal-lattice-structure-finder/structures/hexagonal-close-packed.ts +30 -0
  54. package/src/tool/crystal-lattice-structure-finder/structures/index.ts +25 -0
  55. package/src/tool/crystal-lattice-structure-finder/structures/perovskite.ts +20 -0
  56. package/src/tool/crystal-lattice-structure-finder/structures/rock-salt.ts +23 -0
  57. package/src/tool/crystal-lattice-structure-finder/structures/rutile.ts +25 -0
  58. package/src/tool/crystal-lattice-structure-finder/structures/shared.ts +86 -0
  59. package/src/tool/crystal-lattice-structure-finder/structures/simple-cubic.ts +16 -0
  60. package/src/tool/crystal-lattice-structure-finder/structures/wurtzite.ts +22 -0
  61. package/src/tool/crystal-lattice-structure-finder/structures/zinc-blende.ts +23 -0
  62. package/src/tool/double-slit-decoherence/i18n/fr.ts +42 -86
  63. package/src/tool/double-slit-decoherence/i18n/it.ts +28 -72
  64. package/src/tool/double-slit-decoherence/i18n/pt.ts +35 -79
  65. package/src/tool/double-slit-decoherence/i18n/ru.ts +59 -59
  66. package/src/tool/double-slit-decoherence/i18n/sv.ts +33 -81
  67. package/src/tool/double-slit-decoherence/i18n/tr.ts +55 -55
  68. package/src/tool/dyson-sphere-energy-capture/i18n/de.ts +42 -123
  69. package/src/tool/dyson-sphere-energy-capture/i18n/es.ts +6 -6
  70. package/src/tool/dyson-sphere-energy-capture/i18n/fr.ts +63 -131
  71. package/src/tool/dyson-sphere-energy-capture/i18n/it.ts +36 -113
  72. package/src/tool/dyson-sphere-energy-capture/i18n/ja.ts +66 -42
  73. package/src/tool/dyson-sphere-energy-capture/i18n/ko.ts +66 -42
  74. package/src/tool/dyson-sphere-energy-capture/i18n/pl.ts +50 -133
  75. package/src/tool/dyson-sphere-energy-capture/i18n/pt.ts +51 -132
  76. package/src/tool/dyson-sphere-energy-capture/i18n/ru.ts +61 -98
  77. package/src/tool/dyson-sphere-energy-capture/i18n/sv.ts +56 -137
  78. package/src/tool/dyson-sphere-energy-capture/i18n/tr.ts +61 -142
  79. package/src/tool/dyson-sphere-energy-capture/i18n/zh.ts +67 -43
  80. package/src/tool/epidemic-sir-simulator/bibliography.astro +15 -0
  81. package/src/tool/epidemic-sir-simulator/bibliography.ts +37 -0
  82. package/src/tool/epidemic-sir-simulator/component.astro +384 -0
  83. package/src/tool/epidemic-sir-simulator/entry.ts +30 -0
  84. package/src/tool/epidemic-sir-simulator/epidemic-sir-simulator.css +624 -0
  85. package/src/tool/epidemic-sir-simulator/i18n/de.ts +347 -0
  86. package/src/tool/epidemic-sir-simulator/i18n/en.ts +347 -0
  87. package/src/tool/epidemic-sir-simulator/i18n/es.ts +347 -0
  88. package/src/tool/epidemic-sir-simulator/i18n/fr.ts +347 -0
  89. package/src/tool/epidemic-sir-simulator/i18n/id.ts +347 -0
  90. package/src/tool/epidemic-sir-simulator/i18n/it.ts +347 -0
  91. package/src/tool/epidemic-sir-simulator/i18n/ja.ts +347 -0
  92. package/src/tool/epidemic-sir-simulator/i18n/ko.ts +347 -0
  93. package/src/tool/epidemic-sir-simulator/i18n/nl.ts +347 -0
  94. package/src/tool/epidemic-sir-simulator/i18n/pl.ts +347 -0
  95. package/src/tool/epidemic-sir-simulator/i18n/pt.ts +347 -0
  96. package/src/tool/epidemic-sir-simulator/i18n/ru.ts +347 -0
  97. package/src/tool/epidemic-sir-simulator/i18n/sv.ts +347 -0
  98. package/src/tool/epidemic-sir-simulator/i18n/tr.ts +347 -0
  99. package/src/tool/epidemic-sir-simulator/i18n/zh.ts +347 -0
  100. package/src/tool/epidemic-sir-simulator/index.ts +12 -0
  101. package/src/tool/epidemic-sir-simulator/logic.ts +163 -0
  102. package/src/tool/epidemic-sir-simulator/seo.astro +16 -0
  103. package/src/tool/fermi-paradox-filter-lab/bibliography.astro +14 -0
  104. package/src/tool/fermi-paradox-filter-lab/bibliography.ts +24 -0
  105. package/src/tool/fermi-paradox-filter-lab/component.astro +257 -0
  106. package/src/tool/fermi-paradox-filter-lab/entry.ts +26 -0
  107. package/src/tool/fermi-paradox-filter-lab/fermi-paradox-filter-lab.css +228 -0
  108. package/src/tool/fermi-paradox-filter-lab/i18n/de.ts +192 -0
  109. package/src/tool/fermi-paradox-filter-lab/i18n/en.ts +192 -0
  110. package/src/tool/fermi-paradox-filter-lab/i18n/es.ts +193 -0
  111. package/src/tool/fermi-paradox-filter-lab/i18n/fr.ts +192 -0
  112. package/src/tool/fermi-paradox-filter-lab/i18n/id.ts +192 -0
  113. package/src/tool/fermi-paradox-filter-lab/i18n/it.ts +192 -0
  114. package/src/tool/fermi-paradox-filter-lab/i18n/ja.ts +192 -0
  115. package/src/tool/fermi-paradox-filter-lab/i18n/ko.ts +192 -0
  116. package/src/tool/fermi-paradox-filter-lab/i18n/nl.ts +192 -0
  117. package/src/tool/fermi-paradox-filter-lab/i18n/pl.ts +193 -0
  118. package/src/tool/fermi-paradox-filter-lab/i18n/pt.ts +192 -0
  119. package/src/tool/fermi-paradox-filter-lab/i18n/ru.ts +192 -0
  120. package/src/tool/fermi-paradox-filter-lab/i18n/sv.ts +192 -0
  121. package/src/tool/fermi-paradox-filter-lab/i18n/tr.ts +192 -0
  122. package/src/tool/fermi-paradox-filter-lab/i18n/zh.ts +192 -0
  123. package/src/tool/fermi-paradox-filter-lab/index.ts +11 -0
  124. package/src/tool/fermi-paradox-filter-lab/logic.ts +125 -0
  125. package/src/tool/fermi-paradox-filter-lab/seo.astro +15 -0
  126. package/src/tool/global-albedo-snowball-simulator/i18n/ru.ts +50 -63
  127. package/src/tool/lorenz-attractor/i18n/de.ts +52 -78
  128. package/src/tool/lorenz-attractor/i18n/es.ts +5 -5
  129. package/src/tool/lorenz-attractor/i18n/fr.ts +51 -77
  130. package/src/tool/lorenz-attractor/i18n/it.ts +36 -36
  131. package/src/tool/lorenz-attractor/i18n/pt.ts +51 -75
  132. package/src/tool/mandelbrot-fractal/i18n/de.ts +31 -58
  133. package/src/tool/mandelbrot-fractal/i18n/es.ts +5 -5
  134. package/src/tool/mandelbrot-fractal/i18n/fr.ts +39 -57
  135. package/src/tool/mandelbrot-fractal/i18n/it.ts +30 -48
  136. package/src/tool/mandelbrot-fractal/i18n/pl.ts +34 -52
  137. package/src/tool/mandelbrot-fractal/i18n/pt.ts +30 -30
  138. package/src/tool/mandelbrot-fractal/i18n/ru.ts +42 -60
  139. package/src/tool/mandelbrot-fractal/i18n/sv.ts +38 -65
  140. package/src/tool/natural-selection-drift/i18n/it.ts +18 -95
  141. package/src/tool/natural-selection-drift/i18n/pt.ts +76 -19
  142. package/src/tool/natural-selection-drift/i18n/ru.ts +27 -24
  143. package/src/tool/natural-selection-drift/i18n/sv.ts +9 -6
  144. package/src/tool/phase-diagram-critical-points/i18n/de.ts +30 -68
  145. package/src/tool/phase-diagram-critical-points/i18n/es.ts +4 -4
  146. package/src/tool/phase-diagram-critical-points/i18n/fr.ts +37 -75
  147. package/src/tool/phase-diagram-critical-points/i18n/it.ts +8 -8
  148. package/src/tool/planet-atmosphere-survival/i18n/ru.ts +49 -192
  149. package/src/tool/radioactive-decay/i18n/de.ts +16 -32
  150. package/src/tool/radioactive-decay/i18n/es.ts +5 -5
  151. package/src/tool/radioactive-decay/i18n/fr.ts +24 -40
  152. package/src/tool/radioactive-decay/i18n/it.ts +18 -35
  153. package/src/tool/radioactive-decay/i18n/pt.ts +34 -34
  154. package/src/tool/roche-limit-satellite-disruption/i18n/pl.ts +52 -128
  155. package/src/tool/temperature-timeline/i18n/de.ts +46 -174
  156. package/src/tool/temperature-timeline/i18n/es.ts +5 -5
  157. package/src/tool/temperature-timeline/i18n/fr.ts +69 -144
  158. package/src/tool/temperature-timeline/i18n/it.ts +34 -133
  159. package/src/tool/temperature-timeline/i18n/pt.ts +35 -174
  160. package/src/tool/temperature-timeline/i18n/ru.ts +48 -174
  161. package/src/tool/twin-paradox-visualizer/i18n/es.ts +5 -5
  162. package/src/tool/twin-paradox-visualizer/i18n/fr.ts +34 -34
  163. package/src/tool/twin-paradox-visualizer/i18n/pt.ts +31 -71
  164. package/src/tools.ts +4 -0
@@ -0,0 +1,347 @@
1
+ import { bibliography } from '../bibliography';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+
4
+ const slug = 'epidemic-sir-simulator';
5
+ const title = '传染病SIR模拟器';
6
+ const description = '通过交互式SIR模型探索病原体传播过程,实时调整R0、致死率、疫苗接种、传染期、峰值负荷和传播曲线。';
7
+
8
+ const howTo = [
9
+ {
10
+ name: '设置基本再生数',
11
+ text: '拖动R0滑块,表示一个感染者在一个完全易感的人群中平均能造成多少二次感染。',
12
+ },
13
+ {
14
+ name: '添加疫苗接种与疫苗有效性',
15
+ text: '提高疫苗接种覆盖率或疫苗有效性,将已保护人群从易感人群中移除,从而降低有效再生数。',
16
+ },
17
+ {
18
+ name: '调整致死率与传染期',
19
+ text: '改变致死率百分比和传染天数,观察重症结果和曲线时序如何响应。',
20
+ },
21
+ {
22
+ name: '解读SIR曲线',
23
+ text: '使用图表和日期查看器,比较模拟疫情中易感者、感染者、康复者和预估死亡人数的曲线变化。',
24
+ },
25
+ ];
26
+
27
+ const faq = [
28
+ {
29
+ question: 'SIR模型展示的是什么?',
30
+ answer: 'SIR模型将人群分为易感者、感染者和康复者三个群体。它模拟了随着传染和康复的进行,人群在这三个群体之间随时间推移的变化过程。',
31
+ },
32
+ {
33
+ question: '疫苗接种如何改变曲线?',
34
+ answer: '当疫苗能够预防感染时,疫苗接种会减少易感人群数量。在本模拟器中,有效的疫苗接种会降低有效再生数,并可能缩小或推迟感染峰值。',
35
+ },
36
+ {
37
+ question: 'R0和Re是一样的吗?',
38
+ answer: '不一样。R0描述的是病原体在完全易感人群中的传播能力。Re是有效再生数,考虑了免疫、疫苗接种或行为改变减少了可被感染的人数之后的实际传播水平。',
39
+ },
40
+ {
41
+ question: '为什么感染曲线会先上升后下降?',
42
+ answer: '当每个感染者产生的新感染数量少于因康复或死亡而离开感染群体的人数时,感染曲线就会下降。在基本SIR模型中,易感人群的耗尽是最主要的驱动因素。',
43
+ },
44
+ {
45
+ question: '这个模拟器能预测真实的疫情吗?',
46
+ answer: '这是一个教学和场景探索工具,并非预测工具。真实的疫情模型需要考虑年龄结构、接触网络、地理分布、报告延迟、行为变化、病毒变异、免疫力衰退和经过校准的数据。',
47
+ },
48
+ {
49
+ question: '攻击率和感染峰值有什么区别?',
50
+ answer: '攻击率衡量的是疫情结束时总人口中被感染过的比例。感染峰值衡量的是同一时间点同时处于传染期的最大人数。低峰值并不保证低攻击率,反之亦然。',
51
+ },
52
+ {
53
+ question: 'Re低于1在图表上看起来是什么样?',
54
+ answer: '当Re降至1以下时,每个感染者平均产生少于一个的新感染。在图表上,感染曲线不会急剧上升,甚至可能从起始病例就开始下降,这意味着疫情无法持续。',
55
+ },
56
+ {
57
+ question: '传染期如何影响疫情?',
58
+ answer: '较长的传染期延长了每个感染者可以传播病原体的时间。这会拉长疫情的时间线,推迟峰值出现的时间,并且在相同的R0下,会产生更宽、峰值更低的曲线。',
59
+ },
60
+ {
61
+ question: '为什么致死率不影响感染曲线?',
62
+ answer: '在这个SIR模型中,致死率只影响从康复组分支出来的死亡计数。它不会反馈到传播过程中,因为死亡和康复都以相同的速率将人们从传染池中移除。',
63
+ },
64
+ {
65
+ question: '什么是群体免疫阈值?',
66
+ answer: '群体免疫阈值是指需要免疫的人口比例,使Re降至1以下。其近似值为1 - 1/R0。对于R0约为12的麻疹,阈值超过91%。对于R0约为1.3的季节性流感,阈值接近23%。',
67
+ },
68
+ ];
69
+
70
+ export const content: ToolLocaleContent = {
71
+ slug,
72
+ title,
73
+ description,
74
+ ui: {
75
+ liveScenario: '实时疫情场景',
76
+ curveChart: 'SIR传播曲线',
77
+ inspectDay: '查看模拟第n天',
78
+ legend: '曲线图例',
79
+ susceptible: '易感者',
80
+ infected: '感染者',
81
+ recovered: '康复或免疫',
82
+ deaths: '死亡',
83
+ controls: '疫情控制参数',
84
+ modelName: '舱室模型',
85
+ peakInfected: '感染峰值',
86
+ peakDay: '峰值日期',
87
+ attackRate: '攻击率',
88
+ estimatedDeaths: '预估死亡人数',
89
+ r0: '基本再生数 R0',
90
+ lethality: '致死率',
91
+ vaccination: '疫苗接种覆盖率',
92
+ vaccineEffectiveness: '疫苗有效性',
93
+ infectiousDays: '传染期',
94
+ initialInfected: '初始感染人数',
95
+ dayLabel: '第',
96
+ daysUnit: '天',
97
+ infectiousLabel: '传染中',
98
+ reLabel: 'Re',
99
+ },
100
+ seo: [
101
+ {
102
+ type: 'title',
103
+ text: '传染病SIR模拟器:R0、疫苗接种、致死率与传播曲线分析',
104
+ level: 2,
105
+ },
106
+ {
107
+ type: 'paragraph',
108
+ html: '这个传染病SIR模拟器让你探索病原体如何在人群中传播-易感者被感染后,通过康复或死亡离开传染群体。它面向学生、科学传播者、公共卫生学习者,以及任何希望快速直观理解为什么传播或免疫方面的微小变化就能重塑疫情的人。',
109
+ },
110
+ {
111
+ type: 'paragraph',
112
+ html: '交互式控制面板聚焦于人们最常想要测试的变量:<strong>R0</strong>、致死率、疫苗接种覆盖率、疫苗有效性、传染期和初始感染比例。图表实时更新,让你可以将易感者、感染者、康复者、免疫者和死亡曲线作为一个相互关联的疫情系统进行比较。',
113
+ },
114
+ {
115
+ type: 'title',
116
+ text: 'SIR模型的工作原理',
117
+ level: 3,
118
+ },
119
+ {
120
+ type: 'paragraph',
121
+ html: '基本SIR模型将人口分为三个主要舱室。<strong>S</strong>代表仍可能被感染的易感人群。<strong>I</strong>代表当前具有传染性、可以传播病原体的感染者。<strong>R</strong>代表因康复、获得免疫或其它原因离开传播链条、不再具有传染性的人群。该模拟器还从离开感染组的人群中追踪预估死亡数,作为重症结果的统计分支。',
122
+ },
123
+ {
124
+ type: 'paragraph',
125
+ html: '传播速率与R0和传染期相关联。如果R0较高或人们传染时间较长,在感染组缩小之前会产生更多的新感染。如果疫苗接种从易感人群中移除足够多的人,有效再生数就会下降,疫情峰值可以变得小得多。',
126
+ },
127
+ {
128
+ type: 'table',
129
+ headers: ['控制参数', '改变的内容', '典型曲线效果'],
130
+ rows: [
131
+ ['R0', '考虑免疫之前的传播潜能', 'R0越高,感染曲线上升越快,峰值越高。'],
132
+ ['疫苗接种覆盖率', '受保护后被移出易感人群的比例', '覆盖率越高,Re越低,可以平缓疫情曲线。'],
133
+ ['疫苗有效性', '在此简化模型中疫苗预防感染的程度', '有效性越高,相同的覆盖率提供更强的保护。'],
134
+ ['传染期', '人们保持传染性的平均时间', '传染期越长,改变疫情时序,可能延长疫情。'],
135
+ ['致死率', '离开感染组后被计为死亡的比例', '致死率越高,死亡曲线上升,但不直接影响传播。'],
136
+ ],
137
+ },
138
+ {
139
+ type: 'title',
140
+ text: 'R0、Re与群体免疫直觉',
141
+ level: 3,
142
+ },
143
+ {
144
+ type: 'paragraph',
145
+ html: 'R0是一个感染者在完全易感人群中平均造成的二代病例数。有效再生数Re在部分人群已有免疫力、接种疫苗、隔离或其它原因无法被感染时会降低。在本模拟器中,有效疫苗接种直接降低易感性,因此展示的Re会随着受保护覆盖率的上升而下降。',
146
+ },
147
+ {
148
+ type: 'paragraph',
149
+ html: '一个常用的群体免疫近似公式是<strong>1 - 1 / R0</strong>。对于R0为3的情况,阈值约为66.7%的有效免疫。该模拟器让这个阈值变得直观:当有效疫苗接种低于阈值时,疫情仍可能增长;当高于阈值时,传播难以维持。',
150
+ },
151
+ {
152
+ type: 'title',
153
+ text: '感染峰值意味着什么',
154
+ level: 3,
155
+ },
156
+ {
157
+ type: 'paragraph',
158
+ html: '感染峰值是模拟人群中同时处于传染期的最大人数。它在实际操作中通常比总感染人数更重要,因为医院、实验室、隔离项目和接触者追踪团队承受的压力来自同时存在的活跃病例。即使最终攻击率没有降为零,降低峰值也可能具有重要意义。',
159
+ },
160
+ {
161
+ type: 'paragraph',
162
+ html: '攻击率估算的是整个模拟周期结束时的总感染比例。高攻击率意味着病原体在易感人群耗尽或被控制之前感染了大量人群。低攻击率意味着免疫、疫苗接种或弱传播阻止了广泛扩散。',
163
+ },
164
+ {
165
+ type: 'title',
166
+ text: '现实世界中的R0值及其对群体免疫的启示',
167
+ level: 3,
168
+ },
169
+ {
170
+ type: 'paragraph',
171
+ html: '基本再生数R0并不是某个病原体的固定生物学常数。它取决于接触模式、人口密度、文化习惯和环境因素。同一病毒在密集城市和农村地区,或在室内聚集更多的季节,可能具有不同的R0值。以下数值来自已发表研究的参考范围。',
172
+ },
173
+ {
174
+ type: 'table',
175
+ headers: ['病原体', '典型R0范围', '群体免疫阈值 (1 - 1/R0)', '主要传播特征'],
176
+ rows: [
177
+ ['季节性流感', '1.2 - 1.4', '17% - 29%', '传染期短,具有季节性变化'],
178
+ ['SARS-CoV-2 (原始株)', '2.0 - 3.0', '50% - 67%', '症状前传播,气溶胶途径'],
179
+ ['SARS-CoV-2 (Delta)', '5.0 - 8.0', '80% - 87%', '病毒载量增加,传播更快'],
180
+ ['SARS-CoV-2 (Omicron)', '8.0 - 12.0', '87% - 92%', '免疫逃逸,上呼吸道嗜性'],
181
+ ['小儿麻痹症', '5.0 - 7.0', '80% - 86%', '粪口传播,长期无症状排毒'],
182
+ ['天花', '5.0 - 7.0', '80% - 86%', '通过全球疫苗接种运动被消灭'],
183
+ ['麻疹', '12.0 - 18.0', '92% - 94%', '传染性极强,空气传播,传染期长'],
184
+ ['百日咳', '12.0 - 17.0', '92% - 94%', '免疫力衰退导致重复感染'],
185
+ ],
186
+ },
187
+ {
188
+ type: 'paragraph',
189
+ html: '高群体免疫阈值并不意味着当覆盖率低于阈值时疫苗接种就毫无意义。即使部分免疫也能减缓传播、降低峰值、减少重症结果,并为医疗系统争取时间。当你将疫苗接种滑块移动到中间值时,模拟器会展示这一效果。',
190
+ },
191
+ {
192
+ type: 'title',
193
+ text: '疫情过程中有效再生数Re如何变化',
194
+ level: 3,
195
+ },
196
+ {
197
+ type: 'paragraph',
198
+ html: 'Re是疫情某一时刻的有效再生数。与假设完全易感人群的R0不同,Re考虑了免疫、疫苗接种以及任何降低易感性的因素。在此模拟器中,Re的计算公式为<strong>R0 x (1 - 受保护比例)</strong>,其中受保护比例是通过疫苗接种获得有效免疫的人口比例。',
199
+ },
200
+ {
201
+ type: 'paragraph',
202
+ html: '模拟器头部显示的Re值会随着你调整控制参数而更新。当Re保持在1以上时,疫情持续增长。当它降至1以下时,每个感染者平均产生少于一个的新感染,疫情无法自我维持。这就是疫情控制的核心思路:通过免疫、行为改变或干预措施使Re降至并维持在1以下。',
203
+ },
204
+ {
205
+ type: 'title',
206
+ text: '攻击率、峰值负荷及其揭示的疫情严重程度',
207
+ level: 3,
208
+ },
209
+ {
210
+ type: 'paragraph',
211
+ html: '攻击率是在整个模拟疫情中总人口被感染的比例。这是疫情波过后最常引用的总结性指标。高攻击率意味着病原体在易感人群耗尽或控制措施阻止传播之前感染了大多数易感者。低攻击率意味着免疫、疫苗接种或传播能力本身较弱阻止了大规模感染。',
212
+ },
213
+ {
214
+ type: 'paragraph',
215
+ html: '感染峰值-同时处于传染期的最大人数-对短期医疗压力更为重要。一个攻击率中等但峰值极高且尖锐的疫情波可能压垮医院,即使总病例数并不极端。相反,一条平缓的曲线可能在数周内达到类似的攻击率,给医疗系统留出处理病例的时间。这就是为什么公共卫生官员强调<strong>平缓曲线</strong>作为一个独立于预防所有感染的操作目标。',
216
+ },
217
+ {
218
+ type: 'title',
219
+ text: 'SIR模型中的平缓曲线与医疗容量',
220
+ level: 3,
221
+ },
222
+ {
223
+ type: 'paragraph',
224
+ html: 'SIR模型中的感染曲线可以理解为同时需要医疗救助的人数。在真实疫情中,每个需要病床、氧气支持或重症监护的人都依赖于有限的资源池。当感染曲线超过可用容量时,由于系统无法提供足够的医疗服务,各种原因的死亡率都会上升。',
225
+ },
226
+ {
227
+ type: 'paragraph',
228
+ html: '在此模型中,疫苗接种通过在被感染之前将人们从易感人群中移除来平缓曲线。通过其它措施-口罩、通风、社交距离、检测、隔离-降低R0也会在更完整的模型中降低峰值。这个模拟使机制变得可见:随着有效疫苗接种覆盖率的增加,峰值缩小、推迟或完全消失。',
229
+ },
230
+ {
231
+ type: 'title',
232
+ text: 'SIR模型背后的数学可视化',
233
+ level: 3,
234
+ },
235
+ {
236
+ type: 'paragraph',
237
+ html: '在SIR模型中,每个时间步长的新感染率取决于三个量:传播率<strong>beta</strong>、当前感染人数<strong>I</strong>以及仍处于易感状态的有效人口比例<strong>S / N</strong>。乘积<strong>beta x I x S / N</strong>被称为感染力。每一天,这个感染力度量了有多少易感者进入感染舱室。',
238
+ },
239
+ {
240
+ type: 'paragraph',
241
+ html: '人们以康复率<strong>gamma = 1 / 传染期</strong>离开感染舱室。感染力和康复率之间的平衡决定了疫情是增长还是收缩。当beta x S / N超过gamma时,新感染超过康复,疫情扩大。当易感比例S / N下降到足够低时,gamma占主导地位,疫情收缩。',
242
+ },
243
+ {
244
+ type: 'paragraph',
245
+ html: '参数<strong>beta</strong>在界面上并不直接可见。它通过关系式<strong>beta = R0 x gamma</strong>从R0和传染期推导得出。这就是为什么改变R0或传染期会产生相似但不完全相同的曲线形状。两个参数都影响感染力,但传染期还会拉长疫情的时间轴。',
246
+ },
247
+ {
248
+ type: 'title',
249
+ text: '如何在学习与教学中使用本模拟器',
250
+ level: 3,
251
+ },
252
+ {
253
+ type: 'list',
254
+ items: [
255
+ '<strong>比较高R0与低R0场景:</strong>将R0设为1.5(季节性流感范围),然后设为6.0(疫苗接种前的小儿麻痹症范围)。注意即使所有其它控制参数相同,峰值高度、峰值时间和攻击率如何变化。',
256
+ '<strong>探索群体免疫阈值:</strong>从R0为3.0且无疫苗接种开始。记录攻击率。然后增加疫苗接种覆盖率直到Re降至1以下。比较覆盖率刚好低于和刚好高于阈值时的峰值和攻击率。',
257
+ '<strong>测试慢响应与快响应的效果:</strong>在不同水平设置疫苗接种覆盖率,观察峰值出现的时间。更高的覆盖率不仅降低峰值高度,通常还会推迟峰值出现,为医疗准备争取时间。',
258
+ '<strong>区分致死率与传播:</strong>将致死率设为0%,观察感染曲线。然后将致死率设为10%而不改变其它设置。感染曲线不变,但死亡人数上升。这展示了病死率和传播速度是截然不同的流行病学维度。',
259
+ '<strong>检查传染期的影响:</strong>在相同的R0下比较4天传染期与18天传染期。较长的传染期拉长曲线,推迟峰值,产生更长但更低的疫情波。',
260
+ '<strong>课堂练习-寻找阈值:</strong>让学生找出对于给定R0使Re降至1以下的最低疫苗接种覆盖率,然后将结果与公式1 - 1/R0进行比较。',
261
+ ],
262
+ },
263
+ {
264
+ type: 'title',
265
+ text: '何时以及为何使用本模拟器',
266
+ level: 3,
267
+ },
268
+ {
269
+ type: 'list',
270
+ items: [
271
+ '<strong>流行病学学生:</strong>在使用微分方程或编程建模之前,将数学SIR框架与交互式曲线形状联系起来。',
272
+ '<strong>科学传播者与记者:</strong>生成图表、截图或实时解说,展示R0、疫苗接种和传染期为何对疫情轨迹至关重要。',
273
+ '<strong>公共卫生学习者:</strong>测试不同的干预组合如何改变疫情峰值和攻击率,培养对疫情应对中各种权衡的直觉。',
274
+ '<strong>任何对疫情数学感兴趣的人:</strong>无需编写代码或安装软件即可探索SIR模型。每个控制参数都会实时更新图表。',
275
+ ],
276
+ },
277
+ {
278
+ type: 'title',
279
+ text: '模型扩展:SEIR、SIRS及其它',
280
+ level: 3,
281
+ },
282
+ {
283
+ type: 'paragraph',
284
+ html: '本模拟器使用基本的SIR结构。几种常见的扩展增加了现实性。<strong>SEIR模型</strong>增加了一个暴露(E)舱室,代表已感染但尚未具有传染性的人群,即潜伏期。与SIR模型相比,暴露组将疫情峰值向外推迟,因为感染在潜伏期内不参与传播。',
285
+ },
286
+ {
287
+ type: 'paragraph',
288
+ html: '<strong>SIRS模型</strong>允许康复者随时间推移失去免疫力并重新进入易感舱室。这模拟了百日咳或SARS-CoV-2等免疫力会衰退的病原体,产生持续或反复的疫情波。<strong>年龄结构模型</strong>将人口按年龄组划分,具有不同的接触矩阵和严重程度特征,这对于儿童和老年人预后差异很大的疾病至关重要。',
289
+ },
290
+ {
291
+ type: 'paragraph',
292
+ html: '其它扩展包括地理传播(集合种群模型)、行为改变(自适应接触率)、随机噪声(适用于小种群中随机灭绝很重要的情况)以及干预时机(学校关闭、封锁、旅行限制的开启与关闭)。每个扩展都增加了对预测至关重要的复杂性,但可能掩盖基本SIR模型清晰展示的核心传播动力学。',
293
+ },
294
+ {
295
+ type: 'title',
296
+ text: '本疫情模拟器的局限性',
297
+ level: 3,
298
+ },
299
+ {
300
+ type: 'paragraph',
301
+ html: '这是一个简化的确定性SIR模型。它不包括年龄组、家庭结构、学校、旅行、超级传播、潜伏期、无症状传播、季节性、病毒变异、免疫力衰退、行为改变、检测延迟或随时间开启和关闭的公共卫生干预措施。这些细节对于真实预测至关重要。',
302
+ },
303
+ {
304
+ type: 'paragraph',
305
+ html: '该模型假设一个人口充分混合的环境,每个易感者与每个感染者有同等的接触概率。真实人群具有强聚集性、年龄同配性、地理结构和个体传染性差异的接触网络。这些特征可能产生较慢的初始增长、超级传播事件和异质性疫情模式,而同质模型无法捕捉这些现象。',
306
+ },
307
+ {
308
+ type: 'paragraph',
309
+ html: '请使用本模拟器理解机制和比较场景,而非用于医疗、政策或紧急决策。现实世界的疫情评估需要监测数据、本地背景、不确定性分析和专业的流行病学建模。',
310
+ },
311
+ ],
312
+ faq,
313
+ bibliography,
314
+ howTo,
315
+ schemas: [
316
+ {
317
+ '@context': 'https://schema.org',
318
+ '@type': 'SoftwareApplication',
319
+ name: title,
320
+ description,
321
+ applicationCategory: 'ScientificApplication',
322
+ operatingSystem: 'Any',
323
+ },
324
+ {
325
+ '@context': 'https://schema.org',
326
+ '@type': 'FAQPage',
327
+ mainEntity: faq.map((item) => ({
328
+ '@type': 'Question',
329
+ name: item.question,
330
+ acceptedAnswer: {
331
+ '@type': 'Answer',
332
+ text: item.answer,
333
+ },
334
+ })),
335
+ },
336
+ {
337
+ '@context': 'https://schema.org',
338
+ '@type': 'HowTo',
339
+ name: title,
340
+ step: howTo.map((step) => ({
341
+ '@type': 'HowToStep',
342
+ name: step.name,
343
+ text: step.text,
344
+ })),
345
+ },
346
+ ],
347
+ };
@@ -0,0 +1,12 @@
1
+ import { epidemicSirSimulator } from './entry';
2
+ import type { ToolDefinition } from '../../types';
3
+
4
+ export * from './entry';
5
+
6
+ export const EPIDEMIC_SIR_SIMULATOR_TOOL: ToolDefinition = {
7
+ entry: epidemicSirSimulator,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
12
+
@@ -0,0 +1,163 @@
1
+ export interface SirSimulationInput {
2
+ population: number;
3
+ initialInfectedPercent: number;
4
+ r0: number;
5
+ infectiousDays: number;
6
+ vaccinationPercent: number;
7
+ vaccineEffectiveness: number;
8
+ fatalityPercent: number;
9
+ days: number;
10
+ }
11
+
12
+ export interface SirDayPoint {
13
+ day: number;
14
+ susceptible: number;
15
+ infected: number;
16
+ recovered: number;
17
+ vaccinated: number;
18
+ deaths: number;
19
+ newInfections: number;
20
+ }
21
+
22
+ export interface SirSummary {
23
+ peakInfected: number;
24
+ peakDay: number;
25
+ totalInfected: number;
26
+ attackRate: number;
27
+ finalDeaths: number;
28
+ effectiveR0: number;
29
+ }
30
+
31
+ export interface SirSimulationResult {
32
+ points: SirDayPoint[];
33
+ summary: SirSummary;
34
+ }
35
+
36
+ interface SirRuntimeState {
37
+ susceptible: number;
38
+ infected: number;
39
+ recovered: number;
40
+ deaths: number;
41
+ }
42
+
43
+ interface SirPreparedInput {
44
+ population: number;
45
+ days: number;
46
+ beta: number;
47
+ recoveryRate: number;
48
+ protectedVaccinated: number;
49
+ fatalityShare: number;
50
+ effectivePopulation: number;
51
+ initialInfected: number;
52
+ }
53
+
54
+ function clamp(value: number, min: number, max: number) {
55
+ return Math.max(min, Math.min(max, value));
56
+ }
57
+
58
+ function prepareInput(input: SirSimulationInput): SirPreparedInput {
59
+ const population = Math.max(1, input.population);
60
+ const days = Math.max(1, Math.round(input.days));
61
+ const recoveryRate = 1 / Math.max(1, input.infectiousDays);
62
+ const beta = Math.max(0, input.r0) * recoveryRate;
63
+ const vaccinated = population * clamp(input.vaccinationPercent / 100, 0, 0.98);
64
+ const protectedVaccinated = vaccinated * clamp(input.vaccineEffectiveness / 100, 0, 1);
65
+ const initialInfected = population * clamp(input.initialInfectedPercent / 100, 0.0001, 0.2);
66
+ const fatalityShare = clamp(input.fatalityPercent / 100, 0, 0.25);
67
+ const effectivePopulation = Math.max(1, population - protectedVaccinated);
68
+
69
+ return {
70
+ population,
71
+ days,
72
+ beta,
73
+ recoveryRate,
74
+ protectedVaccinated,
75
+ fatalityShare,
76
+ effectivePopulation,
77
+ initialInfected,
78
+ };
79
+ }
80
+
81
+ function initialState(prepared: SirPreparedInput): SirRuntimeState {
82
+ return {
83
+ susceptible: Math.max(0, prepared.population - prepared.protectedVaccinated - prepared.initialInfected),
84
+ infected: Math.min(prepared.initialInfected, prepared.effectivePopulation),
85
+ recovered: 0,
86
+ deaths: 0,
87
+ };
88
+ }
89
+
90
+ function pointFromState(day: number, state: SirRuntimeState, protectedVaccinated: number, newInfections: number) {
91
+ return {
92
+ day,
93
+ susceptible: state.susceptible,
94
+ infected: state.infected,
95
+ recovered: state.recovered,
96
+ vaccinated: protectedVaccinated,
97
+ deaths: state.deaths,
98
+ newInfections,
99
+ };
100
+ }
101
+
102
+ function nextState(state: SirRuntimeState, prepared: SirPreparedInput) {
103
+ const forceOfInfection = prepared.beta * state.infected * state.susceptible / prepared.effectivePopulation;
104
+ const newInfections = Math.min(state.susceptible, Math.max(0, forceOfInfection));
105
+ const leavingInfected = Math.min(state.infected + newInfections, state.infected * prepared.recoveryRate);
106
+ const newDeaths = leavingInfected * prepared.fatalityShare;
107
+
108
+ return {
109
+ newInfections,
110
+ state: {
111
+ susceptible: Math.max(0, state.susceptible - newInfections),
112
+ infected: Math.max(0, state.infected + newInfections - leavingInfected),
113
+ recovered: state.recovered + leavingInfected - newDeaths,
114
+ deaths: state.deaths + newDeaths,
115
+ },
116
+ };
117
+ }
118
+
119
+ function summarize(points: SirDayPoint[], population: number, input: SirSimulationInput, protectedVaccinated: number) {
120
+ let peakInfected = 0;
121
+ let peakDay = 0;
122
+
123
+ for (const point of points) {
124
+ if (point.infected > peakInfected) {
125
+ peakInfected = point.infected;
126
+ peakDay = point.day;
127
+ }
128
+ }
129
+
130
+ const finalPoint = points[points.length - 1] ?? points[0];
131
+ const finalRecovered = finalPoint.recovered;
132
+ const finalDeaths = finalPoint.deaths;
133
+ const finalInfected = finalPoint.infected;
134
+ const outbreakBurden = finalRecovered + finalDeaths + finalInfected;
135
+
136
+ return {
137
+ peakInfected,
138
+ peakDay,
139
+ totalInfected: outbreakBurden,
140
+ attackRate: outbreakBurden / population,
141
+ finalDeaths,
142
+ effectiveR0: input.r0 * (1 - protectedVaccinated / population),
143
+ };
144
+ }
145
+
146
+ export function runSirSimulation(input: SirSimulationInput): SirSimulationResult {
147
+ const prepared = prepareInput(input);
148
+ let state = initialState(prepared);
149
+ let latestNewInfections = prepared.initialInfected;
150
+ const points: SirDayPoint[] = [];
151
+
152
+ for (let day = 0; day <= prepared.days; day += 1) {
153
+ points.push(pointFromState(day, state, prepared.protectedVaccinated, latestNewInfections));
154
+ const next = nextState(state, prepared);
155
+ state = next.state;
156
+ latestNewInfections = next.newInfections;
157
+ }
158
+
159
+ return {
160
+ points,
161
+ summary: summarize(points, prepared.population, input, prepared.protectedVaccinated),
162
+ };
163
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { epidemicSirSimulator } 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 epidemicSirSimulator.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
16
+
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { fermiParadoxFilterLab } 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 fermiParadoxFilterLab.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,24 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ {
5
+ name: 'Drake, F. D. (1961). Project Ozma and the search for extraterrestrial intelligence.',
6
+ url: 'https://en.wikipedia.org/wiki/Project_Ozma',
7
+ },
8
+ {
9
+ name: 'Fermi, E. (1950). Discussion on interstellar travel and the absence of observed extraterrestrial civilizations.',
10
+ url: 'https://www.planetary.org/articles/the-fermi-paradox-where-are-all-the-aliens',
11
+ },
12
+ {
13
+ name: 'The Fermi Paradox: Where are all the aliens?',
14
+ url: 'https://mason.gmu.edu/~rhanson/greatfilter.html',
15
+ },
16
+ {
17
+ name: 'The Galactic Habitable Zone and the Age Distribution of Complex Life in the Milky Way',
18
+ url: 'https://arxiv.org/abs/astro-ph/0401024',
19
+ },
20
+ {
21
+ name: 'NASA Exoplanet Archive. Confirmed planets and occurrence context for planetary systems.',
22
+ url: 'https://exoplanetarchive.ipac.caltech.edu/',
23
+ },
24
+ ];