Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
reaction_table.h
Go to the documentation of this file.
1// reaction_table.h — 反応表の定義(人が書く値)と、GPU と CPU リファレンスが読む整数の表へのベイク(02 §1〜2。T-0014)。
2//
3// データの流れ:
4// ReactionTableDefinition(元素・物質・規則。SI の値。C++ に手で書く〔sim/reaction_test_table〕か、
5// Luau のパッケージから読む〔script/reaction_package。T-0021〕)
6// → BakeReactionTable(整数の数学ライブラリだけで計算する。R7)→ BakedReactionTable(RxSpecies・RxRule・索引・速度の表)
7// → ReactionTableView(CPU の RxStepCellWait 用)/ そのままバッファに載せて GPU へ(shaders/sim/reaction_cells.hlsl)
8//
9// ベイクの検査(02 §2): 元素の釣り合いが崩れた規則・知らない元素と物質・係数や次数の誤り・名前の重なりはエラーにする。
10// 反応熱は生成エンタルピーの差から自動で決まる(手で書かない)ので、エネルギーの釣り合いは構造的に守られる。
11// 文献の反応熱を書いた規則は、生成エンタルピーの差と比べて食い違えば警告を出す(BakedReactionTable::warnings。02 §2 の 2)。
12// 並びに依らない(T-0021・ADR-0032): 元素・物質・規則・規則の項は名前のバイト順に並べ直してから ID を振る。
13// 定義の並び(C++ の配列の順・Luau の表の順・Mod を入れた順)が違っても、中身が同じなら同じバイト列の表になる。
14// 浮動小数点は使わない(engine/src/sim は検査の対象。04 §4)。
15#pragma once
16
17#include <cstdint>
18#include <expected>
19#include <optional>
20#include <span>
21#include <string>
22#include <string_view>
23#include <vector>
24
25#include "common/reaction.hlsli"
27
28namespace bicameral::sim {
29
30 // --- 定義(人が書く値)---
31
33 std::string name;
34 uint32_t atomicMass = 0; // mg/mol(C = 12,011)
35 };
36
37 struct ElementCount {
38 std::string element;
39 uint32_t count = 0;
40 };
41
43 std::string name;
44 std::vector<ElementCount> composition; // 物質 1 mol あたりの元素の数
45 int64_t formationEnthalpy = 0; // J/mol(298.15 K)
46 uint32_t heatCapacity = 0; // mJ/(mol·K)(一定と置く)
47 uint32_t thermalConductivity = 0; // mW/(m·K)(一定と置く。T-0089)
48 };
49
50 struct RuleTerm {
51 std::string species;
52 uint32_t coefficient = 1;
53 bool firstOrder = false; // 反応物だけ: 速度式で次数 1(false なら次数 0)
54 };
55
56 // Arrhenius: k = A · exp(−Ea / RT)。A は SI(次数 1 の反応物が n 個なら (m³/mol)^(n−1) / s)で、仮数 × 10^指数 で書く
60 int64_t activationEnergy = 0; // J/mol
61 };
62
64 std::string name;
65 std::vector<RuleTerm> reactants;
66 std::vector<RuleTerm> products;
68
69 // --- 検査だけに使う(表には入らない)---
70 // 文献の反応熱 ΔH(298.15 K、J/mol)。書いたら、生成エンタルピーの差との食い違いを警告する(02 §2 の 2)
71 std::optional<int64_t> declaredReactionEnthalpy;
72 };
73
75 std::vector<ElementDefinition> elements;
76 std::vector<SpeciesDefinition> species;
77 std::vector<RuleDefinition> rules;
78 };
79
80 // --- ベイクした表 ---
81
82 static_assert(sizeof(reaction::RxSpecies) == 24);
83 static_assert(sizeof(reaction::RxRule) == 80);
84 static_assert(sizeof(reaction::RxCell) == 112);
85
86 // CPU の RxStepCellWait に渡す表の読み方(reaction.hlsli の Table の約束)
88 std::span<const reaction::RxSpecies> species;
89 std::span<const reaction::RxRule> rules;
90 std::span<const uint32_t> ruleIndex;
91 std::span<const uint64_t> rates;
92
93 [[nodiscard]] reaction::RxSpecies Species(uint32_t id) const { return species[id]; }
94 [[nodiscard]] reaction::RxRule Rule(uint32_t id) const { return rules[id]; }
95 [[nodiscard]] uint32_t RuleIndex(uint32_t position) const { return ruleIndex[position]; }
96 [[nodiscard]] uint64_t Rate(uint32_t rule, uint32_t kelvin) const {
97 return rates[(size_t{rule} * reaction::RX_RATE_TABLE_KELVINS) + kelvin];
98 }
99 };
100
102 // --- GPU に載せる(添字 = ID。物質の 0 は使わない)---
103 std::vector<reaction::RxSpecies> species;
104 std::vector<reaction::RxRule> rules;
105 std::vector<uint32_t> ruleIndex; // 物質ごとの規則の一覧をつないだもの(RxSpecies::ruleBegin から)
106 std::vector<uint64_t> rates; // 規則 × RX_RATE_TABLE_KELVINS(仮数 << 32 | 指数)
107
108 // --- CPU だけ(検査と表示)---
109 std::vector<std::string> elementNames;
110 std::vector<std::string> speciesNames; // 添字 = 物質 ID
111 std::vector<std::string> ruleNames; // 添字 = 規則 ID
112 std::vector<uint32_t> molarMasses; // mg/mol。添字 = 物質 ID
113 std::vector<uint32_t> speciesElements; // 物質 ID × 元素の数 + 元素
114 std::vector<std::string> warnings; // ベイクは通したが、書いた人に見せる食い違い(文献の反応熱など)
115
116 [[nodiscard]] ReactionTableView View() const {
117 return {.species = species, .rules = rules, .ruleIndex = ruleIndex, .rates = rates};
118 }
119
120 // 名前から物質 ID(無ければ 0)
121 [[nodiscard]] uint32_t SpeciesId(std::string_view name) const;
122 };
123
124 [[nodiscard]] std::expected<BakedReactionTable, std::string> BakeReactionTable(
125 const ReactionTableDefinition& definition);
126
127 // --- セルを作る・調べる(テストと、T-0089 の初期状態)---
128
130 uint32_t species = 0;
131 uint64_t amount = 0; // µmol
132 };
133
134 // 成分と温度(mK)からセルを作る。エネルギー = 化学 + 熱容量 × 温度 を mJ に切り上げる(熱が負にならないように)
135 [[nodiscard]] reaction::RxCell MakeReactionCell(const BakedReactionTable& table,
136 std::span<const SpeciesAmount> amounts,
137 int32_t temperatureMilliKelvin);
138
139 // 同じ値の、成分の数に上限が無いセル(T-0175。成分が RX_MAX_CELL_SPECIES を超えてよい)
141 std::span<const SpeciesAmount> amounts,
142 int32_t temperatureMilliKelvin);
143
144 // 元素ごとの数(µmol ぶんの原子の数。添字 = 元素)
145 [[nodiscard]] std::vector<uint64_t> CountElements(const BakedReactionTable& table, const reaction::RxCell& cell);
146 [[nodiscard]] std::vector<uint64_t> CountElements(const BakedReactionTable& table,
147 const reaction::RxWideCell& cell);
148
149 // セルの中身のハッシュ(エネルギーと成分。比べるため。成分が同じなら RxCell と RxWideCell で同じ値)
150 [[nodiscard]] uint64_t HashReactionCell(const reaction::RxCell& cell);
151 [[nodiscard]] uint64_t HashReactionCell(const reaction::RxWideCell& cell);
152
153 // CPU リファレンス: 1 セルの 1 刻み(待ちの丸め。T-0105・D-429)。changedTick = セルのブロックが最後に変わった刻み。
154 // 変わらなければ次に評価が要る刻みも返す
155 [[nodiscard]] reaction::RxWaitStep StepReactionCellWait(const BakedReactionTable& table,
156 const reaction::RxCell& cell, uint64_t worldSeed,
157 uint64_t tick, uint64_t changedTick, uint64_t cellId);
158
159 // 同じ、成分の数に上限が無いセルの版(T-0175。生成物を待たせない = RX_LIMIT_PRODUCTS は立たない)
161 const reaction::RxWideCell& cell, uint64_t worldSeed,
162 uint64_t tick, uint64_t changedTick, uint64_t cellId);
163
164 // CPU リファレンス: 1 セルだけのブロックを tickBegin から tickCount 刻み進める(shaders/sim/reaction_cells.hlsl と同じ関数。T-0130)
165 [[nodiscard]] reaction::RxLoneCell AdvanceLoneReactionCell(const BakedReactionTable& table,
166 const reaction::RxLoneCell& lone, uint64_t worldSeed,
167 uint64_t tickBegin, uint32_t tickCount, uint64_t cellId);
168
169} // namespace bicameral::sim
RxWaitStepOf< RxWideCell > RxWideWaitStep
uint64_t HashReactionCell(const RxCell &cell)
RxCell MakeReactionCell(const BakedReactionTable &table, std::span< const SpeciesAmount > amounts, int32_t temperatureMilliKelvin)
RxLoneCell AdvanceLoneReactionCell(const BakedReactionTable &table, const RxLoneCell &lone, uint64_t worldSeed, uint64_t tickBegin, uint32_t tickCount, uint64_t cellId)
std::expected< BakedReactionTable, std::string > BakeReactionTable(const ReactionTableDefinition &definition)
RxWaitStep StepReactionCellWait(const BakedReactionTable &table, const RxCell &cell, uint64_t worldSeed, uint64_t tick, uint64_t changedTick, uint64_t cellId)
std::vector< uint64_t > CountElements(const BakedReactionTable &table, const RxCell &cell)
RxWideCell MakeWideReactionCell(const BakedReactionTable &table, std::span< const SpeciesAmount > amounts, int32_t temperatureMilliKelvin)
std::vector< std::string > speciesNames
std::vector< std::string > ruleNames
std::vector< uint32_t > ruleIndex
std::vector< std::string > warnings
uint32_t SpeciesId(std::string_view name) const
std::vector< uint32_t > speciesElements
std::vector< reaction::RxSpecies > species
std::vector< reaction::RxRule > rules
std::vector< uint32_t > molarMasses
std::vector< std::string > elementNames
ReactionTableView View() const
std::vector< ElementDefinition > elements
std::vector< SpeciesDefinition > species
std::vector< RuleDefinition > rules
uint32_t RuleIndex(uint32_t position) const
std::span< const uint64_t > rates
uint64_t Rate(uint32_t rule, uint32_t kelvin) const
std::span< const uint32_t > ruleIndex
std::span< const reaction::RxSpecies > species
std::span< const reaction::RxRule > rules
reaction::RxSpecies Species(uint32_t id) const
reaction::RxRule Rule(uint32_t id) const
std::vector< RuleTerm > reactants
std::vector< RuleTerm > products
std::optional< int64_t > declaredReactionEnthalpy
std::vector< ElementCount > composition