R语言 如何在模拟中从单个粒子增加倍数

8ljdwjyq  于 2023-03-27  发布在  其他
关注(0)|答案(1)|浏览(88)

一个项目的一部分是模拟气体粒子对墙壁的冲击,测量随着时间推移的作用力。到目前为止,我已经成功地完成了一个粒子的模拟,在考虑如何添加更多粒子时,我感觉到有一种更有效的方法来构建代码。
我的想法基本上是对第一个粒子做同样的事情(用相关数据建立一个数据框),然后为每个要添加到图中的新粒子添加另一个geom_point。
我会很感激一些建议,如何添加更多的粒子到这个模拟,而不必有一个脚本,其中包含了很多类似的段落。
以下是到目前为止我对单个粒子的理解:

library(tidyverse)
library(magick)
library(gganimate)
library(stringi)
library(ggtext)

particle_mass <- 4.65 #(x 10^-26) 
## A constant that dictates the number of decimal places that the data will be rounded to. Having this set as a constant avoids having to manually adjust a number which appears multiples times throughout the code if you wanted to change the number of decimal places for any reason.
rnd <- 4 
## initial particle coordinates 
x_i <- round(runif(n = 1, min = 0,max = 1), rnd) 
y_i <- round(runif(n = 1, min = 0,max = 1), rnd)

x_velocity <- 400 ## A constant for the x velocity in m/s
y_velocity <- 200 ##A  constant for the y velocity in m/s
time_step <- 0.001 ## A constant for the duration of a single time step in seconds
frame_total <- 200 ## A constant for the number of frames that the animation will be split into (two additional frames are added on at the end)
rate_factor <- 35 ## A factor that controls the playback rate of the animation. Increase this number and the animation will be slowed 

## The x and y displacement components per time step with a factor to slow down the animation to a more easily perceptible speed.
x_jump <- (x_velocity/rate_factor)/frame_total 
y_jump <- (y_velocity/rate_factor)/frame_total 

## Vectors of equal length for x position, y position, and corresponding time steps are initialized.
x_pos <- round(seq(x_i, (x_i + (frame_total*x_jump))-x_jump, x_jump), rnd)
y_pos <- round(seq(y_i, (y_i + (frame_total*y_jump))-y_jump, y_jump), rnd) 
times <- seq(0, (frame_total*time_step)-time_step, time_step)

## A loop that adjusts the direction of the x velocity vector when x = 1 or x = 0 to simulate wall contact
for (i in 2:length(x_pos)) {
  if (x_pos[i-1] > 1 | x_pos[i-1] < 0 ){x_jump = x_jump*(-1)}
  x_pos[[i]] <- round(x_pos[[i-1]] + x_jump, rnd)
}

##  A loop that adjusts the direction of the y velocity vector when y = 1 or y = 0 to simulate wall contact
for (i in 2:length(y_pos)) {
  if (y_pos[i-1] > 1 | y_pos[i-1] < 0 ){y_jump = y_jump*(-1)}
  y_pos[[i]] <- round(y_pos[[i-1]] + y_jump, rnd)
}

bounce <- data.frame(times, x_pos, y_pos) ##time and coordinate position information
bounce$x_dif <- c(0, diff(bounce$x_pos))## change in x position between time steps
bounce$x_turn <- c(0, diff(sign(bounce$x_dif))) ## helps detect changes in direction

## impulse magnitude experienced by the right wall at each time step
bounce <- bounce %>% mutate(impulse_right = case_when((bounce$x_turn) == -2 ~ 2*(x_velocity*particle_mass),
                                                      (bounce$x_turn) != -2 ~ 0))

bounce$cume_impulse_right <- cumsum(bounce$impulse_right) #cumulative impulse on the right wall 
## static plots of the moving particle
bounce_plot <- ggplot(bounce) +
  geom_point(aes(x_pos, y_pos), color = "azure4", size = 1) +
  labs(title = "Single gas particle model") + 
  ylab("") + xlab("") +
  theme_classic() +
  ## axis lines and tick marks
  theme(axis.line = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.x = element_blank(), axis.ticks.y = element_blank(),
        axis.text.y = element_blank()) +
  ## plot title aesthetics
  theme(plot.title = element_text(family = "Arial", face = "bold", hjust = 0.5, 
                                  size = 9, color = "gray38")) +
  ## The vertical and horizontal lines create a border around the 1 x 1 box that contains the particle
  geom_vline(xintercept=0, color="snow3", size=2) + 
  geom_hline(yintercept=0, color="snow3", size=2) + 
  geom_hline(yintercept=1, color="snow3", size=2) +
  geom_vline(xintercept=1, color="#CC6666", size=1) 

bounce_anim <- bounce_plot + transition_time(times)

## The desired number of frames and duration of the animation are set within the 'animate' function.
bounce_gif <- animate(bounce_anim, width = 3, height = 3, units = "in", res = 200,
                      nframes = frame_total, renderer = magick_renderer())

我的计划是为每个新粒子创建一个新的数据框,然后使用新数据在ggplot函数下添加一条新的geom_point线。我担心如果我添加5个,10个或更多粒子,代码会很长。有没有一种方法可以在模拟中添加多个粒子而无需编写这么多代码?任何帮助都将不胜感激。

gfttwv5a

gfttwv5a1#

下面是一个基于更长的 Dataframe 来容纳多个id的方法:

particle_mass <- 4.65
ids = 5
frames = 200
n = nrow(df)
df <- expand.grid(frame = 1:frames, id = 1:ids)
df$x_pos = runif(n)
df$y_pos = runif(n)
df$x_vel = runif(n, min = -0.1, max = 0.1) 
df$y_vel = runif(n, min = -0.1, max = 0.1) 
df$impulse_right = 0

for(i in 1:ids) {
  for(f in 2:frames) {
    r = f + (i - 1) * frames
    df$x_pos[r] = df$x_pos[r-1] + df$x_vel[r-1]
    df$x_vel[r] = df$x_vel[r-1] * ifelse(df$x_pos[r] < 0 | df$x_pos[r] > 1, -1, 1)
    df$impulse_right[r] = ifelse(df$x_vel[r] < df$x_vel[r-1], 
                                 df$x_vel[r-1] * particle_mass, 0)
    df$y_pos[r] = df$y_pos[r-1] + df$y_vel[r-1]
    df$y_vel[r] = df$y_vel[r-1] * ifelse(df$y_pos[r] < 0 | df$y_pos[r] > 1, -1, 1)
  }
}

ggplot(df, aes(x_pos, y_pos)) +
  geom_point(aes(color = id)) +
  transition_states(frame)

相关问题