18 constexpr int MAX_KELVIN =
static_cast<int>(lab::LAB_MAX_TEMPERATURE_MILLIKELVIN / 1000);
28 return reaction::RxComputeThermal(table.
View(), cell).temperature;
33 const float t = std::clamp((
static_cast<float>(milliKelvin) / 1000.0f - 300.0f) / (
HOT_KELVIN - 300.0f),
35 return {0.15f + (0.85f * std::min(1.0f, t * 2.0f)), std::max(0.0f, (t * 2.0f) - 1.0f) * 0.9f, 0.1f, 1.0f};
39 return {.x =
static_cast<uint32_t
>(cell[0]),
40 .y =
static_cast<uint32_t
>(cell[1]),
41 .z =
static_cast<uint32_t
>(cell[2])};
47 if (device !=
nullptr)
48 static_cast<void>(device->QueryInterface(IID_PPV_ARGS(&
m_device)));
96 ImGui::SetNextWindowPos({12.0f, 420.0f}, ImGuiCond_FirstUseEver);
97 ImGui::Begin(
"実験室",
nullptr, ImGuiWindowFlags_AlwaysAutoResize);
99 ImGui::TextUnformatted(
100 "小さな箱(8³ セル・0.5 m 角・300 K の空気)に物を置いて反応を試し、\n"
101 "GPU と CPU リファレンスを刻みごとに比べる(試験の表: 燃焼・熱分解)");
102 if (ImGui::Button(
"箱を作る"))
106 ImGui::TextColored({1.0f, 0.4f, 0.4f, 1.0f},
"%s",
m_error.c_str());
124 ImGui::Text(
"次の刻み %llu ・ 置いて待っているコマンド %zu",
125 static_cast<unsigned long long>(
m_session->NextTick()),
m_session->PendingCommands());
126 if (
const auto& mismatch =
m_session->Mismatch(); mismatch) {
127 ImGui::TextColored({1.0f, 0.3f, 0.3f, 1.0f},
"GPU と CPU が食い違った: %s", mismatch->what.c_str());
128 if (mismatch->cell != lab::LAB_NO_CELL) {
129 ImGui::Text(
" エネルギー CPU %lld mJ / GPU %lld mJ、物質の数 CPU %u / GPU %u",
130 static_cast<long long>(mismatch->cpu.energy),
static_cast<long long>(mismatch->gpu.energy),
131 mismatch->cpu.speciesCount, mismatch->gpu.speciesCount);
134 ImGui::TextColored({0.4f, 1.0f, 0.4f, 1.0f},
"刻み 0〜%llu で GPU と CPU がビット一致",
135 static_cast<unsigned long long>(
m_session->NextTick() - 1));
138 if (
const auto divergence =
m_session->ReplayDivergence(); divergence)
139 ImGui::TextColored({1.0f, 0.6f, 0.2f, 1.0f},
"再生が記録と刻み %llu で違う",
140 static_cast<unsigned long long>(*divergence));
143 ImGui::TextColored({1.0f, 0.4f, 0.4f, 1.0f},
"%s",
m_error.c_str());
147 ImGui::SeparatorText(
"置く(次の刻みのコマンド)");
151 if (ImGui::BeginCombo(
"材料", preview)) {
167 if (ImGui::Button(
"温度だけ"))
168 static_cast<void>(
m_session->SetTemperature(PositionOf(
m_cell), milliKelvin));
171 ImGui::SeparatorText(
"刻む");
172 ImGui::BeginDisabled(
m_session->Mismatch().has_value());
173 for (
const uint32_t ticks : {1u, 10u, 60u}) {
174 if (ImGui::Button(std::format(
"{} 刻み", ticks).c_str()))
181 ImGui::EndDisabled();
183 if (ImGui::Button(
"最初に戻す")) {
192 ImGui::SeparatorText(std::format(
"断面 z = {}(色 = CPU の温度)",
m_cell[2]).c_str());
196 const uint32_t index = multires::MrCellIndex(
static_cast<uint32_t
>(x),
static_cast<uint32_t
>(y),
197 static_cast<uint32_t
>(
m_cell[2]));
199 const std::string
id = std::format(
"##lab{}_{}", x, y);
202 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
204 if (ImGui::ColorButton(
id.c_str(), TemperatureColor(temperature), ImGuiColorEditFlags_NoTooltip,
205 {SLICE_CELL_PIXELS, SLICE_CELL_PIXELS})) {
211 ImGui::PopStyleVar();
213 if (ImGui::IsItemHovered())
214 ImGui::SetTooltip(
"(%d, %d, %d) %.1f K", x, y,
m_cell[2],
215 static_cast<double>(temperature) / 1000.0);
218 ImGui::SameLine(0.0f, 2.0f);
230 ImGui::SeparatorText(std::format(
"セル ({}, {}, {})",
m_cell[0],
m_cell[1],
m_cell[2]).c_str());
231 ImGui::Text(
"温度 CPU %.1f K / GPU %.1f K エネルギー CPU %lld mJ / GPU %lld mJ",
232 static_cast<double>(TemperatureOf(table, cpu)) / 1000.0,
233 static_cast<double>(TemperatureOf(table,
gpu)) / 1000.0,
static_cast<long long>(cpu.energy),
234 static_cast<long long>(
gpu.energy));
235 if (!ImGui::BeginTable(
"lab_cell", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit))
238 ImGui::TableSetupColumn(
"物質");
239 ImGui::TableSetupColumn(
"CPU (µmol)");
240 ImGui::TableSetupColumn(
"GPU (µmol)");
241 ImGui::TableHeadersRow();
242 for (uint32_t species = 1; species < table.
speciesNames.size(); ++species) {
243 const auto amountOf = [&](
const reaction::RxCell& cell) {
244 for (uint32_t i = 0; i < cell.speciesCount; ++i) {
245 if (cell.species[i] == species)
246 return cell.amounts[i];
252 const uint64_t a = amountOf(cpu);
253 const uint64_t b = amountOf(
gpu);
254 if (a == 0 && b == 0)
257 ImGui::TableNextRow();
258 ImGui::TableNextColumn();
259 ImGui::TextUnformatted(table.
speciesNames[species].c_str());
260 ImGui::TableNextColumn();
261 ImGui::Text(
"%llu",
static_cast<unsigned long long>(a));
262 ImGui::TableNextColumn();
264 ImGui::Text(
"%llu",
static_cast<unsigned long long>(b));
266 ImGui::TextColored({1.0f, 0.3f, 0.3f, 1.0f},
"%llu",
static_cast<unsigned long long>(b));
274 ImGui::SeparatorText(
"記録");
275 std::array<char, 260> path{};
277 if (ImGui::InputText(
"ファイル", path.data(), path.size()))
280 if (ImGui::Button(
"記録を保存")) {
283 file.write(
reinterpret_cast<const char*
>(bytes.data()),
static_cast<std::streamsize
>(bytes.size()));
290 if (ImGui::Button(
"読んで再生")) {
292 const std::vector<char> chars{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
298 m_message = std::format(
"{} 刻みを再生した", recording->tickCount);
303 ImGui::TextUnformatted(
m_message.c_str());
319 for (uint32_t i = 0; i < 8; ++i)
320 static_cast<void>(
m_session->Place({.x = 3 + (i & 1u), .y = 3 + ((i >> 1) & 1u), .z = 3 + (i >> 2)},
321 wood->contents, 300000));
323 auto result =
m_session->Step(AUTO_IGNITE_TICK);
324 static_cast<void>(
m_session->SetTemperature({.x = 3, .y = 3, .z = 3}, AUTO_IGNITE_MILLIKELVIN));
326 result =
m_session->Step(AUTO_TICKS - AUTO_IGNITE_TICK);
330 if (result && parsed)
336 !result ? result.error() : (
m_session->Mismatch() ?
m_session->Mismatch()->what :
"再生が記録と違う"));
ComPtr< ID3D12Device5 > m_device
std::string m_recordingPath
std::array< int, 3 > m_cell
std::optional< sim::LabSession > m_session
void Report(const std::expected< void, std::string > &result)
std::vector< sim::LabMaterial > m_materials
LabPanel(ID3D12Device *device)
static std::expected< LabSession, std::string > Create(ID3D12Device5 *device, D3D12_COMMAND_LIST_TYPE queueType, const BakedReactionTable &table)
constexpr float SLICE_CELL_PIXELS
constexpr uint32_t AUTO_TICKS
int32_t TemperatureOf(const sim::BakedReactionTable &table, const reaction::RxCell &cell)
constexpr float HOT_KELVIN
ImVec4 TemperatureColor(int32_t milliKelvin)
constexpr uint32_t AUTO_IGNITE_TICK
sim::LabCellPosition PositionOf(const std::array< int, 3 > &cell)
constexpr uint32_t AUTO_IGNITE_MILLIKELVIN
std::expected< LabRecording, std::string > ParseLabRecording(std::span< const std::byte > bytes)
RxCell LabBoxCell(const MultiresNest &nest, uint32_t index)
std::vector< LabMaterial > MakeLabMaterials(const BakedReactionTable &table)
constexpr uint32_t LAB_BOX_EDGE
std::expected< BakedReactionTable, std::string > BakeReactionTable(const ReactionTableDefinition &definition)
ReactionTableDefinition MakeCombustionTestTable()
std::vector< std::byte > SerializeLabRecording(const LabRecording &recording)
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
std::vector< std::string > speciesNames
ReactionTableView View() const
std::vector< uint64_t > hashes