修复循环信号与事件采样并接入 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 通过。
This commit is contained in:
1 parent
1aac220084
commit
7611f13208
85 files changed
+11358
-33
No files matched your search
+10
-1
@@ -34,6 +34,14 @@ bash bat/setup-native-linux.sh
|
||||
|
||||
通用诊断包含 `stateCount`、`sampleCount`。`pressureFlow`、`stream` 属于 Python 后端的可选诊断;C 后端不报告未计算的方程残差,网页仅在该值存在时显示它。
|
||||
|
||||
### 事件输出语义(version 2)
|
||||
|
||||
原生结果的 `outputSemantics` 描述采样合同。规则网格保持实际浮点时间,不吸附到附近事件。内部时间事件额外保存 `nextafter(event, -INFINITY)` 和事件时刻:前者是积分段的实际左侧终点,后者沿用连续状态、使用事件后的信号。状态重置事件在可用的积分区间内额外保存左邻时刻的插值状态,再保存事件后的接受状态。同一实际时间仍以后一次接受状态为准;时间序列严格递增,采样总数可能超过规则网格点数。
|
||||
|
||||
输出根据保存时间及完整状态重放;已有摩擦离散模式随状态保存。事件左邻样本是有限精度下的左侧样本,不声称保存了同一数学时刻的精确左极限。初始时刻或当前步起点的即时状态重置没有新的左侧区间,不倒填样本。仿真终点不额外制造一个时间事件。纯求解模式不增加采样或模型输出求值;此改动不注册新的机械事件。
|
||||
|
||||
与外部结果对照时,必须核验事件侧别。八路评估脚本使用全部 STEP/UD00 输出核验驱动阶段,所有变量共用同一对原始样本行;仅允许在输出间隔的 `1e-7` 范围内匹配时间舍入误差。无法匹配的点明确报告,不跨跳变插值、不根据力或压力误差选择样本。这个规则不代表已验证未注册的接触模式。
|
||||
|
||||
原生运行库已在本机 GCC 8.1 / SUNDIALS 7.4.0 验证。构建需要对应 C 头文件、导入库和 DLL,单纯安装 Python 包不能代替这些文件。构建器根据环境变量或当前 Python 基础环境查找 SUNDIALS,根据环境变量或 PATH 查找 GCC。
|
||||
|
||||
## 仿真结果分块存储
|
||||
@@ -103,7 +111,8 @@ Jacobian 构建内现已按完整输入的浮点位复用储气物性、PH 温
|
||||
- 扩展编译器上限 1024 状态、16384 输出;无连续状态的信号系统使用隐藏常量状态驱动输出。气动网络必须有压力状态锚点;独立气腔之间不能无阻力直接相连。兼容固定管路容腔是已实现的合并例外。闭合未收敛或方程欠定时明确失败,不静默回退。
|
||||
- 支持原生 RK45 与 CVODE BDF。CVODE 默认按可证明的结构启用着色差分,使用稠密线性求解;不支持分组的模型自动保留逐列差分。
|
||||
- 网页和 Python CLI 默认 `rtol=1e-8`;生成的状态绝对误差限为质量 `1e-14 kg`、内能 `1e-8 J`、速度/位移 `1e-12`(各自 SI 单位)。独立 C 程序默认 `rtol=1e-6`,对照时应显式传入。CLI 可覆盖 rtol;本版不支持自定义 atol 或 first_step。不同积分器相同局部容差不保证全局曲线误差完全相同。
|
||||
- 时间信号显式分段,塑性/反弹端挡用稠密插值定位并重启。试探 RHS 不修改已接受状态。柔性接触沿用现有分段力公式,不改变刚度或阻尼来提速。
|
||||
- 时间信号显式分段,塑性/反弹端挡用稠密插值定位并重启。LSTP00A 默认定位间隙过零;非负力模式还定位接触区内的原始力过零。两个生成路径均从机械状态索引发出 `NativeContact` 描述,检测不调用整模型 RHS,接触事件不重置位移/速度。试探 RHS 不修改已接受状态,接触力仍用原分段公式。MASS 弹性限位及连续释放的独立实验没有合入默认路径。
|
||||
- LSTP 的防重复触发记录仅在接受事件时提交,不参与 RHS、雅可比或输出重放;同一浮点时刻的接触事件合并重启,并沿用输出语义 v2 保存两侧。结果的 `contactEvents` 提供检查、密集插值、二分和接触/力截断计数。相对速度换向时分段检测同号间隙的中间过零;这依赖已解析的积分步,不能保证捕获一步内任意多次未解析振荡。专项见 `tests/test_native_contact_events.py`。
|
||||
- 每任务独立进程,支持进度、取消及超时。进程崩溃不会作为成功返回,受控失败保留最后接受状态。
|
||||
- 编译缓存位于 `app/data/native-builds/`:`models/<SHA>/` 保存完整模型,`objects/<SHA>/` 保存可跨模型复用的模块目标文件。按当前模型使用的元件函数及其依赖选择模块,最多并行编译 4 个缺失单元。预处理后的实际 C 内容、工具链和编译选项组成对象键;完整模型键另含生成源码、组件合同及链接依赖。模型数值参数仍特化入 C,但仅重编受到影响的单元;时间、步长、rtol、采样选项仍是运行参数。
|
||||
- 完整模型默认预算 256 MiB、对象预算 128 MiB,可分别设置非负整数环境变量 `SIMULATION_NATIVE_MODEL_CACHE_MB`、`SIMULATION_NATIVE_OBJECT_CACHE_MB`。按目录最近使用时间执行 LRU;活跃构建/运行及最后一个单独超额的条目保留并报告超额,因此是安全软上限。预算计算受管理文件的逻辑字节,不含旧版根级缓存、锁及文件系统元数据。
|
||||
|
||||
@@ -1,37 +1,81 @@
|
||||
#include "kernels.h"
|
||||
#include <math.h>
|
||||
|
||||
double native_signal(double t, double start, int stages, int cyclic, const double *data) {
|
||||
if (t < start) return data[0];
|
||||
double elapsed = t-start, duration = 0, offset = 0;
|
||||
for (int i=0;i<stages;i++) duration += data[16+i];
|
||||
if (cyclic && duration > 0) elapsed = fmod(elapsed,duration);
|
||||
else if (elapsed >= duration) return data[8+stages-1];
|
||||
for (int i=0;i<stages;i++) {
|
||||
double d = data[16+i];
|
||||
if (elapsed < offset+d || i == stages-1)
|
||||
return d <= 0 ? data[8+i] : data[i] + (elapsed-offset)/d*(data[8+i]-data[i]);
|
||||
offset += d;
|
||||
typedef struct {
|
||||
int stage;
|
||||
double begin, next;
|
||||
} SignalInterval;
|
||||
|
||||
/* Both queries must construct exactly the same absolute boundary. In
|
||||
* particular, fmod(t-start,period) can still be below a stage offset at the
|
||||
* double returned by the event query (e.g. 44 s for periods of .8+10 s). */
|
||||
static double signal_boundary(double start, double offset, double cycle, double period) {
|
||||
return cycle==0 ? start+offset : (start+offset)+cycle*period;
|
||||
}
|
||||
|
||||
static int signal_cycle(double t, double start, double period, double *current, double *next) {
|
||||
double lo=fmax(0,floor((t-start)/period));
|
||||
if (!isfinite(lo)) return 0;
|
||||
double hi=fmax(lo+1,nextafter(lo,INFINITY));
|
||||
if (signal_boundary(start,0,lo,period)>t) {
|
||||
hi=lo; lo=fmax(0,hi-1);
|
||||
if (signal_boundary(start,0,lo,period)>t) lo=0;
|
||||
} else {
|
||||
/* Usually hi=lo+1 already brackets t. At coarse time resolution,
|
||||
* several cycles can round to the same double: bracket/bisect them
|
||||
* rather than advancing one cycle at a time or shifting t. */
|
||||
while (signal_boundary(start,0,hi,period)<=t) {
|
||||
lo=hi; hi=fmax(2*hi,nextafter(hi,INFINITY));
|
||||
if (!isfinite(hi)) return 0;
|
||||
}
|
||||
}
|
||||
return data[8+stages-1];
|
||||
for (;;) {
|
||||
double mid=floor(lo+(hi-lo)/2);
|
||||
if (mid<=lo || mid>=hi) break;
|
||||
if (signal_boundary(start,0,mid,period)<=t) lo=mid;
|
||||
else hi=mid;
|
||||
}
|
||||
*current=lo; *next=hi;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SignalInterval signal_interval(double t, double start, int stages, int cyclic,
|
||||
const double *data) {
|
||||
if (t<start) return (SignalInterval){-1,start,start};
|
||||
double offsets[9]={0};
|
||||
for (int i=0;i<stages;i++) offsets[i+1]=offsets[i]+data[16+i];
|
||||
double period=offsets[stages], cycle=0, next_cycle=0;
|
||||
cyclic=cyclic && period>0 && isfinite(period);
|
||||
if (cyclic && !signal_cycle(t,start,period,&cycle,&next_cycle))
|
||||
return (SignalInterval){-2,NAN,NAN};
|
||||
for (int i=0;i<stages;i++) {
|
||||
/* A cycle's last endpoint is the next cycle's first boundary,
|
||||
* never a separately rounded (start+period)+cycle*period. */
|
||||
double right=cyclic && offsets[i+1]==period ? signal_boundary(start,0,next_cycle,period) :
|
||||
signal_boundary(start,offsets[i+1],cycle,period);
|
||||
if (t<right)
|
||||
return (SignalInterval){i,signal_boundary(start,offsets[i],cycle,period),right};
|
||||
}
|
||||
return (SignalInterval){stages,0,INFINITY};
|
||||
}
|
||||
|
||||
double native_signal(double t, double start, int stages, int cyclic, const double *data) {
|
||||
SignalInterval interval=signal_interval(t,start,stages,cyclic,data);
|
||||
if (interval.stage==-2) return NAN;
|
||||
if (interval.stage==-1) return data[0];
|
||||
if (interval.stage==stages) return data[8+stages-1];
|
||||
int i=interval.stage;
|
||||
double d=data[16+i];
|
||||
if (d<=0) return data[8+i];
|
||||
double fraction=fmin(1,fmax(0,(t-interval.begin)/d));
|
||||
if (fraction==0) return data[i];
|
||||
if (fraction==1) return data[8+i];
|
||||
return data[i]+fraction*(data[8+i]-data[i]);
|
||||
}
|
||||
|
||||
double native_signal_break(double t, double end, double start, int stages,
|
||||
int cyclic, const double *data) {
|
||||
double duration=0, offset=0, result=end;
|
||||
for (int i=0;i<stages;i++) duration += data[16+i];
|
||||
/* A cyclic endpoint is already the next cycle's start. Listing both can
|
||||
create two nearly equal floating-point break times for one event. */
|
||||
for (int i=0;i<stages+(cyclic?0:1);i++) {
|
||||
double event=start+offset;
|
||||
if (cyclic && duration > 0 && event <= t) {
|
||||
double cycle=fmax(0,floor((t-event)/duration)+1);
|
||||
event += cycle*duration;
|
||||
if (event <= t) event += duration;
|
||||
}
|
||||
if (event > t) result=fmin(result,event);
|
||||
if (i < stages) offset += data[16+i];
|
||||
}
|
||||
return result;
|
||||
SignalInterval interval=signal_interval(t,start,stages,cyclic,data);
|
||||
return interval.stage==-2 ? NAN : fmin(end,interval.next);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#ifndef NFRICTIONS
|
||||
#define NFRICTIONS 0
|
||||
#endif
|
||||
#ifndef NCONTACTS
|
||||
#define NCONTACTS 0
|
||||
#endif
|
||||
#define NATIVE_JACOBIAN_STATS_COUNT 3
|
||||
typedef struct {
|
||||
double start, stop, sample_step, max_step, rtol, timeout;
|
||||
@@ -44,6 +47,13 @@ typedef struct {
|
||||
char message_buffer[384];
|
||||
int status; /* 0 completed, 1 cancelled, 2 failed */
|
||||
const char *message;
|
||||
/* Accepted-event latch only; never used by RHS or output force replay. */
|
||||
#if NCONTACTS
|
||||
double contact_last[2*NCONTACTS];
|
||||
int contact_direction[2*NCONTACTS], contact_pending[2*NCONTACTS];
|
||||
#endif
|
||||
unsigned long contact_checks, contact_dense, contact_roots;
|
||||
unsigned long contact_transitions, contact_clipping;
|
||||
NativePropertyWarning property_warnings[6];
|
||||
} NativeRun;
|
||||
typedef int (*NativeDense)(void *context, double t, double *state);
|
||||
@@ -55,6 +65,7 @@ int native_fail(NativeRun *run, const char *reason, const char *operation, const
|
||||
int native_rhs(NativeRun *run, double t, const double *y, double *dy);
|
||||
int native_jacobian_rhs(NativeRun *run, double t, const double *y, double *dy);
|
||||
int native_append(NativeRun *run, double t, const double *y);
|
||||
int native_time_boundary_samples(NativeRun *run, double boundary, const double *state);
|
||||
int native_accept(NativeRun *run, double t, double next, const double *old,
|
||||
const double *trial, NativeDense dense, void *context,
|
||||
double *accepted_time, double *accepted_state);
|
||||
|
||||
+32
-5
@@ -86,6 +86,16 @@ int native_append(NativeRun *r, double t, const double *y) {
|
||||
return native_samples_append(r,t,y);
|
||||
}
|
||||
|
||||
/* The integrators already stop at the left-adjacent double of an internal
|
||||
* time event. Preserve that actual endpoint and the continuous state at the
|
||||
* event, so output replay can evaluate BOTH forcing phases without snapping
|
||||
* a regular-grid timestamp or changing integration history. */
|
||||
int native_time_boundary_samples(NativeRun *r,double boundary,const double *state) {
|
||||
if (!r->options.record_samples || boundary>=r->options.stop) return 1;
|
||||
return native_samples_append(r,nextafter(boundary,-INFINITY),state) &&
|
||||
native_samples_append(r,boundary,state);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
@@ -166,12 +176,14 @@ static double locate_breakaway(NativeRun *r,int index,double left,double right,
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "contact_events.h"
|
||||
|
||||
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;
|
||||
double when[2*(NSTOPS+NFRICTIONS+NCONTACTS+1)], bounds[2*(NSTOPS+NFRICTIONS+NCONTACTS+1)]={0};
|
||||
double restitution[2*(NSTOPS+NFRICTIONS+NCONTACTS+1)]={0}, thresholds[2*(NSTOPS+NFRICTIONS+NCONTACTS+1)]={0};
|
||||
int indices[2*(NSTOPS+NFRICTIONS+NCONTACTS+1)], friction[2*(NSTOPS+NFRICTIONS+NCONTACTS+1)], count=0;
|
||||
for (int j=0;j<NSTOPS;j++) {
|
||||
NativeStop s=model_stops[j]; int v=s.velocity_index, x=v+1;
|
||||
double vt0=1e-12*fmax(fabs(old[v]),1), vt1=1e-12*fmax(fabs(trial[v]),1);
|
||||
@@ -219,6 +231,9 @@ int native_accept(NativeRun *r, double t, double next, const double *old,
|
||||
bounds[count]=restitution[count]=thresholds[count]=0;count++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if NCONTACTS
|
||||
if(!contact_candidates(r,t,next,old,trial,dense,context,when,indices,friction,&count)) return -1;
|
||||
#endif
|
||||
double stop=next;
|
||||
for (int i=0;i<count;i++) stop=fmin(stop,when[i]);
|
||||
@@ -232,8 +247,17 @@ int native_accept(NativeRun *r, double t, double next, const double *old,
|
||||
}
|
||||
if (count) {
|
||||
if (!dense(context,stop,accepted_state)) return -1;
|
||||
/* Keep the last representable pre-event sample before applying a
|
||||
* reset. Discrete modes are part of the saved state, never replayed
|
||||
* from a mutable global mode. An event at the current step start has
|
||||
* no new left interval; do not invent or back-date a sample. */
|
||||
double before=nextafter(stop,-INFINITY);
|
||||
if (r->options.record_samples && before>=t && before<stop) {
|
||||
double sy[NSTATES];
|
||||
if (!dense(context,before,sy) || !native_samples_append(r,before,sy)) return -1;
|
||||
}
|
||||
for (int i=0;i<count;i++) if (fabs(when[i]-stop)<=1e-12*fmax(fabs(stop),1)) {
|
||||
if(friction[i]>=0) continue;
|
||||
if(friction[i]!=-1) 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];
|
||||
@@ -242,7 +266,7 @@ int native_accept(NativeRun *r, double t, double next, const double *old,
|
||||
/* 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;i<count;i++) if(friction[i]<0 && fabs(when[i]-stop)<=1e-12*fmax(fabs(stop),1)) {
|
||||
for(int i=0;i<count;i++) if(friction[i]==-1 && fabs(when[i]-stop)<=1e-12*fmax(fabs(stop),1)) {
|
||||
for(int j=0;j<NFRICTIONS;j++) if(model_frictions[j].velocity_index==indices[i]) {
|
||||
double velocity=accepted_state[indices[i]];
|
||||
accepted_state[model_frictions[j].mode_index]=velocity>0?1:(velocity<0?-1:0);
|
||||
@@ -254,6 +278,9 @@ int native_accept(NativeRun *r, double t, double next, const double *old,
|
||||
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 NCONTACTS
|
||||
contact_commit(r,stop,accepted_state,when,indices,friction,count);
|
||||
#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++;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Private implementation included by common.c. All probes use state-only
|
||||
* descriptors: no model RHS, property evaluation, mode mutation or reset. */
|
||||
#if NCONTACTS
|
||||
#include <float.h>
|
||||
|
||||
static double contact_penetration(int j,const double *y) {
|
||||
NativeContact c=model_contacts[j];
|
||||
double x1=y[c.velocity1+1],x2=y[c.velocity2+1];
|
||||
/* Match each lowering's existing force formula, including association. */
|
||||
return c.subtract_first ? -(c.gap0+(x2-x1)) : -(c.gap0+x2-x1);
|
||||
}
|
||||
static double contact_velocity(int j,const double *y) {
|
||||
NativeContact c=model_contacts[j];
|
||||
return y[c.velocity1]-y[c.velocity2];
|
||||
}
|
||||
static double contact_value(int j,int kind,const double *y) {
|
||||
if(kind==2) return contact_velocity(j,y);
|
||||
double p=contact_penetration(j,y);
|
||||
if(!kind) return p;
|
||||
NativeContact c=model_contacts[j];
|
||||
double fraction=c.depth>0 ? -expm1(-fmax(p,0)/c.depth) : 1;
|
||||
return c.stiffness*p+fraction*c.damping*contact_velocity(j,y);
|
||||
}
|
||||
static double contact_locate(NativeRun *r,int j,int kind,double left,double right,
|
||||
double sign,NativeDense dense,void *context) {
|
||||
double y[NSTATES];
|
||||
for(int k=0;k<60;k++) {
|
||||
double mid=left+.5*(right-left);
|
||||
if(mid<=left || mid>=right) break;
|
||||
r->contact_dense++;r->contact_roots++;
|
||||
if(!dense(context,mid,y)) return NAN;
|
||||
double g=contact_value(j,kind,y);
|
||||
if(!isfinite(g)) return NAN;
|
||||
if(sign<0 ? g>=0 : g<=0) right=mid; else left=mid;
|
||||
}
|
||||
return right;
|
||||
}
|
||||
static double contact_bracket(NativeRun *r,int j,int force,double left,double right,
|
||||
double a,double b,NativeDense dense,void *context,int *direction) {
|
||||
int slot=2*j+force;
|
||||
if(r->contact_direction[slot] && r->contact_last[slot]==left)
|
||||
a=r->contact_direction[slot]*DBL_MIN;
|
||||
if(a==0 || (a>0 ? b>0 : b<0)) return INFINITY;
|
||||
*direction=a<0 ? 1 : -1;
|
||||
return contact_locate(r,j,force,left,right,a,dense,context);
|
||||
}
|
||||
static double contact_candidate(NativeRun *r,int j,int force,double t,double next,
|
||||
const double *old,const double *trial,
|
||||
NativeDense dense,void *context,int *direction) {
|
||||
double a=contact_value(j,force,old),b=contact_value(j,force,trial);
|
||||
if(!isfinite(a) || !isfinite(b)) return NAN;
|
||||
/* Resolve a same-sign endpoint excursion by splitting at velocity reversal.
|
||||
* This assumes resolved steps, not arbitrarily many oscillations per step. */
|
||||
if(!force) {
|
||||
double va=contact_velocity(j,old),vb=contact_velocity(j,trial);
|
||||
if((va<0 && vb>0) || (va>0 && vb<0)) {
|
||||
double turn=contact_locate(r,j,2,t,next,va,dense,context),y[NSTATES];
|
||||
if(!isfinite(turn)) return NAN;
|
||||
r->contact_dense++;
|
||||
if(!dense(context,turn,y)) return NAN;
|
||||
double g=contact_value(j,force,y);
|
||||
if(g==0 && ((a<0 && b<0) || (a>0 && b>0))) return INFINITY;
|
||||
double found=contact_bracket(r,j,force,t,turn,a,g,dense,context,direction);
|
||||
if(isfinite(found) || isnan(found)) return found;
|
||||
return contact_bracket(r,j,force,turn,next,g,b,dense,context,direction);
|
||||
}
|
||||
}
|
||||
return contact_bracket(r,j,force,t,next,a,b,dense,context,direction);
|
||||
}
|
||||
static int contact_candidates(NativeRun *r,double t,double next,const double *old,const double *trial,
|
||||
NativeDense dense,void *context,double *when,int *indices,int *kinds,int *count) {
|
||||
for(int j=0;j<NCONTACTS;j++) {
|
||||
r->contact_checks++;
|
||||
for(int force=0;force<2;force++) {
|
||||
if(force && (model_contacts[j].signed_force==1 || contact_penetration(j,old)<=0)) continue;
|
||||
int direction=0;
|
||||
double at=contact_candidate(r,j,force,t,next,old,trial,dense,context,&direction);
|
||||
if(isnan(at)) return 0;
|
||||
if(isfinite(at)) {
|
||||
if(force) {
|
||||
double y[NSTATES];r->contact_dense++;
|
||||
if(!dense(context,at,y)) return 0;
|
||||
if(contact_penetration(j,y)<=0) continue;
|
||||
}
|
||||
int n=(*count)++;
|
||||
when[n]=at;indices[n]=j;kinds[n]=force?-3:-2;
|
||||
r->contact_pending[2*j+force]=direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static void contact_commit(NativeRun *r,double t,const double *y,
|
||||
const double *when,const int *indices,const int *kinds,int count) {
|
||||
for(int i=0;i<count;i++) {
|
||||
if(kinds[i]>-2 || when[i]!=t) continue;
|
||||
int j=indices[i],force=kinds[i]==-3,slot=2*j+force;
|
||||
r->contact_last[slot]=t;r->contact_direction[slot]=r->contact_pending[slot];
|
||||
if(force) r->contact_clipping++; else r->contact_transitions++;
|
||||
fprintf(stderr,"{\"phase\":\"mechanical-event\",\"kind\":\"%s\",\"index\":%d,\"time\":%.17g,\"direction\":%d,\"penetration\":%.17g,\"relativeVelocity\":%.17g}\n",
|
||||
force?"force-clip":"lstp-contact",j,t,r->contact_direction[slot],
|
||||
contact_penetration(j,y),contact_velocity(j,y));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -366,6 +366,10 @@ int native_bdf(NativeRun *r) {
|
||||
r->starts++;
|
||||
}
|
||||
}
|
||||
if (!native_time_boundary_samples(r,boundary,N_VGetArrayPointer(y))) {
|
||||
native_fail(r,"sample-storage-failure","native_time_boundary_samples","Cannot store time-event samples.");
|
||||
goto cleanup;
|
||||
}
|
||||
t=boundary; r->final_time=t;
|
||||
memcpy(r->final_state,N_VGetArrayPointer(y),NSTATES*sizeof(double));
|
||||
double sample=r->options.start+r->sample_index*r->options.sample_step;
|
||||
|
||||
@@ -51,7 +51,15 @@ static int write_result(NativeRun *r, const char *path, const char *index_path)
|
||||
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"};
|
||||
|
||||
@@ -108,6 +108,7 @@ int native_rk45(NativeRun *r) {
|
||||
if (impact) restart=1;
|
||||
else memcpy(f,K[6],sizeof(f));
|
||||
}
|
||||
if (!native_time_boundary_samples(r,boundary,y)) return 0;
|
||||
t=boundary; r->final_time=t; memcpy(r->final_state,y,sizeof(y));
|
||||
double sample=r->options.start+r->sample_index*r->options.sample_step;
|
||||
if (r->options.record_samples && sample<=t && sample<=r->options.stop) {
|
||||
|
||||
Reference in new issue
Block a user