I made a solution "the cumbersome" way. Wish I could structure it better from the input I have got. Appreciate suggestions for improvements so that the code could be shorter. I feel clumsy with tuple-handling. Further creation of the table and later graph could be more separated from the tank-process. That would facilitate re-use to other simulation tasks in CodeWorld. Improvements welcome!
--
I have now updated the code and made it shorter but also added more data points in the table. The state_and_table must be a tuple in CodeWorld and handling of tuples not possible with an index.
Further, you can now change the pump rate qin with arrows up and down.
Expand to two tanks (or more) make it more of a challenge to control the water height and leave that as an exercise to you! Enjoy!
Link to run 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))