Line data Source code
1 : #include "cfd/io/output_registry.h"
2 : #include "../io/csv_output_internal.h"
3 : #include "../io/vtk_output_internal.h"
4 : #include "cfd/api/simulation_api.h"
5 : #include "cfd/core/filesystem.h"
6 : #include "cfd/core/logging.h"
7 : #include "cfd/core/memory.h"
8 :
9 :
10 : #include <string.h>
11 :
12 : //=============================================================================
13 : // OUTPUT REGISTRY STRUCTURE
14 : //=============================================================================
15 :
16 : #define MAX_OUTPUT_CONFIGS 16
17 :
18 : struct OutputRegistry {
19 : output_config configs[MAX_OUTPUT_CONFIGS];
20 : int count;
21 : char run_dir[512]; // Cached run directory path
22 : int run_dir_created; // Flag to track if run directory was created
23 : };
24 :
25 : //=============================================================================
26 : // OUTPUT REGISTRY LIFECYCLE
27 : //=============================================================================
28 :
29 75 : output_registry* output_registry_create(void) {
30 75 : output_registry* reg = (output_registry*)cfd_calloc(1, sizeof(struct OutputRegistry));
31 75 : if (reg) {
32 75 : reg->count = 0;
33 75 : reg->run_dir[0] = '\0';
34 75 : reg->run_dir_created = 0;
35 : }
36 75 : return reg;
37 : }
38 :
39 76 : void output_registry_destroy(output_registry* reg) {
40 76 : if (reg) {
41 75 : cfd_free(reg);
42 : }
43 76 : }
44 :
45 : //=============================================================================
46 : // OUTPUT REGISTRY CONFIGURATION
47 : //=============================================================================
48 :
49 19 : void output_registry_add(output_registry* reg, output_field_type field_type, int interval,
50 : const char* prefix) {
51 19 : if (!reg) {
52 : return;
53 : }
54 :
55 : // Check if we have space
56 18 : if (reg->count >= MAX_OUTPUT_CONFIGS) {
57 0 : cfd_warning("Maximum number of output configurations reached");
58 0 : return;
59 : }
60 :
61 : // Add new configuration
62 18 : reg->configs[reg->count].field_type = field_type;
63 18 : reg->configs[reg->count].interval = interval;
64 18 : reg->configs[reg->count].prefix = prefix;
65 18 : reg->count++;
66 : }
67 :
68 9 : void output_registry_clear(output_registry* reg) {
69 9 : if (reg) {
70 8 : reg->count = 0;
71 : }
72 9 : }
73 :
74 7 : int output_registry_count(const output_registry* reg) {
75 7 : return reg ? reg->count : 0;
76 : }
77 :
78 51 : int output_registry_has_type(const output_registry* reg, output_field_type field_type) {
79 51 : if (!reg) {
80 : return 0;
81 : }
82 :
83 80 : for (int i = 0; i < reg->count; i++) {
84 44 : if (reg->configs[i].field_type == field_type) {
85 : return 1;
86 : }
87 : }
88 : return 0;
89 : }
90 :
91 : //=============================================================================
92 : // RUN DIRECTORY MANAGEMENT
93 : //=============================================================================
94 :
95 9 : const char* output_registry_get_run_dir(output_registry* reg, const char* base_dir,
96 : const char* run_prefix, size_t nx, size_t ny) {
97 9 : if (!reg) {
98 : return NULL;
99 : }
100 :
101 : // Only create once
102 9 : if (reg->run_dir_created) {
103 4 : return reg->run_dir;
104 : }
105 :
106 : // Create run directory with prefix using custom base directory (re-entrant)
107 5 : const char* prefix = run_prefix ? run_prefix : "sim";
108 5 : cfd_create_run_directory_ex_with_base(reg->run_dir, sizeof(reg->run_dir), base_dir, prefix, nx,
109 : ny);
110 :
111 5 : reg->run_dir_created = 1;
112 5 : return reg->run_dir;
113 : }
114 :
115 : //=============================================================================
116 : // OUTPUT DISPATCH
117 : //=============================================================================
118 :
119 : // Function pointer type for output dispatchers (with derived fields)
120 : typedef void (*output_dispatch_func)(const char* run_dir, const char* prefix, int step,
121 : double current_time, const flow_field* field,
122 : const derived_fields* derived, const grid* grid,
123 : const ns_solver_params_t* params, const ns_solver_stats_t* stats);
124 :
125 : // VTK output wrappers
126 1 : static void dispatch_vtk_velocity_magnitude(const char* run_dir, const char* prefix, int step,
127 : double current_time, const flow_field* field,
128 : const derived_fields* derived, const grid* grid,
129 : const ns_solver_params_t* params,
130 : const ns_solver_stats_t* stats) {
131 1 : (void)current_time;
132 1 : (void)field;
133 1 : (void)params;
134 1 : (void)stats;
135 : // Use pre-computed velocity magnitude from derived fields
136 1 : if (derived && derived->velocity_magnitude) {
137 1 : vtk_write_scalar_field(run_dir, prefix, step, "velocity_magnitude",
138 : derived->velocity_magnitude, grid);
139 : }
140 1 : }
141 :
142 1 : static void dispatch_vtk_velocity(const char* run_dir, const char* prefix, int step,
143 : double current_time, const flow_field* field,
144 : const derived_fields* derived, const grid* grid,
145 : const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
146 1 : (void)current_time;
147 1 : (void)derived;
148 1 : (void)params;
149 1 : (void)stats;
150 1 : vtk_dispatch_output(VTK_OUTPUT_VELOCITY, run_dir, prefix, step, field, grid);
151 1 : }
152 :
153 0 : static void dispatch_vtk_full_field(const char* run_dir, const char* prefix, int step,
154 : double current_time, const flow_field* field,
155 : const derived_fields* derived, const grid* grid,
156 : const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
157 0 : (void)current_time;
158 0 : (void)derived;
159 0 : (void)params;
160 0 : (void)stats;
161 0 : vtk_dispatch_output(VTK_OUTPUT_FULL_FIELD, run_dir, prefix, step, field, grid);
162 0 : }
163 :
164 : // CSV output wrappers (pass derived fields)
165 1 : static void dispatch_csv_timeseries(const char* run_dir, const char* prefix, int step,
166 : double current_time, const flow_field* field,
167 : const derived_fields* derived, const grid* grid,
168 : const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
169 1 : csv_dispatch_output(CSV_OUTPUT_TIMESERIES, run_dir, prefix, step, current_time, field, derived,
170 : grid, params, stats);
171 1 : }
172 :
173 0 : static void dispatch_csv_centerline(const char* run_dir, const char* prefix, int step,
174 : double current_time, const flow_field* field,
175 : const derived_fields* derived, const grid* grid,
176 : const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
177 0 : csv_dispatch_output(CSV_OUTPUT_CENTERLINE, run_dir, prefix, step, current_time, field, derived,
178 : grid, params, stats);
179 0 : }
180 :
181 1 : static void dispatch_csv_statistics(const char* run_dir, const char* prefix, int step,
182 : double current_time, const flow_field* field,
183 : const derived_fields* derived, const grid* grid,
184 : const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
185 1 : csv_dispatch_output(CSV_OUTPUT_STATISTICS, run_dir, prefix, step, current_time, field, derived,
186 : grid, params, stats);
187 1 : }
188 :
189 : // Output dispatch table - indexed by output_field_type
190 : // This provides O(1) lookup with no branch prediction issues
191 : static const output_dispatch_func output_dispatch_table[] = {
192 : dispatch_vtk_velocity_magnitude, // OUTPUT_VELOCITY_MAGNITUDE = 0
193 : dispatch_vtk_velocity, // OUTPUT_VELOCITY = 1
194 : dispatch_vtk_full_field, // OUTPUT_FULL_FIELD = 2
195 : dispatch_csv_timeseries, // OUTPUT_CSV_TIMESERIES = 3
196 : dispatch_csv_centerline, // OUTPUT_CSV_CENTERLINE = 4
197 : dispatch_csv_statistics // OUTPUT_CSV_STATISTICS = 5
198 : };
199 :
200 : #define OUTPUT_DISPATCH_TABLE_SIZE \
201 : (sizeof(output_dispatch_table) / sizeof(output_dispatch_table[0]))
202 :
203 : //=============================================================================
204 : // OUTPUT WRITING
205 : //=============================================================================
206 :
207 9 : void output_registry_write_outputs(output_registry* reg, const char* run_dir, int step,
208 : double current_time, const flow_field* field,
209 : const derived_fields* derived, const grid* grid,
210 : const ns_solver_params_t* params, const ns_solver_stats_t* stats) {
211 9 : if (!reg || !run_dir) {
212 : return;
213 : }
214 :
215 : // Process each registered output
216 17 : for (int i = 0; i < reg->count; i++) {
217 8 : output_config* config = ®->configs[i];
218 :
219 : // Skip if not time to output
220 8 : if (config->interval <= 0 || step % config->interval != 0) {
221 4 : continue;
222 : }
223 :
224 : // Bounds check and dispatch via function table
225 : // This is cacheable and avoids branch prediction issues
226 4 : if (config->field_type < OUTPUT_DISPATCH_TABLE_SIZE) {
227 4 : output_dispatch_table[config->field_type](run_dir, config->prefix, step, current_time,
228 : field, derived, grid, params, stats);
229 : } else {
230 0 : cfd_warning("Unknown output type, skipping");
231 : }
232 : }
233 : }
|