Difference between revisions of "Dynamics of interacting particles"

From Department of Theoretical and Applied Mechanics
Jump to: navigation, search
 
(10 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 +
[[ru:Динамика взаимодействующих частиц]]
 
[[Virtual laboratory]] > [[Dynamics of interacting particles]] <HR>
 
[[Virtual laboratory]] > [[Dynamics of interacting particles]] <HR>
  
 
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 [[Потенциал_Леннард-Джонса|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>
  
Download program: [[Медиа:Balls_v6_release.zip|Balls_v6_release.zip]]
+
Download program: [[Media:Balls_v6_release.zip|Balls_v6_release.zip]]
 
<div class="mw-collapsible mw-collapsed">
 
<div class="mw-collapsible mw-collapsed">
'''The text of the program is written in JavaScript (the developers are [[Кривцов Антон]], [[Цветков Денис]]):''' <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) {
  
     canvas.onselectstart = function () {return false;};    // запрет выделения canvas
+
     canvas.onselectstart = function () {return false;};    // lock allocation canvas
  
     // Предварительные установки
+
     // Starting parameters
  
     var context = canvas.getContext("2d");                  // на context происходит рисование
+
     var context = canvas.getContext("2d");                  // context for drawing
     canvas.oncontextmenu = function (e) {return false;};    // блокировка контекстного меню
+
     canvas.oncontextmenu = function (e) {return false;};    // lock context menu
  
     var Pi = 3.1415926;                // число "пи"
+
     var Pi = 3.1415926;                // "pi"
  
     var m0 = 1;                        // масштаб массы
+
     var m0 = 1;                        // weight
     var t0 = 1;                        // масштаб времени (период колебаний исходной системы)
+
     var t0 = 1;                        // Time (period of fluctuation)
     var a0 = 1;                        // масштаб расстояния (диаметр шара)
+
     var a0 = 1;                        // length (sphere's diameter)
  
     var g0 = a0 / t0 / t0;              // масштаб ускорения (ускорение, при котором за t0 будет пройдено расстояние a0)
+
     var g0 = a0 / t0 / t0;              // acceleration
     var k0 = 2 * Pi / t0;              // масштаб частоты
+
     var k0 = 2 * Pi / t0;              // frequency
     var C0 = m0 * k0 * k0;              // масштаб жесткости
+
     var C0 = m0 * k0 * k0;              // rigidity
     var B0 = 2 * m0 * k0;              // масштаб вязкости
+
     var B0 = 2 * m0 * k0;              // viscosity
  
     // *** Задание физических параметров ***
+
     // *** Physical parameters  ***
  
     var Ny = 5;                        // число шаров, помещающихся по вертикали в окно (задает размер шара относительно размера окна)
+
     var Ny = 5;                        // The number of the spheres which are placed vertically
     var m = 1 * m0;                    // масса
+
     var m = 1 * m0;                    // weight
     var CWall = 10 * C0;                // жесткость стен
+
     var CWall = 10 * C0;                // wall's rigidity
     var CBall = 0.1 * CWall;            // жесткость между частицами
+
     var CBall = 0.1 * CWall;            // rigidity between spheres
     var BVisc = 0.008 * B0;            // вязкость среды
+
     var BVisc = 0.008 * B0;            // viscosity of the environment
     var BInternal = 0.01 * B0;          // внутреннее трение
+
     var BInternal = 0.01 * B0;          // internal friction
     var BWall = 0.03 * B0;              // вязкость на стенках
+
     var BWall = 0.03 * B0;              // viscosity of the walls
     var mg = 0.25 * m * g0;            // сила тяжести
+
     var mg = 0.25 * m * g0;            // gravitational force
     var r = 0.5 * a0;                  // радиус частицы в расчетных координатах
+
     var r = 0.5 * a0;                  // radius of the sphere
     var K = 0.7;                        // все силы, зависящие от радиуса, ограничиваются значением, реализующимся при r/a = K
+
     var K = 0.7;                        // forces depending on radius
     var a = 2 * r;                      // равновесное расстояние между частицами
+
     var a = 2 * r;                      // equilibrium state between particles
     var aCut = 2 * a;                  // радиус обрезания
+
     var aCut = 2 * a;                  // cutting radius
     var TGoalK = 2;                    // целевая температура системы равна TGoalK * D
+
     var TGoalK = 2;                    // temperature of the system TGoalK * D
     var TActualMaxK = 200;              // макимальная температура, при которой работает термостат равна TActualMaxK * D
+
     var TActualMaxK = 200;              // maximum temperature when thermostat work
  
     // *** Задание вычислительных параметров ***
+
     // *** Calculation parameters ***
  
     var fps = 60;                      // frames per second - число кадров в секунду (качечтво отображения)
+
     var fps = 60;                      // frames per second
     var spf = 100;                      // steps per frame   - число шагов интегрирования между кадрами (скорость расчета)
+
     var spf = 100;                      // steps per frame
     var dt  = 0.04 * t0 / fps;          // шаг интегрирования (качество расчета)
+
     var dt  = 0.04 * t0 / fps;          // integration step
  
     // Выполнение программы
+
     // Running program
  
     var r2 = r * r;                    // ___в целях оптимизации___
+
     var r2 = r * r;                    // optimizing
     var a2 = a * a;                    // ___в целях оптимизации___
+
     var a2 = a * a;                    // optimizing
     var D = a2 * CBall / 72;            // энергия связи между частицами
+
     var D = a2 * CBall / 72;            // bond's energy between spheres
     var LJCoeff = 12 * D / a2;          // коэффициент для расчета потенциала Л-Дж
+
     var LJCoeff = 12 * D / a2;          // coefficient for Lennard-Jones potential
     var b = Math.pow(13 / 7, 6) * a;    // коэффициент для SLJ потенциала
+
     var b = Math.pow(13 / 7, 6) * a;    // coefficient for SLJ potential
     var b2 = b * b;                    // ___в целях оптимизации___
+
     var b2 = b * b;                    // optimizing
     var SLJDenominator = 1 / (aCut * aCut - b2);    // знаменатель для расчета SLJ потенциала
+
     var SLJDenominator = 1 / (aCut * aCut - b2);    // denominator for calculation SLJ potential
  
     var thermostatEnabled = document.getElementById('checkbox_02').checked;    // термостат применяется к вязкости среды
+
     var thermostatEnabled = document.getElementById('checkbox_02').checked;    // thermostat for viscosity of the environment
     var addRandomV = document.getElementById('checkbox_03').checked;            // случайные скорости для разгона
+
     var addRandomV = document.getElementById('checkbox_03').checked;            // random speed for acceleration of the spheres
     var T0 = 1 * D;                    // масштаб температуры
+
     var T0 = 1 * D;                    // temperature
     var TGoal = TGoalK * T0;            // целевая температура системы
+
     var TGoal = TGoalK * T0;            // goal temperature
     var TActualMax = TActualMaxK * T0;  // макимальная температура, при которой работает термостатс (для избежания беск. скоростей)
+
     var TActualMax = TActualMaxK * T0;  // maximum temperature for thermostat
     var TActual = 0;                    // актуальная температура
+
     var TActual = 0;                    // actual temperature
     var k = 1;                          // постоянную Больцмана примем за единицу
+
     var k = 1;                          // Boltzmann constant
     var Tk = m / k;                    // ___в целях оптимизации___
+
     var Tk = m / k;                    // optimizing
     var viscFrictionTh = document.getElementById('checkbox_04').checked;        // термостат применяется к вязкости среды
+
     var viscFrictionTh = document.getElementById('checkbox_04').checked;        // application thermostat for the viscosity environment
     var internalFrictionTh = document.getElementById('checkbox_05').checked;    // термостат применяется к внутреннему трению
+
     var internalFrictionTh = document.getElementById('checkbox_05').checked;    // application thermostat for the internal friction
    var TempIntervalID;
 
  
     var Ka = K * a;                    // ___в целях оптимизации___
+
     var Ka = K * a;                    // optimizing
     var K2a2 = K * K * a2;              // ___в целях оптимизации___
+
     var K2a2 = K * K * a2;              // optimizing
  
     var dNd = null;                    // ссылка на захваченный курсором шар (drag & drop)
+
     var dNd = null;                    // taken sphere by cursor (drag & drop)
     var grad;                          // должен ли работать градиент (регулируется в функции setNy())
+
     var grad;                          // gradient
 
     var SLJEnabled = document.getElementById('checkbox_01').checked;
 
     var SLJEnabled = document.getElementById('checkbox_01').checked;
  
     this.setSlider_01 = function(c) {mg = c * m * g0;}; // функция для слайдера гравитации
+
     this.setSlider_01 = function(c) {mg = c * m * g0;}; // gravitation function
     this.setSlider_02 = function(c) {TGoal = c;};      // функция для слайдера термостата
+
     this.setSlider_02 = function(c) {TGoal = c;};      // thermostat function
 
     this.setNy = function(ny) {
 
     this.setNy = function(ny) {
 
         Ny = ny;
 
         Ny = ny;
 
         if (Ny > 8) {
 
         if (Ny > 8) {
             grad = false;                  // градиент не работает, если Ny > 8
+
             grad = false;                  // gradient doesn't work if Ny > 8
             context.fillStyle = "#3070d0";  // цвет, шара
+
             context.fillStyle = "#3070d0";  // sphere's colour
 
         } else
 
         } else
 
             grad = true;
 
             grad = true;
 
     };
 
     };
     this.setNy(Ny);                        // запускаем с уже присвоенным значением, чтобы обновились настройки градиента
+
     this.setNy(Ny);                        // start
 
     this.setCheckbox_01 = function(bool) {SLJEnabled = bool;};
 
     this.setCheckbox_01 = function(bool) {SLJEnabled = bool;};
 
     this.setCheckbox_02 = function(bool) {
 
     this.setCheckbox_02 = function(bool) {
Line 103: Line 103:
 
         document.getElementById('text_02').disabled = !bool;
 
         document.getElementById('text_02').disabled = !bool;
 
         if (bool) {
 
         if (bool) {
             TempIntervalID = setInterval(  // обновление информации о температуре
+
             TempIntervalID = setInterval(  // update informtion about temperature
 
                 function(){document.getElementById('Temperature').innerHTML = TActual.toFixed(3);}, 1000 / 3);
 
                 function(){document.getElementById('Temperature').innerHTML = TActual.toFixed(3);}, 1000 / 3);
 
         }
 
         }
 
         else {
 
         else {
             clearInterval(TempIntervalID);  // температура больше не подсчитывается - удаляем обновление информации о ней
+
             clearInterval(TempIntervalID);  // delete updating informtion about temperature
 
             document.getElementById('Temperature').innerHTML = "???"
 
             document.getElementById('Temperature').innerHTML = "???"
 
         }
 
         }
 
     };
 
     };
     this.setCheckbox_02(thermostatEnabled); // запускаем сразу, чтобы обновить состояния элементов интерфейса
+
     this.setCheckbox_02(thermostatEnabled); // start for updating parameters
 
     this.setCheckbox_03 = function(bool) {addRandomV = bool;};
 
     this.setCheckbox_03 = function(bool) {addRandomV = bool;};
 
     this.setCheckbox_04 = function(bool) {viscFrictionTh = bool;};
 
     this.setCheckbox_04 = function(bool) {viscFrictionTh = bool;};
 
     this.setCheckbox_05 = function(bool) {internalFrictionTh = bool;};
 
     this.setCheckbox_05 = function(bool) {internalFrictionTh = bool;};
  
     // Настройка интерфейса
+
     // Setup of the interface
  
 
     slider_01.min = 0;              slider_01.max = 5;
 
     slider_01.min = 0;              slider_01.max = 5;
 
     slider_01.step = 0.05;
 
     slider_01.step = 0.05;
     slider_01.value = mg / m / g0;          // начальное значение ползунка должно задаваться после min и max
+
     slider_01.value = mg / m / g0;          // the first position of the slider
 
     text_01.value = mg / m / g0;
 
     text_01.value = mg / m / g0;
 
     slider_02.min = 0;              slider_02.max = 5;
 
     slider_02.min = 0;              slider_02.max = 5;
 
     slider_02.step = 0.05;
 
     slider_02.step = 0.05;
     slider_02.value = TGoal;                // начальное значение ползунка должно задаваться после min и max
+
     slider_02.value = TGoal;                // the first position of the slider
 
     text_02.value = TGoal.toFixed(1);
 
     text_02.value = TGoal.toFixed(1);
  
     // Запуск новой системы
+
     // Start of the new system
  
     // следующие переменные должны пересчитываться каждый раз, когда мы изменяем значение Ny
+
     // the following variables have to be recalculated every time when we change value Ny
 
     var scale, w, h;
 
     var scale, w, h;
 
     var rScale13, rScaleShift;
 
     var rScale13, rScaleShift;
 
     this.newSystem = function() {
 
     this.newSystem = function() {
         scale = canvas.height / Ny / a0;    // масштабный коэффициент для перехода от расчетных к экранным координатам
+
         scale = canvas.height / Ny / a0;    // coefficient for transition from settlement to screen coordinates
         w = canvas.width / scale;          // ширина окна в расчетных координатах
+
         w = canvas.width / scale;          // window width in settlement coordinates
         h = canvas.height / scale;          // высота окна в расчетных координатах
+
         h = canvas.height / scale;          // window height in settlement coordinates
  
         rScale13 = r * scale * 1.3;        // ___в целях оптимизации___
+
         rScale13 = r * scale * 1.3;        // optimizing
         rScaleShift = r * scale / 5;        // ___в целях оптимизации___
+
         rScaleShift = r * scale / 5;        // optimizing
  
         this.setRandom();                  // задаем случайную конфигурацию
+
         this.setRandom();                  // set random configuration
 
     };
 
     };
  
 
     // Работа с мышью
 
     // Работа с мышью
  
     var mx_, my_;                              // буфер позиции мыши (для расчета скорости при отпускании шара)
+
     var mx_, my_;                              // cursor position
  
     canvas.onmousedown = function(e) {          // функция при нажатии клавиши мыши
+
     canvas.onmousedown = function(e) {          // function for press on the cursor
         var m = mouseCoords(e);                // получаем расчетные координаты курсора мыши
+
         var m = mouseCoords(e);                // coordinates for calculation of the cursor position
         // цикл в обратную сторону, чтобы захватывать шар, нарисованный "сверху"
+
         //return cycle
        // (т.к. цикл рисования идет в обычном порядке)
 
 
         for (var i = balls.length - 1; i >= 0; i--) {
 
         for (var i = balls.length - 1; i >= 0; i--) {
 
             var b = balls[i];
 
             var b = balls[i];
 
             var rx = b.x - m.x;
 
             var rx = b.x - m.x;
 
             var ry = b.y - m.y;
 
             var ry = b.y - m.y;
             var rLen2 = rx * rx + ry * ry;              // квадрат расстояния между курсором и центром шара
+
             var rLen2 = rx * rx + ry * ry;              // length between cursor and sphere
             if (rLen2 <= r2) {                          // курсор нажал на шар
+
             if (rLen2 <= r2) {                          // the cursor has pressed on a sphere
                 if (e.which == 1) {                    // нажата левая клавиша мыши
+
                 if (e.which == 1) {                    // mouse's left button has pressed
 
                     dNd = b;
 
                     dNd = b;
                     dNd.xPlus = dNd.x - m.x;            // сдвиг курсора относительно центра шара по x
+
                     dNd.xPlus = dNd.x - m.x;            // shift of the cursor on X
                     dNd.yPlus = dNd.y - m.y;            // сдвиг курсора относительно центра шара по y
+
                     dNd.yPlus = dNd.y - m.y;            // shift of the cursor on Y
 
                     mx_ = m.x;    my_ = m.y;
 
                     mx_ = m.x;    my_ = m.y;
                     canvas.onmousemove = mouseMove;    // пока клавиша нажата - работает функция перемещения
+
                     canvas.onmousemove = mouseMove;    // shift function work while button press
                 } else if (e.which == 3)                // нажата правая клавиша мыши
+
                 } else if (e.which == 3)                // mouse's right button has pressed
                     balls.splice(i, 1);                // удалить шар
+
                     balls.splice(i, 1);                // delete sphere
 
                 return;
 
                 return;
 
             }
 
             }
 
         }
 
         }
  
         // если не вышли по return из цикла - нажатие было вне шара, добавляем новый
+
         // if don't exit - add new sphere
 
         if (e.which == 1) {
 
         if (e.which == 1) {
             dNd = addNewBall(m.x, m.y, true);  // добавляем шар и сразу захватываем его курсором
+
             dNd = addNewBall(m.x, m.y, true);  // add sphere and take it
             if (dNd == null) return;            // если шар не добавился (из за стен или других шаров) - возвращаемся
+
             if (dNd == null) return;            // return if the ball doesn't add
             dNd.xPlus = 0;  dNd.yPlus = 0;      // держим шар по центру
+
             dNd.xPlus = 0;  dNd.yPlus = 0;      // set ball in center position
 
             mx_ = m.x;    my_ = m.y;
 
             mx_ = m.x;    my_ = m.y;
             canvas.onmousemove = mouseMove;    // пока клавиша нажата - работает функция перемещения
+
             canvas.onmousemove = mouseMove;    // shift function works while mouse's button has pressed
 
         }
 
         }
 
     };
 
     };
  
     document.onmouseup = function(e) {          // функция при отпускании клавиши мыши
+
     document.onmouseup = function(e) {          // function when mouse's button let off
         canvas.onmousemove = null;              // когда клавиша отпущена - функции перемещения нету
+
         canvas.onmousemove = null;              // shift function equal null when mouse's button let of
         dNd = null;                            // когда клавиша отпущена - захваченного курсором шара нету
+
         dNd = null;                            // ball doesn't exist when mouse's button let of
 
     };
 
     };
  
     function mouseMove(e) {                    // функция при перемещении мыши, работает только с зажатой ЛКМ
+
     function mouseMove(e) {                    // shift function when left mouse's button pressed
         var m = mouseCoords(e);                // получаем расчетные координаты курсора мыши
+
         var m = mouseCoords(e);                // coordinates for calculation
 
         dNd.x = m.x + dNd.xPlus;
 
         dNd.x = m.x + dNd.xPlus;
 
         dNd.y = m.y + dNd.yPlus;
 
         dNd.y = m.y + dNd.yPlus;
Line 192: Line 191:
 
     }
 
     }
  
     function mouseCoords(e) {                  // функция возвращает расчетные координаты курсора мыши
+
     function mouseCoords(e) {                  // function returns settlement coordinates of the cursor of a mouse
 
         var m = [];
 
         var m = [];
 
         var rect = canvas.getBoundingClientRect();
 
         var rect = canvas.getBoundingClientRect();
Line 202: Line 201:
 
     // Работа с массивом
 
     // Работа с массивом
  
     var balls = [];                            // массив шаров
+
     var balls = [];                            // sphere's massive
 
     var addNewBall =  function(x, y, check) {
 
     var addNewBall =  function(x, y, check) {
         // проверка - не пересекается ли новый шар со стенами или уже существующими шарами
+
         // check - whether is crossed a new sphere with walls or already existing spheres
 
         if (check) {
 
         if (check) {
 
             if (x - r < 0 || x + r > w || y - r < 0 || y + r > h) return null;
 
             if (x - r < 0 || x + r > w || y - r < 0 || y + r > h) return null;
Line 217: Line 216:
 
         var b = [];
 
         var b = [];
  
         b.x = x;                b.y = y;        // расчетные координаты шара
+
         b.x = x;                b.y = y;        // calculation cordinates
         b.fx = 0;              b.fy = mg;      // сила, действующая на шар
+
         b.fx = 0;              b.fy = mg;      // force
         b.vx = 0;              b.vy = 0;      // скорость
+
         b.vx = 0;              b.vy = 0;      // velocity
  
         balls[balls.length] = b;                // добавить элемент в конец массива
+
         balls[balls.length] = b;                // add an element in end of massive
 
         return b;
 
         return b;
 
     };
 
     };
  
     this.setEmpty = function() {balls = [];};  // пустое поле
+
     this.setEmpty = function() {balls = [];};  // free area
  
     this.setRandom = function() {              // случайная конфигурация
+
     this.setRandom = function() {              // random configuration
 
         balls = [];
 
         balls = [];
 
         for (var i = 0; i < 1000; i++)
 
         for (var i = 0; i < 1000; i++)
Line 234: Line 233:
  
 
     var sqrt3 = Math.sqrt(3);
 
     var sqrt3 = Math.sqrt(3);
     this.setTriangularLattice = function() {            // задать на поле треугольную решетку
+
     this.setTriangularLattice = function() {            // triangle lattice
 
         balls = [];
 
         balls = [];
         var center = (w - Math.floor(w / r) * r) / 2;  // сдвиг, решетка будет появляться по середине по горизонтали
+
         var center = (w - Math.floor(w / r) * r) / 2;  // shift
 
         for (var j = 0; j < Math.floor(h / (sqrt3 * r)); j++)
 
         for (var j = 0; j < Math.floor(h / (sqrt3 * r)); j++)
 
             for (var i = 0; i < Math.floor(w / r) - 1; i++)
 
             for (var i = 0; i < Math.floor(w / r) - 1; i++)
Line 242: Line 241:
 
     };
 
     };
  
     // Основной цикл программы
+
     // program's cycle
  
 
     function control() {
 
     function control() {
Line 249: Line 248:
 
     }
 
     }
  
     // Расчетная часть программы
+
     // calculation part
  
     function physics() {                            // то, что происходит каждый шаг времени
+
     function physics() {                             
 
         for (var s = 1; s <= spf; s++) {
 
         for (var s = 1; s <= spf; s++) {
  
Line 265: Line 264:
 
                     TActual = Tk * v2Average;
 
                     TActual = Tk * v2Average;
  
                     if (addRandomV) {              // случайные скорости, если температура слишком мала
+
                     if (addRandomV) {              // random velocities if the temperature is low
 
                         if (TGoal > 0.15 && TActual < 0.1) {
 
                         if (TGoal > 0.15 && TActual < 0.1) {
 
                             for (var i2 = 0; i2 < balls.length; i2++) {
 
                             for (var i2 = 0; i2 < balls.length; i2++) {
Line 274: Line 273:
 
                     }
 
                     }
  
                     if (TActual < TActualMax) {        // из за того, что мышкой можно задать шарам запредельную скорость
+
                     if (TActual < TActualMax) {        //because of the fact that a mouse spheres can set ultraboundary speed
                         if (viscFrictionTh) BViscTh = BVisc * (TActual - TGoal);                // действие термостата
+
                         if (viscFrictionTh) BViscTh = BVisc * (TActual - TGoal);                // thermostat
                         if (internalFrictionTh) BInternalTh = BInternal * (TActual - TGoal);    // действие термостата
+
                         if (internalFrictionTh) BInternalTh = BInternal * (TActual - TGoal);    // thermostat
 
                     }
 
                     }
 
                 } else
 
                 } else
                     TActual = 0;                        // для датчика температуры на странице
+
                     TActual = 0;                        // for thermostat on page
 
             }
 
             }
  
             // пересчет сил идет отдельным массивом, т.к. далее будут добавляться силы взаимодействия между шарами
+
             //recalculation of forces goes the certain massif since forces of interaction between spheres will be added further
 
             for (var i0 = 0; i0 < balls.length; i0++) {
 
             for (var i0 = 0; i0 < balls.length; i0++) {
 
                 balls[i0].fx = - BViscTh * balls[i0].vx;
 
                 balls[i0].fx = - BViscTh * balls[i0].vx;
Line 289: Line 288:
  
 
             for (var i = 0; i < balls.length; i++) {
 
             for (var i = 0; i < balls.length; i++) {
                 // расчет взаимодействия производится со всеми следующими шарами в массиве,
+
                 // calculation of interaction is made with all following spheres in the massif not to consider each interaction twice
                // чтобы не считать каждое взаимодействие дважды
 
 
                 var b = balls[i];
 
                 var b = balls[i];
 
                 for (var j = i + 1; j < balls.length; j++) {
 
                 for (var j = i + 1; j < balls.length; j++) {
 
                     var b2 = balls[j];
 
                     var b2 = balls[j];
                     var rx = b.x - b2.x;  var ry = b.y - b2.y;        // вектор смотрит на первый шар (b)
+
                     var rx = b.x - b2.x;  var ry = b.y - b2.y;        // vector on the first ball (b)
                     var r2 = rx * rx + ry * ry;                        // квадрат расстояния между шарами
+
                     var r2 = rx * rx + ry * ry;                        // length between spheres
 
                     var rLen = (Math.sqrt(r2));
 
                     var rLen = (Math.sqrt(r2));
                     if (rLen > aCut) continue;                          // проверка на радиус обрезания
+
                     if (rLen > aCut) continue;                          // check for cutting radius
  
                     // если расстояние между частицами мало, силы будут посчитаны для K * a
+
                     // if the distance between particles isn't enough, forces will be counted like K * a
 
                     if (rLen < Ka) {
 
                     if (rLen < Ka) {
                         if (rLen > 0.00001) {                          // проверка, чтобы избежать деления на 0
+
                         if (rLen > 0.00001) {                          // check
 
                             rx = rx / rLen * Ka;
 
                             rx = rx / rLen * Ka;
 
                             ry = ry / rLen * Ka;
 
                             ry = ry / rLen * Ka;
 
                         }
 
                         }
 
                         r2 = K2a2;
 
                         r2 = K2a2;
                         rLen = Ka;                                     // корень K2a2
+
                         rLen = Ka;                                
 
                     }
 
                     }
  
 
                     // сила взаимодействия
 
                     // сила взаимодействия
                     var s2 = a2 / r2;        var s4 = s2 * s2;        // ___в целях оптимизации___
+
                     var s2 = a2 / r2;        var s4 = s2 * s2;        // optimizing
                     var F = LJCoeff * s4 * s4 * (s4 * s2 - 1);          // сила взаимодействия Леннарда-Джонса
+
                     var F = LJCoeff * s4 * s4 * (s4 * s2 - 1);          // LJ interaction force
 
                     if (SLJEnabled) {
 
                     if (SLJEnabled) {
                         var kSLJ;                                          // k(r) - сглаживающий коэффициент SLJ потенциала
+
                         var kSLJ;                                          // k(r) - coefficient SLJ potential
 
                         if (r <= b) kSLJ = 1;
 
                         if (r <= b) kSLJ = 1;
 
                         else {
 
                         else {
 
                             var brackets = (r2 - b2) * SLJDenominator;
 
                             var brackets = (r2 - b2) * SLJDenominator;
 
                             kSLJ = 1 - brackets * brackets;
 
                             kSLJ = 1 - brackets * brackets;
                         }                                                   // случай rLen > aCut обработан выше
+
                         }                                                
 
                         F *= kSLJ;
 
                         F *= kSLJ;
 
                     }
 
                     }
  
                     // сила внутреннего трения между частицами
+
                     // internal friction between spheres
 
                     if (r2 < a2) {
 
                     if (r2 < a2) {
                         var vx21 = b.vx - b2.vx;    var vy21 = b.vy - b2.vy;    // вектор смотрит на первый шар (b)
+
                         var vx21 = b.vx - b2.vx;    var vy21 = b.vy - b2.vy;    // vector on the first ball (b)
 
                         var ex = rx / rLen;        var ey = ry / rLen;
 
                         var ex = rx / rLen;        var ey = ry / rLen;
 
                         var v = vx21 * ex + vy21 * ey;
 
                         var v = vx21 * ex + vy21 * ey;
Line 330: Line 328:
 
                     }
 
                     }
  
                     // суммируем силы
+
                     // summirize forces
 
                     var Fx = F * rx;        var Fy = F * ry;
 
                     var Fx = F * rx;        var Fy = F * ry;
  
Line 337: Line 335:
 
                 }
 
                 }
  
                 if (b == dNd) continue;  // если шар схвачен курсором - его вз. со стенами и перемещение не считаем
+
                 if (b == dNd) continue;  // if the sphere is grabbed with the cursor - his interaction with walls and we don't consider movement
  
 
                 if (b.y + r > h) { b.fy += -CWall * (b.y + r - h) - BWall * b.vy; }
 
                 if (b.y + r > h) { b.fy += -CWall * (b.y + r - h) - BWall * b.vy; }
Line 350: Line 348:
 
     }
 
     }
  
     // Рисование
+
     // drawing
 
     function draw() {
 
     function draw() {
         context.clearRect(0, 0, w * scale, h * scale);      // очистить экран
+
         context.clearRect(0, 0, w * scale, h * scale);      // clear
 
         for (var i = 0; i < balls.length; i++){
 
         for (var i = 0; i < balls.length; i++){
 
             var xS = balls[i].x * scale;          var yS = balls[i].y * scale;
 
             var xS = balls[i].x * scale;          var yS = balls[i].y * scale;
 
             if (grad) {
 
             if (grad) {
                 // расчет градиента нужно проводить для каждого шара
+
                 // calculation of a gradient needs to be carried out for each sphere
 
                 var gradient = context.createRadialGradient(xS, yS, rScale13, xS - rScaleShift, yS + rScaleShift, 0);
 
                 var gradient = context.createRadialGradient(xS, yS, rScale13, xS - rScaleShift, yS + rScaleShift, 0);
 
                 gradient.addColorStop(0, "#0000bb");
 
                 gradient.addColorStop(0, "#0000bb");
Line 370: Line 368:
 
     }
 
     }
  
     // Запуск системы
+
     // Start
 
     this.newSystem();
 
     this.newSystem();
 
     setInterval(control, 1000 / fps);
 
     setInterval(control, 1000 / fps);
     // след. функция обновляет информацию о количестве частиц на поле
+
     // function updates information on quantity of particles in the field
 
     setInterval(function(){document.getElementById('ballsNum').innerHTML = balls.length;}, 1000 / 20);
 
     setInterval(function(){document.getElementById('ballsNum').innerHTML = balls.length;}, 1000 / 20);
 
}
 
}
Line 393: Line 391:
 
         mg =
 
         mg =
 
         <input id="text_01" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
 
         <input id="text_01" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
             // если введено не число - строка не пройдет валидацию по паттерну выше, и checkValidity() вернет false
+
             // if not the number is entered - the line won't pass validation on a pattern above, and checkValidity () will return false
 
             if (!this.checkValidity()) return;
 
             if (!this.checkValidity()) return;
 
             app.setSlider_01(this.value);
 
             app.setSlider_01(this.value);
Line 427: Line 425:
 
         <input type="range" id="slider_02" style="width: 150px;" oninput="app.setSlider_02(this.value); document.getElementById('text_02').value = this.value;">
 
         <input type="range" id="slider_02" style="width: 150px;" oninput="app.setSlider_02(this.value); document.getElementById('text_02').value = this.value;">
 
         T ⋅ T0 = <input id="text_02" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
 
         T ⋅ T0 = <input id="text_02" style="width: 5ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" oninput="
             // если введено не число - строка не пройдет валидацию по паттерну выше, и checkValidity() вернет false
+
             // if not the number is entered - the line won't pass validation on a pattern above, and checkValidity () will return false
 
             if (!this.checkValidity()) return;
 
             if (!this.checkValidity()) return;
 
             app.setSlider_02(this.value);
 
             app.setSlider_02(this.value);
Line 456: Line 454:
 
</div>
 
</div>
  
[[JavaScript_-_Balls|Here]] you can find the previous versions of the program.  
+
[[:ru:JavaScript_-_Balls|Here]] you can find the previous versions of the program.  
  
== The proposed directions of the stand development ==
+
== The proposed development directions ==
*Add the ability to set the temperature with the help of the Hoover‘s thermostat.
+
*Add temperature regulation by Hoover‘s thermostat.
*Explore the speed of the program in order to optimize it.  
+
*The program's evaluation speed in order to optimize it.  
*Apply the [[Принцип копирования|principle of copying]].
+
*Apply the [[:ru:Принцип копирования|copying principle]].
 
*Add different potentials.  
 
*Add different potentials.  
 
*Add the ability to set viscoelastic walls anywhere.
 
*Add the ability to set viscoelastic walls anywhere.
*Add the ability of inclusion of the periodic boundary conditions.  
+
*Add the periodic boundary conditions.  
  
[[Category: Virtual laboratory]]
+
<!--[[Category: Virtual laboratory]]
 
[[Category: Programming]]
 
[[Category: Programming]]
[[Category: JavaScript]]
+
[[Category: JavaScript]]-->

Latest revision as of 18:45, 18 January 2017

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[edit]

  • 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.