Difference between revisions of "Dynamics of interacting particles"

From Department of Theoretical and Applied Mechanics
Jump to: navigation, search
(The proposed directions of the development)
m
Line 2: Line 2:
  
 
Here is the latest version of the program, which simulates the dynamics of interacting particles.
 
Here is the latest version of the program, which simulates the dynamics of interacting particles.
Each particle is a viscoelastic sphere. The interaction between the spheres is described by [[:ru:Потенциал_Леннард-Джонса|Lennard-Jones potential]]
+
Each particle is a viscoelastic sphere. The interaction between the spheres is described by the [[:ru:Потенциал_Леннард-Джонса|Lennard-Jones potential]]
  
 
<htmlet nocache="yes">Tcvetkov/Balls/Balls_v6_release/Balls_v6_TM</htmlet>
 
<htmlet nocache="yes">Tcvetkov/Balls/Balls_v6_release/Balls_v6_TM</htmlet>
Line 9: Line 9:
 
<div class="mw-collapsible mw-collapsed">
 
<div class="mw-collapsible mw-collapsed">
 
'''The text of the program is written in JavaScript (developed by [[Anton Krivtsov]], [[Tsvetkov Denis]]):''' <div class="mw-collapsible-content">  
 
'''The text of the program is written in JavaScript (developed by [[Anton Krivtsov]], [[Tsvetkov Denis]]):''' <div class="mw-collapsible-content">  
Файл '''"Balls_v6_release.js"'''
+
File '''"Balls_v6_release.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
function MainBalls(canvas, slider_01, text_01, slider_02, text_02) {
 
function MainBalls(canvas, slider_01, text_01, slider_02, text_02) {

Revision as of 02:22, 9 June 2016

Virtual laboratory > Dynamics of interacting particles

Here is the latest version of the program, which simulates the dynamics of interacting particles. Each particle is a viscoelastic sphere. The interaction between the spheres is described by the Lennard-Jones potential


Гравитация: mg = ⋅ m ⋅ g0

Сколько шаров помещается по вертикали:  

Конфигурация:

Short Lennard-Jones potential

Термостат: T ⋅ T0 =
Разгон случайными скоростями
Термостат действует на: Внешнее трение Внутреннее трение
T ≈

Количество частиц:

Download program: Balls_v6_release.zip

The text of the program is written in JavaScript (developed by Anton Krivtsov, Tsvetkov Denis):

File "Balls_v6_release.js"

  1 function MainBalls(canvas, slider_01, text_01, slider_02, text_02) {
  2 
  3     canvas.onselectstart = function () {return false;};     // lock allocation canvas
  4 
  5     // Starting parameters
  6 
  7     var context = canvas.getContext("2d");                  // context for drawing
  8     canvas.oncontextmenu = function (e) {return false;};    // lock context menu
  9 
 10     var Pi = 3.1415926;                 // "pi"
 11 
 12     var m0 = 1;                         // weight
 13     var t0 = 1;                         // Time (period of fluctuation)
 14     var a0 = 1;                         // length (sphere's diameter)
 15 
 16     var g0 = a0 / t0 / t0;              // acceleration
 17     var k0 = 2 * Pi / t0;               // frequency
 18     var C0 = m0 * k0 * k0;              // rigidity
 19     var B0 = 2 * m0 * k0;               // viscosity
 20 
 21     // *** Physical parameters  ***
 22 
 23     var Ny = 5;                         // The number of the spheres which are placed vertically
 24     var m = 1 * m0;                     // weight
 25     var CWall = 10 * C0;                // wall's rigidity
 26     var CBall = 0.1 * CWall;            // rigidity between spheres
 27     var BVisc = 0.008 * B0;             // viscosity of the environment
 28     var BInternal = 0.01 * B0;          // internal friction
 29     var BWall = 0.03 * B0;              // viscosity of the walls
 30     var mg = 0.25 * m * g0;             // gravitational force
 31     var r = 0.5 * a0;                   // radius of the sphere
 32     var K = 0.7;                        // forces depending on radius
 33     var a = 2 * r;                      // equilibrium state between particles
 34     var aCut = 2 * a;                   // cutting radius
 35     var TGoalK = 2;                     // temperature of the system TGoalK * D
 36     var TActualMaxK = 200;              // maximum temperature when thermostat work
 37 
 38     // *** Calculation parameters ***
 39 
 40     var fps = 60;                       // frames per second
 41     var spf = 100;                      // steps per frame
 42     var dt  = 0.04 * t0 / fps;          // integration step
 43 
 44     // Running program 
 45 
 46     var r2 = r * r;                     // optimizing
 47     var a2 = a * a;                     // optimizing
 48     var D = a2 * CBall / 72;            // bond's energy between spheres
 49     var LJCoeff = 12 * D / a2;          // coefficient for Lennard-Jones potential
 50     var b = Math.pow(13 / 7, 6) * a;    // coefficient for SLJ potential
 51     var b2 = b * b;                     // optimizing
 52     var SLJDenominator = 1 / (aCut * aCut - b2);    // denominator for calculation SLJ potential
 53 
 54     var thermostatEnabled = document.getElementById('checkbox_02').checked;     // thermostat for viscosity of the environment 
 55     var addRandomV = document.getElementById('checkbox_03').checked;            // random speed for acceleration of the spheres
 56     var T0 = 1 * D;                     // temperature
 57     var TGoal = TGoalK * T0;            // goal temperature 
 58     var TActualMax = TActualMaxK * T0;  // maximum temperature for thermostat
 59     var TActual = 0;                    // actual temperature 
 60     var k = 1;                          // Boltzmann constant
 61     var Tk = m / k;                     // optimizing
 62     var viscFrictionTh = document.getElementById('checkbox_04').checked;        // application thermostat for the viscosity environment
 63     var internalFrictionTh = document.getElementById('checkbox_05').checked;    // application thermostat for the internal friction
 64 
 65     var Ka = K * a;                     // optimizing
 66     var K2a2 = K * K * a2;              // optimizing
 67 
 68     var dNd = null;                     // taken sphere by cursor (drag & drop)
 69     var grad;                           // gradient
 70     var SLJEnabled = document.getElementById('checkbox_01').checked;
 71 
 72     this.setSlider_01 = function(c) {mg = c * m * g0;}; // gravitation function
 73     this.setSlider_02 = function(c) {TGoal = c;};       // thermostat function
 74     this.setNy = function(ny) {
 75         Ny = ny;
 76         if (Ny > 8) {
 77             grad = false;                   // gradient doesn't work if Ny > 8
 78             context.fillStyle = "#3070d0";  // sphere's colour
 79         } else
 80             grad = true;
 81     };
 82     this.setNy(Ny);                         // start
 83     this.setCheckbox_01 = function(bool) {SLJEnabled = bool;};
 84     this.setCheckbox_02 = function(bool) {
 85         thermostatEnabled = bool;
 86         document.getElementById('checkbox_03').disabled = !bool;
 87         document.getElementById('checkbox_04').disabled = !bool;
 88         document.getElementById('checkbox_05').disabled = !bool;
 89         document.getElementById('slider_02').disabled = !bool;
 90         document.getElementById('text_02').disabled = !bool;
 91         if (bool) {
 92             TempIntervalID = setInterval(   // update informtion about temperature
 93                 function(){document.getElementById('Temperature').innerHTML = TActual.toFixed(3);}, 1000 / 3);
 94         }
 95         else {
 96             clearInterval(TempIntervalID);  // delete updating informtion about temperature
 97             document.getElementById('Temperature').innerHTML = "???"
 98         }
 99     };
100     this.setCheckbox_02(thermostatEnabled); // start for updating parameters
101     this.setCheckbox_03 = function(bool) {addRandomV = bool;};
102     this.setCheckbox_04 = function(bool) {viscFrictionTh = bool;};
103     this.setCheckbox_05 = function(bool) {internalFrictionTh = bool;};
104 
105     // Setup of the interface
106 
107     slider_01.min = 0;               slider_01.max = 5;
108     slider_01.step = 0.05;
109     slider_01.value = mg / m / g0;          // the first position of the slider
110     text_01.value = mg / m / g0;
111     slider_02.min = 0;               slider_02.max = 5;
112     slider_02.step = 0.05;
113     slider_02.value = TGoal;                // the first position of the slider
114     text_02.value = TGoal.toFixed(1);
115 
116     // Start of the new system
117 
118     // the following variables have to be recalculated every time when we change value Ny
119     var scale, w, h;
120     var rScale13, rScaleShift;
121     this.newSystem = function() {
122         scale = canvas.height / Ny / a0;    // coefficient for transition from settlement to screen coordinates
123         w = canvas.width / scale;           // window width in settlement coordinates
124         h = canvas.height / scale;          // window height in settlement coordinates
125 
126         rScale13 = r * scale * 1.3;         // optimizing
127         rScaleShift = r * scale / 5;        // optimizing
128 
129         this.setRandom();                   // set random configuration
130     };
131 
132     // Работа с мышью
133 
134     var mx_, my_;                               // cursor position
135 
136     canvas.onmousedown = function(e) {          // function for press on the cursor
137         var m = mouseCoords(e);                 // coordinates for calculation of the cursor position
138         //return cycle
139         for (var i = balls.length - 1; i >= 0; i--) {
140             var b = balls[i];
141             var rx = b.x - m.x;
142             var ry = b.y - m.y;
143             var rLen2 = rx * rx + ry * ry;              // length between cursor and sphere
144             if (rLen2 <= r2) {                          // the cursor has pressed on a sphere
145                 if (e.which == 1) {                     // mouse's left button has pressed
146                     dNd = b;
147                     dNd.xPlus = dNd.x - m.x;            // shift of the cursor on X
148                     dNd.yPlus = dNd.y - m.y;            // shift of the cursor on Y
149                     mx_ = m.x;    my_ = m.y;
150                     canvas.onmousemove = mouseMove;     // shift function work while button press
151                 } else if (e.which == 3)                // mouse's right button has pressed
152                     balls.splice(i, 1);                 // delete sphere
153                 return;
154             }
155         }
156 
157         // if don't exit - add new sphere
158         if (e.which == 1) {
159             dNd = addNewBall(m.x, m.y, true);   // add sphere and take it
160             if (dNd == null) return;            // return if the ball doesn't add
161             dNd.xPlus = 0;  dNd.yPlus = 0;      // set ball in center position
162             mx_ = m.x;    my_ = m.y;
163             canvas.onmousemove = mouseMove;     // shift function works while mouse's button has pressed
164         }
165     };
166 
167     document.onmouseup = function(e) {          // function when mouse's button let off
168         canvas.onmousemove = null;              // shift function equal null when mouse's button let of
169         dNd = null;                             // ball doesn't exist when mouse's button let of
170     };
171 
172     function mouseMove(e) {                     // shift function when left mouse's button pressed
173         var m = mouseCoords(e);                 // coordinates for calculation
174         dNd.x = m.x + dNd.xPlus;
175         dNd.y = m.y + dNd.yPlus;
176         dNd.vx = 0.6 * (m.x - mx_) / dt / fps;   dNd.vy = 0.6 * (m.y - my_) / dt / fps;
177         mx_ = m.x;    my_ = m.y;
178     }
179 
180     function mouseCoords(e) {                   // function returns settlement coordinates of the cursor of a mouse
181         var m = [];
182         var rect = canvas.getBoundingClientRect();
183         m.x = (e.clientX - rect.left) / scale;
184         m.y = (e.clientY - rect.top) / scale;
185         return m;
186     }
187 
188     // Работа с массивом
189 
190     var balls = [];                             // sphere's massive
191     var addNewBall =  function(x, y, check) {
192         // check - whether is crossed a new sphere with walls or already existing spheres
193         if (check) {
194             if (x - r < 0 || x + r > w || y - r < 0 || y + r > h) return null;
195             for (var i = 0; i < balls.length; i++) {
196                 var rx = balls[i].x - x;
197                 var ry = balls[i].y - y;
198                 var rLen2 = rx * rx + ry * ry;
199                 if (rLen2 < 4 * r2) return null;
200             }
201         }
202 
203         var b = [];
204 
205         b.x = x;                b.y = y;        // calculation cordinates
206         b.fx = 0;               b.fy = mg;      // force
207         b.vx = 0;               b.vy = 0;       // velocity
208 
209         balls[balls.length] = b;                // add an element in end of massive
210         return b;
211     };
212 
213     this.setEmpty = function() {balls = [];};   // free area
214 
215     this.setRandom = function() {               // random configuration
216         balls = [];
217         for (var i = 0; i < 1000; i++)
218             addNewBall(Math.random() * w, Math.random() * h, true);
219     };
220 
221     var sqrt3 = Math.sqrt(3);
222     this.setTriangularLattice = function() {            // triangle lattice
223         balls = [];
224         var center = (w - Math.floor(w / r) * r) / 2;   // shift
225         for (var j = 0; j < Math.floor(h / (sqrt3 * r)); j++)
226             for (var i = 0; i < Math.floor(w / r) - 1; i++)
227                 if ((i + j) % 2 == 0) addNewBall(r * (i + 1) + center, h - r * (1 + sqrt3 * j), false);
228     };
229 
230     // program's cycle
231 
232     function control() {
233         physics();
234         draw();
235     }
236 
237     // calculation part
238 
239     function physics() {                            
240         for (var s = 1; s <= spf; s++) {
241 
242             var BViscTh = BVisc;
243             var BInternalTh = BInternal;
244             // работа термостата
245             if (thermostatEnabled) {
246                 if (balls.length > 0) {
247                     var v2Sum = 0;
248                     for (var i1 = 0; i1 < balls.length; i1++)
249                         v2Sum += balls[i1].vx * balls[i1].vx + balls[i1].vy * balls[i1].vy;
250                     var v2Average = v2Sum / balls.length;
251                     TActual = Tk * v2Average;
252 
253                     if (addRandomV) {               // random velocities if the temperature is low
254                         if (TGoal > 0.15 && TActual < 0.1) {
255                             for (var i2 = 0; i2 < balls.length; i2++) {
256                                 balls[i2].vx += 0.3 * (1 - 2 * Math.random());
257                                 balls[i2].vy += 0.3 * (1 - 2 * Math.random());
258                             }
259                         }
260                     }
261 
262                     if (TActual < TActualMax) {         //because of the fact that a mouse spheres can set ultraboundary speed
263                         if (viscFrictionTh) BViscTh = BVisc * (TActual - TGoal);                // thermostat
264                         if (internalFrictionTh) BInternalTh = BInternal * (TActual - TGoal);    // thermostat
265                     }
266                 } else
267                     TActual = 0;                        // for thermostat on page
268             }
269 
270             //recalculation of forces goes the certain massif since forces of interaction between spheres will be added further
271             for (var i0 = 0; i0 < balls.length; i0++) {
272                 balls[i0].fx = - BViscTh * balls[i0].vx;
273                 balls[i0].fy = mg - BViscTh * balls[i0].vy;
274             }
275 
276             for (var i = 0; i < balls.length; i++) {
277                 // calculation of interaction is made with all following spheres in the massif not to consider each interaction twice
278                 var b = balls[i];
279                 for (var j = i + 1; j < balls.length; j++) {
280                     var b2 = balls[j];
281                     var rx = b.x - b2.x;   var ry = b.y - b2.y;         // vector on the first ball (b)
282                     var r2 = rx * rx + ry * ry;                         // length between spheres
283                     var rLen = (Math.sqrt(r2));
284                     if (rLen > aCut) continue;                          // check for cutting radius
285 
286                     // if the distance between particles isn't enough, forces will be counted like K * a
287                     if (rLen < Ka) {
288                         if (rLen > 0.00001) {                           // check
289                             rx = rx / rLen * Ka;
290                             ry = ry / rLen * Ka;
291                         }
292                         r2 = K2a2;
293                         rLen = Ka;                                 
294                     }
295 
296                     // сила взаимодействия
297                     var s2 = a2 / r2;         var s4 = s2 * s2;         // optimizing
298                     var F = LJCoeff * s4 * s4 * (s4 * s2 - 1);          // LJ interaction force
299                     if (SLJEnabled) {
300                         var kSLJ;                                           // k(r) - coefficient SLJ potential
301                         if (r <= b) kSLJ = 1;
302                         else {
303                             var brackets = (r2 - b2) * SLJDenominator;
304                             kSLJ = 1 - brackets * brackets;
305                         }                                                  
306                         F *= kSLJ;
307                     }
308 
309                     // internal friction between spheres
310                     if (r2 < a2) {
311                         var vx21 = b.vx - b2.vx;    var vy21 = b.vy - b2.vy;    // vector on the first ball (b)
312                         var ex = rx / rLen;         var ey = ry / rLen;
313                         var v = vx21 * ex + vy21 * ey;
314                         F -= F * BInternalTh / rLen * v;
315                     }
316 
317                     // summirize forces
318                     var Fx = F * rx;        var Fy = F * ry;
319 
320                     b.fx += Fx;             b.fy += Fy;
321                     b2.fx -= Fx;            b2.fy -= Fy;
322                 }
323 
324                 if (b == dNd) continue;  // if the sphere is grabbed with the cursor - his interaction with walls and we don't consider movement
325 
326                 if (b.y + r > h) { b.fy += -CWall * (b.y + r - h) - BWall * b.vy; }
327                 if (b.y - r < 0) { b.fy += -CWall * (b.y - r) - BWall * b.vy;}
328                 if (b.x + r > w) { b.fx += -CWall * (b.x + r - w) - BWall * b.vx; }
329                 if (b.x - r < 0) { b.fx += -CWall * (b.x - r) - BWall * b.vx; }
330 
331                 b.vx += b.fx / m * dt;        b.vy += b.fy / m * dt;
332                 b.x += b.vx * dt;             b.y += b.vy * dt;
333             }
334         }
335     }
336 
337     // drawing
338     function draw() {
339         context.clearRect(0, 0, w * scale, h * scale);      // clear
340         for (var i = 0; i < balls.length; i++){
341             var xS = balls[i].x * scale;           var yS = balls[i].y * scale;
342             if (grad) {
343                 // calculation of a gradient needs to be carried out for each sphere
344                 var gradient = context.createRadialGradient(xS, yS, rScale13, xS - rScaleShift, yS + rScaleShift, 0);
345                 gradient.addColorStop(0, "#0000bb");
346                 gradient.addColorStop(1, "#44ddff");
347                 context.fillStyle = gradient;
348             }
349 
350             context.beginPath();
351             context.arc(xS, yS, r * scale, 0, 2 * Math.PI, false);
352             context.closePath();
353             context.fill();
354         }
355     }
356 
357     // Start
358     this.newSystem();
359     setInterval(control, 1000 / fps);
360     // function updates information on quantity of particles in the field
361     setInterval(function(){document.getElementById('ballsNum').innerHTML = balls.length;}, 1000 / 20);
362 }

Файл "Balls_v6_release.html"

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8" />
 5     <title>Balls</title>
 6     <script src="Balls_v6_release.js"></script>
 7 </head>
 8 <body>
 9     <canvas id="canvasBalls" width="800" height="600" style="border:1px solid #000000;"></canvas>
10     <br>
11     <div>Gravitation: 
12         <input type="range" id="slider_01" style="width: 150px;" oninput="app.setSlider_01(this.value); document.getElementById('text_01').value = this.value;">
13         mg =
14         <input id="text_01" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
15             // if not the number is entered - the line won't pass validation on a pattern above, and checkValidity () will return false
16             if (!this.checkValidity()) return;
17             app.setSlider_01(this.value);
18             document.getElementById('slider_01').value = this.value;
19         ">
20      ⋅ m ⋅ g0</div><br>
21 
22     <div>The number of the spheres which are placed vertically: 
23         <input type="button" style="width: 30px" name="" onclick="app.setNy(3); app.newSystem();return false;" value="3"/>
24         <input type="button" style="width: 30px" name="" onclick="app.setNy(4); app.newSystem();return false;" value="4"/>
25         <input type="button" style="width: 30px" name="" onclick="app.setNy(5); app.newSystem();return false;" value="5"/>
26         <input type="button" style="width: 30px" name="" onclick="app.setNy(7); app.newSystem();return false;" value="7"/>
27         &nbsp;  <!--знак пробела-->
28         <input type="button" style="width: 30px" name="" onclick="app.setNy(9); app.newSystem();return false;" value="9"/>
29         <input type="button" style="width: 30px" name="" onclick="app.setNy(12); app.newSystem();return false;" value="12"/>
30         <input type="button" style="width: 30px" name="" onclick="app.setNy(15); app.newSystem();return false;" value="15"/>
31     </div><br>
32 
33     <div>Configuration:
34         <input type="button" name="" onclick="app.setTriangularLattice(); return false;" value="Triangular lattice"/>
35         <input type="button" name="" onclick="app.setRandom(); return false;" value="Systemless"/>
36         <input type="button" name="" onclick="app.setEmpty(); return false;" value="A blank field "/>
37     </div><br>
38 
39     <div>
40         <input type="checkbox" id="checkbox_01" name="" onchange="app.setCheckbox_01(this.checked);"/>
41         <a href="/SLJ" title="SLJ" class="mw-redirect">Short Lennard-Jones</a> potential
42     </div><br>
43 
44     <div>
45         <input type="checkbox" id="checkbox_02" name="" onchange="app.setCheckbox_02(this.checked);"/>
46         Thermostat:  
47         <input type="range" id="slider_02" style="width: 150px;" oninput="app.setSlider_02(this.value); document.getElementById('text_02').value = this.value;">
48         T ⋅ T0 = <input id="text_02" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
49             // if not the number is entered - the line won't pass validation on a pattern above, and checkValidity () will return false
50             if (!this.checkValidity()) return;
51             app.setSlider_02(this.value);
52             document.getElementById('slider_02').value = this.value;
53         ">
54         <br>
55         <input type="checkbox" checked id="checkbox_03" name="" onchange="app.setCheckbox_03(this.checked);"/>Acceleration is achieved by random rates 
56         <br>
57         The thermostat acts on: 
58         <input type="checkbox" checked id="checkbox_04" name="" onchange="app.setCheckbox_04(this.checked);"/>External friction
59         <input type="checkbox" checked id="checkbox_05" name="" onchange="app.setCheckbox_05(this.checked);"/>Internal friction
60         <div>T ≈ <span id="Temperature"></span></div>
61     </div><br>
62 
63     <div>Number of the particles: <span id="ballsNum"></span></div>
64 
65     <script type="text/javascript">var app = new MainBalls(
66             document.getElementById('canvasBalls'),
67             document.getElementById('slider_01'),
68             document.getElementById('text_01'),
69             document.getElementById('slider_02'),
70             document.getElementById('text_02')
71     );</script>
72 </body>
73 </html>

Here you can find the previous versions of the program.

The proposed development directions

  • Add temperature regulation by Hoover‘s thermostat.
  • The program's evaluation speed in order to optimize it.
  • Apply the copying principle.
  • Add different potentials.
  • Add the ability to set viscoelastic walls anywhere.
  • Add the periodic boundary conditions.