#include "runtime.h" #include #include #include #include #include #ifdef _WIN32 #include #endif double native_wall_time(void) { #ifdef _WIN32 LARGE_INTEGER value, frequency; QueryPerformanceCounter(&value); QueryPerformanceFrequency(&frequency); return (double)value.QuadPart / (double)frequency.QuadPart; #else struct timespec value; clock_gettime(CLOCK_MONOTONIC, &value); return value.tv_sec + value.tv_nsec*1e-9; #endif } double native_cpu_time(void) { #ifdef _WIN32 FILETIME creation, exit_time, kernel, user; GetProcessTimes(GetCurrentProcess(), &creation, &exit_time, &kernel, &user); ULARGE_INTEGER k, u; k.LowPart=kernel.dwLowDateTime; k.HighPart=kernel.dwHighDateTime; u.LowPart=user.dwLowDateTime; u.HighPart=user.dwHighDateTime; return (double)(k.QuadPart+u.QuadPart)*1e-7; #else return (double)clock()/CLOCKS_PER_SEC; #endif } int native_fail(NativeRun *r,const char *reason,const char *operation,const char *message) { if (!r->status) { r->status=2; r->failure_reason=reason; r->failure_operation=operation; snprintf(r->message_buffer,sizeof(r->message_buffer),"%s",message); r->message=r->message_buffer; } return 0; } int native_poll(NativeRun *r, double t) { double now=native_wall_time(); if (r->status) return 0; if (now-r->last_progress >= 0.1) { r->last_progress=now; if (r->options.cancel_path) { FILE *f=fopen(r->options.cancel_path,"rb"); if (f) { fclose(f); r->status=1; r->message="Simulation cancelled."; r->failure_reason="cancelled"; r->failure_operation="cancel-check"; return 0; } } fprintf(stderr,"{\"phase\":\"integrating\",\"time\":%.17g,\"nfev\":%lu,\"acceptedSteps\":%lu}\n",t,r->nfev,r->accepted); fflush(stderr); } if (r->options.timeout > 0 && now-r->wall_start > r->options.timeout) return native_fail(r,r->stagnating?"time-stagnation":"timeout","integration", r->stagnating?"Native solver kept returning without advancing time until the run time limit expired.": "Native solve exceeded its time limit."); return 1; } int native_rhs(NativeRun *r, double t, const double *y, double *dy) { double w[NOUTPUTS]; r->nfev++; return model_eval(t,y,dy,w); } /* Derivative probes use deterministic property evaluation: the generated * Jacobian entry point omits cross-storage cache seeds, while the ordinary RHS * retains all existing physical expressions and state-property reuse. */ int native_jacobian_rhs(NativeRun *r, double t, const double *y, double *dy) { #if defined(MODEL_JACOBIAN_CANONICAL_RHS) && MODEL_JACOBIAN_CANONICAL_RHS double w[NOUTPUTS]; r->nfev++; return model_eval_jacobian(t,y,dy,w); #else return native_rhs(r,t,y,dy); #endif } int native_append(NativeRun *r, double t, const double *y) { r->final_time=t; memcpy(r->final_state,y,NSTATES*sizeof(double)); if (!r->options.record_samples) return 1; return native_samples_append(r,t,y); } /* Amesim helium data domains: EOS, ideal Cp/h, viscosity. Warnings describe * the property use, not a component. Trial/Newton/Jacobian evaluations never * publish warnings; replay of output samples is also side-effect free. */ static const double property_lower[3]={2.2,200,200}; static const double property_upper[3]={1500,20000,15000}; static const char *property_domain[3]={"equation-of-state","ideal-caloric","dynamic-viscosity"}; static void property_warning_json(FILE *f,int slot,const NativePropertyWarning *w) { int domain=slot/2,upper=slot%2; fprintf(f,"{\"code\":\"helium-%s-%s\",\"medium\":\"helium\",\"property\":\"%s\"," "\"direction\":\"%s\",\"limit\":%.17g,\"time\":%.17g,\"temperature\":%.17g," "\"extremeTime\":%.17g,\"extremeTemperature\":%.17g}", property_domain[domain],upper?"high":"low",property_domain[domain],upper?"high":"low", upper?property_upper[domain]:property_lower[domain],w->first_time,w->first_temperature, w->extreme_time,w->extreme_temperature); } void native_property_warnings_json(NativeRun *r,FILE *f) { int comma=0;fputc('[',f); for(int i=0;i<6;i++)if(r->property_warnings[i].seen) { if(comma++)fputc(',',f); property_warning_json(f,i,&r->property_warnings[i]); } fputc(']',f); } static void check_property_temperatures(NativeRun *r,double t,const double *y) { #if defined(MODEL_PROPERTY_TEMPERATURES) && MODEL_PROPERTY_TEMPERATURES NativePropertyTemperatures temperatures={0}; /* Additional diagnostic evaluation does not alter states or solver counters. */ if(!model_property_temperatures(t,y,&temperatures))return; for(int d=0;d<3;d++)if(temperatures.seen&(1u<=limit-tolerance)continue; int slot=2*d+upper;NativePropertyWarning *w=&r->property_warnings[slot]; if(!w->seen) { w->seen=1;w->first_time=w->extreme_time=t;w->first_temperature=w->extreme_temperature=T; fputs("{\"phase\":\"property-warning\",\"warning\":",stderr); property_warning_json(stderr,slot,w);fputs("}\n",stderr);fflush(stderr); } else if(upper ? T>w->extreme_temperature : Textreme_temperature) { w->extreme_temperature=T;w->extreme_time=t; } } #else (void)r;(void)t;(void)y; #endif } static double locate(int idx, double bound, int lower, double left, double right, NativeDense dense, void *context) { double state[NSTATES]; for (int i=0;i<60;i++) { double mid=.5*(left+right); if (!dense(context,mid,state)) return NAN; if (lower ? state[idx] <= bound : state[idx] >= bound) right=mid; else left=mid; } return right; } #if NFRICTIONS static int friction_drives(NativeRun *r,double t,const double *state,double *drives) { r->nfev++; return model_friction_drives(t,state,drives); } static double locate_breakaway(NativeRun *r,int index,double left,double right, NativeDense dense,void *context) { double state[NSTATES],drives[NFRICTIONS]; for(int i=0;i<60;i++) { double mid=left+.5*(right-left); if(mid<=left || mid>=right) break; if(!dense(context,mid,state) || !friction_drives(r,mid,state,drives)) return NAN; if(fabs(drives[index])>model_frictions[index].breakaway_force) right=mid; else left=mid; } return right; } #endif int native_accept(NativeRun *r, double t, double next, const double *old, const double *trial, NativeDense dense, void *context, double *accepted_time, double *accepted_state) { double when[2*(NSTOPS+NFRICTIONS+1)], bounds[2*(NSTOPS+NFRICTIONS+1)]; double restitution[2*(NSTOPS+NFRICTIONS+1)], thresholds[2*(NSTOPS+NFRICTIONS+1)]; int indices[2*(NSTOPS+NFRICTIONS+1)], friction[2*(NSTOPS+NFRICTIONS+1)], count=0; for (int j=0;j=bound-tol && old[v]>vt0)) at=t; else if (lower ? (old[x]>bound+tol && trial[x]<=bound) : (old[x]=bound)) at=locate(x,bound,lower,t,next,dense,context); else if (lower ? (old[x]<=bound && old[v]>vt0 && trial[v]<-vt1 && trial[x]<=bound) : (old[x]>=bound && old[v]<-vt0 && trial[v]>vt1 && trial[x]>=bound)) { double turn=locate(v,0,lower,t,next,dense,context); at=locate(x,bound,lower,turn,next,dense,context); } if (isfinite(at)) { when[count]=at; bounds[count]=bound; indices[count]=v; friction[count]=-1; restitution[count]=lower?s.lower_restitution:s.upper_restitution; thresholds[count]=lower?s.lower_threshold:s.upper_threshold; count++; } } } #if NFRICTIONS double before[NFRICTIONS],after[NFRICTIONS]; if(!friction_drives(r,t,old,before) || !friction_drives(r,next,trial,after)) return -1; for(int j=0;jf.breakaway_force) at=t; else if(fabs(after[j])>f.breakaway_force) at=locate_breakaway(r,j,t,next,dense,context); } else { double direction=mode>0?1:-1; if(direction*old[f.velocity_index]>f.velocity_threshold && direction*trial[f.velocity_index]<=f.velocity_threshold) at=locate(f.velocity_index,direction*f.velocity_threshold,mode>0,t,next,dense,context); else if(direction*old[f.velocity_index]>=0 && direction*trial[f.velocity_index]<0) at=locate(f.velocity_index,0,mode>0,t,next,dense,context); } if(isnan(at)) return -1; if(isfinite(at)) { when[count]=at;indices[count]=f.velocity_index;friction[count]=j; bounds[count]=restitution[count]=thresholds[count]=0;count++; } } #endif double stop=next; for (int i=0;ioptions.record_samples) { double st=r->options.start+r->sample_index*r->options.sample_step; while (st<=r->options.stop && (count ? stsample_index++; st=r->options.start+r->sample_index*r->options.sample_step; } } if (count) { if (!dense(context,stop,accepted_state)) return -1; for (int i=0;i=0) continue; double incoming=accepted_state[indices[i]]; accepted_state[indices[i]]=fabs(incoming)<=thresholds[i]?0:-restitution[i]*incoming; accepted_state[indices[i]+1]=bounds[i]; } #if NFRICTIONS /* Stop/reverse only at an accepted event. The discrete mode travels * with saved states so replayed results and Jacobian trials are pure. */ double drives[NFRICTIONS]; for(int i=0;i0?1:(velocity<0?-1:0); } } if(!friction_drives(r,stop,accepted_state,drives)) return -1; for(int i=0;i=0 && fabs(when[i]-stop)<=1e-12*fmax(fabs(stop),1)) { int j=friction[i];NativeFriction f=model_frictions[j]; accepted_state[f.velocity_index]=0; accepted_state[f.mode_index]=fabs(drives[j])>f.breakaway_force ? (drives[j]>0?1:-1) : 0; } #endif if (!native_append(r,stop,accepted_state)) return -1; while (r->options.start+r->sample_index*r->options.sample_step<=stop) r->sample_index++; r->events++; } else memcpy(accepted_state,trial,NSTATES*sizeof(double)); *accepted_time=stop; r->final_time=stop; memcpy(r->final_state,accepted_state,NSTATES*sizeof(double)); check_property_temperatures(r,stop,accepted_state); return count ? 1 : 0; } int native_solve(NativeRun *r) { double y[NSTATES]; if (!model_init(y)) { r->status=2; r->message="Native model initialization failed."; return 0; } #if NFRICTIONS double drives[NFRICTIONS]; if(!friction_drives(r,r->options.start,y,drives)) {r->status=2;r->message="Cannot initialize friction.";return 0;} for(int j=0;jf.breakaway_force) y[f.mode_index]=drives[j]>0?1:-1; } #endif r->sample_index=1; check_property_temperatures(r,r->options.start,y); if (!native_append(r,r->options.start,y)) return 0; r->wall_start=native_wall_time(); r->cpu_start=native_cpu_time(); r->last_progress=r->wall_start-1; int ok=r->options.bdf ? native_bdf(r) : native_rk45(r); r->solve_seconds=native_wall_time()-r->wall_start; r->solve_cpu_seconds=native_cpu_time()-r->cpu_start; if (!ok && !r->status) native_fail(r,"integration-failure","integration","Native integration failed."); if (ok && !r->status && r->final_time!=r->options.stop) native_fail(r,"incomplete-result","integration","Native solver returned before the requested stop time."); if (!r->storage_failed) native_append(r,r->final_time,r->final_state); native_samples_flush(r); if (!r->message) r->message=r->status ? "Native integration failed." : "Simulation completed."; return !r->status; } void native_run_free(NativeRun *r) { free(r->times); free(r->states); if(r->sample_file) fclose(r->sample_file); if(r->output_file) fclose(r->output_file); }