1973 lines
72 KiB
C
1973 lines
72 KiB
C
/*********************************************************************************/
|
|
/* */
|
|
/* Animation of interacting particles in a planar domain */
|
|
/* */
|
|
/* N. Berglund, november 2021 */
|
|
/* */
|
|
/* UPDATE 24/04: distinction between damping and "elasticity" parameters */
|
|
/* UPDATE 27/04: new billiard shapes, bug in color scheme fixed */
|
|
/* UPDATE 28/04: code made more efficient, with help of Marco Mancini */
|
|
/* */
|
|
/* Feel free to reuse, but if doing so it would be nice to drop a */
|
|
/* line to nils.berglund@univ-orleans.fr - Thanks! */
|
|
/* */
|
|
/* compile with */
|
|
/* gcc -o lennardjones lennardjones.c */
|
|
/* -L/usr/X11R6/lib -ltiff -lm -lGL -lGLU -lX11 -lXmu -lglut -O3 -fopenmp */
|
|
/* */
|
|
/* OMP acceleration may be more effective after executing */
|
|
/* export OMP_NUM_THREADS=2 in the shell before running the program */
|
|
/* */
|
|
/* To make a video, set MOVIE to 1 and create subfolder tif_ljones */
|
|
/* It may be possible to increase parameter PAUSE */
|
|
/* */
|
|
/* create movie using */
|
|
/* ffmpeg -i lj.%05d.tif -vcodec libx264 lj.mp4 */
|
|
/* */
|
|
/*********************************************************************************/
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <GL/glut.h>
|
|
#include <GL/glu.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <tiffio.h> /* Sam Leffler's libtiff library. */
|
|
#include <omp.h>
|
|
|
|
#define MOVIE 0 /* set to 1 to generate movie */
|
|
#define DOUBLE_MOVIE 0 /* set to 1 to produce movies for wave height and energy simultaneously */
|
|
|
|
/* General geometrical parameters */
|
|
|
|
#define WINWIDTH 1280 /* window width */
|
|
#define WINHEIGHT 720 /* window height */
|
|
|
|
#define XMIN -2.0
|
|
#define XMAX 2.0 /* x interval */
|
|
#define YMIN -1.125
|
|
#define YMAX 1.125 /* y interval for 9/16 aspect ratio */
|
|
|
|
#define INITXMIN -1.95
|
|
#define INITXMAX 1.95 /* x interval for initial condition */
|
|
#define INITYMIN -1.05
|
|
#define INITYMAX 0.95 /* y interval for initial condition */
|
|
|
|
#define CIRCLE_PATTERN 8 /* pattern of circles, see list in global_ljones.c */
|
|
|
|
#define TWO_TYPES 0 /* set to 1 to have two types of particles */
|
|
#define TPYE_PROPORTION 0.8 /* proportion of particles of first type */
|
|
#define SYMMETRIZE_FORCE 1 /* set to 1 to symmetrize two-particle interaction, only needed if particles are not all the same */
|
|
|
|
#define INTERACTION 1 /* particle interaction, see list in global_ljones.c */
|
|
#define INTERACTION_B 1 /* particle interaction for second type of particle, see list in global_ljones.c */
|
|
#define SPIN_INTER_FREQUENCY 1.0 /* angular frequency of spin-spin interaction */
|
|
#define SPIN_INTER_FREQUENCY_B 2.0 /* angular frequency of spin-spin interaction for second particle type */
|
|
|
|
#define P_PERCOL 0.25 /* probability of having a circle in C_RAND_PERCOL arrangement */
|
|
#define NPOISSON 100 /* number of points for Poisson C_RAND_POISSON arrangement */
|
|
#define PDISC_DISTANCE 5.0 /* minimal distance in Poisson disc process, controls density of particles */
|
|
#define PDISC_CANDIDATES 100 /* number of candidates in construction of Poisson disc process */
|
|
#define RANDOM_POLY_ANGLE 0 /* set to 1 to randomize angle of polygons */
|
|
|
|
#define LAMBDA 2.0 /* parameter controlling the dimensions of domain */
|
|
#define MU 0.015 /* parameter controlling radius of particles */
|
|
#define MU_B 0.02427051 /* parameter controlling radius of particles of second type */
|
|
#define NPOLY 3 /* number of sides of polygon */
|
|
#define APOLY 0.5 /* angle by which to turn polygon, in units of Pi/2 */
|
|
#define MDEPTH 4 /* depth of computation of Menger gasket */
|
|
#define MRATIO 3 /* ratio defining Menger gasket */
|
|
#define MANDELLEVEL 1000 /* iteration level for Mandelbrot set */
|
|
#define MANDELLIMIT 10.0 /* limit value for approximation of Mandelbrot set */
|
|
#define FOCI 1 /* set to 1 to draw focal points of ellipse */
|
|
#define NGRIDX 80 /* number of grid point for grid of disks */
|
|
#define NGRIDY 25 /* number of grid point for grid of disks */
|
|
|
|
#define X_SHOOTER -0.2
|
|
#define Y_SHOOTER -0.6
|
|
#define X_TARGET 0.4
|
|
#define Y_TARGET 0.7 /* shooter and target positions in laser fight */
|
|
|
|
/* Parameters for length and speed of simulation */
|
|
|
|
#define NSTEPS 3100 /* number of frames of movie */
|
|
#define NVID 150 /* number of iterations between images displayed on screen */
|
|
#define NSEG 150 /* number of segments of boundary */
|
|
#define INITIAL_TIME 200 /* time after which to start saving frames */
|
|
#define BOUNDARY_WIDTH 1 /* width of particle boundary */
|
|
#define LINK_WIDTH 2 /* width of links between particles */
|
|
#define CONTAINER_WIDTH 4 /* width of container boundary */
|
|
|
|
#define PAUSE 1000 /* number of frames after which to pause */
|
|
#define PSLEEP 1 /* sleep time during pause */
|
|
#define SLEEP1 1 /* initial sleeping time */
|
|
#define SLEEP2 1 /* final sleeping time */
|
|
#define MID_FRAMES 20 /* number of still frames between parts of two-part movie */
|
|
#define END_FRAMES 100 /* number of still frames at end of movie */
|
|
|
|
/* Boundary conditions, see list in global_ljones.c */
|
|
|
|
#define BOUNDARY_COND 1
|
|
|
|
/* Plot type, see list in global_ljones.c */
|
|
|
|
#define PLOT 0
|
|
#define PLOT_B 6 /* plot type for second movie */
|
|
|
|
#define COLOR_BONDS 0 /* set to 1 to color bonds according to length */
|
|
|
|
/* Color schemes */
|
|
|
|
#define COLOR_PALETTE 10 /* Color palette, see list in global_ljones.c */
|
|
|
|
#define BLACK 1 /* background */
|
|
|
|
#define COLOR_SCHEME 3 /* choice of color scheme, see list in global_ljones.c */
|
|
|
|
#define SCALE 0 /* set to 1 to adjust color scheme to variance of field */
|
|
#define SLOPE 0.5 /* sensitivity of color on wave amplitude */
|
|
#define ATTENUATION 0.0 /* exponential attenuation coefficient of contrast with time */
|
|
|
|
#define COLORHUE 260 /* initial hue of water color for scheme C_LUM */
|
|
#define COLORDRIFT 0.0 /* how much the color hue drifts during the whole simulation */
|
|
#define LUMMEAN 0.5 /* amplitude of luminosity variation for scheme C_LUM */
|
|
#define LUMAMP 0.3 /* amplitude of luminosity variation for scheme C_LUM */
|
|
#define HUEMEAN 220.0 /* mean value of hue for color scheme C_HUE */
|
|
#define HUEAMP -50.0 /* amplitude of variation of hue for color scheme C_HUE */
|
|
|
|
/* particle properties */
|
|
|
|
#define PARTICLE_HUE_MIN 330.0 /* color of original particle */
|
|
#define PARTICLE_HUE_MAX 50.0 /* color of saturated particle */
|
|
#define PARTICLE_EMAX 1.0e3 /* max energy for particle to survive */
|
|
|
|
#define RANDOM_RADIUS 0 /* set to 1 for random circle radius */
|
|
#define DT_PARTICLE 2.0e-6 /* time step for particle displacement */
|
|
#define KREPEL 10.0 /* constant in repelling force between particles */
|
|
#define EQUILIBRIUM_DIST 4.0 /* Lennard-Jones equilibrium distance for second type of particle */
|
|
#define EQUILIBRIUM_DIST_B 5.0 /* Lennard-Jones equilibrium distance for second type of particle */
|
|
#define REPEL_RADIUS 20.0 /* radius in which repelling force acts (in units of particle radius) */
|
|
#define DAMPING 0.0 /* damping coefficient of particles */
|
|
#define PARTICLE_MASS 1.0 /* mass of particle of radius MU */
|
|
#define PARTICLE_MASS_B 0.1 /* mass of particle of radius MU */
|
|
#define PARTICLE_INERTIA_MOMENT 0.05 /* moment of inertia of particle */
|
|
#define PARTICLE_INERTIA_MOMENT_B 0.02 /* moment of inertia of second type of particle */
|
|
#define V_INITIAL 0.5 /* initial velocity range */
|
|
#define OMEGA_INITIAL 2.0 /* initial angular velocity range */
|
|
|
|
#define THERMOSTAT 0 /* set to 1 to switch on thermostat */
|
|
#define SIGMA 5.0 /* noise intensity in thermostat */
|
|
#define BETA 0.3 /* initial inverse temperature */
|
|
#define MU_XI 0.05 /* friction constant in thermostat */
|
|
#define KSPRING_BOUNDARY 2.0e7 /* confining harmonic potential outside simulation region */
|
|
#define NBH_DIST_FACTOR 5.0 /* radius in which to count neighbours */
|
|
#define GRAVITY 0.0 /* gravity acting on all particles */
|
|
|
|
#define ROTATION 0 /* set to 1 to include rotation of particles */
|
|
#define COUPLE_ANGLE_TO_THERMOSTAT 1 /* set to 1 to couple angular degrees of freedom to thermostat */
|
|
#define DIMENSION_FACTOR 1.0 /* scaling factor taking into account number of degrees of freedom */
|
|
#define KTORQUE 7.0 /* force constant in angular dynamics */
|
|
#define KTORQUE_B 10.0 /* force constant in angular dynamics */
|
|
#define KTORQUE_DIFF 150.0 /* force constant in angular dynamics for different particles */
|
|
#define DRAW_SPIN 0 /* set to 1 to draw spin vectors of particles */
|
|
#define DRAW_SPIN_B 0 /* set to 1 to draw spin vectors of particles */
|
|
#define DRAW_CROSS 1 /* set to 1 to draw cross on particles of second type */
|
|
#define SPIN_RANGE 5.0 /* range of spin-spin interaction */
|
|
#define SPIN_RANGE_B 5.0 /* range of spin-spin interaction for second type of particle */
|
|
#define QUADRUPOLE_RATIO 0.6 /* anisotropy in quadrupole potential */
|
|
|
|
#define INCREASE_BETA 0 /* set to 1 to increase BETA during simulation */
|
|
#define BETA_FACTOR 0.001 /* factor by which to change BETA during simulation */
|
|
#define N_TOSCILLATIONS 0.5 /* number of temperature oscillations in BETA schedule */
|
|
#define NO_OSCILLATION 1 /* set to 1 to have exponential BETA change only */
|
|
#define FINAL_CONSTANT_PHASE 200 /* final phase in which temperature is constant */
|
|
|
|
#define DECREASE_CONTAINER_SIZE 1 /* set to 1 to decrease size of container */
|
|
#define SYMMETRIC_DECREASE 0 /* set tp 1 to decrease container symmetrically */
|
|
#define COMPRESSION_RATIO 0.3 /* final size of container */
|
|
#define RESTORE_CONTAINER_SIZE 1 /* set to 1 to restore container to initial size at end of simulation */
|
|
#define RESTORE_TIME 700 /* time before end of sim at which to restore size */
|
|
|
|
#define MOVE_OBSTACLE 0 /* set to 1 to have a moving obstacle */
|
|
#define CENTER_VIEW_ON_OBSTACLE 0 /* set to 1 to center display on moving obstacle */
|
|
#define OBSTACLE_RADIUS 0.27 /* radius of obstacle for circle boundary conditions */
|
|
#define OBSTACLE_XMIN 0.0 /* initial position of obstacle */
|
|
#define OBSTACLE_XMAX 3.0 /* final position of obstacle */
|
|
|
|
#define INCREASE_KREPEL 0 /* set to 1 to increase KREPEL during simulation */
|
|
#define KREPEL_FACTOR 1000.0 /* factor by which to change KREPEL during simulation */
|
|
|
|
#define PART_AT_BOTTOM 0 /* set to 1 to include "seed" particles at bottom */
|
|
#define MASS_PART_BOTTOM 10000.0 /* mass of particles at bottom */
|
|
#define NPART_BOTTOM 100 /* number of particles at the bottom */
|
|
|
|
#define ADD_PARTICLES 0 /* set to 1 to add particles */
|
|
#define ADD_TIME 100 /* time at which to add first particle */
|
|
#define ADD_PERIOD 500 /* time interval between adding further particles */
|
|
#define SAFETY_FACTOR 3.0 /* no particles are added at distance less than MU*SAFETY_FACTOR of other particles */
|
|
|
|
#define FLOOR_FORCE 1 /* set to 1 to limit force on particle to FMAX */
|
|
#define FMAX 1.0e10 /* maximal force */
|
|
|
|
#define HASHX 23 /* size of hashgrid in x direction */
|
|
#define HASHY 14 /* size of hashgrid in y direction */
|
|
#define HASHMAX 100 /* maximal number of particles per hashgrid cell */
|
|
#define HASHGRID_PADDING 0.1 /* padding of hashgrid outside simulation window */
|
|
|
|
#define DRAW_COLOR_SCHEME 0 /* set to 1 to plot the color scheme */
|
|
#define COLORBAR_RANGE 8.0 /* scale of color scheme bar */
|
|
#define COLORBAR_RANGE_B 12.0 /* scale of color scheme bar for 2nd part */
|
|
#define ROTATE_COLOR_SCHEME 0 /* set to 1 to draw color scheme horizontally */
|
|
|
|
#include "global_ljones.c"
|
|
#include "sub_lj.c"
|
|
|
|
double xshift = 0.0; /* x shift of shown window */
|
|
double xspeed = 0.0; /* x speed of obstacle */
|
|
|
|
double gaussian()
|
|
/* returns standard normal random variable, using Box-Mueller algorithm */
|
|
{
|
|
static double V1, V2, S;
|
|
static int phase = 0;
|
|
double X;
|
|
|
|
if (phase == 0)
|
|
{
|
|
do
|
|
{
|
|
double U1 = (double)rand() / RAND_MAX;
|
|
double U2 = (double)rand() / RAND_MAX;
|
|
V1 = 2 * U1 - 1;
|
|
V2 = 2 * U2 - 1;
|
|
S = V1 * V1 + V2 * V2;
|
|
}
|
|
while(S >= 1 || S == 0);
|
|
X = V1 * sqrt(-2 * log(S) / S);
|
|
}
|
|
else X = V2 * sqrt(-2 * log(S) / S);
|
|
|
|
phase = 1 - phase;
|
|
|
|
return X;
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/* animation part */
|
|
/*********************/
|
|
|
|
|
|
|
|
void hash_xy_to_ij(double x, double y, int ij[2])
|
|
{
|
|
static int first = 1;
|
|
static double lx, ly, padding;
|
|
int i, j;
|
|
|
|
if (first)
|
|
{
|
|
if ((BOUNDARY_COND == BC_PERIODIC)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE)) padding = 0.0;
|
|
else padding = HASHGRID_PADDING;
|
|
lx = XMAX - XMIN + 2.0*padding;
|
|
ly = YMAX - YMIN + 2.0*padding;
|
|
first = 0;
|
|
}
|
|
|
|
i = (int)((double)HASHX*(x - XMIN + padding)/lx);
|
|
j = (int)((double)HASHY*(y - YMIN + padding)/ly);
|
|
|
|
if (i<0) i = 0;
|
|
else if (i>=HASHX) i = HASHX-1;
|
|
if (j<0) j = 0;
|
|
else if (j>=HASHY) j = HASHY-1;
|
|
|
|
ij[0] = i;
|
|
ij[1] = j;
|
|
|
|
// printf("Mapped (%.3f,%.3f) to (%i, %i)\n", x, y, ij[0], ij[1]);
|
|
}
|
|
|
|
|
|
double lennard_jones_force(double r, t_particle particle)
|
|
{
|
|
int i;
|
|
double rmin = 0.01, rplus, ratio = 1.0;
|
|
|
|
if (r > REPEL_RADIUS*particle.radius) return(0.0);
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
// ratio = ipow(particle.eq_dist*particle.radius/rplus, 6);
|
|
ratio = ipow(particle.eq_dist*particle.radius/rplus, 3);
|
|
ratio = ratio*ratio;
|
|
|
|
return((ratio - 2.0*ratio*ratio)/rplus);
|
|
}
|
|
}
|
|
|
|
void aniso_lj_force(double r, double ca, double sa, double ca_rel, double sa_rel, double force[2], t_particle particle)
|
|
{
|
|
int i;
|
|
double rmin = 0.01, rplus, ratio = 1.0, c2, s2, c4, s4, a, aprime, f1, f2;
|
|
|
|
if (r > REPEL_RADIUS*particle.radius)
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
// ratio = ipow(particle.eq_dist*particle.radius/rplus, 6);
|
|
ratio = ipow(particle.eq_dist*particle.radius/rplus, 3);
|
|
ratio = ratio*ratio;
|
|
|
|
/* cos(2phi) and sin(2phi) */
|
|
c2 = ca_rel*ca_rel - sa_rel*sa_rel;
|
|
s2 = 2.0*ca_rel*sa_rel;
|
|
|
|
/* cos(4phi) and sin(4phi) */
|
|
c4 = c2*c2 - s2*s2;
|
|
s4 = 2.0*c2*s2;
|
|
|
|
a = 0.5*(9.0 - 7.0*c4);
|
|
aprime = 14.0*s4;
|
|
|
|
f1 = ratio*(a - ratio)/rplus;
|
|
f2 = ratio*aprime/rplus;
|
|
|
|
force[0] = f1*ca - f2*sa;
|
|
force[1] = f1*sa + f2*ca;
|
|
}
|
|
}
|
|
|
|
void penta_lj_force(double r, double ca, double sa, double ca_rel, double sa_rel, double force[2], t_particle particle)
|
|
{
|
|
int i;
|
|
double rmin = 0.01, rplus, ratio = 1.0, c2, s2, c4, s4, c5, s5, a, aprime, f1, f2;
|
|
static double a0, b0;
|
|
static int first = 1;
|
|
|
|
if (first)
|
|
{
|
|
a0 = cos(0.1*PI) + 0.5;
|
|
b0 = a0 - 1.0;
|
|
first = 0;
|
|
}
|
|
|
|
if (r > REPEL_RADIUS*particle.radius)
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
// ratio = ipow(particle.eq_dist*particle.radius/rplus, 6);
|
|
ratio = ipow(particle.eq_dist*particle.radius/rplus, 3);
|
|
ratio = ratio*ratio;
|
|
|
|
|
|
/* cos(2phi) and sin(2phi) */
|
|
c2 = ca_rel*ca_rel - sa_rel*sa_rel;
|
|
s2 = 2.0*ca_rel*sa_rel;
|
|
|
|
/* cos(4phi) and sin(4phi) */
|
|
c4 = c2*c2 - s2*s2;
|
|
s4 = 2.0*c2*s2;
|
|
|
|
/* cos(5phi) and sin(5phi) */
|
|
c5 = ca_rel*c4 - sa_rel*s4;
|
|
s5 = sa_rel*c4 + ca_rel*s4;
|
|
|
|
a = a0 - b0*c5;
|
|
aprime = 5.0*b0*s5;
|
|
|
|
f1 = ratio*(a - ratio)/rplus;
|
|
f2 = ratio*aprime/rplus;
|
|
|
|
force[0] = f1*ca - f2*sa;
|
|
force[1] = f1*sa + f2*ca;
|
|
}
|
|
}
|
|
|
|
double old_golden_ratio_force(double r, t_particle particle)
|
|
/* potential with two minima at distances whose ratio is the golden ratio Phi */
|
|
/* old version that does not work very well */
|
|
{
|
|
int i;
|
|
double x, y, z, rplus, ratio = 1.0, phi, a, phi3;
|
|
static int first = 1;
|
|
static double rmin, b, c, d;
|
|
|
|
if (first)
|
|
{
|
|
rmin = 0.5*particle.radius;
|
|
phi = 0.5*(1.0 + sqrt(5.0));
|
|
phi3 = 1.0/(phi*phi*phi);
|
|
a = 0.66;
|
|
b = 1.0 + phi3 + a;
|
|
d = phi3*a;
|
|
c = phi3 + a + d;
|
|
// b = 7.04;
|
|
// c = 13.66;
|
|
// d = 6.7;
|
|
first = 0;
|
|
printf("a = %.4lg, b = %.4lg, c = %.4lg, d = %.4lg\n", a, b, c, d);
|
|
}
|
|
|
|
if (r > REPEL_RADIUS*particle.radius) return(0.0);
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
x = particle.eq_dist*particle.radius/rplus;
|
|
y = x*x*x;
|
|
z = d - c*y + b*y*y - y*y*y;
|
|
return(x*z/rplus);
|
|
}
|
|
}
|
|
|
|
double golden_ratio_force(double r, t_particle particle)
|
|
/* potential with two minima at distances whose ratio is the golden ratio Phi */
|
|
/* piecewise polynomial/LJ version */
|
|
{
|
|
int i;
|
|
double x, rplus, xm6, y1;
|
|
static int first = 1;
|
|
static double rmin, phi, a, h1, h2, phi6;
|
|
|
|
if (first)
|
|
{
|
|
rmin = 0.5*particle.radius;
|
|
phi = 0.5*(1.0 + sqrt(5.0));
|
|
a = 1.2;
|
|
|
|
h1 = 1.0; /* inner potential well depth */
|
|
h2 = 10.0; /* outer potential well depth */
|
|
phi6 = ipow(phi, 6);
|
|
|
|
first = 0;
|
|
}
|
|
|
|
if (r > REPEL_RADIUS*particle.radius) return(0.0);
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
x = rplus/(particle.eq_dist*particle.radius);
|
|
// xm6 = 1.0/ipow(x, 6);
|
|
xm6 = 1.0/ipow(x, 3);
|
|
xm6 = xm6*xm6;
|
|
|
|
if (x <= 1.0) return(12.0*h1*xm6*(1.0 - xm6)/x);
|
|
else if (x <= a)
|
|
{
|
|
y1 = ipow(a - 1.0, 3);
|
|
return(6.0*h1*(x - 1.0)*(a - x)/y1);
|
|
}
|
|
else if (x <= phi)
|
|
{
|
|
y1 = ipow(phi - a, 3);
|
|
return(6.0*h2*(x - a)*(x - phi)/y1);
|
|
}
|
|
else return(12.0*h2*phi6*(1.0 - phi6*xm6)*xm6/x);
|
|
}
|
|
}
|
|
|
|
void dipole_lj_force(double r, double ca, double sa, double ca_rel, double sa_rel, double force[2], t_particle particle)
|
|
{
|
|
int i;
|
|
double rmin = 0.01, rplus, ratio = 1.0, a, aprime, f1, f2;
|
|
|
|
if (r > REPEL_RADIUS*MU)
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
// ratio = ipow(particle.eq_dist*particle.radius/rplus, 6);
|
|
ratio = ipow(particle.eq_dist*particle.radius/rplus, 3);
|
|
ratio = ratio*ratio;
|
|
|
|
a = 1.0 + 0.25*ca_rel;
|
|
aprime = -0.25*sa_rel;
|
|
|
|
f1 = ratio*(a - ratio)/rplus;
|
|
f2 = ratio*aprime/rplus;
|
|
|
|
force[0] = f1*ca - f2*sa;
|
|
force[1] = f1*sa + f2*ca;
|
|
}
|
|
}
|
|
|
|
void quadrupole_lj_force(double r, double ca, double sa, double ca_rel, double sa_rel, double force[2], t_particle particle)
|
|
{
|
|
int i;
|
|
double rmin = 0.01, rplus, ratio = 1.0, a, aprime, f1, f2, ca2, sa2, x, y, dplus, dminus;
|
|
static int first = 1;
|
|
static double a0, b0, aplus, aminus;
|
|
|
|
if (first)
|
|
{
|
|
dplus = cos(0.2*PI)*cos(0.1*PI);
|
|
// dminus = 0.8*dplus;
|
|
dminus = QUADRUPOLE_RATIO*dplus;
|
|
aplus = ipow(1.0/dplus, 6);
|
|
aminus = ipow(1.0/dminus, 6);
|
|
// aminus = ipow(cos(0.2*PI)*(0.25 + 0.5*sin(0.1*PI)), 6);
|
|
a0 = 0.5*(aplus + aminus);
|
|
b0 = 0.5*(aplus - aminus);
|
|
first = 0;
|
|
}
|
|
|
|
if (r > REPEL_RADIUS*particle.radius)
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
// ratio = ipow(particle.eq_dist*particle.radius/rplus, 6);
|
|
ratio = ipow(particle.eq_dist*particle.radius/rplus, 3);
|
|
ratio = ratio*ratio;
|
|
|
|
/* cos(2*phi) and sin(2*phi) */
|
|
ca2 = ca_rel*ca_rel - sa_rel*sa_rel;
|
|
sa2 = 2.0*ca_rel*sa_rel;
|
|
|
|
a = a0 + b0*ca2;
|
|
// if (a == 0.0) a = 1.0e-10;
|
|
aprime = -2.0*b0*sa2;
|
|
|
|
f1 = ratio*(a - ratio)/rplus;
|
|
f2 = ratio*aprime/rplus;
|
|
|
|
force[0] = f1*ca - f2*sa;
|
|
force[1] = f1*sa + f2*ca;
|
|
}
|
|
}
|
|
|
|
|
|
void quadrupole_lj_force2(double r, double ca, double sa, double ca_rel, double sa_rel, double force[2], t_particle particle)
|
|
{
|
|
int i;
|
|
double rmin = 0.01, rplus, ratio = 1.0, a, aprime, f1, f2, ca2, sa2, x, y, eqdist;
|
|
static int first = 1;
|
|
static double aplus, aminus, a0, b0;
|
|
|
|
if (first)
|
|
{
|
|
aplus = ipow(cos(0.2*PI)*cos(0.1*PI), 6);
|
|
aminus = 0.1*aplus;
|
|
// aminus = 0.0;
|
|
// aminus = -2.0*ipow(cos(0.2*PI)*(0.5*sin(0.1*PI)), 6);
|
|
// aminus = ipow(cos(0.2*PI)*(0.25 + 0.5*sin(0.1*PI)), 6);
|
|
a0 = 0.5*(aplus + aminus);
|
|
b0 = 0.5*(aplus - aminus);
|
|
first = 0;
|
|
}
|
|
|
|
if (r > REPEL_RADIUS*particle.radius)
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (r > rmin) rplus = r;
|
|
else rplus = rmin;
|
|
|
|
/* correct distance */
|
|
// ratio = ipow(particle.eq_dist*particle.radius/rplus, 6);
|
|
ratio = ipow(particle.eq_dist*particle.radius/rplus, 3);
|
|
ratio = ratio*ratio;
|
|
|
|
/* cos(2*phi) and sin(2*phi) */
|
|
|
|
ca2 = ca_rel*ca_rel - sa_rel*sa_rel;
|
|
sa2 = 2.0*ca_rel*sa_rel;
|
|
|
|
a = a0 + b0*ca2;
|
|
if (a == 0.0) a = 1.0e-10;
|
|
aprime = -2.0*b0*sa2;
|
|
|
|
// f1 = ratio*(a - ratio)/rplus;
|
|
// f2 = ratio*aprime/rplus;
|
|
|
|
f1 = ratio*(aplus - ratio)/(rplus);
|
|
f2 = ratio*(aminus - ratio)/(rplus);
|
|
|
|
// force[0] = f1*ca_rel - f2*sa_rel;
|
|
// force[1] = f1*sa_rel + f2*ca_rel;
|
|
|
|
force[0] = f1*ca - f2*sa;
|
|
force[1] = f1*sa + f2*ca;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int compute_repelling_force(int i, int j, double force[2], double *torque, t_particle* particle,
|
|
double krepel, int wrapx, int wrapy)
|
|
/* compute repelling force of particle j on particle i */
|
|
/* returns 1 if distance between particles is smaller than NBH_DIST_FACTOR*MU */
|
|
{
|
|
double x1, y1, x2, y2, x3, y3, distance, r, f, angle, ca, sa, aniso, fx, fy, ff[2], cj, sj, ca_rel, sa_rel, dist_scaled, spin_f;
|
|
double xtemp, ytemp;
|
|
static double deltax = XMAX - XMIN, deltay = YMAX - YMIN, dxhalf = 0.5*(XMAX - XMIN), dyhalf = 0.5*(YMAX - YMIN);
|
|
int periodx, periody, wwrapx;
|
|
// static double factor = 0.5*(XMAX - XMIN);
|
|
// int interaction;
|
|
|
|
x1 = particle[i].xc;
|
|
y1 = particle[i].yc;
|
|
x2 = particle[j].xc;
|
|
y2 = particle[j].yc;
|
|
|
|
/* for monitoring purposes only */
|
|
xtemp = x2;
|
|
ytemp = y2;
|
|
wwrapx = (BOUNDARY_COND == BC_KLEIN)&&(vabs(x2 - x1) > dxhalf);
|
|
|
|
/* case of periodic boundary conditions */
|
|
if ((BOUNDARY_COND == BC_PERIODIC)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE))
|
|
{
|
|
if (wrapy != 0)
|
|
{
|
|
if (y2 > 0.0) y2 -= deltay;
|
|
else y2 += deltay;
|
|
}
|
|
if (wrapx != 0)
|
|
{
|
|
if (x2 > 0.0) x2 -= deltax;
|
|
else x2 += deltax;
|
|
}
|
|
}
|
|
distance = module2(x2 - x1, y2 - y1);
|
|
|
|
if ((distance == 0.0)||(i == j))
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
*torque = 0.0;
|
|
return(1);
|
|
}
|
|
else if (distance > REPEL_RADIUS*particle[i].radius)
|
|
{
|
|
force[0] = 0.0;
|
|
force[1] = 0.0;
|
|
*torque = 0.0;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
ca = (x2 - x1)/distance;
|
|
sa = (y2 - y1)/distance;
|
|
|
|
/* compute relative angle in case particles can rotate */
|
|
if (ROTATION)
|
|
{
|
|
cj = cos(particle[j].angle);
|
|
sj = sin(particle[j].angle);
|
|
ca_rel = ca*cj + sa*sj;
|
|
sa_rel = sa*cj - ca*sj;
|
|
}
|
|
else
|
|
{
|
|
ca_rel = ca;
|
|
sa_rel = sa;
|
|
}
|
|
|
|
switch (particle[j].interaction) {
|
|
case (I_COULOMB):
|
|
{
|
|
f = -krepel/(1.0e-8 + distance*distance);
|
|
force[0] = f*ca;
|
|
force[1] = f*sa;
|
|
break;
|
|
}
|
|
case (I_LENNARD_JONES):
|
|
{
|
|
f = krepel*lennard_jones_force(distance, particle[j]);
|
|
force[0] = f*ca;
|
|
force[1] = f*sa;
|
|
break;
|
|
}
|
|
case (I_LJ_DIRECTIONAL):
|
|
{
|
|
aniso_lj_force(distance, ca, sa, ca_rel, sa_rel, ff, particle[j]);
|
|
force[0] = krepel*ff[0];
|
|
force[1] = krepel*ff[1];
|
|
break;
|
|
}
|
|
case (I_LJ_PENTA):
|
|
{
|
|
penta_lj_force(distance, ca, sa, ca_rel, sa_rel, ff, particle[j]);
|
|
force[0] = krepel*ff[0];
|
|
force[1] = krepel*ff[1];
|
|
break;
|
|
}
|
|
case (I_GOLDENRATIO):
|
|
{
|
|
f = krepel*golden_ratio_force(distance, particle[j]);
|
|
force[0] = f*ca;
|
|
force[1] = f*sa;
|
|
break;
|
|
}
|
|
case (I_LJ_DIPOLE):
|
|
{
|
|
dipole_lj_force(distance, ca, sa, ca_rel, sa_rel, ff, particle[j]);
|
|
force[0] = krepel*ff[0];
|
|
force[1] = krepel*ff[1];
|
|
break;
|
|
}
|
|
case (I_LJ_QUADRUPOLE):
|
|
{
|
|
quadrupole_lj_force(distance, ca, sa, ca_rel, sa_rel, ff, particle[j]);
|
|
force[0] = krepel*ff[0];
|
|
force[1] = krepel*ff[1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ROTATION)
|
|
{
|
|
dist_scaled = distance/(particle[i].spin_range*particle[i].radius);
|
|
spin_f = particle[i].spin_freq;
|
|
*torque = sin(spin_f*(particle[j].angle - particle[i].angle))/(1.0e-8 + dist_scaled*dist_scaled);
|
|
|
|
if (particle[i].type == particle[j].type)
|
|
{
|
|
if (particle[i].type == 0) *torque *= KTORQUE;
|
|
else *torque *= KTORQUE_B;
|
|
}
|
|
else *torque *= KTORQUE_DIFF;
|
|
}
|
|
// if (ROTATION) *torque = -sin(particle[j].angle - particle[i].angle)/(1.0e-8 + distance*distance);
|
|
// if (ROTATION) *torque = ff[0]*sin(particle[i].angle) - ff[1]*cos(particle[i].angle);
|
|
// if (ROTATION) *torque = ff[0]*sin(particle[j].angle) - ff[1]*cos(particle[j].angle);
|
|
else *torque = 0.0;
|
|
|
|
if ((distance < NBH_DIST_FACTOR*particle[i].radius)&&(j != i)) return(1);
|
|
else return(0);
|
|
}
|
|
|
|
|
|
|
|
void update_hashgrid(t_particle* particle, int* hashgrid_number, int* hashgrid_particles)
|
|
{
|
|
int i, j, k, n, m, ij[2], max = 0;
|
|
|
|
// printf("Updating hashgrid_number\n");
|
|
for (i=0; i<HASHX*HASHY; i++) hashgrid_number[i] = 0;
|
|
// printf("Updated hashgrid_number\n");
|
|
|
|
/* place each particle in hash grid */
|
|
for (k=1; k<ncircles; k++)
|
|
// if (circleactive[k])
|
|
{
|
|
// printf("placing circle %i\t", k);
|
|
hash_xy_to_ij(particle[k].xc, particle[k].yc, ij);
|
|
i = ij[0]; j = ij[1];
|
|
// printf("ij = (%i, %i)\t", i, j);
|
|
n = hashgrid_number[i*HASHY + j];
|
|
m = i*HASHY*HASHMAX + j*HASHMAX + n;
|
|
// printf("n = %i, m = %i\n", n, m);
|
|
if (m < HASHX*HASHY*HASHMAX) hashgrid_particles[m] = k;
|
|
else printf("Too many particles in hash cell, try increasing HASHMAX\n");
|
|
hashgrid_number[i*HASHY + j]++;
|
|
particle[k].hashx = i;
|
|
particle[k].hashy = j;
|
|
|
|
if (n > max) max = n;
|
|
// printf("Placed particle %i at (%i,%i) in hashgrid\n", k, ij[0], ij[1]);
|
|
// printf("%i particles at (%i,%i)\n", hashgrid_number[ij[0]][ij[1]], ij[0], ij[1]);
|
|
}
|
|
|
|
printf("Maximal number of particles per hash cell: %i\n", max);
|
|
}
|
|
|
|
int add_particle(double x, double y, double vx, double vy, double mass, short int type, t_particle particle[NMAXCIRCLES])
|
|
{
|
|
int i, closeby = 0;
|
|
double dist;
|
|
|
|
/* test distance to other particles */
|
|
for (i=0; i<ncircles; i++)
|
|
{
|
|
dist = module2(x - particle[i].xc, y - particle[i].yc);
|
|
if (dist < SAFETY_FACTOR*MU) closeby = 1;
|
|
}
|
|
|
|
if ((closeby)||(ncircles >= NMAXCIRCLES))
|
|
{
|
|
printf("Cannot add particle at (%.3lg, %.3lg)\n", x, y);
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
i = ncircles;
|
|
|
|
particle[i].type = type;
|
|
|
|
particle[i].xc = x;
|
|
particle[i].yc = y;
|
|
particle[i].radius = MU*sqrt(mass);
|
|
particle[i].active = 1;
|
|
particle[i].neighb = 0;
|
|
particle[i].thermostat = 1;
|
|
|
|
particle[i].energy = 0.0;
|
|
|
|
if (RANDOM_RADIUS) particle[i].radius = particle[i].radius*(0.75 + 0.5*((double)rand()/RAND_MAX));
|
|
|
|
particle[i].mass_inv = 1.0/mass;
|
|
if (particle[i].type == 0) particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT;
|
|
else particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT;
|
|
|
|
particle[i].vx = vx;
|
|
particle[i].vy = vy;
|
|
particle[i].energy = (particle[i].vx*particle[i].vx + particle[i].vy*particle[i].vy)*particle[i].mass_inv;
|
|
|
|
particle[i].angle = DPI*(double)rand()/RAND_MAX;
|
|
particle[i].omega = 0.0;
|
|
|
|
if (particle[i].type == 1)
|
|
{
|
|
particle[i].interaction = INTERACTION_B;
|
|
particle[i].eq_dist = EQUILIBRIUM_DIST_B;
|
|
particle[i].spin_range = SPIN_RANGE_B;
|
|
particle[i].spin_freq = SPIN_INTER_FREQUENCY_B;
|
|
}
|
|
|
|
ncircles++;
|
|
|
|
printf("Added particle at (%.3lg, %.3lg)\n", x, y);
|
|
printf("Number of particles: %i\n", ncircles);
|
|
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
double neighbour_color(int nnbg)
|
|
{
|
|
if (nnbg > 7) nnbg = 7;
|
|
switch(nnbg){
|
|
case (7): return(340.0);
|
|
case (6): return(310.0);
|
|
case (5): return(260.0);
|
|
case (4): return(200.0);
|
|
case (3): return(140.0);
|
|
case (2): return(100.0);
|
|
case (1): return(70.0);
|
|
default: return(30.0);
|
|
}
|
|
}
|
|
|
|
void draw_one_particle(t_particle particle, double xc, double yc, double radius, double angle, int nsides, double width, double rgb[3])
|
|
/* draw one of the particles */
|
|
{
|
|
double ca, sa, x1, x2, y1, y2, xc1;
|
|
|
|
if (CENTER_VIEW_ON_OBSTACLE)
|
|
{
|
|
xc1 = xc - xshift;
|
|
if (xc1 < XMIN) xc1 += XMAX - XMIN;
|
|
else if (xc1 > XMAX) xc1 -= XMAX - XMIN;
|
|
}
|
|
else xc1 = xc;
|
|
glColor3f(rgb[0], rgb[1], rgb[2]);
|
|
if ((particle.interaction == I_LJ_QUADRUPOLE)||(particle.interaction == I_LJ_DIPOLE))
|
|
draw_colored_rhombus(xc1, yc, radius, angle + APOLY*PID, rgb);
|
|
else draw_colored_polygon(xc1, yc, radius, nsides, angle + APOLY*PID, rgb);
|
|
|
|
/* draw crosses on particles of second type */
|
|
if ((TWO_TYPES)&&(DRAW_CROSS))
|
|
if (particle.type == 1)
|
|
{
|
|
if (ROTATION) angle = angle + APOLY*PID;
|
|
else angle = APOLY*PID;
|
|
ca = cos(angle);
|
|
sa = sin(angle);
|
|
glLineWidth(3);
|
|
glColor3f(0.0, 0.0, 0.0);
|
|
x1 = xc1 - MU_B*ca;
|
|
y1 = yc - MU_B*sa;
|
|
x2 = xc1 + MU_B*ca;
|
|
y2 = yc + MU_B*sa;
|
|
draw_line(x1, y1, x2, y2);
|
|
x1 = xc1 - MU_B*sa;
|
|
y1 = yc + MU_B*ca;
|
|
x2 = xc1 + MU_B*sa;
|
|
y2 = yc - MU_B*ca;
|
|
draw_line(x1, y1, x2, y2);
|
|
}
|
|
|
|
glLineWidth(width);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
if ((particle.interaction == I_LJ_QUADRUPOLE)||(particle.interaction == I_LJ_DIPOLE))
|
|
draw_rhombus(xc1, yc, radius, angle + APOLY*PID);
|
|
else draw_polygon(xc1, yc, radius, nsides, angle + APOLY*PID);
|
|
}
|
|
|
|
void draw_particles(t_particle particle[NMAXCIRCLES], int plot)
|
|
{
|
|
int i, j, k, m, width, nnbg, nsides;
|
|
double ej, hue, rgb[3], radius, x1, y1, x2, y2, angle, ca, sa, length, linkcolor;
|
|
|
|
blank();
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
|
|
/* draw the bonds first */
|
|
if (plot == P_BONDS)
|
|
{
|
|
glLineWidth(LINK_WIDTH);
|
|
for (j=0; j<ncircles; j++) if (particle[j].active)
|
|
{
|
|
// radius = particle[j].radius;
|
|
for (k = 0; k < particle[j].neighb; k++)
|
|
{
|
|
m = particle[j].neighbours[k];
|
|
// angle = particle[j].nghangle[k];
|
|
x1 = particle[j].xc;
|
|
if (CENTER_VIEW_ON_OBSTACLE) x1 -= xshift;
|
|
y1 = particle[j].yc;
|
|
x2 = particle[m].xc;
|
|
if (CENTER_VIEW_ON_OBSTACLE) x2 -= xshift;
|
|
y2 = particle[m].yc;
|
|
|
|
/* case of periodic boundary conditions */
|
|
if ((BOUNDARY_COND == BC_PERIODIC)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE))
|
|
{
|
|
if (x2 - x1 > 0.5*(XMAX - XMIN)) x1 += XMAX - XMIN;
|
|
else if (x2 - x1 < -0.5*(XMAX - XMIN)) x1 -= XMAX - XMIN;
|
|
if (y2 - y1 > 0.5*(YMAX - YMIN)) y1 += YMAX - YMIN;
|
|
else if (y2 - y1 < -0.5*(YMAX - YMIN)) y1 -= YMAX - YMIN;
|
|
}
|
|
|
|
if (COLOR_BONDS)
|
|
{
|
|
length = module2(x1 - x2, y1 - y2)/particle[j].radius;
|
|
if (length < 1.5) linkcolor = 1.0;
|
|
else linkcolor = 1.0 - 0.75*(length - 1.5)/(NBH_DIST_FACTOR - 1.5);
|
|
// printf("length = %.3lg\t color = %.3lg\n", length, linkcolor);
|
|
glColor3f(linkcolor, linkcolor, linkcolor);
|
|
}
|
|
|
|
draw_line(x1, y1, x2, y2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* determine particle color and size */
|
|
for (j=0; j<ncircles; j++) if (particle[j].active)
|
|
{
|
|
switch (plot) {
|
|
case (P_KINETIC):
|
|
{
|
|
ej = particle[j].energy;
|
|
if (ej > 0.0)
|
|
{
|
|
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*ej/PARTICLE_EMAX;
|
|
if (hue > PARTICLE_HUE_MIN) hue = PARTICLE_HUE_MIN;
|
|
if (hue < PARTICLE_HUE_MAX) hue = PARTICLE_HUE_MAX;
|
|
}
|
|
radius = particle[j].radius;
|
|
width = BOUNDARY_WIDTH;
|
|
break;
|
|
}
|
|
case (P_NEIGHBOURS):
|
|
{
|
|
hue = neighbour_color(particle[j].neighb);
|
|
radius = particle[j].radius;
|
|
width = BOUNDARY_WIDTH;
|
|
break;
|
|
}
|
|
case (P_BONDS):
|
|
{
|
|
// if (particle[j].type == 1) hue = 70.0; /* to make second particle type more visible */
|
|
// if (particle[j].type == 1) hue = neighbour_color(7 - particle[j].neighb);
|
|
// else
|
|
hue = neighbour_color(particle[j].neighb);
|
|
radius = particle[j].radius;
|
|
width = 1;
|
|
break;
|
|
}
|
|
case (P_ANGLE):
|
|
{
|
|
hue = particle[j].angle*particle[j].spin_freq/DPI;
|
|
hue -= (double)((int)hue);
|
|
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*hue;
|
|
radius = particle[j].radius;
|
|
width = BOUNDARY_WIDTH;
|
|
break;
|
|
}
|
|
case (P_TYPE):
|
|
{
|
|
if (particle[j].type == 0) hue = 310.0;
|
|
else hue = 70.0;
|
|
radius = particle[j].radius;
|
|
width = BOUNDARY_WIDTH;
|
|
break;
|
|
}
|
|
case (P_DIRECTION):
|
|
{
|
|
hue = argument(particle[j].vx, particle[j].vy);
|
|
if (hue > DPI) hue -= DPI;
|
|
if (hue < 0.0) hue += DPI;
|
|
hue = PARTICLE_HUE_MIN + (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*hue/DPI;
|
|
radius = particle[j].radius;
|
|
width = BOUNDARY_WIDTH;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (particle[j].interaction) {
|
|
case (I_LJ_DIRECTIONAL):
|
|
{
|
|
nsides = 4;
|
|
break;
|
|
}
|
|
case (I_LJ_PENTA):
|
|
{
|
|
nsides = 5;
|
|
break;
|
|
}
|
|
case (I_LJ_QUADRUPOLE):
|
|
{
|
|
nsides = 4;
|
|
break;
|
|
}
|
|
default: nsides = NSEG;
|
|
}
|
|
|
|
if (plot == P_BONDS) hsl_to_rgb_turbo(hue, 0.9, 0.5, rgb);
|
|
else if (plot == P_DIRECTION) hsl_to_rgb_twilight(hue, 0.9, 0.5, rgb);
|
|
else hsl_to_rgb(hue, 0.9, 0.5, rgb);
|
|
angle = particle[j].angle + APOLY*DPI;
|
|
|
|
draw_one_particle(particle[j], particle[j].xc, particle[j].yc, radius, angle, nsides, width, rgb);
|
|
|
|
/* in case of periodic b.c., draw translates of particles */
|
|
if ((BOUNDARY_COND == BC_PERIODIC)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE))
|
|
{
|
|
x1 = particle[j].xc;
|
|
y1 = particle[j].yc;
|
|
|
|
for (i=-2; i<3; i++)
|
|
for (k=-1; k<2; k++)
|
|
draw_one_particle(particle[j], x1 + (double)i*(XMAX - XMIN), y1 + (double)k*(YMAX - YMIN), radius, angle, nsides, width, rgb);
|
|
}
|
|
}
|
|
|
|
/* draw spin vectors */
|
|
if ((DRAW_SPIN)||(DRAW_SPIN_B))
|
|
{
|
|
glLineWidth(width);
|
|
for (j=0; j<ncircles; j++)
|
|
if ((particle[j].active)&&(((DRAW_SPIN)&&(particle[j].type == 0))||((DRAW_SPIN_B)&&(particle[j].type == 1))))
|
|
{
|
|
// x1 = particle[j].xc - 2.0*MU*cos(particle[j].angle);
|
|
// y1 = particle[j].yc - 2.0*MU*sin(particle[j].angle);
|
|
x1 = particle[j].xc;
|
|
// if (CENTER_VIEW_ON_OBSTACLE) x1 -= xshift;
|
|
y1 = particle[j].yc;
|
|
x2 = particle[j].xc + 2.0*MU*cos(particle[j].angle);
|
|
// if (CENTER_VIEW_ON_OBSTACLE) x2 -= xshift;
|
|
y2 = particle[j].yc + 2.0*MU*sin(particle[j].angle);
|
|
draw_line(x1, y1, x2, y2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void draw_container(double xmin, double xmax)
|
|
/* draw the container, for certain boundary conditions */
|
|
{
|
|
int i;
|
|
double rgb[3], x;
|
|
char message[100];
|
|
|
|
switch (BOUNDARY_COND) {
|
|
case (BC_SCREEN):
|
|
{
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
case (BC_RECTANGLE):
|
|
{
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
glLineWidth(CONTAINER_WIDTH);
|
|
|
|
draw_line(INITXMIN, INITYMIN, INITXMAX, INITYMIN);
|
|
draw_line(INITXMIN, INITYMAX, INITXMAX, INITYMAX);
|
|
|
|
if (!SYMMETRIC_DECREASE) draw_line(INITXMAX, INITYMIN, INITXMAX, INITYMAX);
|
|
|
|
draw_line(xmin, INITYMIN, xmin, INITYMAX);
|
|
draw_line(XMIN, 0.5*(INITYMIN + INITYMAX), xmin, 0.5*(INITYMIN + INITYMAX));
|
|
|
|
if (SYMMETRIC_DECREASE)
|
|
{
|
|
draw_line(xmax, INITYMIN, xmax, INITYMAX);
|
|
draw_line(XMAX, 0.5*(INITYMIN + INITYMAX), xmax, 0.5*(INITYMIN + INITYMAX));
|
|
}
|
|
|
|
break;
|
|
}
|
|
case (BC_CIRCLE):
|
|
{
|
|
glLineWidth(CONTAINER_WIDTH);
|
|
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
|
|
for (i=-1; i<2; i++)
|
|
{
|
|
x = xmin + (double)i*(XMAX - XMIN);
|
|
draw_colored_circle(x, 0.0, OBSTACLE_RADIUS, NSEG, rgb);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
draw_circle(x, 0.0, OBSTACLE_RADIUS, NSEG);
|
|
}
|
|
}
|
|
case (BC_PERIODIC_CIRCLE):
|
|
{
|
|
glLineWidth(CONTAINER_WIDTH);
|
|
hsl_to_rgb(300.0, 0.1, 0.5, rgb);
|
|
for (i=-1; i<2; i++)
|
|
{
|
|
if (CENTER_VIEW_ON_OBSTACLE) draw_colored_circle(0.0, 0.0, OBSTACLE_RADIUS, NSEG, rgb);
|
|
// x = xmin + (double)i*(XMAX - XMIN);
|
|
else draw_colored_circle(xmin + (double)i*(XMAX - XMIN), 0.0, OBSTACLE_RADIUS, NSEG, rgb);
|
|
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
draw_circle(x, 0.0, OBSTACLE_RADIUS, NSEG);
|
|
|
|
glColor3f(0.0, 0.0, 0.0);
|
|
sprintf(message, "Speed %.2f", xspeed);
|
|
write_text(-0.17, -0.025, message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void print_parameters(double beta, double temperature, double krepel, double lengthcontainer, double boundary_force,
|
|
short int left)
|
|
{
|
|
char message[100];
|
|
int i;
|
|
double density, hue, rgb[3], logratio, y;
|
|
static double xbox, xtext, xmid, xmidtext, xxbox, xxtext, pressures[100], meanpressure = 0.0;
|
|
static int first = 1, i_pressure, naverage = 100;
|
|
|
|
if (first)
|
|
{
|
|
if (left)
|
|
{
|
|
xbox = XMIN + 0.4;
|
|
xtext = XMIN + 0.08;
|
|
xxbox = XMAX - 0.39;
|
|
xxtext = XMAX - 0.73;
|
|
}
|
|
else
|
|
{
|
|
xbox = XMAX - 0.39;
|
|
xtext = XMAX - 0.73;
|
|
xxbox = XMIN + 0.4;
|
|
xxtext = XMIN + 0.08;
|
|
}
|
|
xmid = 0.5*(XMIN + XMAX) - 0.1;
|
|
xmidtext = xmid - 0.24;
|
|
for (i=0; i<naverage; i++) pressures[i] = 0.0;
|
|
i_pressure = 0;
|
|
|
|
first = 0;
|
|
}
|
|
|
|
/* table of pressures */
|
|
pressures[i_pressure] = boundary_force/(lengthcontainer + INITYMAX - INITYMIN);
|
|
i_pressure++;
|
|
if (i_pressure == naverage) i_pressure = 0;
|
|
|
|
for (i=0; i<naverage; i++) meanpressure += pressures[i];
|
|
meanpressure = meanpressure/(double)naverage;
|
|
|
|
y = YMAX - 0.1;
|
|
if (INCREASE_BETA) /* print temperature */
|
|
{
|
|
logratio = log(beta/BETA)/log(0.5*BETA_FACTOR);
|
|
if (logratio > 1.0) logratio = 1.0;
|
|
if (BETA_FACTOR > 1.0) hue = PARTICLE_HUE_MAX - (PARTICLE_HUE_MAX - PARTICLE_HUE_MIN)*logratio;
|
|
else hue = PARTICLE_HUE_MIN - (PARTICLE_HUE_MIN - PARTICLE_HUE_MAX)*logratio;
|
|
erase_area_hsl_turbo(xbox, y + 0.025, 0.37, 0.05, hue, 0.9, 0.5);
|
|
if ((hue < 90)||(hue > 270)) glColor3f(1.0, 1.0, 1.0);
|
|
else glColor3f(0.0, 0.0, 0.0);
|
|
sprintf(message, "Temperature %.3f", 1.0/beta);
|
|
write_text(xtext, y, message);
|
|
y -= 0.1;
|
|
}
|
|
if (DECREASE_CONTAINER_SIZE) /* print density */
|
|
{
|
|
density = (double)ncircles/((lengthcontainer)*(INITYMAX - INITYMIN));
|
|
erase_area_hsl(xbox, y + 0.025, 0.37, 0.05, 0.0, 0.9, 0.0);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
sprintf(message, "Density %.3f", density);
|
|
write_text(xtext, y, message);
|
|
|
|
erase_area_hsl(xmid, y + 0.025, 0.37, 0.05, 0.0, 0.9, 0.0);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
sprintf(message, "Temperature %.3f", temperature);
|
|
write_text(xmidtext, y, message);
|
|
|
|
erase_area_hsl(xxbox, y + 0.025, 0.37, 0.05, 0.0, 0.9, 0.0);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
sprintf(message, "Pressure %.3f", meanpressure);
|
|
write_text(xxtext, y, message);
|
|
|
|
}
|
|
else if (INCREASE_KREPEL) /* print force constant */
|
|
{
|
|
erase_area_hsl(xbox, y + 0.025, 0.22, 0.05, 0.0, 0.9, 0.0);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
sprintf(message, "Force %.0f", krepel);
|
|
write_text(xtext + 0.28, y, message);
|
|
}
|
|
}
|
|
|
|
|
|
double repel_schedule(int i)
|
|
{
|
|
static double kexponent;
|
|
static int first = 1;
|
|
double krepel;
|
|
|
|
if (first)
|
|
{
|
|
kexponent = log(KREPEL_FACTOR)/(double)(INITIAL_TIME + NSTEPS);
|
|
first = 0;
|
|
}
|
|
krepel = KREPEL*exp(kexponent*(double)i);
|
|
printf("krepel = %.3lg\n", krepel);
|
|
return(krepel);
|
|
}
|
|
|
|
|
|
double temperature_schedule(int i)
|
|
{
|
|
static double bexponent, omega;
|
|
static int first = 1;
|
|
double beta;
|
|
|
|
if (first)
|
|
{
|
|
bexponent = log(BETA_FACTOR)/(double)(INITIAL_TIME + NSTEPS - FINAL_CONSTANT_PHASE);
|
|
omega = N_TOSCILLATIONS*DPI/(double)(INITIAL_TIME + NSTEPS - FINAL_CONSTANT_PHASE);
|
|
first = 0;
|
|
}
|
|
if (i < INITIAL_TIME) beta = BETA;
|
|
else if (i > INITIAL_TIME + NSTEPS - FINAL_CONSTANT_PHASE) beta = BETA*BETA_FACTOR;
|
|
else
|
|
{
|
|
beta = BETA*exp(bexponent*(double)(i - INITIAL_TIME));
|
|
if (!NO_OSCILLATION) beta = beta*2.0/(1.0 + cos(omega*(double)(i - INITIAL_TIME)));
|
|
}
|
|
printf("beta = %.3lg\n", beta);
|
|
return(beta);
|
|
}
|
|
|
|
double container_size_schedule(int i)
|
|
{
|
|
if ((i < INITIAL_TIME)||(i > INITIAL_TIME + NSTEPS - RESTORE_TIME)) return(INITXMIN);
|
|
else
|
|
return(INITXMIN + (1.0-COMPRESSION_RATIO)*(INITXMAX-INITXMIN)*(double)(i-INITIAL_TIME)/(double)(NSTEPS-RESTORE_TIME));
|
|
}
|
|
|
|
double obstacle_schedule_old(int i)
|
|
{
|
|
double time;
|
|
static double t1 = 0.5, t2 = 0.75, t3 = 0.875;
|
|
|
|
if (i < INITIAL_TIME) return(OBSTACLE_XMIN);
|
|
else
|
|
{
|
|
time = (double)(i-INITIAL_TIME)/(double)(NSTEPS);
|
|
|
|
if (time < t1) return(OBSTACLE_XMIN + (OBSTACLE_XMAX - OBSTACLE_XMIN)*time/t1);
|
|
else if (time < t2) return(OBSTACLE_XMIN + (OBSTACLE_XMAX - OBSTACLE_XMIN)*(time - t1)/(t2 - t1));
|
|
else if (time < t3) return(OBSTACLE_XMIN + (OBSTACLE_XMAX - OBSTACLE_XMIN)*(time - t2)/(t3 - t2));
|
|
else return(OBSTACLE_XMAX);
|
|
}
|
|
}
|
|
|
|
double obstacle_schedule(int i)
|
|
{
|
|
double time;
|
|
double x;
|
|
// static double t1 = 0.5, t2 = 0.75, t3 = 0.875;
|
|
|
|
if (i < INITIAL_TIME) return(OBSTACLE_XMIN);
|
|
else
|
|
{
|
|
time = (double)(i-INITIAL_TIME)/(double)(NSTEPS);
|
|
|
|
x = OBSTACLE_XMIN + 50.0*time*time;
|
|
xspeed = 100.0*time;
|
|
while (x > XMAX) x += XMIN - XMAX;
|
|
return(x);
|
|
}
|
|
}
|
|
|
|
double compute_boundary_force(t_particle particle, double *fx, double *fy, double xleft, double xright)
|
|
{
|
|
int i;
|
|
double xmin, xmax, ymin, ymax, padding, r, cphi, sphi, f, fperp = 0.0, x;
|
|
|
|
switch(BOUNDARY_COND){
|
|
case (BC_SCREEN):
|
|
{
|
|
/* add harmonic force outside screen */
|
|
if (particle.xc > XMAX) *fx -= KSPRING_BOUNDARY*(particle.xc - XMAX);
|
|
else if (particle.xc < XMIN) *fx += KSPRING_BOUNDARY*(XMIN - particle.xc);
|
|
if (particle.yc > YMAX) *fy -= KSPRING_BOUNDARY*(particle.yc - YMAX);
|
|
else if (particle.yc < YMIN) *fy += KSPRING_BOUNDARY*(YMIN - particle.yc);
|
|
return(fperp);
|
|
}
|
|
case (BC_RECTANGLE):
|
|
{
|
|
/* add harmonic force outside rectangular box */
|
|
padding = MU + 0.01;
|
|
xmin = xleft + padding;
|
|
xmax = xright - padding;
|
|
ymin = INITYMIN + padding;
|
|
ymax = INITYMAX - padding;
|
|
|
|
if (particle.xc > xmax)
|
|
{
|
|
fperp = KSPRING_BOUNDARY*(particle.xc - xmax);
|
|
*fx -= fperp;
|
|
}
|
|
else if (particle.xc < xmin)
|
|
{
|
|
fperp = KSPRING_BOUNDARY*(xmin - particle.xc);
|
|
*fx += fperp;
|
|
}
|
|
if (particle.yc > ymax)
|
|
{
|
|
fperp = KSPRING_BOUNDARY*(particle.yc - ymax);
|
|
*fy -= fperp;
|
|
}
|
|
else if (particle.yc < ymin)
|
|
{
|
|
fperp = KSPRING_BOUNDARY*(ymin - particle.yc);
|
|
*fy += fperp;
|
|
}
|
|
// if (particle.xc > xmax) *fx -= KSPRING_BOUNDARY*(particle.xc - xmax);
|
|
// else if (particle.xc < xmin) *fx += KSPRING_BOUNDARY*(xmin - particle.xc);
|
|
// if (particle.yc > ymax) *fy -= KSPRING_BOUNDARY*(particle.yc - ymax);
|
|
// else if (particle.yc < ymin) *fy += KSPRING_BOUNDARY*(ymin - particle.yc);
|
|
|
|
return(fperp);
|
|
}
|
|
case (BC_CIRCLE):
|
|
{
|
|
/* add harmonic force outside screen */
|
|
if (particle.xc > XMAX) *fx -= KSPRING_BOUNDARY*(particle.xc - XMAX);
|
|
else if (particle.xc < XMIN) *fx += KSPRING_BOUNDARY*(XMIN - particle.xc);
|
|
if (particle.yc > YMAX) *fy -= KSPRING_BOUNDARY*(particle.yc - YMAX);
|
|
else if (particle.yc < YMIN) *fy += KSPRING_BOUNDARY*(YMIN - particle.yc);
|
|
|
|
/* add harmonic force from obstacle */
|
|
for (i=-1; i<2; i++)
|
|
{
|
|
x = xleft + (double)i*(XMAX - XMIN);
|
|
if (vabs(particle.xc - x) < 1.1*OBSTACLE_RADIUS)
|
|
{
|
|
r = module2(particle.xc - x, particle.yc);
|
|
if (r < 1.0e-5) r = 1.0e-05;
|
|
cphi = (particle.xc - x)/r;
|
|
sphi = particle.yc/r;
|
|
padding = MU + 0.03;
|
|
if (r < OBSTACLE_RADIUS + padding)
|
|
{
|
|
f = KSPRING_BOUNDARY*(OBSTACLE_RADIUS + padding - r);
|
|
*fx += f*cphi;
|
|
*fy += f*sphi;
|
|
}
|
|
}
|
|
}
|
|
return(fperp);
|
|
}
|
|
case (BC_PERIODIC_CIRCLE):
|
|
{
|
|
/* add harmonic force from obstacle */
|
|
for (i=-1; i<2; i++)
|
|
{
|
|
x = xleft + (double)i*(XMAX - XMIN);
|
|
if (vabs(particle.xc - x) < 1.1*OBSTACLE_RADIUS)
|
|
{
|
|
r = module2(particle.xc - x, particle.yc);
|
|
if (r < 1.0e-5) r = 1.0e-05;
|
|
cphi = (particle.xc - x)/r;
|
|
sphi = particle.yc/r;
|
|
padding = MU + 0.03;
|
|
if (r < OBSTACLE_RADIUS + padding)
|
|
{
|
|
f = KSPRING_BOUNDARY*(OBSTACLE_RADIUS + padding - r);
|
|
*fx += f*cphi;
|
|
*fy += f*sphi;
|
|
}
|
|
}
|
|
}
|
|
return(fperp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void animation()
|
|
{
|
|
double time, scale, diss, rgb[3], dissip, gradient[2], x, y, dx, dy, dt, xleft, xright, a, b,
|
|
length, fx, fy, force[2], totalenergy = 0.0, krepel = KREPEL, pos[2], prop, vx,
|
|
beta = BETA, xi = 0.0, xmincontainer = INITXMIN, xmaxcontainer = INITXMAX, torque, torque_ij,
|
|
fboundary = 0.0;
|
|
double *qx, *qy, *px, *py, *qangle, *pangle;
|
|
int i, j, k, n, m, s, ij[2], i0, iplus, iminus, j0, jplus, jminus, p, q, p1, q1, total_neighbours = 0,
|
|
min_nb, max_nb, close, wrapx = 0, wrapy = 0, nactive = 0, nadd_particle = 0;
|
|
static int imin, imax;
|
|
static short int first = 1;
|
|
t_particle *particle;
|
|
int *hashgrid_number, *hashgrid_particles;
|
|
t_hashgrid *hashgrid;
|
|
char message[100];
|
|
|
|
|
|
particle = (t_particle *)malloc(NMAXCIRCLES*sizeof(t_particle)); /* particles */
|
|
|
|
hashgrid = (t_hashgrid *)malloc(HASHX*HASHY*sizeof(t_hashgrid)); /* hashgrid */
|
|
|
|
hashgrid_number = (int *)malloc(HASHX*HASHY*sizeof(int)); /* total number of particles in each hash grid cell */
|
|
hashgrid_particles = (int *)malloc(HASHX*HASHY*HASHMAX*sizeof(int)); /* numbers of particles in each hash grid cell */
|
|
|
|
qx = (double *)malloc(NMAXCIRCLES*sizeof(double));
|
|
qy = (double *)malloc(NMAXCIRCLES*sizeof(double));
|
|
px = (double *)malloc(NMAXCIRCLES*sizeof(double));
|
|
py = (double *)malloc(NMAXCIRCLES*sizeof(double));
|
|
qangle = (double *)malloc(NMAXCIRCLES*sizeof(double));
|
|
pangle = (double *)malloc(NMAXCIRCLES*sizeof(double));
|
|
|
|
/* initialise positions and radii of circles */
|
|
init_particle_config(particle);
|
|
|
|
|
|
/* initialise particles */
|
|
for (i=0; i < NMAXCIRCLES; i++)
|
|
{
|
|
/* set particle type */
|
|
particle[i].type = 0;
|
|
if ((TWO_TYPES)&&((double)rand()/RAND_MAX > TPYE_PROPORTION))
|
|
{
|
|
particle[i].type = 1;
|
|
particle[i].radius = MU_B;
|
|
}
|
|
|
|
particle[i].neighb = 0;
|
|
particle[i].thermostat = 1;
|
|
|
|
// particle[i].energy = 0.0;
|
|
y = particle[i].yc;
|
|
if (y >= YMAX) y -= particle[i].radius;
|
|
if (y <= YMIN) y += particle[i].radius;
|
|
|
|
if (RANDOM_RADIUS) particle[i].radius = particle[i].radius*(0.75 + 0.5*((double)rand()/RAND_MAX));
|
|
|
|
if (particle[i].type == 0)
|
|
{
|
|
particle[i].interaction = INTERACTION;
|
|
particle[i].eq_dist = EQUILIBRIUM_DIST;
|
|
particle[i].spin_range = SPIN_RANGE;
|
|
particle[i].spin_freq = SPIN_INTER_FREQUENCY;
|
|
particle[i].mass_inv = 1.0/PARTICLE_MASS;
|
|
particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT;
|
|
}
|
|
else
|
|
{
|
|
particle[i].interaction = INTERACTION_B;
|
|
particle[i].eq_dist = EQUILIBRIUM_DIST_B;
|
|
particle[i].spin_range = SPIN_RANGE_B;
|
|
particle[i].spin_freq = SPIN_INTER_FREQUENCY_B;
|
|
particle[i].mass_inv = 1.0/PARTICLE_MASS_B;
|
|
particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT_B;
|
|
}
|
|
|
|
particle[i].vx = V_INITIAL*gaussian();
|
|
particle[i].vy = V_INITIAL*gaussian();
|
|
particle[i].energy = (particle[i].vx*particle[i].vx + particle[i].vy*particle[i].vy)*particle[i].mass_inv;
|
|
|
|
px[i] = particle[i].vx;
|
|
py[i] = particle[i].vy;
|
|
|
|
if (ROTATION)
|
|
{
|
|
particle[i].angle = DPI*(double)rand()/RAND_MAX;
|
|
particle[i].omega = OMEGA_INITIAL*gaussian();
|
|
if (COUPLE_ANGLE_TO_THERMOSTAT)
|
|
particle[i].energy += particle[i].omega*particle[i].omega*particle[i].inertia_moment_inv;
|
|
}
|
|
else
|
|
{
|
|
particle[i].angle = 0.0;
|
|
particle[i].omega = 0.0;
|
|
}
|
|
pangle[i] = particle[i].omega;
|
|
}
|
|
/* initialize dummy values in case particles are added */
|
|
for (i=ncircles; i < NMAXCIRCLES; i++)
|
|
{
|
|
particle[i].type = 0;
|
|
particle[i].active = 0;
|
|
particle[i].neighb = 0;
|
|
particle[i].thermostat = 0;
|
|
particle[i].energy = 0.0;
|
|
particle[i].mass_inv = 1.0/PARTICLE_MASS;
|
|
particle[i].inertia_moment_inv = 1.0/PARTICLE_INERTIA_MOMENT;
|
|
particle[i].vx = 0.0;
|
|
particle[i].vy = 0.0;
|
|
px[i] = 0.0;
|
|
py[i] = 0.0;
|
|
particle[i].angle = DPI*(double)rand()/RAND_MAX;
|
|
particle[i].omega = 0.0;
|
|
pangle[i] = 0.0;
|
|
particle[i].interaction = INTERACTION;
|
|
particle[i].eq_dist = EQUILIBRIUM_DIST;
|
|
particle[i].spin_range = SPIN_RANGE;
|
|
particle[i].spin_freq = SPIN_INTER_FREQUENCY;
|
|
}
|
|
|
|
/* add particles at the bottom as seed */
|
|
if (PART_AT_BOTTOM) for (i=0; i<=NPART_BOTTOM; i++)
|
|
{
|
|
x = XMIN + (double)i*(XMAX - XMIN)/(double)NPART_BOTTOM;
|
|
y = YMIN + 2.0*MU;
|
|
add_particle(x, y, 0.0, 0.0, MASS_PART_BOTTOM, 0, particle);
|
|
}
|
|
if (PART_AT_BOTTOM) for (i=0; i<=NPART_BOTTOM; i++)
|
|
{
|
|
x = XMIN + (double)i*(XMAX - XMIN)/(double)NPART_BOTTOM;
|
|
y = YMIN + 4.0*MU;
|
|
add_particle(x, y, 0.0, 0.0, MASS_PART_BOTTOM, 0, particle);
|
|
}
|
|
|
|
/* inactivate particles in obstacle */
|
|
if ((BOUNDARY_COND == BC_CIRCLE)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE))
|
|
for (i=0; i< ncircles; i++)
|
|
if ((module2(particle[i].xc - OBSTACLE_XMIN, particle[i].yc) < 1.2*OBSTACLE_RADIUS))
|
|
particle[i].active = 0;
|
|
|
|
/* count number of active particles */
|
|
for (i=0; i< ncircles; i++) nactive += particle[i].active;
|
|
|
|
xi = 0.0;
|
|
|
|
for (i=0; i<ncircles; i++)
|
|
{
|
|
printf("Particle %i at (%.3f, %.3f) of energy %.3f\n", i, particle[i].xc, particle[i].yc, particle[i].energy);
|
|
}
|
|
sleep(1);
|
|
|
|
b = 0.25*SIGMA*SIGMA*DT_PARTICLE/MU_XI;
|
|
|
|
update_hashgrid(particle, hashgrid_number, hashgrid_particles);
|
|
|
|
blank();
|
|
// glColor3f(0.0, 0.0, 0.0);
|
|
|
|
glutSwapBuffers();
|
|
|
|
sleep(SLEEP1);
|
|
|
|
for (i=0; i<=INITIAL_TIME + NSTEPS; i++)
|
|
{
|
|
printf("Computing frame %d\n",i);
|
|
|
|
if (INCREASE_KREPEL) krepel = repel_schedule(i);
|
|
if (INCREASE_BETA) beta = temperature_schedule(i);
|
|
if (DECREASE_CONTAINER_SIZE)
|
|
{
|
|
xmincontainer = container_size_schedule(i);
|
|
if (SYMMETRIC_DECREASE) xmaxcontainer = -container_size_schedule(i);
|
|
}
|
|
if (MOVE_OBSTACLE)
|
|
{
|
|
xmincontainer = obstacle_schedule(i);
|
|
xshift = xmincontainer;
|
|
while (xshift > XMAX) xshift -= XMAX - XMIN;
|
|
}
|
|
|
|
blank();
|
|
|
|
fboundary = 0.0;
|
|
|
|
for(n=0; n<NVID; n++)
|
|
{
|
|
/* compute forces on particles */
|
|
for (j=0; j<ncircles; j++) if (particle[j].active)
|
|
{
|
|
particle[j].neighb = 0;
|
|
|
|
/* compute repelling force from other particles */
|
|
/* determine neighboring grid points */
|
|
i0 = particle[j].hashx;
|
|
iminus = i0 - 1;
|
|
iplus = i0 + 1;
|
|
|
|
j0 = particle[j].hashy;
|
|
jminus = j0 - 1;
|
|
jplus = j0 + 1;
|
|
|
|
if ((BOUNDARY_COND != BC_PERIODIC)&&(BOUNDARY_COND != BC_PERIODIC_CIRCLE))
|
|
{
|
|
if (iminus < 0) iminus = 0;
|
|
else if (iplus >= HASHX) iplus = HASHX-1;
|
|
|
|
if (jminus < 0) jminus = 0;
|
|
else if (jplus >= HASHY) jplus = HASHY-1;
|
|
}
|
|
|
|
|
|
fx = 0.0;
|
|
fy = 0.0;
|
|
torque = 0.0;
|
|
for (p=iminus; p<= iplus; p++)
|
|
for (q=jminus; q<= jplus; q++)
|
|
{
|
|
p1 = p;
|
|
q1 = q;
|
|
wrapx = 0;
|
|
wrapy = 0;
|
|
/* case of periodic boundary conditions */
|
|
if ((BOUNDARY_COND == BC_PERIODIC)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE))
|
|
{
|
|
if (p1 < 0)
|
|
{
|
|
p1 = HASHX-1;
|
|
wrapx = -1;
|
|
}
|
|
else if (p1 >= HASHX)
|
|
{
|
|
p1 = 0;
|
|
wrapx = 1;
|
|
}
|
|
if (q1 < 0)
|
|
{
|
|
q1 = HASHY-1;
|
|
wrapy = -1;
|
|
}
|
|
else if (q1 >= HASHY)
|
|
{
|
|
q1 = 0;
|
|
wrapy = 1;
|
|
}
|
|
}
|
|
for (k=0; k<hashgrid_number[p1*HASHY+q1]; k++)
|
|
{
|
|
m = p1*HASHY*HASHMAX + q1*HASHMAX + k;
|
|
if ((hashgrid_particles[m]!=j)&&(particle[hashgrid_particles[m]].active))
|
|
{
|
|
close = compute_repelling_force(j, hashgrid_particles[m], force, &torque_ij, particle, krepel, wrapx, wrapy);
|
|
if (SYMMETRIZE_FORCE)
|
|
{
|
|
fx += 0.5*force[0];
|
|
fy += 0.5*force[1];
|
|
close = compute_repelling_force(j, hashgrid_particles[m], force, &torque_ij, particle, krepel, wrapx, wrapy);
|
|
fx += 0.5*force[0];
|
|
fy += 0.5*force[1];
|
|
}
|
|
else
|
|
{
|
|
fx += force[0];
|
|
fy += force[1];
|
|
}
|
|
torque += torque_ij;
|
|
if ((close)&&(particle[j].neighb < MAXNEIGH))
|
|
{
|
|
particle[j].neighbours[particle[j].neighb] = hashgrid_particles[m];
|
|
x = particle[hashgrid_particles[m]].xc;
|
|
y = particle[hashgrid_particles[m]].yc;
|
|
particle[j].nghangle[particle[j].neighb] = argument(x - particle[j].xc, y - particle[j].yc);
|
|
}
|
|
if (close) particle[j].neighb++;
|
|
}
|
|
}
|
|
}
|
|
/* take care of boundary conditions */
|
|
fboundary += compute_boundary_force(particle[j], &fx, &fy, xmincontainer, xmaxcontainer);
|
|
|
|
/* add gravity */
|
|
fy -= GRAVITY;
|
|
|
|
if (FLOOR_FORCE)
|
|
{
|
|
if (fx > FMAX) fx = FMAX;
|
|
if (fx < -FMAX) fx = -FMAX;
|
|
if (fy > FMAX) fy = FMAX;
|
|
if (fy < -FMAX) fy = -FMAX;
|
|
}
|
|
|
|
particle[j].fx = fx;
|
|
particle[j].fy = fy;
|
|
particle[j].torque = torque;
|
|
|
|
// if (j%500 == 1) printf("Particle %i angle = %.5lg omega = %.5lg torque = %.5lg\n", j, particle[j].angle, particle[j].omega, particle[j].torque);
|
|
// if (j%500 == 1) printf("Particle %i x = %.5lg vx = %.5lg fx = %.5lg\n", j, particle[j].xc, particle[j].vx, particle[j].fx);
|
|
}
|
|
|
|
/* timestep of thermostat algorithm */
|
|
for (j=0; j<ncircles; j++) if (particle[j].active)
|
|
{
|
|
particle[j].vx = px[j] + 0.5*DT_PARTICLE*particle[j].fx;
|
|
particle[j].vy = py[j] + 0.5*DT_PARTICLE*particle[j].fy;
|
|
particle[j].omega = pangle[j] + 0.5*DT_PARTICLE*particle[j].torque;
|
|
|
|
px[j] = particle[j].vx + 0.5*DT_PARTICLE*particle[j].fx;
|
|
py[j] = particle[j].vy + 0.5*DT_PARTICLE*particle[j].fy;
|
|
pangle[j] = particle[j].omega + 0.5*DT_PARTICLE*particle[j].torque;
|
|
|
|
particle[j].energy = (px[j]*px[j] + py[j]*py[j])*particle[j].mass_inv;
|
|
if ((COUPLE_ANGLE_TO_THERMOSTAT)&&(particle[j].thermostat))
|
|
particle[j].energy += pangle[j]*pangle[j]*particle[j].inertia_moment_inv;
|
|
|
|
qx[j] = particle[j].xc + 0.5*DT_PARTICLE*px[j]*particle[j].mass_inv;
|
|
qy[j] = particle[j].yc + 0.5*DT_PARTICLE*py[j]*particle[j].mass_inv;
|
|
qangle[j] = particle[j].angle + 0.5*DT_PARTICLE*pangle[j]*particle[j].inertia_moment_inv;
|
|
|
|
if ((THERMOSTAT)&&(particle[j].thermostat))
|
|
{
|
|
px[j] *= exp(- 0.5*DT_PARTICLE*xi);
|
|
py[j] *= exp(- 0.5*DT_PARTICLE*xi);
|
|
}
|
|
if ((COUPLE_ANGLE_TO_THERMOSTAT)&&(particle[j].thermostat))
|
|
pangle[j] *= exp(- 0.5*DT_PARTICLE*xi);
|
|
}
|
|
|
|
/* compute kinetic energy */
|
|
totalenergy = 0.0;
|
|
for (j=0; j<ncircles; j++)
|
|
if ((particle[j].active)&&(particle[j].thermostat))
|
|
totalenergy += particle[j].energy;
|
|
totalenergy *= DIMENSION_FACTOR; /* normalize energy to take number of degrees of freedom into account */
|
|
if (THERMOSTAT)
|
|
{
|
|
a = DT_PARTICLE*(totalenergy - (double)nactive/beta)/MU_XI;
|
|
a += SIGMA*sqrt(DT_PARTICLE)*gaussian();
|
|
xi = (xi + a - b*xi)/(1.0 + b);
|
|
}
|
|
|
|
for (j=0; j<ncircles; j++) if (particle[j].active)
|
|
{
|
|
if ((THERMOSTAT)&&(particle[j].thermostat))
|
|
{
|
|
px[j] *= exp(- 0.5*DT_PARTICLE*xi);
|
|
py[j] *= exp(- 0.5*DT_PARTICLE*xi);
|
|
}
|
|
else
|
|
{
|
|
px[j] *= exp(- DT_PARTICLE*DAMPING);
|
|
py[j] *= exp(- DT_PARTICLE*DAMPING);
|
|
}
|
|
if ((COUPLE_ANGLE_TO_THERMOSTAT)&&(particle[j].thermostat))
|
|
pangle[j] *= exp(- 0.5*DT_PARTICLE*xi);
|
|
|
|
particle[j].xc = qx[j] + 0.5*DT_PARTICLE*px[j]*particle[j].mass_inv;
|
|
particle[j].yc = qy[j] + 0.5*DT_PARTICLE*py[j]*particle[j].mass_inv;
|
|
particle[j].angle = qangle[j] + 0.5*DT_PARTICLE*pangle[j]*particle[j].inertia_moment_inv;
|
|
}
|
|
|
|
}
|
|
|
|
/* reset angular values to [0, 2 Pi) */
|
|
if (ROTATION) for (j=0; j<ncircles; j++)
|
|
{
|
|
while (particle[j].angle > DPI) particle[j].angle -= DPI;
|
|
while (particle[j].angle < 0.0) particle[j].angle += DPI;
|
|
}
|
|
|
|
/* case of periodic boundary conditions */
|
|
if ((BOUNDARY_COND == BC_PERIODIC)||(BOUNDARY_COND == BC_PERIODIC_CIRCLE)) for (j=0; j<ncircles; j++)
|
|
if (particle[j].active)
|
|
{
|
|
if (particle[j].xc > XMAX) particle[j].xc += XMIN - XMAX;
|
|
else if (particle[j].xc < XMIN) particle[j].xc += XMAX - XMIN;
|
|
if (particle[j].yc > YMAX) particle[j].yc += YMIN - YMAX;
|
|
else if (particle[j].yc < YMIN) particle[j].yc += YMAX - YMIN;
|
|
}
|
|
|
|
printf("Mean kinetic energy: %.3f\n", totalenergy/(double)ncircles);
|
|
printf("Boundary force: %.3f\n", fboundary/(double)(ncircles*NVID));
|
|
|
|
total_neighbours = 0;
|
|
min_nb = 100;
|
|
max_nb = 0;
|
|
for (j=0; j<ncircles; j++) if (particle[j].active)
|
|
{
|
|
total_neighbours += particle[j].neighb;
|
|
if (particle[j].neighb > max_nb) max_nb = particle[j].neighb;
|
|
if (particle[j].neighb < min_nb) min_nb = particle[j].neighb;
|
|
}
|
|
printf("Mean number of neighbours: %.3f\n", (double)total_neighbours/(double)ncircles);
|
|
printf("Min number of neighbours: %i\n", min_nb);
|
|
printf("Max number of neighbours: %i\n", max_nb);
|
|
|
|
draw_particles(particle, PLOT);
|
|
draw_container(xmincontainer, xmaxcontainer);
|
|
|
|
/* add a particle */
|
|
if ((ADD_PARTICLES)&&((i - INITIAL_TIME - ADD_TIME + 1)%ADD_PERIOD == 0))
|
|
{
|
|
// add_particle(XMIN + 0.1, 0.0, 50.0, 0.0, 3.0, 0, particle);
|
|
// px[ncircles - 1] = particle[ncircles - 1].vx;
|
|
// py[ncircles - 1] = particle[ncircles - 1].vy;
|
|
// particle[ncircles - 1].radius = 1.5*MU;
|
|
// j = 0;
|
|
// while (module2(particle[j].xc,particle[j].yc) > 0.7) j = rand()%ncircles;
|
|
// x = particle[j].xc + 2.5*MU;
|
|
// y = particle[j].yc;
|
|
|
|
// x = XMIN + (XMAX - XMIN)*rand()/RAND_MAX;
|
|
// y = YMAX + 0.01*rand()/RAND_MAX;
|
|
// add_particle(x, y, 0.0, 0.0, 1.0, 0, particle);
|
|
|
|
x = XMIN + 0.25*(XMAX - XMIN);
|
|
y = YMAX + 0.01;
|
|
prop = 1.0 - (double)nadd_particle/5.0;
|
|
vx = 100.0*prop;
|
|
add_particle(x, y, vx, -10.0, 5.0*prop, 0, particle);
|
|
particle[ncircles - 1].radius = 10.0*MU*prop;
|
|
particle[ncircles - 1].eq_dist = 2.0;
|
|
particle[ncircles - 1].thermostat = 0;
|
|
px[ncircles - 1] = particle[ncircles - 1].vx;
|
|
py[ncircles - 1] = particle[ncircles - 1].vy;
|
|
nadd_particle++;
|
|
}
|
|
|
|
update_hashgrid(particle, hashgrid_number, hashgrid_particles);
|
|
|
|
print_parameters(beta, totalenergy/(double)ncircles, krepel, xmaxcontainer - xmincontainer,
|
|
fboundary/(double)(ncircles*NVID), 0);
|
|
|
|
glutSwapBuffers();
|
|
|
|
|
|
if (MOVIE)
|
|
{
|
|
if (i >= INITIAL_TIME) save_frame_lj();
|
|
else printf("Initial phase time %i of %i\n", i, INITIAL_TIME);
|
|
|
|
if ((i >= INITIAL_TIME)&&(DOUBLE_MOVIE))
|
|
{
|
|
draw_particles(particle, PLOT_B);
|
|
draw_container(xmincontainer, xmaxcontainer);
|
|
print_parameters(beta, totalenergy/(double)ncircles, krepel, xmaxcontainer - xmincontainer,
|
|
fboundary/(double)(ncircles*NVID), 0);
|
|
glutSwapBuffers();
|
|
save_frame_lj_counter(NSTEPS + 21 + counter);
|
|
counter++;
|
|
}
|
|
|
|
/* it seems that saving too many files too fast can cause trouble with the file system */
|
|
/* so this is to make a pause from time to time - parameter PAUSE may need adjusting */
|
|
if (i % PAUSE == PAUSE - 1)
|
|
{
|
|
printf("Making a short pause\n");
|
|
sleep(PSLEEP);
|
|
s = system("mv lj*.tif tif_ljones/");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (MOVIE)
|
|
{
|
|
if (DOUBLE_MOVIE)
|
|
{
|
|
draw_particles(particle, PLOT);
|
|
draw_container(xmincontainer, xmaxcontainer);
|
|
print_parameters(beta, totalenergy/(double)ncircles, krepel, xmaxcontainer - xmincontainer,
|
|
fboundary/(double)(ncircles*NVID), 0);
|
|
glutSwapBuffers();
|
|
}
|
|
for (i=0; i<MID_FRAMES; i++) save_frame_lj();
|
|
if (DOUBLE_MOVIE)
|
|
{
|
|
draw_particles(particle, PLOT_B);
|
|
draw_container(xmincontainer, xmaxcontainer);
|
|
print_parameters(beta, totalenergy/(double)ncircles, krepel, xmaxcontainer - xmincontainer,
|
|
fboundary/(double)(ncircles*NVID), 0);
|
|
glutSwapBuffers();
|
|
}
|
|
for (i=0; i<END_FRAMES; i++) save_frame_lj_counter(NSTEPS + MID_FRAMES + 1 + counter + i);
|
|
|
|
s = system("mv lj*.tif tif_ljones/");
|
|
}
|
|
free(particle);
|
|
free(hashgrid);
|
|
free(hashgrid_number);
|
|
free(hashgrid_particles);
|
|
free(qx);
|
|
free(qy);
|
|
free(px);
|
|
free(py);
|
|
free(qangle);
|
|
free(pangle);
|
|
}
|
|
|
|
|
|
void display(void)
|
|
{
|
|
glPushMatrix();
|
|
|
|
blank();
|
|
glutSwapBuffers();
|
|
blank();
|
|
glutSwapBuffers();
|
|
|
|
animation();
|
|
sleep(SLEEP2);
|
|
|
|
glPopMatrix();
|
|
|
|
glutDestroyWindow(glutGetWindow());
|
|
|
|
}
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
glutInit(&argc, argv);
|
|
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
|
|
glutInitWindowSize(WINWIDTH,WINHEIGHT);
|
|
glutCreateWindow("Particles with Lennard-Jones interaction in a planar domain");
|
|
|
|
init();
|
|
|
|
glutDisplayFunc(display);
|
|
|
|
glutMainLoop();
|
|
|
|
return 0;
|
|
}
|
|
|