如何结合模拟动画与图形在CodeWorld?



我使用CodeWorld并使用"entrypoint"activityOf()来模拟由坦克组成的物理系统。这里我们有一个水箱,其出口流量(h)取决于容积水平的高度。当然,对于严肃的模拟工作,应该使用其他软件,但在这里仍然很有趣!

program = activityOf(initial, change, tank_water)
composedOf = pictures
-- Initial value and parameter
initial(rs) = 5
g = 9.81
area = 0.015
-- Inlet flow
qin = 0.0
-- Dynamics using Euler approximation
qout(h) = area*sqrt(2*g*max(h,0))
change(h, TimePassing(dt)) = max(h,0) - qout(h)*dt + qin*dt
change(h, other) = h
-- Animation of system
tank_water(h) = composedOf [tank, water(h), graph, coordinatePlane]  
tank = translated(thickRectangle(width,height,0.2),position,4)
width = 3
height = 8
position = -5
water(h) = translated(colored(solidPolygon [(-width/2, 0), (-width/2, h), (width/2, h), (width/2,0)], 
light(blue)),position,0)
-- Graph of evolution of h(t) - here used monitored values and about 5 seconds between each data point
graph = polyline [(0,5), (1,3.7), (2,2.3), (3,1.3), (4,0.7), (5,0.2), (6, 0)]

出于教育目的,我认为最好将动画与图表结合起来,以显示身高随时间的变化情况。在这里,我输入了一个"冒牌"代码。图,因为它很容易测量(我也可以输入解析解,使图适应参数)。

我想知道如何包含一个子系统,从动画中收集数据,并在模拟进行时将其呈现在图形中。什么好主意吗?

我想到的一个麻烦的想法是扩展模型状态,包括在模拟期间收集的测量点。我们可以事先说,我们收集了10个h和t的样本,时间距离为5秒,当数据进来时,我们展开图。

另一个想法是以某种方式"add-on";一些通用的数据记录器到activityOf()程序,该程序将数据存储在文件中,之后可以使用任何软件进行研究。也许在haskell环境中已经有这样的记录器了?

但我很不确定如何做到这一点,这里也许有一些更好的和更一般的方法来做到这一点?

我将使用"常规" Haskell环境的语法和类型https://code.world/haskell进行响应。

activityOf的类型为:

activityOf ::
world ->
(Event -> world -> world) ->
(world -> Picture) ->
IO ()

,正如您所看到的,没有内置的方法来记录值并绘制它们。将其添加到activityOf似乎是错误的(我们可能想添加的东西太多了)。

但是对于这样的函数,您希望使用什么类型呢?可能如下:

activityWithGraphOf ::
world ->
(Event -> world -> world) ->
(world -> Picture) ->
(world -> Double) ->
IO ()

记住这个抽象,我将去实现那个函数

  • 它自己当然使用activityOf
  • 它会跟踪world状态
  • 它会记录时间
  • 它会跟踪最近的图形值
  • 也许它将传入的TimePassing分成两部分,如果需要的话,以正确的间隔对图形进行采样。
  • ,它将与图形
  • 组成被包装活动提供的Picture

听起来像是一个有趣的练习。现在我有一个通用的函数,我可以使用许多图形绘制模拟。

我做了一个解决方案"繁琐";道路希望我能根据我得到的信息更好地组织它。感谢建议的改进,使代码可以更短。我觉得处理元组很笨拙。表和稍后的图形的进一步创建可以与储罐流程分开。这将有助于CodeWorld中其他模拟任务的重用。改进欢迎!

,

我现在已经更新了代码,使其更短,但也在表中添加了更多的数据点。state_and_table必须是CodeWorld中的元组,不能使用索引来处理元组。此外,您现在可以使用上下箭头更改泵送速率qin。

扩展到两个(或更多)水箱,使控制水的高度更具挑战性,并将其作为练习留给你!享受吧!

链接运行https://code.world/#P2uQaw2KBSbyspnQSn5E4Gw

