feat: gate heavy solver JSON, field traceability API, GUI rod/export depth
- C: emit profiles/diagnostics/fourier only when enable flags are set; null otherwise - API: fieldTraceability on case parse/default and solve; fix GET /solve/default options - Tests: golden fingerprint, quality gates, C diagnostics invariants; cardQa mean empty guard - Makefile: test-solver-sanitize ASan/UBSan target; README and COMPUTE_PLAN updates - GUI: taper weight/MTS/guides/sinker round-trip, rod catalog, solver output toggles, results (profiles/diagnostics/Fourier/traceability), engineering checks and tabs - Restore canonical WellName in base-case for regression; trace TaperGuidesCountArray Made-with: Cursor
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void print_json_output(const SolverOutputs *outputs) {
|
||||
static void print_json_output(const SolverInputs *inputs, const SolverOutputs *outputs) {
|
||||
printf("{\n");
|
||||
printf(" \"pointCount\": %d,\n", outputs->point_count);
|
||||
printf(" \"maxPolishedLoad\": %.6f,\n", outputs->max_polished_load);
|
||||
@@ -55,57 +55,65 @@ static void print_json_output(const SolverOutputs *outputs) {
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
|
||||
printf(" \"profiles\": {\n");
|
||||
printf(" \"nodeCount\": %d,\n", outputs->profile_node_count);
|
||||
printf(" \"trajectory3D\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"md\": %.6f, \"curvature\": %.9f, \"inclination\": %.9f, \"azimuth\": %.9f}",
|
||||
outputs->profile_md_m[i],
|
||||
outputs->profile_curvature_1pm[i],
|
||||
outputs->profile_inclination_rad[i],
|
||||
outputs->profile_azimuth_rad[i]);
|
||||
if (inputs->enable_profiles) {
|
||||
printf(" \"profiles\": {\n");
|
||||
printf(" \"nodeCount\": %d,\n", outputs->profile_node_count);
|
||||
printf(" \"trajectory3D\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"md\": %.6f, \"curvature\": %.9f, \"inclination\": %.9f, \"azimuth\": %.9f}",
|
||||
outputs->profile_md_m[i],
|
||||
outputs->profile_curvature_1pm[i],
|
||||
outputs->profile_inclination_rad[i],
|
||||
outputs->profile_azimuth_rad[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"sideLoadProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_side_load_n[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"frictionProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_friction_n[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
} else {
|
||||
printf(" \"profiles\": null,\n");
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"sideLoadProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_side_load_n[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"frictionProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_friction_n[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
|
||||
printf(" \"diagnostics\": {\n");
|
||||
printf(" \"valveStates\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"travelingOpen\": %s, \"standingOpen\": %s}",
|
||||
outputs->valve_traveling_open[i] ? "true" : "false",
|
||||
outputs->valve_standing_open[i] ? "true" : "false");
|
||||
if (inputs->enable_diagnostics_detail) {
|
||||
printf(" \"diagnostics\": {\n");
|
||||
printf(" \"valveStates\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"travelingOpen\": %s, \"standingOpen\": %s}",
|
||||
outputs->valve_traveling_open[i] ? "true" : "false",
|
||||
outputs->valve_standing_open[i] ? "true" : "false");
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"chamberPressurePa\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->chamber_pressure_pa[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"gasFraction\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->gas_fraction[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
} else {
|
||||
printf(" \"diagnostics\": null,\n");
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"chamberPressurePa\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->chamber_pressure_pa[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"gasFraction\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->gas_fraction[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
|
||||
printf(" \"fourierBaseline\": ");
|
||||
if (outputs->fourier_harmonics_used > 0) {
|
||||
if (inputs->enable_fourier_baseline && outputs->fourier_harmonics_used > 0) {
|
||||
printf("{\"harmonics\": %d, \"residualRmsPolished\": %.6f, \"residualRmsDownhole\": %.6f, \"card\": [",
|
||||
outputs->fourier_harmonics_used,
|
||||
outputs->fourier_residual_rms_polished,
|
||||
@@ -172,6 +180,6 @@ int main(int argc, char **argv) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
print_json_output(&outputs);
|
||||
print_json_output(&inputs, &outputs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void print_json_output(const SolverOutputs *outputs) {
|
||||
static void print_json_output(const SolverInputs *inputs, const SolverOutputs *outputs) {
|
||||
printf("{\n");
|
||||
printf(" \"pointCount\": %d,\n", outputs->point_count);
|
||||
printf(" \"maxPolishedLoad\": %.6f,\n", outputs->max_polished_load);
|
||||
@@ -55,57 +55,65 @@ static void print_json_output(const SolverOutputs *outputs) {
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
|
||||
printf(" \"profiles\": {\n");
|
||||
printf(" \"nodeCount\": %d,\n", outputs->profile_node_count);
|
||||
printf(" \"trajectory3D\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"md\": %.6f, \"curvature\": %.9f, \"inclination\": %.9f, \"azimuth\": %.9f}",
|
||||
outputs->profile_md_m[i],
|
||||
outputs->profile_curvature_1pm[i],
|
||||
outputs->profile_inclination_rad[i],
|
||||
outputs->profile_azimuth_rad[i]);
|
||||
if (inputs->enable_profiles) {
|
||||
printf(" \"profiles\": {\n");
|
||||
printf(" \"nodeCount\": %d,\n", outputs->profile_node_count);
|
||||
printf(" \"trajectory3D\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"md\": %.6f, \"curvature\": %.9f, \"inclination\": %.9f, \"azimuth\": %.9f}",
|
||||
outputs->profile_md_m[i],
|
||||
outputs->profile_curvature_1pm[i],
|
||||
outputs->profile_inclination_rad[i],
|
||||
outputs->profile_azimuth_rad[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"sideLoadProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_side_load_n[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"frictionProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_friction_n[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
} else {
|
||||
printf(" \"profiles\": null,\n");
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"sideLoadProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_side_load_n[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"frictionProfile\": [");
|
||||
for (int i = 0; i < outputs->profile_node_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->profile_friction_n[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
|
||||
printf(" \"diagnostics\": {\n");
|
||||
printf(" \"valveStates\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"travelingOpen\": %s, \"standingOpen\": %s}",
|
||||
outputs->valve_traveling_open[i] ? "true" : "false",
|
||||
outputs->valve_standing_open[i] ? "true" : "false");
|
||||
if (inputs->enable_diagnostics_detail) {
|
||||
printf(" \"diagnostics\": {\n");
|
||||
printf(" \"valveStates\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("{\"travelingOpen\": %s, \"standingOpen\": %s}",
|
||||
outputs->valve_traveling_open[i] ? "true" : "false",
|
||||
outputs->valve_standing_open[i] ? "true" : "false");
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"chamberPressurePa\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->chamber_pressure_pa[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"gasFraction\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->gas_fraction[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
} else {
|
||||
printf(" \"diagnostics\": null,\n");
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"chamberPressurePa\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->chamber_pressure_pa[i]);
|
||||
}
|
||||
printf("],\n");
|
||||
printf(" \"gasFraction\": [");
|
||||
for (int i = 0; i < outputs->point_count; i++) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("%.6f", outputs->gas_fraction[i]);
|
||||
}
|
||||
printf("]\n");
|
||||
printf(" },\n");
|
||||
|
||||
printf(" \"fourierBaseline\": ");
|
||||
if (outputs->fourier_harmonics_used > 0) {
|
||||
if (inputs->enable_fourier_baseline && outputs->fourier_harmonics_used > 0) {
|
||||
printf("{\"harmonics\": %d, \"residualRmsPolished\": %.6f, \"residualRmsDownhole\": %.6f, \"card\": [",
|
||||
outputs->fourier_harmonics_used,
|
||||
outputs->fourier_residual_rms_polished,
|
||||
@@ -165,6 +173,6 @@ int main(int argc, char **argv) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
print_json_output(&outputs);
|
||||
print_json_output(&inputs, &outputs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -173,6 +173,26 @@ static int test_zero_input_bounded(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Gas fraction and chamber pressure invariants after valve/gas stepping */
|
||||
static int test_diagnostics_invariants(void) {
|
||||
SolverInputs inputs;
|
||||
fill_base_inputs(&inputs);
|
||||
SolverOutputs outputs;
|
||||
if (solver_run_fdm(&inputs, &outputs) != 0) {
|
||||
return 1;
|
||||
}
|
||||
for (int i = 0; i < outputs.point_count; i++) {
|
||||
const double g = outputs.gas_fraction[i];
|
||||
if (g < -1e-6 || g > 1.0 + 1e-6) {
|
||||
return 2;
|
||||
}
|
||||
if (!isfinite(outputs.chamber_pressure_pa[i])) {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FDM vs FEA peak polished load tolerance (regression gate) */
|
||||
static int test_fdm_fea_peak_tolerance(void) {
|
||||
SolverInputs inputs;
|
||||
@@ -247,6 +267,12 @@ int main(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = test_diagnostics_invariants();
|
||||
if (rc != 0) {
|
||||
printf("test_diagnostics_invariants failed: %d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("solver-c tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user