如何优化阀门仿真逻辑?
How to optimize valve simulation logic?
这是一个简单的逻辑编程和优化练习曲,是我自己创建的,有点偶然发现了它。
我有一个简单方案的数值模拟。考虑一些蓄水池(或电容器)Cm
,它不断地增加压力。让我们调用它的当前状态 Vm
:
在它的输出端有一个阀门,或一个门,G
,可以根据以下逻辑打开或关闭:
- 门打开,当压力(或电压)
Vm
超过某个阈值时,称之为Vopen
:Vm > Vopen
- 门保持打开状态,而电流输出
Ia
大于一些 Ihold
:Ia > IHold
- 栅极仅从储层中传导能量(如二极管)
我正在对此进行数值 ODE 求解,即在每个(相等的、小的)时间步长 dt 处确定 Vm
和 Ia
。有这三种变体:
变量类型:
float Vm=0.0, Ia=0.0, Vopen, Ihold, Ra, dt;
int G=0;
循环体 v1(串行):
Vm = Vm + (-Ia*G)*dt;
G |= (Vm > Vopen);
Ia = Ia + (Vm*Ra*G)*dt;
G &= (Ia > Ihold);
循环体 v2(串行,带临时变量,三元条件):
int Gv; // temporary var
Vm = Vm + (-Ia*G)*dt;
Gv = (Vm > Vopen) ? 1 : G;
Ia = Ia + (Vm*Ra*Gv)*dt;
G = (Ia > Ihold) ? Gv : 0;
循环体 v3(并行,带缓存):
// cache new state first
float Vm1 = Vm + (-Ia*G)*dt;
float Ia1 = Ia + (Vm*Ra*G)*dt;
// update memory
G = ( Vm1 > Vopen ) || (( Ia1 > Ihold ) && G);
Vm = Vm1;
Ia = Ia1; // not nesesary to cache, done for readibility
G取自建立以下真理table,加上想象:
问:
- 哪个是正确的?是吗?
- 第三种变体(并行逻辑)与前两种(串行逻辑)有何不同?
- 是否有更有效的方法来执行此逻辑?
PS。我正在尝试针对 SSE 优化它,然后(单独)针对 OpenCL(如果给出优化提示)
PPS。好奇的朋友,这里是my working simulator,涉及这个门(html/js)
总体描述是一样的,应该都能满足你的需求。
您的串行代码将产生半步。这意味着,如果将其分解为离散描述,则 V(t) 可以描述为 I(t) 前面的 1/2 dt。第一个将使 G 每半步变化一次,第二个将使它与 I 同步。但是由于您没有在两者之间评估 G 并不重要。 V 和 I 相距半步也没有真正的问题,但你应该记住这一点,但也许你应该使用 plotting/evaluation 向量 {V(t),(I(t-1)+I (t))/2,G(t)}.
并行代码将使它们全部处于同一时间步。
对于您的纯线性问题,直接积分是一个很好的解决方案。高阶 ode 求解器不会给你买任何东西。 State-space 纯线性系统的表示只用不同的方式写出相同的直接积分。
SIMD 优化没有什么可做的。您需要逐步评估,因为您正在通过 V 更新 I 并通过 I 更新 V。这意味着您不能 运行 并行执行这些步骤,这使得许多有趣的优化成为不可能。
这是一个简单的逻辑编程和优化练习曲,是我自己创建的,有点偶然发现了它。
我有一个简单方案的数值模拟。考虑一些蓄水池(或电容器)Cm
,它不断地增加压力。让我们调用它的当前状态 Vm
:
在它的输出端有一个阀门,或一个门,G
,可以根据以下逻辑打开或关闭:
- 门打开,当压力(或电压)
Vm
超过某个阈值时,称之为Vopen
:Vm > Vopen
- 门保持打开状态,而电流输出
Ia
大于一些Ihold
:Ia > IHold
- 栅极仅从储层中传导能量(如二极管)
我正在对此进行数值 ODE 求解,即在每个(相等的、小的)时间步长 dt 处确定 Vm
和 Ia
。有这三种变体:
变量类型:
float Vm=0.0, Ia=0.0, Vopen, Ihold, Ra, dt;
int G=0;
循环体 v1(串行):
Vm = Vm + (-Ia*G)*dt;
G |= (Vm > Vopen);
Ia = Ia + (Vm*Ra*G)*dt;
G &= (Ia > Ihold);
循环体 v2(串行,带临时变量,三元条件):
int Gv; // temporary var
Vm = Vm + (-Ia*G)*dt;
Gv = (Vm > Vopen) ? 1 : G;
Ia = Ia + (Vm*Ra*Gv)*dt;
G = (Ia > Ihold) ? Gv : 0;
循环体 v3(并行,带缓存):
// cache new state first
float Vm1 = Vm + (-Ia*G)*dt;
float Ia1 = Ia + (Vm*Ra*G)*dt;
// update memory
G = ( Vm1 > Vopen ) || (( Ia1 > Ihold ) && G);
Vm = Vm1;
Ia = Ia1; // not nesesary to cache, done for readibility
G取自建立以下真理table,加上想象:
问:
- 哪个是正确的?是吗?
- 第三种变体(并行逻辑)与前两种(串行逻辑)有何不同?
- 是否有更有效的方法来执行此逻辑?
PS。我正在尝试针对 SSE 优化它,然后(单独)针对 OpenCL(如果给出优化提示)
PPS。好奇的朋友,这里是my working simulator,涉及这个门(html/js)
总体描述是一样的,应该都能满足你的需求。
您的串行代码将产生半步。这意味着,如果将其分解为离散描述,则 V(t) 可以描述为 I(t) 前面的 1/2 dt。第一个将使 G 每半步变化一次,第二个将使它与 I 同步。但是由于您没有在两者之间评估 G 并不重要。 V 和 I 相距半步也没有真正的问题,但你应该记住这一点,但也许你应该使用 plotting/evaluation 向量 {V(t),(I(t-1)+I (t))/2,G(t)}.
并行代码将使它们全部处于同一时间步。
对于您的纯线性问题,直接积分是一个很好的解决方案。高阶 ode 求解器不会给你买任何东西。 State-space 纯线性系统的表示只用不同的方式写出相同的直接积分。
SIMD 优化没有什么可做的。您需要逐步评估,因为您正在通过 V 更新 I 并通过 I 更新 V。这意味着您不能 运行 并行执行这些步骤,这使得许多有趣的优化成为不可能。