Physics: boiling water
Tags: physics, python, colab, moderngl
Published: 2024-01-23 16:33:39

## Idea
This simulation about thermodynamics. Basic boiling of water, I was interested in bubbles, how they "born" and then moves to the surface. A simple heating element and water:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzg9jqsaopehhr2xaaiw.jpg)
In result I have the Jupyter/Colab notebook and the animation:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvx2obvs95046fh7t7tl.gif)
And sure the code, because it made by moderngl, library for python.

---

I was mistaken, in first, by pressure... I thought Archimedes force in bubbles versus pressure(ye I only study physics). But it is was start and I wrote this algorithm:

> Algorithm, transformation cycle of molecule(group):
> 1. if molecule temperature > 100,then check neighbors, if near exist gas then connect, else randomly can transform to gas(change state and remove from the common array). if connected to someone - register the new transformation group and try to reorginize to a big cube shape 
> 2. check registered groups the archimed force(1), if the archimed force more than pressure(!my error) of water then this group now in the transportation state(moving up) 
> 3. check groups which in the transportation state: if not on the surface then move up, else(on surface) check can be free or not(2). can be free if pressure inside the group more than surface tension force. if near other groups - connect(merge)

> Algorithm for all molecules(water):
> 1.Fill holes(shift down if bottom now the gas and not in the common array)
> 2.Try to make the water surface flat
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0hvh74z2ztemrovoxs73.png)

But I dropped the part when count surface tension on surface.
In this algorithm I found an interesting "philosophical" interpretation - bubbles trying to connect to each other after transformation to gas on the bottom, and then together going up to the surface... Also interesting is constants about size and mass of molecules - because with some parameters it works but with some not, but sure they still near to which I found in tables and the internet.

## Code

- https://colab.research.google.com/drive/1-MyJUJe_BF76JA2P1mZTywSaLCJFTT1j?usp=sharing
- https://github.com/fakelaboratory/publish/blob/main/Boiling_water_simulation.ipynb

Possible to split for four parts:
- Import libraries
```
!pip install moderngl
!pip install pyrr
!pip install moviepy
import moderngl as gl
import numpy as np
import random
import pyrr as pyrr
from PIL import Image
import moviepy.editor as mpy
from google.colab import files
```
ModernGL library for GL, pyrr for matrix transformations, moviepy and PIL - for animation creating. Also in the full code you can see creating context in this part, because if something failed then context will not creates.

- Shader and vertices
```
ctx.enable(gl.BLEND)
prog = ctx.program(
    vertex_shader="""
        #version 330
        in vec4 in_vert;
        void main() {
            gl_Position = in_vert;
        }
    """,
    fragment_shader="""
        #version 330
        uniform vec4 u_color;
        out vec4 f_color;
        void main() {
            f_color = u_color;
        }
    """,
)
vertices = np.asarray([
    [-0.5 , -0.5 , -0.5, 1.0] ,
    [ 0.5 , -0.5 , -0.5, 1.0] ,
    [ 0.5 ,  0.5 , -0.5, 1.0] ,
    [ 0.5 ,  0.5 , -0.5, 1.0] ,
    [-0.5 ,  0.5 , -0.5, 1.0] ,
    [-0.5 , -0.5 , -0.5, 1.0] ,

    [-0.5 , -0.5 ,  0.5, 1.0] ,
    [ 0.5 , -0.5 ,  0.5, 1.0] ,
    [ 0.5 ,  0.5 ,  0.5, 1.0] ,
    [ 0.5 ,  0.5 ,  0.5, 1.0] ,
    [-0.5 ,  0.5 ,  0.5, 1.0] ,
    [-0.5 , -0.5 ,  0.5, 1.0] ,

    [-0.5 ,  0.5 ,  0.5, 1.0] ,
    [-0.5 ,  0.5 , -0.5, 1.0] ,
    [-0.5 , -0.5 , -0.5, 1.0] ,
    [-0.5 , -0.5 , -0.5, 1.0] ,
    [-0.5 , -0.5 ,  0.5, 1.0] ,
    [-0.5 ,  0.5 ,  0.5, 1.0] ,

    [ 0.5 ,  0.5 ,  0.5, 1.0] ,
    [ 0.5 ,  0.5 , -0.5, 1.0] ,
    [ 0.5 , -0.5 , -0.5, 1.0] ,
    [ 0.5 , -0.5 , -0.5, 1.0] ,
    [ 0.5 , -0.5 ,  0.5, 1.0] ,
    [ 0.5 ,  0.5 ,  0.5, 1.0] ,

    [-0.5 , -0.5 , -0.5, 1.0] ,
    [ 0.5 , -0.5 , -0.5, 1.0] ,
    [ 0.5 , -0.5 ,  0.5, 1.0] ,
    [ 0.5 , -0.5 ,  0.5, 1.0] ,
    [-0.5 , -0.5 ,  0.5, 1.0] ,
    [-0.5 , -0.5 , -0.5, 1.0] ,

    [-0.5 ,  0.5 , -0.5, 1.0] ,
    [ 0.5 ,  0.5 , -0.5, 1.0] ,
    [ 0.5 ,  0.5 ,  0.5, 1.0] ,
    [ 0.5 ,  0.5 ,  0.5, 1.0] ,
    [-0.5 ,  0.5 ,  0.5, 1.0] ,
    [-0.5 ,  0.5 , -0.5, 1.0]
],dtype='f4')
```
Here basic shader, with the uniform color and vertices for cube. All elements in simulation is cube, and this is the original cube. Also:
```
def transform(v,type,args):
  if(type=="rotation"):
    mat = pyrr.matrix44.create_from_axis_rotation([args[0],args[1],args[2]],args[3])
  if(type=="scale"):
    mat = pyrr.matrix44.create_from_scale([args[0],args[1],args[2]])
  if(type=="translation"):
    mat = pyrr.matrix44.create_from_translation([args[0],args[1],args[2]])
  for i in range(len(v)):
    v[i]= pyrr.matrix44.apply_to_vector(mat, v[i])
```
It is basic transformation, they are used while all code.

- Main logic

This is most complex part, but in general two classes: _Molecule_ and _Water_. Molecule is basic "object" with position, size, and state. State 0 it is water, the state 1 is gas. It is needed in render and in logic.

Water contains all molecules and also _group_transform_, _group_transport_. When molecules change own state and connected with neighbors gas molecules - they are group.
```
#in general if both molecules not in groups, create group
#if first in group but second not then connect second
#if in different groups, merge groups and delete second group
#with adding element to group_transform arrays, with id's of groups
```

Also function _reorganize_, which contain physics calcs and logic with moving from group to group, moving in group. When group in transportation phase, then it is big molecule, and acceleration is force/mass.

- Render

Here just render by creating VAO,VBO for molecules, few colors. And in each frame calling the reorganize function. Also after render - export to GIF.

---

## End
When I tried to count 100000 molecules, RAM(13gb) was crashed. What I think about it? I still code without optimization with idea to be scalable, for more RAM and for more logic, because for more interesting interactions will need a data about each molecules. I mean first what in the head: it is not render all molecules, but only which really need, create illusion, but it is not what I trying to achieve. 
Achieved idea of recursive process, in all scales of the water object the logic will works. Maybe needed to add more layers for the transformation layer, then will heats not only one level of molecules, but millimeter of molecules for example. It'll give big bubbles, because now it in molecular level. 
Big bubbles is big groups.