Files
SystemSimulationApp/native/runtime/main.c
T
ljz 7611f13208 修复循环信号与事件采样并接入 LSTP 接触定位,补充八路验证及复用实验
相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。

- 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。
- 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。
- 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。
- 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。
- 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。
- 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。

验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
2026-09-17 23:50:13 +08:00

180 lines
9.2 KiB
C

#include "runtime.h"
#include "json_numbers.h"
#include <sundials/sundials_config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
static void json_string(FILE *f, const char *value) {
fputc('"',f);
for (const unsigned char *p=(const unsigned char *)value; *p; p++) {
if (*p=='"' || *p=='\\') { fputc('\\',f); fputc(*p,f); }
else if (*p<32) fprintf(f,"\\u%04x",*p);
else fputc(*p,f);
}
fputc('"',f);
}
static void vector(FILE *f, const double *value, int count) {
fputc('[',f);
for (int i=0;i<count;i++) fprintf(f,"%s%.17g",i?",":"",value[i]);
fputc(']',f);
}
static int probe(void) {
double t, y[NSTATES], dy[NSTATES], w[NOUTPUTS];
while (scanf("%lf",&t)==1) {
for (int i=0;i<NSTATES;i++) if (scanf("%lf",&y[i])!=1) return 64;
int ok=model_eval(t,y,dy,w);
printf("{\"success\":%s",ok?"true":"false");
if (ok) { printf(",\"rhs\":"); vector(stdout,dy,NSTATES); printf(",\"outputs\":"); vector(stdout,w,NOUTPUTS); }
printf("}\n");
}
return 0;
}
static int write_result_index(const char *path, int64_t series_start, int64_t series_end,
int64_t result_bytes, size_t sample_count) {
if(series_start<0 || series_end<series_start || result_bytes<series_end) return 0;
FILE *index=fopen(path,"wb"); if(!index) return 0;
int ok=fprintf(index,"{\"version\":1,\"seriesStart\":%lld,\"seriesEnd\":%lld,"
"\"resultBytes\":%lld,\"sampleCount\":%zu}\n",
(long long)series_start,(long long)series_end,(long long)result_bytes,sample_count)>=0;
if(ferror(index)) ok=0;
if(fclose(index)) ok=0;
return ok;
}
static int write_result(NativeRun *r, const char *path, const char *index_path) {
if(index_path && !strcmp(path,index_path)) return 0;
double dy[NSTATES], final[NOUTPUTS];
int final_ok=model_eval(r->final_time,r->final_state,dy,final);
if (!native_samples_outputs(r)) return 0;
FILE *f=fopen(path,"wb"); if (!f) return 0;
fprintf(f,"{\"success\":%s,\"status\":",!r->status?"true":"false");
json_string(f,r->status==0?"completed":r->status==1?"cancelled":"failed");
fprintf(f,",\"message\":"); json_string(f,r->message);
fprintf(f,",\"contactEvents\":{\"checks\":%lu,\"denseCalls\":%lu,\"rootIterations\":%lu,\"transitions\":%lu,\"forceClipTransitions\":%lu}",
r->contact_checks,r->contact_dense,r->contact_roots,r->contact_transitions,r->contact_clipping);
fprintf(f,",\"propertyWarnings\":"); native_property_warnings_json(r,f);
fputs(",\"outputSemantics\":{\"version\":2,"
"\"regularSamples\":\"actual-time-no-snap\","
"\"internalTimeEvents\":\"left-adjacent-and-at-event\","
"\"stateEvents\":\"left-adjacent-when-available-and-post-reset\","
"\"sameTime\":\"last-accepted-state\","
"\"replay\":\"saved-time-and-state-including-discrete-modes\"}",f);
fprintf(f,",\"jacobianReuse\":{\"gasEvaluations\":%lu,\"gasReuses\":%lu",
r->jacobian_gas_evaluations,r->jacobian_gas_reuses);
const char *reuse_names[]={"ph","density","pipe"};
for(int i=0;i<NATIVE_JACOBIAN_SCALAR_KINDS;i++)
fprintf(f,",\"%sEvaluations\":%lu,\"%sReuses\":%lu",reuse_names[i],r->jacobian_scalar_evaluations[i],reuse_names[i],r->jacobian_scalar_reuses[i]);
fputc('}',f);
fprintf(f,",\"solverControl\":{\"reason\":"); json_string(f,r->failure_reason?r->failure_reason:"");
fprintf(f,",\"operation\":"); json_string(f,r->failure_operation?r->failure_operation:"");
fprintf(f,",\"returnCode\":%d,\"sameTimeReturns\":%lu,\"maxSameTimeStreak\":%lu",
r->cvode_flag,r->same_time_returns,r->max_same_time_streak);
const char *names[]={"returnTime","internalTime","lastStep","nextStep"};
double diagnostic[]={r->cvode_return_time,r->cvode_internal_time,r->cvode_last_step,r->cvode_next_step};
for(int i=0;i<4;i++) {
fprintf(f,",\"%s\":",names[i]);
if(isfinite(diagnostic[i])) fprintf(f,"%.17g",diagnostic[i]); else fputs("null",f);
}
fputc('}',f);
fprintf(f,",\"jacobianMode\":\"%s\",\"jacobianRhsCalls\":%lu,"
"\"jacobianColoredEvals\":%lu,\"jacobianFallbacks\":%lu,"
"\"jacobianChecks\":%lu,\"jacobianMismatches\":%lu,"
"\"cvodeRhsCalls\":%lu,\"cvodeLinearRhsCalls\":%lu",
!r->options.bdf?"not-used":r->jacobian_colored?"colored-difference":"dense-difference",
r->jacobian_rhs,r->jacobian_colored_evals,r->jacobian_fallbacks,
r->jacobian_checks,r->jacobian_mismatches,r->cvode_rhs,r->linear_rhs);
fprintf(f,",\"backend\":\"native-c\",\"method\":\"%s\",\"solver\":\"%s\",\"sundialsVersion\":\"%s\","
"\"simulatedUntil\":%.17g,\"solveSeconds\":%.17g,\"solveCpuSeconds\":%.17g,"
"\"nfev\":%lu,\"acceptedSteps\":%lu,\"rejectedSteps\":%lu,\"stateTransitions\":%lu,"
"\"solverStarts\":%lu,\"njev\":%lu,\"nlu\":%lu,\"maxAcceptedStep\":%.17g,\"series\":{",
r->options.bdf?"BDF":"RK45",r->options.bdf?"CVODE":"Dormand-Prince 5(4)",SUNDIALS_VERSION,
r->final_time,r->solve_seconds,r->solve_cpu_seconds,r->nfev,r->accepted,r->rejected,
r->events,r->starts,r->njev,r->nlu,r->max_accepted_step);
/* Binary-mode positions delimit the complete series object, including
both braces. The optional index avoids scanning or parsing its values. */
int64_t series_start=-1,series_end=-1,result_bytes=-1;
if(index_path) {
series_start=native_file_tell(f);
if(series_start>0) series_start--; else series_start=-1;
}
int output_ok=1;
if (r->count) {
fprintf(f,"\"time\":");
output_ok=native_samples_json(r,f,0);
for (int j=0;j<NOUTPUTS && output_ok;j++) {
fputc(',',f); json_string(f,model_output_keys[j]); fputc(':',f);
output_ok=native_samples_json(r,f,(size_t)j+1);
}
}
fputc('}',f);
if(index_path) series_end=native_file_tell(f);
fprintf(f,",\"final\":{");
if (final_ok) for (int j=0;j<NOUTPUTS && output_ok;j++) {
if (j) fputc(',',f);
json_string(f,model_output_keys[j]); fputc(':',f);
output_ok=native_json_write_number(f,final[j]);
}
fprintf(f,"},\"finalState\":");
if (output_ok) output_ok=native_json_write_array(f,r->final_state,NSTATES,1);
fprintf(f,"}\n"); int ok=output_ok && !ferror(f);
if(index_path) {
result_bytes=native_file_tell(f);
if(series_start<0 || series_end<0 || result_bytes<0) ok=0;
}
if (fclose(f)) ok=0;
/* Publish the index only after the complete result was successfully
flushed and closed. An index I/O failure is a failed result write. */
if(ok && index_path) ok=write_result_index(index_path,series_start,series_end,result_bytes,r->count);
return ok;
}
int main(int argc, char **argv) {
NativeRun r={0};
r.options=(NativeOptions){0,10,.02,.001,1e-6,300,0,1,NULL};
const char *output="result.json", *index_path=NULL;
for (int i=1;i<argc;i++) {
const char *arg=argv[i];
if (!strcmp(arg,"--probe")) return probe();
if (!strcmp(arg,"--init")) {
double y[NSTATES]; if (!model_init(y)) return 2;
vector(stdout,y,NSTATES); fputc('\n',stdout); return 0;
}
if (!strcmp(arg,"--storage-control")) { r.storage_control=1; continue; }
if (!strcmp(arg,"--solve-only")) { r.options.record_samples=0; continue; }
if (!strcmp(arg,"--verify-jacobian")) { r.jacobian_verify=1; continue; }
if (i+1==argc) return 64;
const char *value=argv[++i];
if (!strcmp(arg,"--output")) output=value;
else if (!strcmp(arg,"--sample-file")) r.sample_path=value;
else if (!strcmp(arg,"--output-block-file")) r.output_blocks_path=value;
else if (!strcmp(arg,"--result-index")) index_path=value;
else if (!strcmp(arg,"--cancel-file")) r.options.cancel_path=value;
else if (!strcmp(arg,"--method")) {
if (strcmp(value,"RK45") && strcmp(value,"BDF")) return 64;
r.options.bdf=!strcmp(value,"BDF");
} else {
char *end; double number=strtod(value,&end);
if (*end || !isfinite(number)) return 64;
if (!strcmp(arg,"--start")) r.options.start=number;
else if (!strcmp(arg,"--stop")) r.options.stop=number;
else if (!strcmp(arg,"--sample-step")) r.options.sample_step=number;
else if (!strcmp(arg,"--max-step")) r.options.max_step=number;
else if (!strcmp(arg,"--rtol")) r.options.rtol=number;
else if (!strcmp(arg,"--timeout")) r.options.timeout=number;
else return 64;
}
}
if (!(r.options.stop>r.options.start && r.options.sample_step>0 && r.options.max_step>0 &&
r.options.rtol>0 && r.options.timeout>0) ||
r.options.start+r.options.sample_step==r.options.start ||
r.options.start+r.options.max_step==r.options.start ||
!isfinite(r.options.stop-r.options.start)) return 64;
if (r.options.record_samples &&
((r.options.stop-r.options.start)/r.options.sample_step>1000000)) return 64;
native_solve(&r);
int saved=write_result(&r,output,index_path);
int code=saved?(r.status==2?2:0):3;
native_run_free(&r); return code;
}