Задача 4. Одномерный классический одноатомный газ (задача 15.4 из [1]).

  1. Частицы в одномерном "сосуде" (линия)

  2. Упругие столкновения между частицами и со стенками

  3. Вычисление давления, температуры и распределения скоростей

  4. Интерактивное управление параметрами

Физическая модель: Упругие столкновения между частицами (с учетом массы); Отражение от стенок сосуда; Расчет температуры через среднюю кинетическую энергию.

Визуализация: Частицы разного размера (пропорционально массе); Векторы скорости (красные линии); Гистограмма распределения скоростей.

Статистика: Расчет средней скорости; Мгновенная температура системы; Упрощенный показатель давления.

Управление: Регулировка начальной температуры; Кнопка "Сброс"; Кнопка "Пауза".

Программа демонстрирует основные положения кинетической теории газов в одномерном случае. Можно увеличить количество частиц (numParticles)

Как использовать:
Запустите программу - частицы начнут двигаться.
Изменяйте температуру для изменения средней скорости.
Наблюдайте распределение скоростей в реальном времени.
Используйте паузу для детального изучения.

Исполнение онлайн: https://openprocessing.org/sketch/2649109  - не работает онлайн...

Вид экрана

 

Программа:  (скопируйте код в редактор Processing и запустите на выполнение)

import controlP5.*;

ControlP5 cp5;
int numParticles = 10; // Количество частиц
float[] x, v; // Положение и скорость частиц
float[] mass; // Массы частиц
float containerSize = 500; // Размер "сосуда"
float temperature = 2.0; // Начальная температура
boolean isPaused = false; // В начале работы кнопка Пауза "отжата"
 

ArrayList<Float> speedHistory = new ArrayList<Float>();

void setup() {
size(800, 400);
smooth();

// Инициализация частиц
x = new float[numParticles];
v = new float[numParticles];
mass = new float[numParticles];

for (int i = 0; i < numParticles; i++) {
x[i] = random(50, containerSize - 50);
v[i] = randomGaussian() * temperature;
mass[i] = random(5, 15);
}

// Настройка интерфейса
cp5 = new ControlP5(this);
cp5.addSlider("temperature")
.setPosition(20, 20)
.setSize(150, 20)
.setRange(0.1, 5.0)
.setValue(temperature)
.setColorCaptionLabel(color(0, 0, 255)) // Синий цвет текста
.setLabel(" Temperature");

cp5.addButton("reset")
.setPosition(20, 50)
.setSize(80, 30)
.setLabel("Reset");

cp5.addToggle("pause")
.setPosition(20, 90)
.setSize(80, 30)
.setLabel("Pause")
.setValue(isPaused)
.setColorCaptionLabel(color(0, 0, 255)) // Синий цвет текста
//.setColorBackground(color(200)) // Серый фон неактивного состояния
//.setColorActive(color(100, 200, 255)) // Голубой цвет активного состояния
//.setColorForeground(color(150))
; // Серый цвет перехода
}

void draw() {
background(240);

// Обновление физики
if (!isPaused) {
updatePhysics();
}

// Отрисовка
drawContainer();
drawParticles();
drawStats();
drawSpeedDistribution();
}

void updatePhysics() {
// Движение частиц
for (int i = 0; i < numParticles; i++) {
x[i] += v[i];

// Столкновение со стенками
if (x[i] < 0) {
x[i] = 0;
v[i] = -v[i];
}
if (x[i] > containerSize) {
x[i] = containerSize;
v[i] = -v[i];
}
}

// Столкновения между частицами
for (int i = 0; i < numParticles; i++) {
for (int j = i+1; j < numParticles; j++) {
if (abs(x[i] - x[j]) < 10) { // Упрощенное условие столкновения
// Упругое столкновение (1D)
float vi_new = ((mass[i] - mass[j])*v[i] + 2*mass[j]*v[j]) / (mass[i] + mass[j]);
float vj_new = ((mass[j] - mass[i])*v[j] + 2*mass[i]*v[i]) / (mass[i] + mass[j]);

v[i] = vi_new;
v[j] = vj_new;
}
}
}

// Запись скоростей для статистики
if (frameCount % 10 == 0) {
for (float vel : v) {
speedHistory.add(abs(vel));
if (speedHistory.size() > 500) speedHistory.remove(0);
}
}
}

void drawContainer() {
stroke(0);
strokeWeight(2);
line(50, 150, 50 + containerSize, 150);
fill(0);
text("Стенки сосуда", 50, 140);
}

void drawParticles() {
for (int i = 0; i < numParticles; i++) {
float radius = map(mass[i], 5, 15, 8, 20);
fill(100, 150, 255);
noStroke();
ellipse(50 + x[i], 150, radius, radius);

// Вектор скорости
stroke(255, 0, 0);
strokeWeight(1);
line(50 + x[i], 150, 50 + x[i] + v[i]*5, 150);
}
}

void drawStats() {
// Вычисление средней скорости и температуры
float avgSpeed = 0;
float avgEnergy = 0;
for (int i = 0; i < numParticles; i++) {
avgSpeed += abs(v[i]);
avgEnergy += mass[i] * v[i] * v[i] / 2;
}
avgSpeed /= numParticles;
float currentTemp = avgEnergy / numParticles; // Упрощенная температура

fill(0);
textSize(14);
text("Статистика газа:", width - 250, 30);
text(String.format("Частиц: %d", numParticles), width - 250, 50);
text(String.format("Ср. скорость: %.2f", avgSpeed), width - 250, 70);
text(String.format("Температура: %.2f", currentTemp), width - 250, 90);
text(String.format("Давление: %.2f", calculatePressure()), width - 250, 110);
}

void drawSpeedDistribution() {
// Гистограмма распределения скоростей
int[] bins = new int[10];
for (float speed : speedHistory) {
int bin = constrain((int)(speed * 2), 0, 9);
bins[bin]++;
}

// Отрисовка гистограммы
int histWidth = 300;
int histHeight = 100;
int histX = width - histWidth - 50;
int histY = 200;

fill(255);
stroke(0);
rect(histX, histY, histWidth, histHeight);

// Находим максимальное значение для масштабирования
int maxBin = 1;
for (int val : bins) {
if (val > maxBin) maxBin = val;
}

// Рисуем столбцы
fill(100, 200, 255);
noStroke();
for (int i = 0; i < bins.length; i++) {
float h = map(bins[i], 0, maxBin, 0, histHeight);
rect(histX + i * (histWidth/bins.length), histY + histHeight - h,
histWidth/bins.length - 2, h);
}

// Подписи
fill(0);
textSize(12);
text("Распределение скоростей", histX, histY - 5);
text("0", histX - 10, histY + histHeight + 10);
text("5", histX + histWidth/2 - 5, histY + histHeight + 10);
text("10", histX + histWidth - 15, histY + histHeight + 10);
}

float calculatePressure() {
// Упрощенный расчет давления (частота ударов о стенку)
float pressure = 0;
for (float vel : v) {
pressure += abs(vel) * 0.01; // Эмпирический коэффициент
}
return pressure / numParticles;
}

// Обработчики интерфейса
public void reset() {
for (int i = 0; i < numParticles; i++) {
x[i] = random(50, containerSize - 50);
v[i] = randomGaussian() * temperature;
}
speedHistory.clear();
}

public void pause(boolean value) {
isPaused = value;
}

public void temperature(float value) {
temperature = value;
}

 

 

 

Free Web Hosting