/*********************************************************************************/ /* */ /* 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 #include #include #include #include #include #include /* Sam Leffler's libtiff library. */ #include #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 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= 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 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 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 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 XMAX) xshift -= XMAX - XMIN; } blank(); fboundary = 0.0; for(n=0; n= 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 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 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 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 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