|
8 | 8 | #include <hydroc/waves/wave_base.h> |
9 | 9 | #include <hydroc/waves/regular_wave.h> |
10 | 10 | #include <hydroc/waves/irregular_wave.h> |
| 11 | +#include <hydroc/radiation/radiation_types.h> |
11 | 12 | #include <hydroc/version.h> |
12 | 13 | #include <hydroc/logging.h> |
13 | 14 |
|
@@ -985,6 +986,114 @@ void SimulationExporter::SetRunMetadata(const std::string& started_at_utc, |
985 | 986 | impl_->options.run_time_final = time_final_s; |
986 | 987 | } |
987 | 988 |
|
| 989 | +void SimulationExporter::WriteRadiationDiagnostics( |
| 990 | + const std::vector<hydrochrono::hydro::KernelFitDiagnostics>& diagnostics) { |
| 991 | + |
| 992 | + if (diagnostics.empty()) { |
| 993 | + return; |
| 994 | + } |
| 995 | + |
| 996 | + // Create diagnostics group hierarchy |
| 997 | + auto g_diag = impl_->writer.RequireGroup("/diagnostics"); |
| 998 | + auto g_rad = impl_->writer.RequireGroup("/diagnostics/radiation"); |
| 999 | + |
| 1000 | + // Write metadata |
| 1001 | + g_rad.WriteAttribute("description", std::string("State-space radiation kernel fit diagnostics")); |
| 1002 | + g_rad.WriteAttribute("num_bodies", static_cast<double>(diagnostics.size())); |
| 1003 | + |
| 1004 | + // DOF names for reference |
| 1005 | + static const std::vector<std::string> dof_names = { |
| 1006 | + "surge", "sway", "heave", "roll", "pitch", "yaw" |
| 1007 | + }; |
| 1008 | + |
| 1009 | + for (const auto& diag : diagnostics) { |
| 1010 | + // Create group for this body |
| 1011 | + auto g_body = g_rad.CreateGroup(diag.body_name); |
| 1012 | + |
| 1013 | + // Write body metadata |
| 1014 | + g_body.WriteAttribute("body_index", static_cast<double>(diag.body_index)); |
| 1015 | + g_body.WriteAttribute("num_dofs", static_cast<double>(diag.num_dofs)); |
| 1016 | + g_body.WriteAttribute("min_r2", diag.min_r2); |
| 1017 | + g_body.WriteAttribute("max_r2", diag.max_r2); |
| 1018 | + g_body.WriteAttribute("mean_r2", diag.mean_r2); |
| 1019 | + g_body.WriteAttribute("total_modes", static_cast<double>(diag.total_modes)); |
| 1020 | + |
| 1021 | + // Write time vector |
| 1022 | + const hsize_t n_times = static_cast<hsize_t>(diag.time.size()); |
| 1023 | + if (n_times > 0) { |
| 1024 | + std::vector<double> time_vec(diag.time.data(), diag.time.data() + n_times); |
| 1025 | + std::array<hsize_t, 1> dims_1d = {n_times}; |
| 1026 | + g_body.WriteDataset("time", time_vec, dims_1d); |
| 1027 | + g_body.WriteAttribute("time_units", std::string("s")); |
| 1028 | + } |
| 1029 | + |
| 1030 | + // Write K_actual and K_fit matrices |
| 1031 | + const hsize_t n_dofs = static_cast<hsize_t>(diag.K_actual.cols()); |
| 1032 | + if (n_times > 0 && n_dofs > 0) { |
| 1033 | + // Convert Eigen matrices to row-major vectors for HDF5 |
| 1034 | + // K_actual: [n_times, n_dofs] |
| 1035 | + std::vector<double> K_actual_vec(n_times * n_dofs); |
| 1036 | + std::vector<double> K_fit_vec(n_times * n_dofs); |
| 1037 | + |
| 1038 | + for (hsize_t t = 0; t < n_times; ++t) { |
| 1039 | + for (hsize_t d = 0; d < n_dofs; ++d) { |
| 1040 | + K_actual_vec[t * n_dofs + d] = diag.K_actual(t, d); |
| 1041 | + K_fit_vec[t * n_dofs + d] = diag.K_fit(t, d); |
| 1042 | + } |
| 1043 | + } |
| 1044 | + |
| 1045 | + std::array<hsize_t, 2> dims_2d = {n_times, n_dofs}; |
| 1046 | + g_body.WriteDataset("K_actual", K_actual_vec, dims_2d); |
| 1047 | + g_body.WriteDataset("K_fit", K_fit_vec, dims_2d); |
| 1048 | + g_body.WriteAttribute("K_units", std::string("kg/s^2")); |
| 1049 | + g_body.WriteAttribute("K_description", |
| 1050 | + std::string("RIRF kernel values: K_actual=original, K_fit=state-space approximation")); |
| 1051 | + } |
| 1052 | + |
| 1053 | + // Write per-DOF diagnostics |
| 1054 | + if (!diag.dof_pairs.empty()) { |
| 1055 | + const hsize_t n_pairs = static_cast<hsize_t>(diag.dof_pairs.size()); |
| 1056 | + |
| 1057 | + std::vector<double> r_squared(n_pairs); |
| 1058 | + std::vector<double> state_order(n_pairs); |
| 1059 | + std::vector<double> num_exp_modes(n_pairs); |
| 1060 | + std::vector<double> num_osc_modes(n_pairs); |
| 1061 | + std::vector<double> dof_i(n_pairs); |
| 1062 | + std::vector<double> dof_j(n_pairs); |
| 1063 | + |
| 1064 | + for (hsize_t k = 0; k < n_pairs; ++k) { |
| 1065 | + const auto& dp = diag.dof_pairs[k]; |
| 1066 | + r_squared[k] = dp.r_squared; |
| 1067 | + state_order[k] = static_cast<double>(dp.state_order); |
| 1068 | + num_exp_modes[k] = static_cast<double>(dp.num_exp_modes); |
| 1069 | + num_osc_modes[k] = static_cast<double>(dp.num_osc_modes); |
| 1070 | + dof_i[k] = static_cast<double>(dp.dof_i); |
| 1071 | + dof_j[k] = static_cast<double>(dp.dof_j); |
| 1072 | + } |
| 1073 | + |
| 1074 | + std::array<hsize_t, 1> dims_pairs = {n_pairs}; |
| 1075 | + g_body.WriteDataset("r_squared", r_squared, dims_pairs); |
| 1076 | + g_body.WriteDataset("state_order", state_order, dims_pairs); |
| 1077 | + g_body.WriteDataset("num_exp_modes", num_exp_modes, dims_pairs); |
| 1078 | + g_body.WriteDataset("num_osc_modes", num_osc_modes, dims_pairs); |
| 1079 | + g_body.WriteDataset("dof_i", dof_i, dims_pairs); |
| 1080 | + g_body.WriteDataset("dof_j", dof_j, dims_pairs); |
| 1081 | + |
| 1082 | + // Write DOF names for reference |
| 1083 | + std::vector<std::string> dof_labels(n_pairs); |
| 1084 | + for (hsize_t k = 0; k < n_pairs; ++k) { |
| 1085 | + int local_dof_i = static_cast<int>(dof_i[k]) % 6; |
| 1086 | + int local_dof_j = static_cast<int>(dof_j[k]) % 6; |
| 1087 | + dof_labels[k] = dof_names[local_dof_i] + "_from_" + dof_names[local_dof_j]; |
| 1088 | + } |
| 1089 | + g_body.WriteStringArray("dof_labels", dof_labels); |
| 1090 | + } |
| 1091 | + } |
| 1092 | + |
| 1093 | + hydroc::cli::LogInfo(std::string("HDF5: wrote radiation diagnostics for ") + |
| 1094 | + std::to_string(diagnostics.size()) + " body(ies)"); |
| 1095 | +} |
| 1096 | + |
988 | 1097 | } // namespace hydroc |
989 | 1098 |
|
990 | 1099 |
|
0 commit comments