program = activityOf(initial, change, tank_water)
-- Initial values of the total state, i.e. state_and_table
initial(rs) = state_and_table
state_and_table = (time_0, h_0, u_0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
time_0 = 0
h_0 = 7
u_0 = 0
-- Functions to read elements of state_and_table 
time_of(time_value,_,_,_,_,_,_,_,_,_,_,_,_,
_,_,_,_,_,_,_,_,_,_) = time_value
h_of(_,h_value,_,_,_,_,_,_,_,_,_,_,_,
_,_,_,_,_,_,_,_,_,_) = h_value
u_of(_,_,u_value,_,_,_,_,_,_,_,_,_,_,
_,_,_,_,_,_,_,_,_,_) = u_value
table_entry((_,_,_,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,
x11,x12,x13,x14,x15,x16,x17,x18,x19,x20), k)
| k==1 = x1
| k==2 = x2
| k==3 = x3
| k==4 = x4
| k==5 = x5
| k==6 = x6
| k==7 = x7
| k==8 = x8
| k==9 = x9
| k==10 = x10
| k==11 = x11
| k==12 = x12
| k==13 = x13
| k==14 = x14
| k==15 = x15
| k==16 = x16
| k==17 = x17
| k==18 = x18
| k==19 = x19
| k==20 = x20
-- Events of recording state to table at the following times
table_time = [ 5*t-5 | t <- [1..20]]
-- Parameters related to the water flow
g = 9.81
area = 0.025
-- Parameters of the tank
width = 5
height = 8
position = -5
-- Inlet and outlet flow
qin(u) = max(u, 0)
qout(h) = area*sqrt(2*g*max(h,0))  -- Bernoulli's physical model
-- Change of state_and_table 
change(state_and_table, TimePassing(dt)) = 
-- Update time
(time_of(state_and_table) + dt,
-- Physical state equation -- Euler approximation of differantial equation  
max(h_of(state_and_table),0) - qout(h_of(state_and_table))*dt + qin(u_of(state_and_table))*dt, 
-- Control variable u
u_of(state_and_table),
-- Event of recording state to table at predfined times
table_update(state_and_table,1),
table_update(state_and_table,2),
table_update(state_and_table,3),
table_update(state_and_table,4),
table_update(state_and_table,5),
table_update(state_and_table,6),
table_update(state_and_table,7),
table_update(state_and_table,8),
table_update(state_and_table,9),
table_update(state_and_table,10),
table_update(state_and_table,11),
table_update(state_and_table,12),
table_update(state_and_table,13),
table_update(state_and_table,14),
table_update(state_and_table,15),
table_update(state_and_table,16),
table_update(state_and_table,17),
table_update(state_and_table,18),
table_update(state_and_table,19),
table_update(state_and_table,20))
-- Key input
change(state_and_table, KeyPress("Up")) = 
(time_of(state_and_table),
h_of(state_and_table),
u_of(state_and_table)+0.1,
table_update(state_and_table,1),
table_update(state_and_table,2),
table_update(state_and_table,3),
table_update(state_and_table,4),
table_update(state_and_table,5),
table_update(state_and_table,6),
table_update(state_and_table,7),
table_update(state_and_table,8),
table_update(state_and_table,9),
table_update(state_and_table,10),
table_update(state_and_table,11),
table_update(state_and_table,12),
table_update(state_and_table,13),
table_update(state_and_table,14),
table_update(state_and_table,15),
table_update(state_and_table,16),
table_update(state_and_table,17),
table_update(state_and_table,18),
table_update(state_and_table,19),
table_update(state_and_table,20))
change(state_and_table, KeyPress("Down")) = 
(time_of(state_and_table),
h_of(state_and_table),
u_of(state_and_table)-0.1,
table_update(state_and_table,1),
table_update(state_and_table,2),
table_update(state_and_table,3),
table_update(state_and_table,4),
table_update(state_and_table,5),
table_update(state_and_table,6),
table_update(state_and_table,7),
table_update(state_and_table,8),
table_update(state_and_table,9),
table_update(state_and_table,10),
table_update(state_and_table,11),
table_update(state_and_table,12),
table_update(state_and_table,13),
table_update(state_and_table,14),
table_update(state_and_table,15),
table_update(state_and_table,16),
table_update(state_and_table,17),
table_update(state_and_table,18),
table_update(state_and_table,19),
table_update(state_and_table,20))
-- Default equation
change(state_and_table, other) = state_and_table
table_update(state_and_table, k)
| time_of(state_and_table) > table_time # k && table_entry(state_and_table,k)==0 = h_of(state_and_table)
| otherwise = table_entry(state_and_table, k)
-- Animation of system
composedOf = pictures
tank_water(state_and_table) = composedOf [headline, pump(qin(u_of(state_and_table))),
tank, water(h_of(state_and_table)), 
graph(state_and_table),  
coordinatePlane]  
headline = translated(lettering("Tank dynamics"),5,7.5)
pump(qin) = translated(composedOf [lettering("- pump rate ="), translated(lettering(printed(qin)),3.7,0)], 4.5,6.5)
tank = translated(thickRectangle(width,height,0.2),position,4)
water(h) = translated(colored(solidPolygon [(-width/2, 0), (-width/2, h), (width/2, h), (width/2,0)], 
light(blue)), position,0)
-- Graph of evolution of h(t) - note time scale is 1 = 10 s etc
scale = 1/10
graph(state_and_table) = colored(polyline [(scale*table_time#k, table_entry(state_and_table, k)) 
| k <- [1..length(table_time)], table_entry(state_and_table, k) /=0], 
light(blue))